This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
| pt:examples:timer:delay [2015/12/11 18:46] – Criação deste novo documento. artica | pt:examples:timer:delay [2020/07/20 12:00] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | < | ||
| + | ====== Espera ====== | ||
| + | |||
| + | // | ||
| + | [HW] [[pt: | ||
| + | [AVR] [[pt: | ||
| + | [LIB] [[pt: | ||
| + | |||
| + | ===== Teoria ===== | ||
| + | |||
| + | Muitas vezes existe a necessidade de criar programas de espera em microcontroladores, | ||
| + | |||
| + | Se o processador do microcontrolador calcula usando números na forma binária é tão grande como seu barramento interno (o AVR tem 8 bits), então é preciso um disparo do processador para executar uma operação aritmética, | ||
| + | |||
| + | Ao programar em linguagens avançadas (C, por exemplo), os programas não são escritos diretamente na base do comando, a fim de criar uma espera de software, é preciso conhecer também o seu compilador que converte o programa para código de máquina. A partir deste, depende de quantas instruções (e fases portanto) que leva para realizar uma operação aritmética. A complexidade é adicionada pela capacidade dos compiladores de converter o programa em código de máquina de várias formas - por exemplo, fazendo com que o código de máquina, poupe o mais possível a memória ou seja facilmente executável. Estas acções do compilador são as chamadas otimizações. Com diferentes modos de otimização o softaware de espera e as suas durações podem ser diferentes do esperado. | ||
| + | |||
| + | |||
| + | ===== Prática ====== | ||
| + | |||
| + | O que se segue é um exemplo de software para gerar atraso com o microcontrolador AVR. Uma parte de um programa em linguagem C é escrito, e conta a variável x num ciclo de 0 a 100. Dentro de cada ciclo de uma instrução vazia explícita é concluída. Ela é necessária, | ||
| + | |||
| + | <code c> | ||
| + | unsigned char x; | ||
| + | |||
| + | // Cycle until x is 100 | ||
| + | for (x = 0; x < 100; x++) | ||
| + | { | ||
| + | // With empty instruction nop | ||
| + | asm volatile (" | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Esta é a mesma parte do programa depois de compilar. Os 2 números hexadecimais à esquerda são código de máquina e à direita está o comando com operando(s) em linguagem assembler. O código de máquina e a linguagem assembler são conformais; o assembler é apenas para apresentar o código de máquina de uma forma legível para os seres humanos .Na compilação, | ||
| + | |||
| + | <code asm> | ||
| + | 80 e0 | ||
| + | 00 00 | ||
| + | 8f 5f subi r24, 0xFF ; subtracting 255 form the r24 index, that means adding +1 | ||
| + | 84 36 | ||
| + | e1 f7 brne .-8 ; If the comparison was wrong, then transfer 8-baits back | ||
| + | </ | ||
| + | |||
| + | Na forma compilada pode ser visto o que realmente acontece com o ciclo na linguagem C e este pode ser usado para calcular o número de ciclos de relógio são necessários para completar um ciclo de um período. A informação sobre o efeito das instruções e tempo de operação pode ser encontrada a partir das instruções da datasheet do AVR. No exemplo dado, são necessários 4 ciclos de relógio para completar 4 instruções num período de ciclo, porque todas as instruções exigem uma taxa de relógio. Além disso, uma taxa de relógio é usado antes do ciclo para a instrução de carregamento e depois uma taxa de relógio extra para sair do ciclo. Assumindo que a taxa de relógio em funcionamento do controlador é 14,7456 MHz, todo a espera produzida pelo programa pode ser calculada. | ||
| + | |||
| + | (1 + 100 ⋅ 4 + 1) / 14745600 = 27,26 μs | ||
| + | |||
| + | A espera produzida no exemplo, é em microssegundos e a variável usada é de 8 bits pelo que o código de máquina é bastante simples. Para produzirmos uma espera na ordem dos milissegundos, | ||
| + | |||
| + | O objetivo deste exercício não é a criação de uma espera de software precisa ao nível de código de máquina, porque isto constitui um trabalho muito preciso e temos já as funções necessárias para produzir esperas na avr-libc e na biblioteca da HomeLab. Estes são utilizados também nos exemplos a seguir. | ||
| + | |||
| + | Ao lidar com o software de espera é importante saber, que, independentemente de sua simplicidade de base, é um método extremamente ineficiente do ponto de vista do consumo de energia. Durante todos os ciclos de relógio quando o microcontrolador está a contar o inútil, energia é consumida. Portanto, quando no uso de aplicações que operam com baterias, não é sábio escrever software com esperas muito grandes. Mais sábio é usar temporizadores de hardware, que trabalham de forma independente e despertam o processador da hibernação quando chega o momento de continuar o trabalho. | ||
| + | |||
| + | As esperas por software não são o único método para a criação de intervalos. O mesmo pode ser feito usando timer. Timer é um hardware que conta a uma certa frequência. A freqüência de relógio do timer pode ser gerada a partir da frequência do microcontrolador ou de algum outro tipo de ritmo proveniente de fora. Em geral, a frequência de relógio pode ser dividida com um multiplicador de frequência para alcançar uma menor - isto é feito com um prescaler. Facto importante é que o valor do temporizador de freqüência de relógio fixo é linearmente proporcional ao tempo. O tempo pode ser calculado multiplicando o período da frequência de relógio do temporizador pelo valor do temporizador. | ||
| + | |||
| + | [{{ : | ||
| + | |||
| + | Os contadores AVR podem informar sobre o overflow do contador ou quando ocorre um match. O overflow ocorre quando o contador tem o valor máximo possível e o ciclo começa tudo de novo a partir de 0. O alcançar de um valor pré-definido durante o momento de crescimento do valor do contador é comparado com o valor fornecido pelo utilizador. Na ocorrência do evento, os bits nos índices de status da AVR são automaticamente definidos como high. | ||
| + | |||
| + | Para gerar uma espera utilizando um temporizador, | ||
| + | |||
| + | O código seguinte de um programa é acerca da função de espera de software // | ||
| + | |||
| + | <code c> | ||
| + | // Software delay in milliseconds | ||
| + | void sw_delay_ms(unsigned short count) | ||
| + | { | ||
| + | // Counting the variable of the delay to 0 | ||
| + | while (count-- > 0) | ||
| + | { | ||
| + | // 1ms delay with a special function | ||
| + | _delay_ms(1); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | O programa seguinte usa a função dada, criando duas esperas no loop infinito: 100 ms e 900 ms. Durante a espera mais curta o LED é iluminado e durante a mais longa este é desligado. Resultado: o LED pisca periodicamente. | ||
| + | |||
| + | <code c> | ||
| + | // The demonstration program of the software delay of the HomeLab | ||
| + | // The program is blinking a LED for a moment after ~1 second | ||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | // Main program | ||
| + | int main(void) | ||
| + | { | ||
| + | // Setting the pin of the LED as output | ||
| + | pin_setup_output(led_debug); | ||
| + | |||
| + | // Endless loop | ||
| + | while (true) | ||
| + | { | ||
| + | // Lighting the LED | ||
| + | pin_clear(led_debug); | ||
| + | |||
| + | // Software delay for 100 ms | ||
| + | sw_delay_ms(100); | ||
| + | |||
| + | // Switching off the LED | ||
| + | pin_set(led_debug); | ||
| + | |||
| + | // Software delay for 900 milliseconds | ||
| + | sw_delay_ms(900); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Embora pareça que o indicador pisca a cada segundo, o tempo é na verdade um pouco mais, porque as chamadas das funções de espera | ||
| + | |||
| + | O código que se segue é uma função de espera com base num temporizador um pouco simplificado. O princípio de contagem é o mesmo da função de espera de software - uma quantidade desejada de esperas de 1 ms são produzidas. O atraso é produzido com um contador 0 de 8 bits. É calculado anteriormente que à frequência do relógio de 14,7456 MHz, o sinal de sincronismo tem de ser dividido, pelo menos, 64 vezes, de modo a que o contador não chegue ao overflow em 1 ms. O valor que o contador deve ter para que o overflow ocorra após 1 ms é apresentado sob a forma de uma expressão sendo a variável // | ||
| + | |||
| + | No ciclo ocorre a inicialização do contador e a da flag de overflow (que será 1 quando ocorre). De seguida, espera-se até que o contador atinja 256 a partir do valor inicial, ou seja, o overflow. No momento do overflow a flag de overflow passa a high e a espera de 1 ms teve já lugar. No final da função do temporizador é parado. | ||
| + | |||
| + | |||
| + | <code c> | ||
| + | // Hardware delay in milliseconds | ||
| + | void hw_delay_ms(unsigned short count) | ||
| + | { | ||
| + | // Calculating the initial value of the timer | ||
| + | register unsigned char timer_start = 256 - F_CPU / 1000 / 64; | ||
| + | |||
| + | // Starting the timer | ||
| + | timer0_init_normal(TIMER0_PRESCALE_64); | ||
| + | |||
| + | // Counting the variable of the delay to the 0 | ||
| + | while (count-- > 0) | ||
| + | { | ||
| + | // Initializing the timer | ||
| + | timer0_set_value(timer_start); | ||
| + | |||
| + | // Zeroing the overflow flag | ||
| + | timer0_overflow_flag_clear(); | ||
| + | |||
| + | // Waiting for overflow | ||
| + | while (!timer0_overflow_flag_is_set()) | ||
| + | { | ||
| + | asm volatile (" | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Zeroing the overflow flag | ||
| + | timer0_overflow_flag_clear(); | ||
| + | |||
| + | // Stoping the timer | ||
| + | timer0_stop(); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | A função de espera referenciada usa uma biblioteca de temporizador cujo código-fonte para o controlador ATmega é parecida com o seguinte: | ||
| + | |||
| + | <code c> | ||
| + | // Timer 0 prescaler selection type | ||
| + | typedef enum | ||
| + | { | ||
| + | TIMER0_NO_PRESCALE | ||
| + | TIMER0_PRESCALE_8 | ||
| + | TIMER0_PRESCALE_32 | ||
| + | TIMER0_PRESCALE_64 | ||
| + | TIMER0_PRESCALE_128 | ||
| + | TIMER0_PRESCALE_256 | ||
| + | TIMER0_PRESCALE_1024 | ||
| + | } | ||
| + | timer0_prescale; | ||
| + | |||
| + | // Setting Timer 0 to a normal mode | ||
| + | inline void timer0_init_normal(timer0_prescale prescale) | ||
| + | { | ||
| + | TCCR0 = prescale & 0x07; | ||
| + | } | ||
| + | |||
| + | // Stopping the Taimer 0 | ||
| + | inline void timer0_stop() | ||
| + | { | ||
| + | TCCR0 = 0x00; | ||
| + | } | ||
| + | |||
| + | // Taimer 0 value set | ||
| + | inline void timer0_set_value(unsigned char value) | ||
| + | { | ||
| + | TCNT0 = value; | ||
| + | } | ||
| + | |||
| + | // Timer 0 overflow flag clear | ||
| + | inline void timer0_overflow_flag_clear(void) | ||
| + | { | ||
| + | bit_set(TIFR, | ||
| + | } | ||
| + | |||
| + | // Timer 0 overflow flag state reading | ||
| + | inline bool timer0_overflow_flag_is_set(void) | ||
| + | { | ||
| + | return (bit_is_set(TIFR, | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | O que se segue é um programa semelhante ao exemplo de espera de software. Nos meios-períodos mais curtos de 100 ms, o LED é iluminado e nos meios-períodos mais longos é desligado. Como resultado o LED pisca a cada segundo. Infelizmente, | ||
| + | |||
| + | <code c> | ||
| + | // Demonstration program of hardware delay of the HomeLab | ||
| + | // The Program blinks LED for a moment after every ~1 second | ||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | // Main program | ||
| + | int main(void) | ||
| + | { | ||
| + | // Setting the pin of the LED as output | ||
| + | pin_setup_output(led_debug); | ||
| + | |||
| + | // Endless loop | ||
| + | while (true) | ||
| + | { | ||
| + | // Lighting the LED | ||
| + | pin_clear(led_debug); | ||
| + | |||
| + | // Hardware delay for 100 milliseconds | ||
| + | hw_delay_ms(100); | ||
| + | |||
| + | // Switch off of the LED | ||
| + | pin_set(led_debug); | ||
| + | |||
| + | // Hardware delay for 900 milliseconds | ||
| + | hw_delay_ms(900); | ||
| + | } | ||
| + | } | ||
| + | </ | ||