Escolar Documentos
Profissional Documentos
Cultura Documentos
AULA 08
Prof. Rodrigo Rech
1. Timers
Prescaler Registrador de
(Divisor de contagem do Interrupção
Frequência) timer
Entrada de clock do
Frequência dividida
relógio
Para que seja possível incrementar o timer de uma forma mais lenta existe um componente
chamado “Prescaler”, ou simplesmente, divisor de frequência. Com ele é possível inserir um fator
de divisão antes de incrementar o temporizador. Este fator pode ser de 1, 8, 64, 256 ou 1024.
É possível, também, alterar o valor de início da contagem de um timer, fazendo com que o
valor final seja menor do que o valor máximo, por exemplo: é necessário que o estouro do timer de
8 bits aconteça com um tempo de 10ms, então podemos utilizar o seguinte cálculo:
𝑡𝑒𝑚𝑝𝑜𝑑𝑒𝑠𝑒𝑗𝑎𝑑𝑜 ∗𝐹𝑜𝑠𝑐 0,01∗16000000
𝑽𝒂𝒍𝒐𝒓 𝒊𝒏𝒊𝒄𝒊𝒂𝒍 𝒅𝒆 𝒄𝒐𝒏𝒕𝒂𝒈𝒆𝒎 = 256 − = 256 − = 98,75 ≅ 𝟗𝟗
𝑃𝑟𝑒𝑠𝑐𝑎𝑙𝑒𝑟 1024
2 1
No bloco “1” é feita a seleção da fonte de clock. Caso a fonte escolhida ser a “clk_I/O”, o
incremento do timer será realizado a partir de um pino do microcontrolador, transformando-o em
um contador de eventos. Caso a fonte escolhida ser o “T/C oscilator”, a fonte será o oscilador.
O bloco “2” é responsável pelo modo de funcionamento do Timer. Pelo fato de existirem várias
funções internas, como o PWM por exemplo, é necessário configurá-lo antes do uso. Para o
funcionamento normal do timer (figura 1), o modo de funcionamento é o “0”.
E o bloco “3” representa o registrador de contagem do timer, é nele que estão os 8 bits (Timers
0 e 2) ou os 16 bits (Timer 1). Sempre que o valor deste registrador ultrapassa seu limite, o evento
de overflow é gerado e ele é zerado.
Para a configuração do timer devem ser utilizados até 7 registradores: TCCRnA, TCCRnB,
OCRnA, OCRnB, TCNTn, TIMSKn e TIFRn, sendo “n” o número do timer que será utilizado.
Para este primeiro momento, para configurar o timer no modo normal, utilizaremos apenas 3 e os
demais podem ser consultados no datasheet do componente ou no livro indicado.
OBS: Os valores dos prescalers para cada timer são diferentes, portanto, consulte o datasheet
para verificar as configurações dos timers 1 e 2.
4. TIFRn (Timer/Counter n Interrupt Flag Register): Contém os flags de estouro dos timers.
Exemplo 1: Configurando o Timer 0 para um estouro no tempo máximo (16,384ms)
#include <avr/io.h>
#include <avr/interrupt.h>
int main() {
DDRD = 0b00100000; //somente pino do LED como saída
PORTD = 0b00000000; //apaga LED e habilita pull-ups nos pinos não utilizados
TCCR0B = 0b00000101; //TC0 com prescaler de 1024, a 16 MHz gera uma interrupção a cada 16,384 ms
TIMSK0 = 0b00000001; //habilita a interrupção do TC0
sei(); //habilita a chave de interrupção global
while (1) {
/*A cada estouro do Timer 0 o programa desvia para ISR(TIMER0_OVF_vect)*/
}
}
#include <avr/io.h>
#include <avr/interrupt.h>
int main() {
DDRD = 0b00100000; //somente pino do LED como saída
PORTD = 0b00000000; //apaga LED e habilita pull-ups nos pinos não utilizados
TCCR1B = 0b00000101; //TC1 com prescaler de 1024, a 16 MHz gera uma interrupção a cada 1s
TCNT1 = 49911; //Inicia a contagem em 49911 para, no final, gerar 1s
TIMSK1 = 0b00000001; //habilita a interrupção do TC1
sei(); //habilita a chave de interrupção global
while (1) {
/*A cada estouro do Timer 1 o programa desvia para ISR(TIMER1_OVF_vect)*/
}
}
Exemplo 3: Utilizando 2 timers simultaneamente. Timer 0 (inverte o pino a cada 500ms) e Timer
1 (inverte o pino a cada 1s).
#include <avr/io.h>
#include <avr/interrupt.h>
ISR(TIMER1_OVF_vect)
{
TCNT1 = 49911; //Recarrega o registrador para gerar 1s novamente
cpl_bit(PORTD, LED2); //Inverte o estado do LED
}
ISR(TIMER0_OVF_vect)
{
contador++;
TCNT0 = 100; //Recarrega o registrador para gerar 10ms novamente
if(contador == 50){
contador = 0;
cpl_bit(PORTD, LED1);
}
}
int main() {
DDRD = 0b00101000;
PORTD = 0b00000000;
while (1) {
}
}
Exemplo 4: Criando vários tempos diferentes com apenas um Timer:
#include <avr/io.h>
#include <avr/interrupt.h>
ISR(TIMER0_OVF_vect)
{
TCNT0 = 99; //Recarrega o Timer 0 para que a contagem seja 10ms novamente
millis++; //Incrementa a variável millis a cada 10ms
}
int main() {
DDRD = 0b11111111;
PORTD = 0b00000000;
while (1) {
É possível notar na figura 2 que, quanto maior o tempo ativo, isto é, ligado, maior é a
intensidade do LED. Este efeito ocorre porque a frequência de chaveamento (ligado-desligado) é
alta e o componente “enxerga” este sinal como uma tensão média sobre ele.
Para se determinar o nível médio de tensão sob a carga, é utilizado o seguinte cálculo:
Outro detalhe importante sobre o PWM é que sua frequência deve ser determinada de acordo
com a carga que será controlada. Comumente cargas resistivas e semicondutores costumam
utilizar frequências altas (na ordem de KHz), já cargas indutivas, como motores, o ideal é que se
utilize frequências baixas, pois a resposta do componente é mais lenta.
Cada um dos Timers do AVR permite a geração de 2 PWMs em pinos específicos, porém, para
isso, é necessária uma configuração dos limites máximos e mínimos da contagem dos timers, o
que exige um conhecimento um pouco maior sobre este periférico. A proposta deste capítulo é realizar
a configuração do modo normal do Timer para se obter, em qualquer pino do microcontrolador,
um sinal de PWM configurável. Para configuração dos timers no modo de PWM, consulte o
capítulo 9 do livro “AVR e Arduino: Técnicas de projetos”.
Passos para se desenvolver um PWM via software:
2- Defina a resolução desejada para este PWM, isto é, em quantas partes o ciclo ativo será
dividido. Neste exemplo, usaremos 100, assim a resolução ficará entre 0 e 100%;
3- Configure um timer para que ele gere uma interrupção respeitando o seguinte cálculo:
𝑃𝑒𝑟í𝑜𝑑𝑜𝑃𝑊𝑀 0,001
𝑃𝑒𝑟í𝑜𝑑𝑜𝑒𝑠𝑡𝑜𝑢𝑟𝑜 = = = 10𝑢𝑠
𝑟𝑒𝑠𝑜𝑙𝑢çã𝑜 100
4- Na interrupção, incremente uma variável de contagem e ative o pino desejado quando ela
atingir o valor da resolução e desligue o pino quando a contagem atingir o ciclo ativo
desejado.
#include <avr/io.h>
#include <avr/interrupt.h>
/*O modificador "volatile" permite que estas variáveis possam ser utilizadas em qualquer
momento do programa sem ser modificada pela otimização do compilador*/
volatile unsigned int Passo_PWM1 = 0;
volatile unsigned int Ciclo_Ativo_PWM1;
int main() {
DDRD = 0b11111111; //PORTD como saída
PORTD = 0b00000001; //acende apenas o LED0
TCCR0B = 0b00000001; //TC0 com prescaler de 1
TIMSK0 = 0b00000001; //habilita a interrupção do TC0
TCNT0 = 96; //Inicia a contagem em 96 para gerar a interrupção a cada 10us
sei(); //habilita as interrupções
Ciclo_Ativo_PWM1 = 5; //Determina o ciclo ativo para o PWM1 (0 - 100)
while (1) {
}
}
Exercícios:
1. Configure o Timer 1 (Exemplo 2) para que ele gere uma interrupção e inverta o estado de um
LED a cada 2 segundos;
2. Configure o PWM do exemplo 5 para que a frequência seja alterada para 500Hz e uma
resolução de 200.
Dica: Pode ser utilizada a função sprintf(buffer, “%d:%d:%d”, hora, minuto, segundo).
5. Utilizando a técnica de PWM (Com Timer 0), crie um efeito “fade” na iluminação do display
LCD. O display deverá atingir o brilho total em 5 segundos e, depois, diminuir o brilho até
o mínimo também em 5 segundos. Utilize para este controle o Timer 2 juntamente com a
sua interrupção. O pino responsável por controlar a luminosidade do display é o PC5. É
necessário, também, que mude o jumper do Backlight da posição “ON” para a posição
“PWM”.
6.Por fim, adicione um alarme fixo neste relógio. Quando as horas e os minutos atingirem um
valor pré-determinado, o LED 3 deve ser ligado. Ele será desligado após passar 1 minuto do
horário definido. Os testes condicionais para acionamento do alarme deverão ser feitos no
loop infinito do programa principal.