Você está na página 1de 10

Engenharia de Sistemas Microprocessados

Professor: Márcio Gil Faccin


Laboratório 5

Objetivos
Timers de 8 e de 16 bits.
Fontes de relógio: uso do preescaler e entrada externa.
Modos de interrupção: overflow, comparação e capture.
Gerador de forma de onda (WGM).

Orientações
O professor irá demonstrar os conceitos através de exemplos de aplicação. Os alunos deverão
entender os exemplos apresentados, questionando as construções usadas pelo professor e os
resultados obtidos.
Em horário extra-classe os alunos, em grupos de no máximo 3, deverão ter o material necessário
para a execução da prática de laboratório proposta, incluindo o datasheet dos CIs utilizados, o
computador com a IDE de programação e os instrumentos necessários para as medições. Após a
montagem e as medições, os alunos deverão entregar um relatório com as informações solicitadas,
comprovando o entendimento dos conceitos apresentados pelo professor ao longo da atividade.

Material de Apoio
Os microcontroladores da família AVR usados no Arduino possuem diversos timers internos, com
diferentes características, mas que podem ser configurados separadamente.
O ATMEGA 328 (usado no Arduino Uno), por exemplo, possui 2 timers de 8 bits (o Timer 0 e o
Timer 2) e um timer de 16 bits (o Timer 1). Destes três timers, dois podem ser usados como
contadores síncronos para sinais externos rápidos (o Timer 0 e o Timer 1), e um (o Timer 2) pode
ser usado como contador assíncrono para um sinal de oscilador externo (neste caso o
microcontrolador deve usar como fonte de relógio o oscilador interno). Neste documento, assim
como no datasheet do microcontrolador, muitas das referências aos registradores utilizará o símbolo
n para indicar o índice do Timer a ser configurado (0, 1 ou 2).
O Timer 0 é usado pela biblioteca do Arduino para controlar diversas funções internas (as funções
millis e delay, por exemplo, dependem do funcionamento deste timer), e seu vetor de interrupções
não pode ser sobrescrito. Além disso, apesar de permitida pelo compilador, a reconfiguração de
registradores do Timer 0 pode afetar o comportamento destas funções e, portanto, não deve ser
executada.
Os timers possuem diversos modos de operação e diversos sinais associados. Como regra geral,
para usarmos uma interrupção de timer, precisamos configurar o modo de operação do timer, a fonte
de relógio, habilitar a interrupção e criar a função de tratamento dessa interrupção. A biblioteca
padrão do Arduino não oferece suporte para a configuração de timers. Desse modo, a configuração
deve ser feita diretamente pela escrita nos registradores ou pelo uso de bibliotecas de terceiros
(como, por exemplo, a biblioteca MsTimer2).
Os timers também podem ser usados como geração de forma de onda, através dos pinos OCnA e
OCnB, que podem ser acionados diretamente pelo hardware na ocorrência dos eventos de
comparação.
Além disso, os timers e os pinos OCn também são usados na geração dos sinais de saída PWM (a
serem vistos mais adiante nesta disciplina); portanto a configuração e o uso de uma dessas features
afetará o comportamento da outra.
As figuras a seguir (extraídas do datasheet do ATMega 328p) mostram o diagrama de blocos
padrão dos Timers de 8 e de 16 bits do Arduino Uno.
Engenharia de Sistemas Microprocessados
Professor: Márcio Gil Faccin
Laboratório 5

Fonte de Relógio
A fonte de relógio é configurada através dos bits menos significativos do registrador TCCRnB
(onde n corresponde ao número do timer que estiver sendo configurado). Este conjunto de bits
recebe a designação de CSn (Clock Select do Timer n).
Para os timers que podem ser utilizados como contadores (Timer 0 ou Timer 1, no caso do
Arduino Uno), a configuração do CSn pode usar valores semelhantes aos da tabela abaixo (válida para
o Timer 1):
Engenharia de Sistemas Microprocessados
Professor: Márcio Gil Faccin
Laboratório 5

Para os timers que não possuem a opção de fonte de relógio externa (como no caso do Timer 2 do
Arduino Uno), as opções incluem prescaler de 1/32 e 1/128.
Fonte de Relógio pelo pino externo
Como indicado acima, nem todos os timers internos permitem o uso de pinos externos como
entrada de relógio. Além disso, cada timer que permite esse recurso possui um único pino que pode
ser usado como sinal de relógio; permitindo apenas a configuração da borda a ser usada (borda de
subida ou borda de descida, conforme configuração através dos bits CSn).
A configuração deve ser feita através dos bits CSn (Clock Select do Timer n) presentes no
registrador TCCRnB (ver maiores informações no datasheet do microcontrolador). Por exemplo, a
instrução abaixo configura o Timer 1 para efetuar a contagem a cada borda de subida do pino T1
(pino 5 nos conectores externos do Arduino).
TCCR1B = 7;
O uso no modo contador permite as mesmas interrupções do modo timer, configuradas da mesma
forma, como será visto a seguir.
Modos de Operação
Modo Normal
O modo normal de operação dos timers corresponde ao uso como timer incremental, com sinal
de interrupção gerado no overflow do contador (quando o contador volta ao valor 0). O modo normal
é configurado com o valor 0 escrito nos bits WGMn[2:0] (presentes nos registradores TCCRnA e
TCCRnB).
Por exemplo, para configurar o Timer 2 para operar no modo normal com prescaler de 1/256,
devemos configurar TCCR2A = 0 e TCCR2B = 6.
Modo Clear Timer on Compare Match (CTC)
O modo CTC corresponde ao uso como timer incremental, porém interrompendo a contagem
quando o valor do contador atingir o valor configurado no registrador OCRnA, e voltando
automaticamente ao valor zero. O modo CTC é configurado com o valor 2 escrito nos bits WGMn[2:0]
(presentes nos registradores TCCRnA e TCCRnB).
Por exemplo, para configurar o Timer 2 para operar no modo CTC com prescaler de 1/64,
devemos configurar TCCR2A = 2 e TCCR2B = 4. Essa mesma configuração no Timer 1 é feita
escrevendo TCCR1A = 0 e TCCR1B = 11.
O Timer 1 possui um segundo modo de CTC, no qual o valor de limite da contagem é definido pelo
registrador ICR (Input Capture) ao invés do registrador OCRA.
Modos PWM
Os timers são utilizados para acionamento/controle das saídas PWM. Maiores informações sobre
estes modos serão discutidas oportunamente, mas por enquanto é importante lembrar que a
biblioteca padrão do Arduino configura os timers para operar no modo PWM e que, qualquer
alteração na configuração para algum uso específico pode inviabilizar o uso do PWM correspondente.
Engenharia de Sistemas Microprocessados
Professor: Márcio Gil Faccin
Laboratório 5

Habilitar a Interrupção
Cada Timer possui 3 ou 4 vetores de interrupção.
Um vetor de interrupção corresponde ao evento de timer overflow (TIMERn_OVF_vect), que deve
ser habilitado no pino TOIEn do registrador TIMSKn. O evento de overflow ocorre sempre que o
contador volta ao valor 0.
Outros dois vetores de interrupção correspondem aos eventos de match na comparação do valor
do timer com os valores dos registradores OCRnA (TIMERn_COMPA_vect) e OCRnB
(TIMERn_COMPB_vect). Estes eventos devem ser habilitados, respectivamente, nos pinos OCIEnA e
OCIEnB do registrador TIMSKn.
O outro evento (que não está disponível em todos os timers) corresponde ao evento de Timer
Capture.

Timer Capture
A interrupção de timer capture ocorre quando um evento é identificado pelo bloco de captura de
entrada, conforme a figura abaixo. Simultaneamente à geração da interrupção, é feita uma cópia do
registrador do Timer n para o registrador ICRn.

No Arduíno Uno, este bloco de interrupção está disponível somente no timer de 16 bits (o Timer
1). O pino ICP1 está disponível no conector externo 8. Através do bit ICES1 (bit 6 do registrador
TCCR1B) é possível configurar a interrupção de captura para borda de subida ou borda de descida
deste pino. Também no TCCR1B, o bit ICNC1 (bit 7) habilita um filtro para eliminação de ruído (Input
Capture Noise Canceler), que faz com que uma borda somente seja reconhecida se ela for mantida no
mesmo estado por 4 períodos consecutivos de clock do processador.
As instruções a seguir configuram o Timer 1 para operar com as interrupções de Input Capture e
Timer Overflow, usando o clock do Timer sem prescaler (CS1[2:0] = 1), usando borda de subida do
ICP1 para geração do evento, e sem o uso do filtro.
TCCR1A = 0;
TCCR1B = 0x41; // ou TCCR1B = 0b01000001
TIMSK1 = 0x21; // ou TIMSK1 = 0b00100001
Para uso desta interrupção, deve ser declarada a função de atendimento ao vetor
TIMER1_CAPT_vect, através da diretiva ISR.

Gerador de forma de onda (WGM)


Engenharia de Sistemas Microprocessados
Professor: Márcio Gil Faccin
Laboratório 5

Os timers do Arduino também podem ser usados para geração de forma de onda, através dos
pinos OCnA e OCnB, que podem ser acionados diretamente pelo hardware na ocorrência dos eventos
de comparação. Por exemplo, o pino OC1A pode ser acionado nos eventos de comparação do Timer
1 com o valor do registrador OCR1A, independente da configuração de interrupções e sem a
interferência do software, apenas pela configuração dos bits COM1A1 e COM1A0 do registrador
TCCR1A.
É importante lembrar que o comportamento do hardware em função dessa configuração pode
mudar de acordo com o modo de operação do Timer (maiores informações sobre este assunto serão
discutidas na aula sobre PWM). Mas, para os modos normal e CTC, existem 4 comportamentos
possíveis para cada um dos comparadores (A e B) de cada um dos 3 Timers, de acordo com a
combinação dos bits do registrador:

COMnX1 COMnX0 Descrição

0 0 Operação normal da porta, pino OCnX não é afetado pelo timer.

No evento de comparação do Timer n com o registrador OCRnX,


0 1
o pino OCnX inverte de estado.

No evento de comparação do Timer n com o registrador OCRnX,


1 0
o pino OCnX é forçado para o nível baixo.

No evento de comparação do Timer n com o registrador OCRnX,


1 1
o pino OCnX é forçado para o nível alto.

OBS: Na tabela acima, n equivale ao número do Timer (0, 1 ou 2) e X equivale ao registrador de


comparação (A ou B).

Exemplos e demonstrações
1. Faça um programa que acione um LED através do evento de timer overflow do Timer 1
(testando as diferentes possibilidades de prescaler, para identificar qual o intervalo
entre interrupções para cada um dos valores configurados).

#define LED1 13
unsigned long timestamp = 0;
unsigned long lasttime = 0;

ISR (TIMER1_OVF_vect) {
timestamp = millis();
digitalWrite ( LED1, !digitalRead ( LED1 ) );
}

void setup() {
// Configura o pino como saída
pinMode ( LED1, OUTPUT );
// Inicializa a saída em nível baixo
digitalWrite ( LED1, LOW );
// Configura para Normal port mode com Output Compare disconnected
TCCR1A = 0;
Engenharia de Sistemas Microprocessados
Professor: Márcio Gil Faccin
Laboratório 5

// Configura para clk source interno com preescaler de X:


// TCCR1B = 5  X = 1/1024
// TCCR1B = 4  X = 1/256
// TCCR1B = 3  X = 1/64
// TCCR1B = 2  X = 1/8
// TCCR1B = 1  X = 1/1
// TCCR1B = 0  Sem fonte de relógio
TCCR1B = 5;
// Habilita a interrupção de Timer Overflow (OVF)
TIMSK1 = 1;
// E configura a serial para 115200 bps
Serial.begin (115200);
}

void loop() {
// A cada nova interrupção, envia o dt
if (timestamp != lasttime) {
unsigned long dt = timestamp - lasttime;
Serial.println (dt);
lasttime = timestamp;
}
}

2. Faça um programa que acione um LED através do evento de timer overflow do Timer 1
(com preescaler de 1/1024) e que acione um segundo LED na mesma frequência do
segundo LED, porém com 90 graus de defasagem, usando o evento de timer compare.

#define LED1 13
#define LED2 12

ISR (TIMER1_OVF_vect) {
digitalWrite ( LED1, !digitalRead ( LED1 ) );
}
ISR (TIMER1_COMPA_vect) {
digitalWrite ( LED2, !digitalRead ( LED2 ) );
}

void setup() {
// Configura os pinos como saída
pinMode ( LED1, OUTPUT );
pinMode ( LED2, OUTPUT );
// Inicializa as saídas em nível baixo
digitalWrite ( LED1, LOW );
digitalWrite ( LED2, LOW );
// Configura para Normal port mode com Output Compare disconnected
Engenharia de Sistemas Microprocessados
Professor: Márcio Gil Faccin
Laboratório 5

TCCR1A = 0;
// Clk com preescaler de 1/1024
TCCR1B = 5;
// Configura o OCR1A para comparar na metade do timer
OCR1A = 0x8000;
// Habilita as duas interrupções (OVF e CMPA)
TIMSK1 = 3;
}

void loop() {
// No laço principal não faz nada...
}

3. Faça um programa que acione um LED através do evento de timer overflow do Timer 1,
usando como base de contagem um gerador de clock externo, conforme o da figura
abaixo (com R = 1KΩ).

#define LED1 13

unsigned long timestamp = 0;


unsigned long lasttime = 0;

ISR (TIMER1_OVF_vect) {
timestamp = millis();
digitalWrite ( LED1, !digitalRead ( LED1 ) );
}

void setup() {
// Configura o pino como saída
pinMode ( LED1, OUTPUT );
// E configure a entrada de clock como pino de entrada
pinMode ( 5, INPUT );
// Inicializa a saída em nível baixo
digitalWrite ( LED1, LOW );
// Configura para Normal port mode com Output Compare disconnected
TCCR1A = 0;
// Configura para clk source externo
TCCR1B = 6;
// Habilita a interrupção de Timer Overflow (OVF)
TIMSK1 = 1;
// E configura a serial para 115200 bps
Serial.begin (115200);
}

void loop() {
Engenharia de Sistemas Microprocessados
Professor: Márcio Gil Faccin
Laboratório 5

// A cada nova interrupção, envia o dt


if (timestamp != lasttime) {
unsigned long dt = timestamp - lasttime;
Serial.println (dt);
lasttime = timestamp;
}
}

4. Usando o mesmo circuito do exemplo anterior, faça um programa que conte 100 pulsos
do contador T1 acionado pelo sinal externo e meça o período desse sinal.

#define LED1 13

unsigned long timestamp = 0;


unsigned long lasttime = 0;

ISR (TIMER1_COMPA_vect) {
timestamp = millis();
digitalWrite ( LED1, !digitalRead ( LED1 ) );
}

void setup() {
// Configura o pino como saída
pinMode ( LED1, OUTPUT );
// E configure a entrada de clock como pino de entrada
pinMode ( 5, INPUT );
// Inicializa a saída em nível baixo
digitalWrite ( LED1, LOW );
// Configura para CTC com Output Compare disconnected
TCCR1A = 0;
// Configura para clk source externo
TCCR1B = 0b00001110;
// Configura para interromper após 100 pulsos
OCR1A = 100;
// Habilita a interrupção de Timer Compare A
TIMSK1 = 2;
// E configura a serial para 115200 bps
Serial.begin (115200);
}

void loop() {
// A cada nova interrupção, envia o dt
if (timestamp != lasttime) {
unsigned long dt = timestamp - lasttime;
char msg[30];
Engenharia de Sistemas Microprocessados
Professor: Márcio Gil Faccin
Laboratório 5

sprintf (msg, "%d.%d ms", dt/100, dt%100);


Serial.println (msg);
lasttime = timestamp;
}
}

5. Crie um programa que implemente uma onda quadrada com 0,5Hz usando o gerador
de forma de onda.

#define LED1 9

void setup() {
// Configura os pinos como saída
pinMode ( LED1, OUTPUT );
// Inicializa as saídas em nível baixo
digitalWrite ( LED1, LOW );
// Configura para CTC com Output Compare Toggle on Match A
TCCR1A = 0b01000000;
// Configura para clk source interno com preescaler de 1/1024
TCCR1B = 0b00001101;
// E configura o valor de comparação para um meio-período de 1 segundo
OCR1A = 15630;
}

void loop() {
// Não precisa fazer nada dentro do loop...
}

Exercícios Propostos
1. Implemente o exemplo 1, medindo com o uso de um osciloscópio digital o intervalo
entre interrupções para cada um dos valores de prescaler possíveis de serem
configurados.
2. Repita o exercício 1, utilizando como base de geração de tempo o Timer 2. Indique as
alterações feitas em relação ao software original.
3. Repita o exercício 1, utilizando como base de geração de tempo a interrupção de Timer
Compare do Timer 0. Indique as alterações feitas em relação ao software original e
indique também se apareceram efeitos colaterais no comportamento do software.
4. Implemente o exemplo 4, medindo com o uso de um osciloscópio digital o período do
oscilador e comparando com o valor medido pelo sistema.
5. Repita o exercício 4 usando diferentes valores de resistores (utilize 3 valores diferentes
entre 330Ω e 2k2Ω) e indique os valores medidos.
6. Observe que o exemplo 4 utiliza o modo CTC para reduzir o intervalo entre os eventos
de interrupção. Implemente um programa usando o modo CTC com o Timer 2
configurado com o prescaler de 1/64 de modo que o intervalo de interrupções pela
Engenharia de Sistemas Microprocessados
Professor: Márcio Gil Faccin
Laboratório 5

comparação do valor do registrador OCR2A ocorra a cada 500us +/- 10us. Inverta o
estado de um pino de saída a cada interrupção e meça com o osciloscópio de modo a
garantir que o intervalo está correto.
7. Outra forma de obtermos o mesmo efeito do exercício 6 consiste em, usando o modo
normal de operação e usando a interrupção de overflow, sobreescrever o valor inicial
do contador no início da rotina de tratamento da interrupção. Mantendo o uso do Timer
2 com um prescaler de 1/64, configure o valor inicial do TCNT2 para que o intervalo de
interrupções ocorra a cada 500us (+/- 10us) também neste modo.
8. Implemente o exemplo 5 e meça, com o auxílio do osciloscópio, a duração do pulso para
diferentes valores configurados no registrador OCR1A (ajuste OCR1A de modo a
configurar pulsos com largura de 10ms, 100ms e 2,5s). Modifique o prescaler para 1/1
e ajuste o valor do OCR1A para geração de pulsos de 50us e 250us.
9. Utilizando como base uma interrupção do Timer 1 com um período de 3 segundos,
implemente um software que inverta o estado de um LED a cada 30 segundos.

Você também pode gostar