This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| pt:avr:timers [2015/12/01 14:42] – artica | pt:avr:timers [2020/07/20 12:00] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== Contadores/ | ||
| + | Os contadores, que em certo sentido, também podem ser chamados temporizadores, | ||
| + | |||
| + | ===== Modo padrão de um Contador ===== | ||
| + | |||
| + | No modo padrão, um contador não faz nada mais do que contar continuamente números sequenciais. O seu valor pode, claro, ser lido e alterado a partir do programa a qualquer momento. A única função adicional no modo padrão é causar uma interrupção no overflow do contador. O modo padrão é normalmente usado para executar uma seção do programa a determinados intervalos. | ||
| + | |||
| + | <box 100% round # | ||
| + | |||
| + | Task: Make an 8 MHz ATmega128 fire an interrupt every 10 ms (frequency 100 Hz). For this task, the 8-bit counter 0 is suitable. | ||
| + | |||
| + | <code c> | ||
| + | #include < | ||
| + | |||
| + | ISR(TIMER0_OVF_vect) | ||
| + | { | ||
| + | // Give the counter such a value | ||
| + | // that the next overflow occurs in 10 ms. | ||
| + | // Formula: 256 - 8 MHz / 1024 / 100 Hz = 177,785 = ~178 | ||
| + | TCNT0 = 178; | ||
| + | } | ||
| + | |||
| + | int main() | ||
| + | { | ||
| + | // To make the first overflow interrupt fire in 10 ms as well, | ||
| + | // the counter needs to be initialized here. | ||
| + | TCNT0 = 178; | ||
| + | |||
| + | // Prescaler value 1024 | ||
| + | TCCR0 = 0x07; | ||
| + | |||
| + | // Allow overflow interrupts | ||
| + | TIMSK |= (1 << TOIE0); | ||
| + | |||
| + | // Allow interrupts globally | ||
| + | sei(); | ||
| + | |||
| + | // Endless loop | ||
| + | while (1) continue; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | O contador neste exemplo não gerará a interrupção a exactamente cada 10 ms. Para fazer isso seria necessário dar ao contador um valor decimal, e isto não é possível. Para conseguir um intervalo preciso entre as interrupções, | ||
| + | |||
| + | </ | ||
| + | |||
| + | ==== Contador de Relógio Externo ==== | ||
| + | |||
| + | É também possível a utilização de uma fonte de relógio externo como sinal de contador de relógio. Um AVR tem um pino chamado Tn para esta finalidade, sendo n o número associado ao contador. O sinal de relógio externo e a polaridade podem ser selecionados usando o registro prescaler. | ||
| + | |||
| + | ==== Eventos Temporizados ==== | ||
| + | |||
| + | Uma vez que os contadores permitem operações de temporização, | ||
| + | |||
| + | <box 100% round # | ||
| + | |||
| + | Task: Measure the frequency of an external 122 Hz - 100 kHz logical square signal using an 8 MHz ATmega128. The measurement has to be at 1 Hz precision. The program uses a 16-bit counter with 1 input capture unit. | ||
| + | |||
| + | <code c> | ||
| + | #include < | ||
| + | |||
| + | unsigned long frequency; | ||
| + | |||
| + | // Interrupt for the event | ||
| + | ISR(TIMER1_CAPT_vect) | ||
| + | { | ||
| + | // Counter to 0 | ||
| + | TCNT1 = 0; | ||
| + | |||
| + | // The result is valid only if the counter | ||
| + | // has not overflowed yet | ||
| + | if (!(TIFR & (1 << TOV1))) | ||
| + | { | ||
| + | // Calculating the frequency from the period | ||
| + | frequency = (unsigned long)8000000 / | ||
| + | (unsigned long)ICR1; | ||
| + | } | ||
| + | else | ||
| + | { | ||
| + | // Frequency is less than 122 Hz | ||
| + | frequency = 0; | ||
| + | |||
| + | // Set the counter' | ||
| + | TIFR &= ~(1 << TOV1); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | int main() | ||
| + | { | ||
| + | // Register a rising front, prescaler value 1 | ||
| + | TCCR1B = (1 << ICES1) | (1 << CS10); | ||
| + | |||
| + | // Allow event interrupts | ||
| + | TIMSK = (1 << TICIE1); | ||
| + | |||
| + | // Allow interrupts globally | ||
| + | sei(); | ||
| + | |||
| + | // Endless loop | ||
| + | while (1) continue; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | O programa dispara uma interrupção de cada vez que uma frente ascendente ocorre no sinal externo. Durante a interrupção, | ||
| + | |||
| + | </ | ||
| + | |||
| + | Apanhar eventos e registar o tempo que levou para que eles ocorram também pode ser resolvido no nível do software. É possível utilizar interrupções externas ou outras e ler o valor do contador durante estes eventos. O apanhar de eventos ao nível de hardware deverá ser executado de forma independente do programa principal e em eventos de tempo relativamente curtos (ou frequentes). | ||
| + | |||
| + | ===== Geração de Sinal ===== | ||
| + | |||
| + | Os contadores mais complexos podem gerar um sinal, além de cronometrarem a duração dos mesmos. Para este efeito, o contador tem uma unidade de comparação à saída e uma unidade de comparação de correspondência. A unidade de saída de comparação tem registros com a mesma largura de bits que o contador e os valores desses registos são comparados com o valor do contador enquanto ele estiver em execução. Uma interrupção pode ser gerada e os valores dos pinos especiais podem ser alterados de cada vez que o valor do contador for igual ao valor no registo na unidade de comparação. Neste momento, um pino pode ser definido como alto, baixo ou invertido. O sinal é gerado por mudanças no valor do pino de saída. | ||
| + | |||
| + | Em alguns modos de geração de sinal, o valor máximo do contador pode ser alterado. O tamanho físico do contador permanecerá o mesmo, mas um registo de comparação é usado para repor o contador numa contagem específica. Os exemplos anteriores também podem ser resolvidos usando este método, mas a função serve principalmente para mudar o período do sinal. Além disso, um contador pode ser configurado de modo a funcionar com incrementação ou decrementação. | ||
| + | |||
| + | Os contadores e os modos de geração de sinal que os usam são um dos módulos periféricos mais complexos num AVR. Escrever sobre todos eles aqui vai além do âmbito deste texto, e, normalmente, | ||
| + | |||
| + | ==== Pulse Width Modulation ==== | ||
| + | |||
| + | A modulação de largura de impulso (PWM) é um tipo de sinal, em que a frequência e período são (tipicamente) ambos constantes, mas o comprimento do meio período varia. Os sinais PWM são usados para controlar dispositivos electro-mecânicos, | ||
| + | |||
| + | <box 100% round # | ||
| + | |||
| + | Task: Using an 8MHz ATmega128, generate two speed regulating servo motor signals. Use pin PB5 (OC1A) to generate a pulse width of 1 ms and pin PB6 (OC1B) to generate pulse width of 2 ms. | ||
| + | |||
| + | <code c> | ||
| + | #include < | ||
| + | |||
| + | int main() | ||
| + | { | ||
| + | // Set pins as outputs | ||
| + | DDRB |= (1 << PIN5) | (1 << PIN6); | ||
| + | |||
| + | // Set outputs A and B low for comparison, | ||
| + | // "Fast PWM" mode, prescaler value 8 | ||
| + | TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11); | ||
| + | TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11); | ||
| + | |||
| + | // Maximum value of the counter. Formula: | ||
| + | // TOP = 8 MHz / 8 / 50 Hz | ||
| + | ICR1 = 20000; | ||
| + | |||
| + | // Half-period of the first motor is 1 ms, and second 2 ms | ||
| + | OCR1A = 1000; | ||
| + | OCR1B = 2000; | ||
| + | |||
| + | // Endless loop | ||
| + | while (1) continue; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | </ | ||