You are on page 1of 34

1

UNIVERSIDADE FEDERAL DE SANTA


CATARINA
DEPARTAMENTO DE AUTOMAÇÃO E SISTEMAS

Arquitetura e Programação de Sistemas Microcontrolados

Apostila Arduino e Microcontroladores AVR ATMega

Maio de 2018
Sumário

1 Introdução ao microcontrolador ATMega 328p 3

2 Portas de E/S digital 5


2.1 Diagramas eletrônicos dos pinos de portas de E/S . . . . . . . . . . . . . . . 5
2.2 Configurações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.3 Programação no ambiente ATMEL . . . . . . . . . . . . . . . . . . . . . . . 6

3 Interrupções externas com Arduino e ATmega 328p 8


3.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3.2 Configuração de interrupções . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.3 Interrupções externas 0 e 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.3.1 Uso no ambiente Arduino . . . . . . . . . . . . . . . . . . . . . . . . 10
3.3.2 Programação e configuração com recursos do ATmega 328p . . . . . 11
3.4 Interrupções por mudança de nı́vel em pinos . . . . . . . . . . . . . . . . . . 13
3.4.1 Configurações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.4.2 Exemplo de uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

4 Contadores/temporizadores do ATmega 328p 16


4.1 Noções básicas de contadores digitais . . . . . . . . . . . . . . . . . . . . . . 16
4.1.1 Implementação de contadores com flip-flops . . . . . . . . . . . . . . 16
4.1.2 Interpretação de contador como divisor de frequência . . . . . . . . . 17
4.2 Temporizadores/contadores do ATmega 328p . . . . . . . . . . . . . . . . . 18
4.3 Temporizador/contador de 16 bits TCNT1 . . . . . . . . . . . . . . . . . . . 19
4.3.1 Caracterı́sticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.3.2 Uso como temporizador . . . . . . . . . . . . . . . . . . . . . . . . . 20
4.4 Interrupções de temporização . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.4.1 Eventos que geram interrupções . . . . . . . . . . . . . . . . . . . . . 23
4.4.2 Modos de operação de Timer 1 . . . . . . . . . . . . . . . . . . . . . 24
4.5 Resumo das configurações/habilitações/tratadores das interrupções do Timer 1 24

5 Geração de ondas quadradas e PWM com


uso do Timer 1 (16 bits) 26
5.1 Descrição geral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
5.2 Configuração básica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.3 Onda quadrada sem PWM (modo CTC) . . . . . . . . . . . . . . . . . . . . 27

2
3
5.3.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.3.2 Configuração dos registradores do Timer1 para geração de onda qua-
drada (modo CTC) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.3.3 Configuração da frequência da onda quadrada . . . . . . . . . . . . . 28
5.4 PWM rápido (fast PWM) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

6 Introdução à programação modular em linguagem C 31


6.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
6.2 Exemplo: acionamento de E/S digital . . . . . . . . . . . . . . . . . . . . . 31
6.2.1 Programa em arquivo (“módulo”) único . . . . . . . . . . . . . . . . 32
6.2.2 Separação em módulos . . . . . . . . . . . . . . . . . . . . . . . . . . 32
6.3 Edição de programas modularizados no ATMEL Studio . . . . . . . . . . . 33
Capı́tulo 1

Introdução ao microcontrolador
ATMega 328p

Este modelo de microcontrolador de 8 bits pertence à famı́lia “AVR” da ATMEL [?].


As caracterı́sticas principais do dispositivo são:

Encapsulamento e pinagem: na versão de interesse, o encapsulamento é do tipo PDIP


com 28 pinos, sendo praticamente todos disponı́veis como portas de E/S digital mul-
tiplexadas com funções especiais do microcontrolador;

Programação: instruções com 14 bits e conjunto de 35 instruções da famı́lia “AVR”;

Memórias:

ˆ memória FLASH de programa com 32 kwords (1 word tem 16 bits);


ˆ Memória EEPROM com 256 bytes;
ˆ Memória RAM com 368 bytes;

Periféricos:

ˆ Três temporizadores/contadores (timer/counter ) (2x8 bits e 1x16 bits);


ˆ Comunicações seriais: SPI, PC e USART;
ˆ Conversores analógicos de 10 bits (8x) e comparadores analógicos (2x);
ˆ Um módulo CCP: Capture, Compare e PWM;
ˆ Dois módulos adicionais com PWM;

Recursos de Reset:

ˆ Watchdog timer ;
ˆ Power-on reset;
ˆ Brown-out reset;

4
5

Figura 1.1: Pinagem do microcontrolador ATMega 328p


Capı́tulo 2

Portas de E/S digital

2.1 Diagramas eletrônicos dos pinos de portas de E/S

Figura 2.1: Diagrama simplificado de pino de E/S digital.

2.2 Configurações
Configuração como “saı́da”de pinos de E/S digital, no caso do pino PB1, porta B
(demais pinos e portas têm identificadores similares):

DDRB = ( 1 << DDB1 ); // Configura pino PORTB1 (PB1) como saı́da

6
7

Figura 2.2: Diagrama completo de pino de E/S digital.

2.3 Programação no ambiente ATMEL


Exemplo de programa criado como parte de projeto “C/C++ Executable”:

#include <avr/io.h>
#include <util/delay.h>

void setup(){
DDRB = 0xff; // 8 pinos da porta B configurados como saı́das
}

void main(void){
setup();
while (1) {
PORTB ^= 0xFF; // Complementa saı́da
_delay_ms(100);
}
}
8

Figura 2.3: Configuração de E/S e de pullup.

Figura 2.4: Registradores de configuração, PORTB (as demais são similares).

Referências
ˆ Datasheet do componente ATMega 328p

ˆ Lima, C. B. e Villaça, M. V. M.; AVR e Arduino - Técnicas de Projeto. 2a edição,


Ed. dos Autores, 2012.
Capı́tulo 3

Interrupções externas com


Arduino e ATmega 328p

3.1 Introdução
O mecanismo de interrupções está presente em microprocessadores para permitir o tra-
tamento de eventos assı́ncronos. Por “eventos assı́ncronos”entende-se a sinalização de
condições tais como, por exemplo, o pressionar de um botão por operador de máquina, o
transcurso de um intervalo de tempo definido por relógio externo, o recebimento de pacote
de transmissão serial, e o fim de uma conversão de analógico para digital.
No caso do programa estar preparado corretamente, a ocorrência de evento de inter-
rupção provoca a execução de uma função associada à interrupção. Esta função recebe o
nome de tratador de interrupção, ou interrupt service routine - ISR na nomenclatura em
inglês.
No microcontrolador ATmega 328p há vinte e seis fontes de interrupções, conforme
apresentadas na Tabela 3.1. Associado a cada interrupção, consta o endereço da instrução
de desvio para o respectivo tratador.
Com isso, pode-se descrever como a função do Arduino attachInterrupt() realiza a
ligação entre o evento de interrupção e o tratador correspondente:
ˆ o endereço do tratador é usado para compor a instrução jump localizada no endereço
mostrado na Tab. 3.1;

ˆ durante a execução do programa, o evento de interrupção desvia o processador para


o endereço da tabela, no qual reside a instrução jump tratador interrupcao.
Observe, entretanto, que o Arduino somente prevê tratamento de duas interrupções da Tab.
3.1, que são as interrupções externas 0 e 1. A programação e uso das demais interrupções
requer recursos extras que são abordados neste documento.
A diversidade de fontes de interrupção do microcontrolador é reflexo dos vários “pe-
riféricos na pastilha” (on-chip peripherals). Isto é, o circuito integrado do microcontrolador
contém, além da unidade de processamento, vários componentes de hardware para compor
um sistema completo em uma única pastilha. Exemplos de periféricos são a interface de
comunicação serial USART, o conversor de tensão analógica para valor digital, e os tempo-
rizadores/contadores. Vale ressaltar que microprocessadores para computadores pessoais

9
10

Figura 3.1: Tabela de endereços de tratadores (“vetores”) de interrupção do ATmega 328p.

não contém periféricos em suas pastilhas, pois usam todo o recurso tecnológico disponı́vel
de fabricação de circuitos semicondutores para maximização do poder de processamento.

3.2 Configuração de interrupções


Programadores de sistemas microprocessados não precisam se preocupar com eventos
de interrupção caso não queiram tratá-los. Por exemplo, o sistema Arduino pode ser
usado em diversas aplicações sem que seja programado para tratamento de eventos de
interrupção. Por outro lado, caso desejem ou necesssitem de tratamento de interrupção,
cabe aos programadores realizar a configuração para seu uso.
A configuração para uso de interrupções consiste dos passos seguintes:

1. Associação do tratador da interrupção ao evento em si;

2. Configuração do hardware de sinalização do evento de interrupção; e

3. Habilitação da interrupção.
11
Neste momento, interessa conhecer detalhes de hardware e software associados às inter-
rupções externas, conforme apresentado a seguir. Tanto as interrupções cuja sinalização é
configurável (interrupções externas 0 e 1) como as interrupções por mudança de nı́vel serão
estudadas. Demais eventos geradores de interrupção do ATmega 328p mostrados na Tab.
3.1 serão estudados quando da análise dos periféricos aos quais estão associados.

3.3 Interrupções externas 0 e 1


3.3.1 Uso no ambiente Arduino
As interrupções externas 0 e 1 estão acessı́veis ao programador no ambiente Arduino
por meio da função attachInterrupt(), que pode ser descrita como:

Sintaxe:

ˆ attachInterrupt(digitalPinToInterrupt(pino), ISR, modo); (recomendado)


ˆ attachInterrupt(interrupção, ISR, modo); (não recomendado)
ˆ attachInterrupt(pino, ISR, modo). (não recomendado - Arduino Due, Zero,
MKR1000, 101 apenas)

Parâmetros:

interrupção: o número da interrupção (int)


pino: o número do pino (Arduino Due, Zero, MKR1000 apenas)
ISR: tratador a ser chamado quando a interrupção ocorre; essa função deve não
tomar nenhum parâmetro nem retornar valores (isto é, deve ser do tipo void ).
modo: define quando a interrupção deve ser ativada. Quatro constantes estão pre-
definidas como valores válidos:
ˆ LOW: acionar a interrupção quando o estado do pino for LOW
ˆ CHANGE: acionar a interrupção quando o sempre estado do pino mudar
ˆ RISING: acionar a interrupção quando o estado do pino for de LOW para
HIGH apenas,
ˆ FALLING: acionar a interrupção quando o estado do pino for de HIGH
para LOW apenas.

Observe que, do ponto de vista da configuração das interrupções apresentada na Seção


3.2, a função attachInterrupt() realiza os seguintes passos:

1. associação do tratador da interrupção (ISR);

2. configuração do hardware de sinalização (modo); e

3. habilitação da interrupção (transparente ao programador).


12

Figura 3.2: Registrador de configuração dos modos de acionamento das interrupções INT0
e INT1.

Figura 3.3: Registrador de habilitação das interrupções INT0 e INT1.

3.3.2 Programação e configuração com recursos do ATmega 328p


Apesar da simplicidade do uso das interrupções externas 0 e 1 no Arduino, é útil exami-
nar como realizar a mesma funcionalidade por meio de recursos do ATmega 328p usando a
biblioteca AVRLibc. A principal razão é didática: sendo estas as interrupções mais simples
de configurar, servem de introdução aos princı́pios gerais de uso das demais interrupções
do microcontrolador que não estão disponı́ves em Arduino.
A forma de configurar as interrupções envolve os passos seguintes:

1. associação dos tratadores aos eventos com a construção


ISR( INTi_vect ){ .. código .. }, substituindo “i” por 0 ou 1;

2. configuração do modo, atribuindo valores apropriados ao registrador EICRA, con-


forme mostrado na Fig. 3.2;

3. habilitação da interrupção desejada no registrador EIMSK, conforme mostrado na


Fig. 3.3.

Cabe ressaltar que as interrupções externas 0 e 1 estão associadas aos pinos 2 e 3,


respectivamente, do kit Arduino, conforme pinagem mostrada na Fig. 3.4.
13

Figura 3.4: Correspondência das numerações de pinos do kit Arduino e do microcontrolador


ATmega 328p.

Em programas Arduino escritos para uso direto dos recursos do ATmega 328p, o código
a seguir habilita a interrupção externa 0, modo FALLING:

#include <AVRLib.h> // Define uso da biblioteca

void setup(){
EICRA = ( 1 << ISC01 ); // ISC01 = 1, resultando em "0000 0010" binário
EIMSK = ( 1 << INT0 ); // INT0 = 0, resultando em "0000 0001" binário
}

void loop(){
// Código do programa principal vem aqui
}

ISR( INT0_vect ){
// Código do tratador vem aqui
}

Obs.: a construção ( 1 << bit_id ) funciona da forma seguinte:

ˆ o número “1” pode ser entendido como o byte 00000001 em binário;

ˆ o operador “<<” realiza deslocamentos binários para a esquerda (shift left);

ˆ o identificador “bit id” é a posição do bit no registrador (isto é, um número de 0 a


7);

ˆ como resultado, tem-se o deslocamento do “1” para a esquerda “bit id” vezes.

Ou seja, linha EICRA = ( 1 << ISC01 ) equivale a escrever EICRA = 2 ou, ainda,
EICRA = 0b10.
14

3.4 Interrupções por mudança de nı́vel em pinos


Outro tipo de interrupção externa disponı́vel no ATmega 328p ocorre pela mudança de
nı́vel lógico em um pino de E/S digital. Denominada de pin change interrupt (PCINT) em
inglês, esse tipo de interrupção está ausente das funcionalidades básicas do Arduino. Por-
tanto, para usá-la é necessário usar as facilidades do ATmega 328p por meio da biblioteca
AVRLibc.
Microcontroladores da famı́lia AVR podem reconhecer três PCINTs distintas, cada qual
acionada por grupos de até oito pinos. No caso do ATmega 328p, os pinos associados a
cada PCINT são (ver Fig. 3.4):

PCINT0: pinos PB0 a PB7 (8 pinos), correspondentes aos eventos PCINT0 a PCINT7;

PCINT1: pinos PC0 a PC6 (7 pinos), correspondentes aos eventos PCINT8 a PCINT14;

PCINT2: pinos PD0 a PD7 (8 pinos), correspondentes aos eventos PCINT16 a PCINT23.

Note que há certa confusão na nomenclatura adotada pela Atmel, pois os eventos PCINT0
a PCINT2 do grupo PCINT0 têm a mesma denominação dos três grupos. Na prática, o
contexto do programa tende a deixar claro a qual elemento se refere o sı́mbolo (a um dos
três grupos ou a eventos do grupo PCINT0).

3.4.1 Configurações
O uso das interrupções de mudança de nı́vel envolve os três passos de configuração
comuns a todas as interrupções do ATmega 328p. São eles:

1. associação dos tratadores aos grupos, com a construção:

ISR( PCINTi_vect ){
.. código ..
},

substituindo “i” por 0, 1 ou 2 conforme o grupo desejado;

2. habilitação da interrupção associada ao grupo desejado, atribuindo valores apropria-


dos aos registrador PCICR mostrado na Fig. 3.5:

PCIE2: colocar “1”neste bit para habilitar grupo 2 (eventos 16 a 23);


PCIE1: colocar “1”neste bit para habilitar grupo 1 (eventos 8 a 14);
PCIE0: colocar “1”neste bit para habilitar grupo 0 (eventos 0 a 7);

3. colocar “1”no(s) bit(s) de habilitação do(s) pino(s) apto(s) a receber sinalizações


de interrupção em cada grupo, usando o(s) registrador(es) apropriado(s) conforme
mostrado na Fig. 3.6.
15

Figura 3.5: Registrador de habilitação das interrupções PCINT0 a PCINT2.

Figura 3.6: Registradores de configuração dos pinos aptos a sinalizarem ocorrências das
interrupções PCINT.

3.4.2 Exemplo de uso


O fragmento de código a seguir mostra a estrutura básica de um programa que usa o
grupo 0, evento PCINT5 (pino 13 do Arduino), para implementação de interrupção por
mudança de nı́vel:

void setup() {
PCICR = ( 1 << PCIE0 ); // Habilita interrupç~
oes PCINT do grupo 0
PCMSK0 = ( 1 << PCINT5 ); // Configura evento 5 do grupo 0 (pino 13 do
// Arduino) para sinalizar interrupç~
ao
}

void loop() {
// programa principal vem aqui
}

// Tratador da interrupç~
ao por mudança no pino 13
// do Arduino - grupo PCINT0
//
ISR( PCINT0_vect ){
// código do tratador vem aqui
}
16

Apêndice
Lista de tratadores reconhecidos pela AVRLibc para ATmega
328p
Capı́tulo 4

Contadores/temporizadores do
ATmega 328p

4.1 Noções básicas de contadores digitais


Contadores digitais são circuitos que avançam com pulsos externos aplicados à entrada
de “relógio”(clock input em inglês). O avanço ocorre ao longo de uma sequência pré-
definida; tipicamente, a sequência vai de zero até 2n − 1 em contadores de n bits. Porém,
existem contadores programáveis para realizar contagens regressivas, ou com avanços em
“BCD”(Binary-Coded Decimal ), ou ainda com avanços com passo maior que 1; estes casos
estão além do interesse deste curso.

4.1.1 Implementação de contadores com flip-flops


A Figura 4.1 mostra a implementação de um contador digital tı́pico para n = 4. Os
flip-flops T (toggle) têm suas saı́das complementadas quando T = 1 e ocorre uma transição
positiva de pulso na entrada de clock. Quando T = 0, as saı́das permanecem inalteradas
com os pulsos de clock.
Verificando-se o avanço do circuito com os pulsos de clock, pode-se deduzir as formas
de onda mostradas na Fig. 4.2, pois:

Figura 4.1: Contador sı́ncrono de 4 bits; os flip-flops T têm saı́das complemen-


tadas quando T = 1 e ocorre uma transição positiva do pulso na entrada de
clock (CLK) do flip-flop. (Adaptado de: Electronics-tutorials em www.electronics-
tutorials.ws/counter/count 3.html).

17
18

Figura 4.2: Evolução das saı́das do contador de 4 bits, indicando que um bit de
saı́da Qi é complementado sempre que todos os bits anteriores valem 1 e ocorre uma
transição positiva do pulso de clock. (Fonte: Electronics-tutorials em www.electronics-
tutorials.ws/counter/count 3.html).

1. o flip-flop mais à esquerda tem entradas T = 1, portanto sua saı́da QA é comple-


mentada a cada transição positiva do pulso de clock ;

2. o flip-flop seguinte tem sua saı́da QB complementada quando a saı́da anterior QA é


1 e o pulso de clock tem transição positiva;

3. no caso do flip-flop com saı́da QC , a porta lógica “E” (&) implica em mudança na
saı́da quando QA e QB forem 1; e assim por diante.

Observa-se que a evolução do circuito resulta em contagem binária, conforme indica a


linha “Count” da Fig. 4.2. Pode-se ver que a saı́da QA da Fig. 4.1 foi tomada como
sendo o bit menos significativo (representando o bit b0 20 ), e assim por diante até QD , que
representa o bit b3 23 .
O módulo da contagem é dado por 2n e, portanto, o valor máximo de contagem é 2n − 1.

4.1.2 Interpretação de contador como divisor de frequência


Um efeito muito importante que decorre da operação dos circuitos contadores consiste
na divisão por 2 da frequência de uma saı́da Qi em relação à saı́da anterior Qi−1 . Para
ilustrar o efeito, a Fig. 4.3 mostra um contador de 2 bits e as respectivas formas de onda
de saı́da em relação a um sinal de clock. No exemplo da figura, há 16 perı́odos do clock
em 1 s de tempo, ou seja, a frequência é de 16 Hz. A contagem de perı́odos de onda em
1 s para QA e QB resulta em 8 Hz e 4 Hz, respectivamente, mostrando a divisão por 2 a
cada saı́da sucessiva do contador.
Portanto, um contador binário de n bits realiza divisões da frequência de clock desde 2
(bit 0) até 2n (bit n − 1). Assim, um contador realiza máxima divisão de frequência igual
ao módulo da contagem, 2n .
19

Figura 4.3: Evolução das saı́das em contador de 2 bits em um intevalo de 1 s. Note que na
saı́da QA tem frequência dividida por 2 em relação à frequência dos pulsos de clock, e QB
tem frequência fP /4.

No contexto desta disciplina, contadores aplicados exclusivamente à divisão de


frequência são denominados “prescalers” (ou pré-escalonadores), sendo usados em vários
periféricos on chip tais como em temporizadores, no conversor AD e no transmis-
sor/receptor de comunicação serial. Em geral, prescalers apresentam menos opções de
divisão de frequência do que o número de bits de contagem n.

4.2 Temporizadores/contadores do ATmega 328p


O microcontrolador ATmega 328p dipõe de três contadores/temporizadores (ti-
mers/counters em inglês):

TCNT0: contagem em 8 bits (módulo 28 = 256), geração de onda quadrada e PWM;

TCNT1: contagem em 16 bits (módulo 216 = 65.536), geração de onda quadrada e PWM,
módulo de captura de eventos;

TCNT2: contagem em 8 bits, geração de onda quadrada e PWM, entrada de sinal de


clock assı́ncrono externo.

O diagrama da Fig. 4.4 mostra o esquema básico dos contadores deste microcontrolador,
com n sendo substituı́vel por 0, 1 ou 2 de acordo com o contador em análise. O registrador
T CN T n representa os contadores em si, cujo avanço se dá pelo sinal “Count”. Os sinais
“Clear” e “Direction” comandam, respectivamente, a atribuição de zero ao contador e a
direção de contagem, se progressiva ou regressiva. “TOVn” sinaliza que a contagem atingiu
o valor máximo (Timer OVerflow ) e “clkTn ” é o sinal de clock. A figura indica, ainda, que
o sinal “TOVn” está associado a um evento de interrupção (Int. Req.), a serem abordados
mais adiante.
Os três contadores / temporizadores têm comportamentos semelhantes, distinguindo-se
por funcionalidades especı́ficas e pelo módulo de contagem. Neste documento, opta-se por
analisar em detalhes o contador TCNT1, por ser de 16 bits (permitindo temporizações
relativamente longas, compatı́veis com acionamentos e amostragens próximos da escala de
1 s) e por conter mais funcionalidades do que os demais.
20

Figura 4.4: Diagrama do esquema básico dos três contadores do ATmega 328p, com a
letra n nos sı́mbolos podendo ser lida como 0, 1 ou 2; o registrador TCNTn representa
os contadores em si e mantém o valor da contagem; “Count” provoca o avanço da conta-
gem, “Clear” zera o valor de contagem, e “Direction” comanda contagem progressiva ou
regressiva. “TOVn” sinaliza que a contagem atingiu o valor máximo (Timer Overflow ) e
“clkTn ” é o sinal de clock.

4.3 Temporizador/contador de 16 bits TCNT1


Apresenta-se na Fig. 4.5 o diagrama com os componentes do temporizador/contador
TCNT1, denominado daqui para diante como timer 1 (na figura, leia-se “1” no lugar de “n”
nos nomes de registradores e de bits). Como se observa na figura, há várias extensões em
relação ao hardware geral apresentado na Fig. 4.4. Algumas dessas extensões aparecem,
também, em TCNT0 e TCNT2, especialmente os registradores de comparação OCRnA e
OCRnB. Outras, como o registrador ICR1, são especı́ficas deste timer. Essas extensões
implementam funcionalidades úteis ao programador, conforme apresentado a seguir.

4.3.1 Caracterı́sticas
O timer 1 disponibiliza vários recursos ao programador, dos quais interessam neste
capı́tulo aquelas relacionadas às temporizações periódicas. Ainda assim, apresentam-se
abaixo todas as funcionalidades do periférico, algumas das quais serão abordadas em
capı́tulos posteriores. São elas:

ˆ Contagem em módulo 216 , isto é, o “vai-um” da contagem ocorre na passagem de


216 − 1 (65535) para 0. Este evento é sinalizado pelo bit TOV1 (ver canto superior
direito da Fig. 4.5).

ˆ Bloco Clock Select, o qual define se o avanço da contagem se dá por pulso externo
no pino “T1” (modo de contagem de pulsos) ou de acordo com “clock” interno pré-
escalonado (From Prescaler ).

ˆ Pré-escalonador (“prescaler”), que permite efetuar divisão de frequência do sinal de


clock do processador. No caso do Arduino Uno, o clock é de 16 MHz; se for usado
sem nenhuma divisão, serão geradas ocorrências de “vai-um” de TCNT1 após cada
intervalo de
21
1 1
Tov1 = = 216 ≈ 4.1ms, (4.1)
fCLK IO 16 MHz
na qual fCLK IO é a frequência de clock do kit Arduino Uno. Os valores admissı́veis
de divisão de frequência serão abordados na seção 4.3.2.

ˆ Dois registradores para comparação de igualdade, “OCR1A” e “OCR1B”, cada qual


gerando sinalização pelos bits OC1A e OC1B, respectivamente. Estas podem gerar
tanto eventos de interrupção como mudanças de nı́vel nos pinos OC1A e OC1B (pinos
15 e 16 do ATmega 328p, pinos 9 e 10 do Arduino) para, neste caso, geração de formas
de onda quadrada e PWM a serem abordadas em capı́tulo posterior.

ˆ Registrador de captura de eventos, o qual grava o valor atual de TCNT1 quando da


ocorrência de mudança de nı́vel no pino ICP1 ou de igualdade entre sinais analógicos,
a serem abordados em capı́tulo posterior.

ˆ Registradores de configuração “TCCR1A”, “TCCR1B” e “TCCR1C” (não mostrado


na Fig. 4.5), que permitem controlar o funcionamento do timer 1 de acordo com as
funcionalidades desejadas pelo programador.

4.3.2 Uso como temporizador


Como o nome do periférico indica, as duas funções básicas que desempenha são a con-
tagem de pulsos e a temporização. No primeiro caso, os pulsos são assı́ncronos (em relação
ao relógio do sistema, CLKIO ) e não-periódicos, sendo gerados por eventos externos tais
como os giros de uma catraca de controle de acesso, a passagem de pacotes por sensor de
presença em linha de manufatura, etc.
No caso da função de temporização, a contagem avança de acordo com pulsos internos,
sı́ncronos e periódicos. Assim, a cronometragem do tempo é dada por:
1
Tc = Nc , (4.2)
fp

com Nc sendo o número de contagens (pulsos) realizadas e fp sendo a frequência dos pulsos
de avanço do contador. Se não for usada divisão de frequência, então

fp = fCLK IO ,

que, no caso do kit Arduino Uno, é de 16 MHz.


Na prática, é comum desejar-se temporizar sinalizações em escala da ordem de 1 s. Para
isso, a frequência do clock do sistema fCLK IO pode ser muito alta. Por exemplo, no caso
do kit Arduino Uno, a expressão (4.2) fornece o intervalo máximo de temporização, isto é,
o intervalo para a contagem de Nc = 216 pulsos, como sendo:
1 1
Tc = Nc = 65536 ≈ 4.1ms,
fp 16 MHz

número já apresentado em (4.1).


22

Figura 4.5: Diagrama temporizador/contador 1, de 16 bits; registradores e funcionalidades


associadas são descritos na Seção 4.3.

Tabela 4.1: Valores de divisão de frequência do ATmega 328p, indicando os valores res-
pectivos dos bits de configuração (localizados no registrador TCCR1B); notar que CS12 =
CS11 = CS10 = 0 implica em temporizador desligado.
23
Por essa razão, temporizadores de sistemas microcontrolados têm divisores de frequência
associados, os quais permitem o uso de ondas de pulsos com frequências menores do que
o clock do sistema. No caso do ATmega 328p, as divisões de frequência disponı́veis estão
mostradas na Tab. 4.1.
Com o uso de prescalers, a expressão (4.2) pode ser reescrita como:
PRSC
Tc = Nc , (4.3)
fCLK IO
com PRSC ∈ {1, 8, 64, 256, 1024} no caso do ATmega 328p.
Em aplicações práticas, o programador está interessado em obter um certo tempo de-
sejado Td para temporização de eventos periódicos. Isso implica em determinar:

ˆ o número de contagens Nc que resultam no tempo desejado Td ; e

ˆ o valor de PRSC associado a essas contagens.

Para tanto, pode-se reescrever (4.3) como:


fCLK IO
Nc = Td . (4.4)
PRSC
O exemplo a seguir ilustra a aplicação dos cálculos.
Exemplo: Encontre o número de contagens necessárias para temporização de 100 ms em
um Arduino Uno, considerando o uso do timer 1.

Solução: A Tab. 4.2 mostra os valores de Nc para diferentes valores de PRSC. Observa-se
que nem todas as contagens são adequadas para o timer 1, pois:

ˆ No caso de PRSC igual a 1 e 8, as contagens necessárias excedem o módulo do


contador (216 );

ˆ No caso de PRSC igual a 1024, resulta em um número fracionário de contagens, que


não pode ser realizado pelo hardware.

Assim, apenas os valores de PRSC igual a 64 e 256 devem ser considerados. A escolha
entre eles é arbitrária.

PRSC Nc
1 1.600.000
8 200.000
64 25.000
256 6.250
1024 1.562,5

Tabela 4.2: Valores de contagens para temporização de 100 ms no caso de fCLK IO = 16


MHz; note que os valores para PRSC = 1 e 8 estão além da capacidade do registrador
TCNT1 e que o valor de contagem para PRSC = 1024 implica em número fracionário de
contagens, descartando o uso prático dessas divisões de frequência.
24

4.4 Interrupções de temporização


Na apresentação a seguir, define-se como TOP o valor máximo de contagem a partir do
qual um incremento gera sinalização de evento de ultrapassagem.

4.4.1 Eventos que geram interrupções


Overflow : Ocorre quando a contagem no temporizador atinge 216 (TOP = 216 − 1 =
0xFFFF), isto é, quando ocorre a ultrapassagem da capacidade de armazenamento
do registrador TCNT1. O valor deste é zerado (como em contadores convencionais),
e o bit TOV1 recebe valor “1”.

Comparação com OCR1A: Ocorre no avanço seguinte da contagem à igualdade com o


valor armazenado no registrador OCR1A. O evento sinalizado é a contagem do pulso
seguinte. O bit “OC1A” recebe valor “1”. A operação no modo CTC zera TCNT1
quando ocorre a igualdade; isto é, TOP = OCR1A neste caso.

Comparação com OCR1B: Ocorre no avanço seguinte da contagem à igualdade com o


valor armazenado no registrador OCR1B. O evento sinalizado é a contagem do pulso
seguinte. O bit “OC1B” recebe valor “1”.

As interrupções geradas pelos eventos descritos acima têm, respectivamente, tratadores


definidos por:

Overflow: tratador TIMER1_OVF_vect;

Comparação com OCR1A: : tratador TIMER1_COMPA_vect;

Comparação com OCR1B: : tratador TIMER1_COMPB_vect;

A habilitação das interrupções é definida pelos bits respectivos do registrador TIMSK1


(Fig. 4.6):

TOIE1: interrupção de ”overflow”, a cada 21 6 contagens;

OCIE1A: interrupção de comparação com registrador OCR1A;

OCIE1B: interrupção de comparação com registrador OCR1B.

Figura 4.6: Registrador de habilitação de interrupções do Timer 1.


25

4.4.2 Modos de operação de Timer 1


Neste momento, interessam dois modos dentre os dezesseis modos disponı́veis (a serem
estudados posteriormente):

Normal: Contagem de 0 até 0xFFFF (TOP = 216 − 1 ).

CTC : Neste modo, o contador volta para zero no momento da comparação com OCR1A,
daı́ a designação em inglês como Clear Timer on Compare.

Para configurar a operação do temporizador em um dos modos, segue-se a configuração


de bits mostrada na Tab. 4.3:

Tabela 4.3: Configuração de bits para operação em modo normal e em modo CTC (Clear
Timer on Compare).

4.5 Resumo das configurações/habilitações/tratadores das


interrupções do Timer 1
Registradores principais:
TCNT1: armazena valor atual da contagem;

OCR1A: armazena valor de comparação para sinalização de igualdade com TCNT1 e


para reset no modo CTC;

OCR1B: armazena valor de comparação para sinalização de igualdade com TCNT1;

TIMSK1: contém os bits de habilitação de interrupção dos eventos disponibilizados no


ATmega;

TCCR1A: registrador de configuração de modos de geração de onda (nas aplicações cor-


rentes, permanece zerado);

TCCR1B: registrador de configuração com bits para modo CTC e para valor de prescaler.

Figura 4.7: Registrador TCCR1A, configuração do Timer 1.


26

Figura 4.8: Registrador TCCR1B, configuração do Timer 1; ver configuração de modos de


operação na Tab. 4.3 e de valores de prescaler na Tab. 4.1.
Capı́tulo 5

Geração de ondas quadradas e


PWM com
uso do Timer 1 (16 bits)

5.1 Descrição geral


A geração de formas de onda (waveform generation, no jargão da fabricante ATMEL)
refere-se à capacidade do hardware do microcontrolador para produzir ondas quadradas e
ondas de pulsos com larguras moduláveis (pulse-width modulation, ou PWM). No caso do
microntrolador ATMega 328p, as ondas são geradas nos pinos relativos ao temporizador
1, OC1A (pino 15) e OC1B (pino 16). Os demais temporizadores (0 e 2) também geram
formas de onda, cada qual em pinos respectivos, conforme mostrado na Fig. 5.1.

Para uso das formas de onda, segue-se o procedimento de configuração dos temporiza-
dores conforme discutido nas seções seguintes. Aborda-se apenas o temporizador 1, de 16
bits. Os demais têm configurações similares.

Figura 5.1: Pinagem do microcontrola-


dor ATMega 328p com destaque para os
pinos associados à geração de formas de
ondas pelos temporizadores; os rótulos
OCxA/OCxB referem-se aos temporiza-
dores x, com x = 0, 1 e 2.

27
28

Tabela 5.1: Tabela de configuração de TCCR1A para onda quadrada, modo CTC.

5.2 Configuração básica


Tanto no caso de ondas quadradas como de PWM, devem ser configurados como
”saı́da”os pinos correspondentes. Por exemplo, no caso de OC1A, deve-se fazer:

DDRB = ( 1 << DDB1 ); // Configura pino PORTB1 (PB1/OC1A) como saı́da

5.3 Onda quadrada sem PWM (modo CTC)


5.3.1 Introdução
Ondas quadradas consistem de sinais com dois nı́veis de tensão alternados periodica-
mente e mesmo tempo de permanência em cada nı́vel. Para gerá-las de forma simples,
usa-se o modo Clear Timer on Compare (CTC). A cada ocorrência da condição de igual-
dade entre o registrador de contagem TCNT1 e o registrador de comparação OCR1A,
alterna-se o nı́vel da onda no pino OC1A.

Registradores envolvidos:

TCNT1: registrador de contagem do timer


OCR1A: registrador de comparação
TCCR1A: registrador de configuração (ver tabelas)
TCCR1B: registrador de configuração (ver tabelas)

5.3.2 Configuração dos registradores do Timer1 para geração de onda


quadrada (modo CTC)
A Tab. 5.1 mostra as configurações dos bits relativos à geração de ondas quadradas
considerando-se operação no modo CTC. As informações da tabela revelam os comporta-
mentos seguintes, de acordo com os bits de configuração:

00: a operação não gera forma de onda, caracterizando a forma usual de uso do Timer1 e
liberando o pino associado à OC1x para operar como pino de E/S digital convecional;

01: modo “toggle”, isto é, a saı́da em OC1x é complementada a cada igualdadade entre a
contagem em TCNT1 e o valor em OCR1A;
29

Figura 5.2: Localização dos bits de configuração do registrador TCCR1A; para geração de
ondas quadradas no modo “toggle”, deve-se atribuir 1 aos bits COM1A0 (para geração em
OC1A) e COM1B0 (para geração em OC1B).

Figura 5.3: Localização dos bits de configuração do registrador TCCR1B; para o modo
CTC, o bit WGM12 deve ser colocado em 1.

10: modo “clear”, que atribui o nı́vel lógico 0 ao pino;

11: modo “set”, que atribui o nı́vel lógico 1 ao pino.

Os bits indicados na tabela e na lista acima estão localizados no registrador TCCR1A,


conforme mostrado na Fig. 5.2. Ressalta-se que, para operação em modo CTC, é preciso
configurar o bit WGM12 do registradro TCCR1B, o qual está mostrado na Fig. 5.3.
Para fins de geração de ondas quadradas, interessa principalmente o modo “toggle”.
Neste caso, a frequência da onda depende de configurações adicionais, conforme visto na
seção seguinte.

5.3.3 Configuração da frequência da onda quadrada


A frequência da onda quadrada é determinada pela combinação da frequência do clock
do processador e das configurações de prescaler e do registrador de comparação OCR1A
de acordo com a fórmula:
fclk I/O
fOC1A = (5.1)
2N (1 + OCR1A)
na qual fclk I/O é a frequência do oscilador do microcontrolador e N é o valor do prescaler.
Observe que a divisão de fclk I/O por 2 (além, é claro, dos demais fatores de divisão) é
fruto da operação em modo “toggle”. Isso porque a cada perı́odo completo de contagem em
TCNT1 até o valor em OCR1A, o pino permanece no mesmo nı́vel lógico. Assim, é preciso
aguardar o transcurso de 2 perı́odos de contagem para que haja um perı́odo completo da
onda quadrada, caracterizando a divisão por 2 na frequência.
30

Figura 5.4: Tabela de configuração dos 16 modos de operação do TIMER1; no caso de


geração de ondas PWM Fast, interessam os modos 5, 6, 7, 14 e 15 .

5.4 PWM rápido (fast PWM)


No modo PWM rápido, tanto a frequência da onda gerada como a largura do pulso
periódico podem ser ajustadas por software, continuamente. A figura 5.5 mostra o método
de geração da onda com pulso de largura variável, na qual o pequeno segmento horizontal (–
) representa o valor do registrador de comparação. No modo não-invertido, as saı́das OC1x
são “zeradas” quando ocorre a igualdade e setadas no reinı́cio da contagem (BOTTOM).
No modo invertido, o contrário ocorre.

Registradores envolvidos:

TCNT1: registrador de contagem do timer


OCR1A: registrador de comparação
ICR1: registrador de comparação
TCCR1A: registrador de configuração (ver tabelas)
TCCR1B: registrador de configuração (ver tabelas)

Conforme indicado na Tabela 16-4 do manual do componente (ver acima), há cinco
modos diferentes de configuração do PWM rápido:

ˆ modos 5, 6 e 7: valores fixos de topo de contagem;

ˆ modos 14 e 15: topo de contagem definido por valor de registrador.


31

Figura 5.5: Ilustração da geração de onda PWM; segmentos horizontais (–) indicam
condição de igualdade com registradores de comparação.

Em qualquer modo, a frequência da onda é dada por:


fclk I/O
fOC1AP W M = ,
N (1 + TOP)

em que TOP representa o valor do topo da contagem. Assim, as opções de frequência da


onda nos modos com topo fixo são obtidas com diferentes valores de N , isto é, do prescaler.
Nos modos 14 e 15, o topo da contagem é dado, respectivamente, pelos registradores
ICR1 e OCR1A. Como se trata de registradores de 16 bits, há uma gama maior de opção
de frequências. Quando OCR1A é usado como topo, a saı́da OC1A não pode mais ser
considerada para fins de modulação do pulso. Apenas OC1B está disponı́vel, com valor de
OCR1B sendo usado para comparação.
Capı́tulo 6

Introdução à programação
modular em linguagem C

6.1 Introdução
Os módulos de programas em linguagem C consistem de códigos auto-contidos e defi-
nidos em arquivos individualizados. Assim, os blocos implementam códigos agrupados de
acordo com funcionalidades comuns em arquivos separados.
As principais utilidades dos módulos são:

ˆ Facilitar a verificação de erros no programa, uma vez que os módulos sejam separados
e conectados apenas por meio de interfaces “leves” e bem definidas.

ˆ Falicitar a reutilização de código, seja na forma de bibliotecas, seja no reuso do mesmo


código-fonte em diversos projetos diferentes.

ˆ Permitir o desenvolvimento de sistemas computacionais em paralelo por vários mem-


bros de um projeto, no qual cada membro implementa um conjunto de funcionalidades
relacionadas a um aspecto do projeto.

6.2 Exemplo: acionamento de E/S digital


Para ilustrar a implementação de um programa em diversos módulos, considere o exem-
plo muito simples de realizar, ciclicamente, a complementação de um pino de E/S digital.
O programa poderia ser usado para geração de onda quadrada por software no pino PB1
(o mesmo que OC1A discutido no Cap. 5.

32
33

6.2.1 Programa em arquivo (“módulo”) único


meuPrograma.c

#include <avr/io.h>

void setup(){
DDRB = ( 1 << DDB1 ); // Configura pino PB1 como saı́da
}

void main(void){
setup();
while (1) {
PORTB ^= ( 1 << PB1 ); // Complementa saı́da
}
}

6.2.2 Separação em módulos


Programa principal:
meuPrograma.c
#include ‘‘portasES.h’’
void setup(){
myPinMode( PINB1, OUTPUT ); // Configura pino PB1 como saı́da
}

int main(void){
setup();
while (1){
complementPortB( PINB1 ); // Complementa PB1
}
}

Módulo de funções de E/S digital, arquivo de cabeçalho (header )


portasES.h
#define PINB1 1
#define OUTPUT 1

extern void portB_PinMode( char pin, char mode );


extern void complementPortB( char pin ); // Complementa saı́da

Módulo de funções de E/S digital, arquivo de implementação:


34

portasES.c

// Dependencias externas do sistema


#include <avr/io.h>

// Dependencias definidas pelo programador (locais ao programa)


#include ‘‘portasES.h’’

void portB_PinMode( char pin, char mode ){


DDRB |= ( mode << pin );
}

void complementPortB( char pin ){ // Complementa saı́da


PORTB ^= ( 1 << pin );
}

6.3 Edição de programas modularizados no ATMEL Studio


1. Inicie o ambiente e edite um projeto cujo código será modularizado;

2. Abra o painel “Solution Explorer”caso não o encontre na área de trabalho do ambiente


(opção ”View → Solution Explorer”do menu principal);

3. No ı́cone de pasta do projeto, clique com o botão direito e, no menu que aparece,
escolha “Add → New item..”;

4. Na janela de diálogo, selecione:

ˆ “Include file”, no caso de arquivos “.h”de definição da interface do módulo;


ˆ “C file”, no caso de arquivo de implementação do módulo.