Académique Documents
Professionnel Documents
Culture Documents
Instrumentação – UnBall
Engenharia Mecatrônica – Universidade de Brasília
12 de Abril de 2010
Sumário
1. Introdução
2. Microcontrolador Atmel AVR ATmega8
2.1 Arquitetura AVR RISC
2.2 Memória EEPROM
2.3 Memória Flash
2.4 Memória SRAM
2.5 Registradores do Avr
3. Tipos de Variáveis
4. Arquitetura de um Programa
4.1 Estrutura Fundamental de um Programa
4.2 Códigos Exemplos
5. Registradores e Comandos I/O
5.1 Registradores I/O
5.2 Acesso às portas
6. Interrupções
6.1Funcionamento Básico
6.2 Registradores de Controle
6.3 Códigos Exemplos
7. Timers/Counters
7.1Características dos Temporizadores
7.2 Códigos Exemplos
8. USART
8.1 Registradores de Controle
8.2 Códigos Exemplos
9. Conclusões
10. Bibliografia
1. Introdução
A memória de programa é do tipo flash, ou seja, com as instruções relativas de “jump” e “call”,
todo o espaço de endereçamento de 8 K é diretamente acessado. A maioria das instruções AVR tem
um único formato de palavra de 16-bit. Todo endereço da memória de programa contém uma
instrução de 32-bit.
A partir desse tutorial, o grupo acredita ser possível que os próximos integrantes do mesmo
possam adquirir informações importantes para manter a continuação dos projetos em
desenvolvimento.
3. Tipos de Variáveis
Para fazer o processamento, muitas vezes é necessário uso de variáveis para armazenar valores a
serem usados posteriormente. Como a memória é limitada, é interessante que o compilador saiba o
tanto de espaço na memória para reservar em relação a cada tipo de variável, e que o programador
saiba utilizá-las de forma a otimizar o uso de memória.
O compilador gcc-avr nos permite a criação dessas variáveis através de funções que alocam
essas variáveis em endereços na memória do microcontrolador. A biblioteca referenciada pelo
cabeçalho inttypes.h, define os seguintes tipos:
Nessa técnica, temos que o programa fica envolvido por um laço continuo em que os dados de
saída retornam rotina inicial indefinidamente, como observado na Figura 4.1.
Neste método, primeiramente, são ativadas as entradas das interrupções para permitir a utilização
das mesmas. Assim, dentro do loop contínuo do programa, temos que, se uma interrupção é ativada,
automaticamente o código referente à interrupção é executado.
Para um programa com interrupções controladas, temos uma implementação que pode ser
visualizada na Figura 4.4. Neste caso, temos que o programa responde a uma interrupção externa, a
qual é feita através de um botão para alterar o estado de um LED.
Para leitura, podem-se acessar os registradores de I/O como simples variáveis. Em códigos
fonte, o acesso de leitura é feito pela função inp(). Em versões mais atuais do compilador o acesso
pode ser feito de forma direta e a função inp() não é mais necessária, como observado no código
abaixo.
#include <arv/io.h>
Uint8_t foo;
...
int main(void)
{
// Copia o status dos pinos da porta B na variavél foo
foo = PINB
...
}
Para escrita, podem-se acessar os registradores de I/O como simples variáveis. Em códigos fonte
o acesso de leitura é feito pela função outp(). Em versões mais atuais do compilador o acesso pode
ser feito de forma direta e a função outp() não é mais necessária, como observado a seguir.
#include <arv/io.h>
...
int main(void)
{
DDRA = 0xff; // Define todos os pintos como saída
PORTA = 0x03 // Define o nivel de lógico de cada saída
...
}
Uma das maneiras de se alterar o valor de um bit individual é através de operações lógicas.
No exemplo abaixo, temos que o segundo bit do registrador está sendo ativado e, em seguida,
está sendo desativado.
#include <arv/io.h>
#define MyBIT 2
Há três endereços de memória (registradores) que são alocados para cada uma das portas do
AVR: PORTx (Data Register), DDRx (Data Direction Register) e PINx (Port Inputs Pins). Este
último só é utilizado para leitura enquanto que os outros dois podem ser utilizados tanto para leitura
como escrita. Estes estão especificados na Tabela 5.1.
Para definir os pinos de 0 a 4 da porta B como saída, podemos utilizar os seguintes comandos:
DDRB=0x0F; ou DDRB = (1 << DDB0)|(1 << DDB1)|(1 << DDB2)|(1 << DDB3);
Assim, para definir uma saída de nível lógico alto, podemos utilizar os seguintes comandos:
Interrupções são eventos de software ou de hardware que ocasionam uma mudança no fluxo do
programa para uma sub-rotina que tratará esse evento.
RESET
External interrupt 0 - (INT0_vect)
External interrupt 1 - (INT1_vect)
Timer/Counter 2 Compare match - (TIMER2_COMP_vect)
Timer/Counter 2 overflow - (TIMER2_OVF_vect)
Timer/Counter 1A Compare match - (TIMER1_COMPA_vect)
Timer/Counter 1B Compare match - (TIMER1_COMPB_vect)
Timer/Counter 1 overflow - (TIMER1_OVF_vect)
Timer/Counter 0 overflow - (TIMER0_OVF_vect)
UART indication receive - (USART_RXC_vect)
UART indication sent - (USART_TXC_vect)
UART data pointer empty - (USART_UDRE_vect)
Este registrador contém os bits de controle para interrupções e funções gerais do MCU.
Bit 3,2 – ISC11, ISC10: Interrupt Sense Control 1 Bit 1 and Bit 0
A interrupção externa 1 é ativada pelo pino INT1 (PD3) caso o 1-bit de SREG e a mascara de
interrupção correspondente no registrador GICR está ativados. Para mais informações de como gerar
uma interrupção a partir de INT1, observar a Tabela 6.1.
Tabela 6.1 – Bits de controle para a interrupção externa 1
Bit 1,0 – ISC01, ISC00: Interrupt Sense Control 0 Bit 1 and Bit 0
A interrupção externa 0 é ativada pelo pino INT0 (PD2) caso o 1-bit de SREG e a mascara de
interrupção correspondente no registrador GICR está ativados. Para mais informações de como gerar
uma interrupção a partir de INT0, observar a Tabela 6.2.
Quando este bit é ativado junto com o 1-bit de SREG, temos que o pino de interrupção externa é
ativado, no caso, o pino PD3. Os bits de controle ISC11 e ISC10 vão definir o modo como a
interrupção será ativada.
Quando este bit é ativado junto com o 1-bit de SREG, temos que o pino de interrupção externa é
ativado, no caso, o pino PD2. Os bits de controle ISC01 e ISC00 vão definir o modo como a
interrupção será ativada.
Quando um evento no pino INT1 dispara um pedido de interrupção, temos que ITNF1 é ativado.
Se o 1-bit de SREG e o bit INT1 do GICR estão ativados, o MCU vai para o correspondente vetor de
interrupção. O flag é desativado quando a rotina de interrupção é executada.
Quando um evento no pino INT0 dispara um pedido de interrupção, temos que ITNF0 é ativado.
Se o 1-bit de SREG e o bit INT0 do GICR estão ativados, o MCU vai para o correspondente vetor de
interrupção. O flag é desativado quando a rotina de interrupção é executada.
Para nosso exemplo, utilizaremos a interrupção para alterarmos o valor dos pinos da porta B à
medida que fazemos uma transição de 0 para 5V na porta PD2. Antes de qualquer coisa habilitamos a
interrupção geral no registrador SREG (Status-Register, Accumulator flags) através do comando
sei(), para mais detalhes: http://www.avr-asm-tutorial.net/avr_en/beginner/PDETAIL.html#top.
int main(void)
{
// Configurando a interrupcao
sei(); // habilitacao global das interrupcoes
MCUCR = _BV(ISC01) | _BV(ISC00); // Transicao positiva em PD2
(INT0)
GICR = _BV(INT0); // habilita interrupcao INT0
DDRB = _BV(DDB1); // Definindo Port B como saída
for(;;)
{ }
}
Por último, é comum encontrarmos exemplos que usam as macros INTERRUPT() ou SIGNAL()
em programas mais antigos. A diferença de uma e da outra é que a primeira permite que outras
interrupções ocorram durante outra interrupção que ainda está sendo executada, enquanto que a
segunda não permite assim como IST() que é a versão mais nova de SIGNAL().
7. Timers/Counters
Os timers são facilidades presentes no AVR responsáveis por realizar contagens de eventos
externos, geração de freqüência e de sinais PWM (Pulse Width Modulation) e outros.
Tais valores podem ser o máximo valor disponível pelo contador, constantes, valores de outros
registradores ou zero. O AVR compara continuamente o valor do contador com algum outro
especificado por nós e, assim, pode realizar algum evento caso ocorra uma igualdade.
Cada timer difere na sua resolução (8 bits ou 16 bits), nos valores que podemos definir como
TOP, em quais I/O pinos são acessíveis pelo contador e quais eventos os timers podem fornecer
durante a contagem. Estes eventos podem ativar certo tipo de interrupção (a qual é a única forma para
executar um código arbritário) ou set/clear/toggle o valor de um I/O pino.
Há três tipos de timers no ATmega8: timer0 (8-bit), timer1 (16-bit) e timer2 (8-bit). O timer2
tem acesso a um I/O pino (OC2) e o timer1 tem acesso a dois I/O pinos (OC1A e OC1B). Enquanto
que o timer0 não possui acesso externo.
É o mais simples dos temporizadores no ATmega8 e não possui acesso a I/O pino. Assim, ele
não pode ser utilizado diretamente para PWM ou como um gerador de freqüência externa. O
registrador que armazena o valor da contagem é o TCNT0.
Em relação à forma que o contador pode ser clockeado, depende de valores atribuídos aos bits
CS02:0 que estão definidos no registrador de controle do timer0 (TCCR0). Basicamente, há duas
formas: internamente (diretamente ou por frações do clock interno) ou externamente.
De qualquer forma, o timer0 não apresenta uma unidade de comparação, ou seja, ele
simplesmente inicia a contagem de 0 (BOTTOM) a 255 ou 0xFF (MAX) repetidamente. Há somente
uma interrupção quando ocorre overflow (retorna de 255 a 0). Além disso, a freqüência de operação é
igual a de entrada divida por 256.
7.1.1.2 – Timer/Counter Register Description
Esses três bits selecionam a fonte do clock a ser usado pelo contador. Para informações
complementares, observar Tabela 7.1.
Quando o TOIE0 (Timer/Counter0 Overflow Interrupt Enable) é ativado, temos que uma
interrupção pode ser executada se ocorrer um overflow no timer2.
IV) Timer/Counter Interrupt Flag Register – TIFR
7.1.2 Timer2
A Unidade de Comparação continuamente compara TCNT2 com OCR2. Toda vez que ocorre
uma igualdade entre estes dois registradores, OCF2 = 1 para o próximo ciclo de clock. Caso OCIE2 =
1, temos que uma interrupção pode ser gerada. Assim, após a execução da interrupção, temos que
OCF2 = 0.
NORMAL MODE
Pode ser utilizado para uma base de tempo bem variável ou geração de uma onda
quadrada de freqüência variável no pino OC2;
O contador sempre incrementará;
Recomeça a contagem quando TCNT2 = OCR2;
Pode disparar alguma interrupção quando TCNT2 = OCR2 ( e overflows se OCR2 =
MAX);
Freqüência = clock/(2 * prescale * ( 1 + OCR2) );
Possibilita a geração da forma de onda PWM com alta resolução no pino OC2;
A forma de onda de portadora é triangular (dual-slope operation), ou seja, o contador
incrementa do BOTTOM ao MAX e, em seguida, do MAX ao BOTTOM;
OC2 sofre o toggle quando TCNT2 = OCR2 durante o processo de incremento ou
durante o processe de decremento;
Preferido para aplicações que envolvam o controle de motores;
Freqüência = clock/(prescale * 510) ;
Normal mode e CTC mode são bons para temporização interna e não necessita do pino OC2, ao
menos que se deseja fazer uma geração de freqüência externa. Os outros dois modos necessitam que o
pino OC2 esteja conectado a algo.
O bit FOC2 é somente ativo quando WGW bits especificam Non-PWM mode (Normal ou CTC
Mode). Quando ativado, imediatamente uma comparação é forçada, logo, OC2 vai apresentar um
comportamento de acordo com valores presentes nos bits COM2:0.
Estes bits são responsáveis por controlar a seqüência de contagem do contador, definir o valor de
TOP e qual o tipo de geração da forma de onda a ser utilizada. Informações complementares seguem
na Tabela 7.3.
Esses três bits selecionam a fonte do clock a ser usado pelo contador. Para informações
complementares, observar Tabela 7.7.
Oferece direto acesso e permite operações de escrita e leitura para o contador. Escrevendo no
registrador, temos que a comparação entre TCNT2 e OCR2 é bloqueada no próximo intervalo de
clock.
Quando TOIE2 (Timer/Counter2 Overflow Interrupt Enable) é ativado, temos que uma
interrupção pode ser executada se ocorrer um overflow no timer2.
Quando OCF2 (Output Compare Flag 2) é ativado, uma interrupção será executada quando a
igualdade entre TCNT2 e OCR2 for válida, logo OCIE2 também precisa está ativado.
Quando TOV2 (Timer/Counter2 Overflow Flag 2) é ativado, uma interrupção será executada
quando ocorrer um overflow no timer2, logo TOIE2 também precisa está ativado.
7.1.3 Timer1
Um ponto importante a se ressaltar é capacidade de gerar sinais PWM de forma mais eficiente
que o timer2. Isso se deve há presença de duas unidades de comparação (OC1A e OC1B) e, além
disso, com 16 bits em vez de 8 aumenta-se a quantidade de diferentes de duty cycles disponíveis (256
para 65536).
Em relação aos registradores, temos que TCNT1 (Timer/Counter 1), OCR1A/B (Output
Compare Registers) e o ICR1 (Input Capture Register 1) são todos de 16 bits. Há também o
TCCR1A/B (Timer/Counter Control Registers), mas esses são de 8 bits. As interrupções são realizadas
mediante aos bits de controle presentes nos registradores TIFR e TIMSK.
Sabe-se que os valores armazenados em OCR1A/B são comparados com valor presente no
contador todo o tempo. O resultado dessa comparação pode ser utilizado na geração de formas de onda
ou um sinal PWM.
O valor de TOP, ou o máximo valor permitido pelo contador, pode, em alguns modos de
operação, ser definido pelo registrador OCR1A, pelo ICRI ou por valores já fixados. No entanto, a
utilização de cada um pode causar algumas desvantagens, por exemplo, se utilizarmos o OCR1A como
TOP no modo PWM, este não pode ser utilizado para a geração de sinais PWM na saída. Além de
TOP, algumas outras definições para o timer0 são importantes de serem analisadas, como podemos
observar na Tabela 7.8.
Em relação à forma que o contador pode ser clockeado, depende de valores atribuídos aos bits
CS12:0 que estão definidos no registrador de controle B do timer0 (TCCR1B). Basicamente, há duas
formas: internamente (diretamente ou por frações do clock interno) ou externamente.
A seqüência de contagem será determinada a partir dos valores atribuídos aos bits WGM13:0, os
quais estão definidos nos registradores de controle A e B do timer0 (TCCR1A e TCCR1B).
Escolhendo o modo de operação é que vai ser possível determinar como será a forma de onda gerada
na saída. Além disso, a geração de uma interrupção é determinada a partir de uma flag especifica do
timer1 TOV1.
Assim, a partir da combinação entre os bits CS12:0 e WGM13:0, podemos definir um dos 5
modos de operação do contador. O primeiro grupo de bits não interfere na seqüência de contagem
enquanto que o segundo interfere.
Além disso, os bits CS12:0 se comportam de forma diferente entre os modos. Naqueles que
fornecem um sinal PWM na saída, temos que são responsáveis por determinar se a saída deve ser ou
não invertida. No outro caso, temos que são responsáveis por determinar se a saída vai ser ativada,
desativado ou invertida (toggle) quando a unidade de comparação informa que ocorreu uma igualdade.
Os diagramas de tempo para cada um dos modos pode ser visualizado do datasheet. As
características gerais de cada um podem ser visualizadas a seguir.
NORMAL MODE
Utilizado para geração de sinais PWM (Pulse Width Modulation) de alta freqüência;
Difere dos modos seguintes ao utilizar uma onda dente-de-serra (single-slope operation)
para geração dos sinais PWM;
O contador somente incrementará;
No modo não-inversor, temos que OC1x será desativado (cleared) quando TCNT1 =
OCR1x, enquanto que será ativado (set) quando o contador retorna ao BOTTOM. No
modo inversor, ocorre a situação contrária;
Utilizados para regulação de potência e retificação;
TOP é definido a partir dos valores atribuídos aos bits WGM13:0 podendo ser: valores
fixos definidos, ICR1 ou OCR1A. Ao atingir o TOP, o contador voltará a zero no ciclo de
clock seguinte;
TOV1 é ativado a cada vez que o contador atingir TOP. Além disso, as flag OCF1A e
ICF1 também são ativadas no mesmo ciclo de clock que TOV1 quando um deles é
definido como TOP;
Para sinas PWM, temos que freqüência = clock/(prescale * ( 1 + TOP) ) ;
PHASE CORRECT PWM MODE
Utilizado para geração de sinais PWM (Pulse Width Modulation) de alta freqüência;
Utiliza uma onda triangular (dual-slope operation) para geração dos sinais PWM, ou seja,
o contador incrementa do BOTTOM ao MAX e, em seguida, do MAX ao BOTTOM;
No modo não-inversor, temos que OC1x será desativado (cleared) quando TCNT1 =
OCR1x durante o processo de incremento, enquanto que será ativado (set) durante o
processo de decremento. No modo inversor, ocorre a situação contrária;
TOP é definido a partir dos valores atribuídos aos bits WGM13:0 podendo ser: valores
fixos definidos, ICR1 ou OCR1A (recomendado). Ao atingir o TOP, o contador voltará a
zero no ciclo de clock seguinte;
A atualização do registrador OCR1x pode ser feita durante um período;
Para sinas PWM, temos que freqüência = clock/(2* prescale * (TOP));
Utilizado para aplicações que envolvem controle de motores;
Utilizado para geração de sinais PWM (Pulse Width Modulation) de alta freqüência;
Utiliza uma onda triangular (dual-slope operation) para geração dos sinais PWM, ou seja,
o contador incrementa do BOTTOM ao MAX e, em seguida, do MAX ao BOTTOM;
No modo não-inversor, temos que OC1x será desativado (cleared) quando TCNT1 =
OCR1x durante o processo de incremento, enquanto que será ativado (set) durante o
processo de decremento. No modo inversor, ocorre a situação contrária;
TOP é definido a partir dos valores atribuídos aos bits WGM13:0 podendo ser: ICR1 ou
OCR1A;
A principal diferença, em relação ao modo anterior, é que a saída gerada será simétrica
em todos os períodos. Isso se deve ao fato que o valor dos registradores OCR1x só são
atualizados no fim de um período, garantido assim a simetria da saída;
Para sinas PWM, temos que freqüência = clock/(2* prescale * (TOP));
Utilizado para aplicações que envolvem controle de motores;
Esses pares de bits controlam o comportamento dos pinos OC1A e OC1B respectivamente. Além
disso, o modo de operação é fundamental para determina esse junto com os bits como podemos ver nas
Tabelas 7.9, 7.10 e 7.11.
Tabela 7.9 – Comportamento de OC1A e de OC1B para o Non-PWM Mode
Tabela 7.11 – Comportamento de OC1A e de OC1B para o Phase Correct e Phase and
Frequency Correct PWM Mode
Esses pares de bits somente são ativados quando estamos em Non-PWM mode. Assim, deve-se
garantir que eles estão desativados quando estivermos operando em modo PWM. Quando ativamos
FOC1A/FOC1B, força-se uma comparação que pode ser utilizada para gerar uma forma de onda. A
saída é determinada a partir da Tabela 09.
Combinado com os pares de bits WGM13:2 presentes no registrador TCCR1B, são responsáveis
por controlar a seqüência de contagem do contador, o valor definido para TOP e que tipo de geração
da saída (forma de onda) a ser usada. Para informações mais detalhadas, observar a Tabela 7.12.
Tabela 7.12 – Wave Generation Mode Bit Description
Quando ativado, requere-se que 4 amostras iguais na entrada para que se possa alterar a saída, ou
seja, ocorre uma filtragem para garantir a qualidade do sinal de entrada, no entanto, teremos um atraso
na resposta da saída.
O nível lógico deste bit indica qual a borda utilizada para definir um evento no pino ICP1. Caso
seja devido uma mudança 1 para 0 (borda negativa), devemos desativar ICES1. Caso contrário, seja
devido por borda positiva, devemos ativá-lo. Em cada um dos casos, temos que o valor do contador
será copiado para ICR1.
Este bit é reservado para uso futuro, logo se deve mantê-lo desativado.
Esses três bits selecionam a fonte do clock a ser usado pelo contador. Para informações
complementares, observar Tabela 7.13.
Oferecem direto acesso e permite operações de escrita e leitura para o contador de 16 bits.
Cada registrador armazena um valor de 16-bits que é constantemente comparado com o valor em
TCNT1. Uma igualdade entre os valores pode ser utilizada para gerar uma forma de onda (saída) no
pino OC1A ou OC1B.
V) Input Capture Register 1 – ICR1H e ICR1L
É atualizado com o valor armazenado em TCNT1 a cada vez que um evento ocorre no pino
ICP1. Além disso, esse registrador pode ser utilizado para definir o valor de TOP.
Quando TICIE1 (Input Capture Interrupt Enable) é ativado, temos que uma interrupção pode
ocorrer caso ocorra uma captura de evento no pino de entrada ICP1.
Quando OCIE1A (Output Compare A Match Interrupt Enable) é ativado, temos que uma
interrupção pode ocorrer se a igualdade entre TCNT1 e OCR1A for válida.
Quando OCIE1B (Output Compare B Match Interrupt Enable) é ativado, temos que uma
interrupção pode ocorrer se a igualdade entre TCNT1 e OCR1B for válida.
Quando TOIE1 (Overflow Interrupt Enable) é ativado, temos que uma interrupção pode ser
executada se ocorrer um overflow.
ICF1 (Input Capture Flag) é ativado quando ocorre a captura de um evento no pino ICP1, logo
pode ser usado para executar uma interrupção.
OCF1A (Output Compare A Match Flag) é ativado no ciclo de clock após a igualdade entre
TCNT1 e OCR1A é válida, logo podendo ser usado para executar uma interrupção caso OCIE1A
também esteja ativado.
OCF1B (Output Compare B Match Flag) é ativado no ciclo de clock após a igualdade entre
TCNT1 e OCR1B é válida, logo podendo ser usado para executar uma interrupção caso OCIE1B
também esteja ativado.
TOV1 (Overflow Flag 1) tem seu comportamento definido, ou seja, será ativado de acordo com
os WGM13:2 e WGM11:0. Além disso, TOIE1 também precisa está ativado. Para maiores detalhes,
observar a Tabela 12.
Para melhor compreender a forma de utilizar os registradores de controle junto com os timers
para a geração de sinais PWM, vamos analisar o seguinte código. De forma geral, podemos dizer que o
programa se resume a definir os registradores de controle para o timer1 com o propósito de utilizar o
“Phase and Frequency Correct PWM Mode” e, em seguida, determina uma interrupção para quando
ocorrer um overflow no contador.
#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#define OC1A 1
#define OC1B 2
void inicializa_pwm(void)
{
OCR1A = 0;
OCR1B = 0;
switch(sentido)
{
case 1:
if(++pwm == ICR1)
sentido = 0;
break;
case 0:
if(--pwm == 0)
sentido = 1;
break;
}
OCR1A = pwm;
OCR1B = pwm;
}
while(1)
sleep_mode();
return(0);
}
A USART difere da tradicional RS232 somente pelos níveis de tensão utilizados (na RS232 „0‟ є
(3V,25V) e „1‟ є(-3V,-25V) ). Desta forma, com o auxilio de apenas um adaptador de nível de tensão
(MAX232, por exemplo), podemos acoplar um PC diretamente a um AVR através da porta serial.
Existem diversas configurações possíveis para a interface USART, dado que se deve escolher
tanto entre o modo síncrono (clock MASTER ou SLAVE) ou o assíncrono de operação, quanto às
características de protocolo como nº de bits de dados, nº de bits de parada, tipo de paridade e taxa de
transmissão. O controle dessas variáveis, que definem a transmissão, é realizado obviamente através
de registradores.
No entanto, antes de descrever estes registradores é importante saber como determinar o fator
UBRR (USART Baud Rate Register). Ele especifica a taxa de amostragem da porta, portanto é
fundamental inicializá-lo com um valor correto, para que transmissor e receptor estejam
sincronizados. Como o AVR possui um clk interno, o calculo de UBRR está atrelado a este valor,
dessa forma podemos calcular UBRR:
É importante lembrar que esta formula é adequada para o modo de operação que seguiremos
neste tutorial. Caso fosse utilizado um modo alternativo, como o Asynchronous Double Speed
Mode(fator 8 ao invés de 16 no denominador), ou os Modos síncronos obteríamos uma formula
diferente (fator 2 ao invés de 16 no denominador).
Registrador que contém os dados recebidos e a serem enviados. Apesar de estarem endereçados
no mesmo registrador, os dados recebidos e transmitidos estão atrelados a pinos diferentes da porta
D: RXD(PD0) e TXD(PD1).
Deve-se ressaltar que só é permitido utilizar a operação de escrita quando a flag UDRE alocada
no registrador UCSRA está ativada, se não, a mesma é simplesmente ignorada.
Este bit sinaliza quando há dados não-lidos no buffer de entrada. Pode ser utilizado como flag de
interrupção através do bit RXCIE alocado no registrador UCSRB.
Este bit sinaliza quando o buffer de transmissão está vazio. Pode, da mesma forma, ser utilizado
como flag de interrupção através do bit TXCIE alocado no registrador UCSRB.
Indica quando o buffer de transmissão esta pronto para receber dados. Basicamente nos diz que o
AVR está pronto para enviar uma mensagem nova. Também é um flag que, se habilitado através do bit
UDRIE alocado no registrador UCSRB, pode ser utilizado para realizar interrupções.
Sinaliza quando ocorre um erro no formato da recepção, ou seja, o primeiro bit de parada possui
um valor incorreto. Este flag só é válido enquanto o valor de UDR não é lido, portanto, para ser
monitorado, este deve ser lido antes de UDR, o mesmo vale para DOR e PE. É importante que quando
formos escrever em UCSRA, deveremos escrever 0 neste bit.
Indica que houve uma indicação de nova mensagem enquanto os buffers de entrada estavam
cheios, ou seja, uma nova mensagem chegou e não pôde ser lida. É importante que quando formos
escrever em UCSRA, deveremos escrever 0 neste bit.
Avisa quando ocorre um erro de paridade na mensagem recebida. É importante que quando
formos escrever em UCSRA, deveremos escrever 0 neste bit.
Quando é ativado, temos que este bit habilita interrupções para a flag RCX.
Quando é ativado, temos que este bit habilita interrupções para a flag TCX.
Quando é ativado, temos que este bit habilita interrupções para a flag UDRE.
Ativa o receptor da USART. Caso não seja ativado, o pino comporta-se como um i/o
normalmente.
Ativa o transmissor da USART. Caso não seja ativado, o pino comporta-se como um i/o
normalmente. Entretanto, enquanto houver mensagens pendentes ou mensagens sendo transmitidas não
surtirá efeito.
Combinado com UCSZ1:0, o qual está alocado no registrador UCSRC, define a quantidade de
bits de dados em cada mensagem.
Comporta-se como nono bit de dados recebido quando a quantidade de bits de dado em cada
mensagem é igual a 9. Deve ser lido antes de UDR, pois após a leitura deste, RXB8 é apagado.
Mesma função de RXB8, mas para transmissão. Neste caso, deve ser escrito antes de UDR, para
que seja mandado.
Existe um detalhe com relação ao UCSRC. Ele compartilha com UBRRH a mesma localização
de i/o. Sendo assim, para escrevê-lo, é necessário ativar URSEL, caso contrário o valor será escrito em
UBRRH.
Bit 7 - URSEL – Register Select
Como explanado anteriormente, este bit seleciona qual dos registradores será escrito.
Estes dois bits definem os bits de paridade da mensagem. Para maiores informações, observar a
Tabela 8.1.
Como exposto anteriormente, junto com UCSZ2 estes bits definem o Character Size, ou seja, a
quantidade de bits de dados em cada mensagem. Para maiores informações, observar a Tabela 8.2.
Define as bordas utilizadas para amostragem e transmissão de dados quando se utiliza o modo
síncrono. Deve ser escrito como 0 quando o modo a ser utilizado é o assíncrono. Para maiores
informações, observar a Tabela 8.3.
São os registradores que armazenam o valor que será utilizado para calcular a taxa de
amostragem e transmissão de dados, de acordo com a fórmula apresentada anteriormente.
UBRRH constitui os 4 bits mais significativos de UBRR. Além disso, temos que definir URSEL
como 0 para que a operação de escrita seja em UBRRH e não em UCSRC. Os outros bits são
reservados, mas devem ser escritos como 0 quando estivermos escrevendo em UBRRH por segurança.
Além disso, temos que UBRRL armazena os bits menos significativos de UBRR.
Agora um programa que recebe uma mensagem. Neste caso, devemos habilitar a RXC, que
sinalizará a chegada de uma mensagem e habilitar o próprio receptor.
Figura 8.2 – Segundo programa para receber mensagem pela interface serial
Os dois programas anteriores realizam suas tarefas de enviar e receber mensagens, entretanto
este código pode ser considerado extremamente ineficiente. Numa aplicação real, é bastante provável
que diversas mensagens sejam enviadas e recebidas.
Nos exemplos anteriores, o tempo gasto para esperar as sinalizações de que existia uma
mensagem nova ou que o AVR estava pronto para enviar mensagens era tempo gasto de CPU, ou
seja, o processador estava exclusivamente dedicado à incumbência de monitorar estes eventos,
impossibilitando a realização de outras tarefas.
Sendo assim, é mais eficiente utilizar os recursos disponíveis de interrupção, desta forma, é
possível nos dedicarmos a processar outras tarefas enquanto novas mensagens não chegam ou
enquanto não podemos enviar novas mensagens. Para tal, será utilizada a macro ISR (Nome do vetor
de interrupção), onde também necessitaremos de outra macro, a sei (void), para habilitar as
interrupções. Cli (void) é a macro que devemos utilizar para desabilitar as interrupções.
Outro aspecto importante é o consumo. Supondo que tivéssemos um sistema simplesmente
reativo, ou que, apenas enviasse mensagens na ocorrência de novas mensagens recebidas, poderíamos
simplesmente deixar o AVR em standby até a ocorrência destes eventos, economizando bastante
energia e, muitas vezes até viabilizando aplicações que sofreriam por limitações de bateria e etc. Para
isto, é necessário acrescentar duas bibliotecas: a <avr/interrupt.h> e a <avr/sleep.h>.
Agora escreveremos um programa que simplesmente espera por uma mensagem e responde a
mesma mensagem.
Observe que, desta vez, habilitamos tanto o receptor como o transmissor, além da flag de
interrupção RXC, mantendo a mesma configuração de transmissão dos exemplos anteriores. Observe
também que o programa principal tornou-se leve, já que o loopback da mensagem é realizado por
interrupção.
Figura 8.3 – Terceiro programa para receber mensagem e enviá-la pela interface serial
9. Conclusão
Este tutorial procurou desenvolver uma breve descrição das características do AVR e, em
seguida, aprofundar um pouco mais a forma como o AVR as disponibiliza para os seus usuários.
Assim, servindo como fonte de consulta durante o desenvolvimento dos protótipos no decorrer de sua
participação no grupo de Instrumentação.
Acredita-se que há ainda outros diversos pontos a serem abordados dentro deste tutorial a fim
de, cada vez mais, deixá-lo mais completo, logo se espera lançar novas versões à medida que o grupo
obter novas informações sobre o microcontrolador.
A partir das informações presentes nesse tutorial, espera-se que o leitor se estimule a buscar
outras fontes de consultas na internet ou no próprio datasheet do AVR com o objetivo de incrementar
seu conhecimento teórico e, assim, iniciar o desenvolvimento de projetos práticos utilizando o
microcontrolador como, por exemplo, a construção de um carrinho de controle remoto.
10. Bibliografia