Serial do Arduino Usando AVR Libc
Autor: Renato Aloi | Publicado: 2016-05-16T10:30:00-07:00






Este artigo vai para quem utiliza as placas do Arduino para
desenvolvimento de hardware, mas que não gosta muito são das bibliotecas do
Arduino. Pois elas oferecem apenas encapsulamentos sobre as funções do AVR
Libc, ou então apenas reescrevem as funções que a Libc já provém. 



Comunicação Serial é um bom exemplo disso. O Arduino fornece
sua própria implementação de funções como Serial.print(), Serial.println() e
Serial.read(). Ao mesmo tempo a AVR Libc já provê funções como printf(), puts()
e getchar(). Este artigo explica quão fácil é implementar essas funções da Libc
na comunicação Serial da plataforma megaAVR da Atmel.

Se você não possui muita experiência em programação é melhor
provavelmente você continuar usando as bibliotecas do Arduino. Elas são boas em
esconder as características mais complicadas da programação embarcada.
Entretanto você deve migrar enquanto amadurece no desenvolvimento dos seus
projetos. Os datasheets da Atmel não são tão complicados como parecem de
início. E você deve estudar também o código pronto deste artigo.





Configurando UART



Microcontroladores AVR possuem três registradores de
controle e de status. O registrador UCSRA normalmente contém dos dados de
status. Já os registradores UCSRB e UCSRC contém os conjuntos de configurações.
Veja as tabelas no fim deste artigo para conhecer todos os possíveis valores.

A AVR Libc fornece macros auxiliares para cálculos de ‘baud
rate’, incluindo em um arquivo de cabeçalho chamado ‘setbaud.h’, que precisa que
as macros F_CPU e BAUD sejam definidas. Ao incluir esse arquivo de cabeçalho,
as macros UBRRL_VALUE, UBRRL_VALUE e USE_2X são definidas. As duas primeiras
são usadas para configurar a velocidade da Serial. A última é usada para
determinar se a Serial deve ser configurada para rodar no dobro da velocidade,
dado um determinado baud rate.



UCSZ2, UCSZ1 e UCSZ0 são registradores que controlam o
tamanho dos dados. São possíveis as configurações de 5-bit (000), 6-bit (001),
7-bit (010), 8-bit (011) e 9-bit (111). A mais comum é o tamanho de 8-bit. O
formato mais conhecido e utilizado de comunicação Serial é o 8N1, ou seja,
8-bit, sem paridade e com 1-bit de parada. O que configura o bit de parada para
1-bit é o registrador USBS. E os registradores que configuram o modo de
paridade são os UPM1 e UPM0. Para o modo 8N1 basta não configurar nada nesses
registradores, ou seja, ambos precisam ser zero para desativar a paridade.



Utilizando os bits ensinados acima, podemos então configurar
a Serial:



#define
F_CPU 16000000UL

#define
BAUD 9600



#include &lt;util/setbaud.h&gt;




void
uart_init(void) {

&nbsp;&nbsp;&nbsp; UBRRH = UBRRH_VALUE;

&nbsp;&nbsp;&nbsp; UBRRL = UBRRL_VALUE;

#if USE_2X

&nbsp;&nbsp;&nbsp; UCSRA |= _BV(U2X);

#else

&nbsp;&nbsp;&nbsp; UCSRA &amp;= ~(_BV(U2X));

#endif

&nbsp;&nbsp;&nbsp; UCSRC = _BV(URSEL) | _BV(USBS) | _BV(UCSZ1)
| _BV(UCSZ0);&nbsp; // format 8N1

&nbsp;&nbsp;&nbsp; UCSRB = _BV(RXEN) | _BV(TXEN);&nbsp;&nbsp; /* Enable RX and TX */

}



Lendo e Escrevendo na Serial



Você pode transmitir dados pela Serial escrevendo bytes no
registrador de dados UDR. Primeiro você deve certificar que a Serial está
pronta para transmitir novos dados (ou seja, que já transmitiu os dados
anteriores). 



Aqui você tem duas opções, esperar que o registrador de
dados vazios UDRE seja ativado, ou você pode, após o envio de cada byte,
esperar a confirmação da transmissão, através do registrador de transmissão
completada TXC, que é ativado quando a transmissão foi concluída.



Opção 1, através do registrador UDRE:



void
uart_putchar(char c) {

&nbsp;&nbsp;&nbsp; loop_until_bit_is_set(UCSRA, UDRE);&nbsp;/* Wait
until data register empty. */

&nbsp;&nbsp;&nbsp; UDR = c;

}



Opção 2, através do registrador TXC:



void
uart_putchar(char c) {

&nbsp;&nbsp;&nbsp; UDR = c;

&nbsp;&nbsp;&nbsp; loop_until_bit_is_set(UCSRA, TXC); /* Wait
until transmission ready. */

}



Para receber dados pela Serial, você pode ler bytes do
registrador de dados UDR. O processo de recebimento concluído é indicado pelo
registrador RXC que é ativado quando existe dados disponíveis na serial.



char
uart_getchar(void) {

&nbsp;&nbsp;&nbsp; loop_until_bit_is_set(UCSRA, RXC); /* Wait
until data exists. */

&nbsp;&nbsp;&nbsp; return UDR;

}



Redirecionando Fluxo de Dados Para a Serial



Existe uma macro chamada FDEV_SETUP_STREAM que pode ser
usada para configurar um buffer que é válido para operações de entrada/saída
padrão. O buffer iniciado deve ser do tipo FILE. Você pode definir buffers separados
para entrada e para saída. Ou você pode definir um único buffer que funciona
para ambos, tanto para entrada como para saída. O primeiro e segundo parâmetro
da função recebem os nomes das funções a serem chamadas quando data está para
ser lida ou escrita no buffer.



Utilizando um buffer separado para cada operação:



FILE
uart_output = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);



FILE
uart_input = FDEV_SETUP_STREAM(NULL, uart_getchar, _FDEV_SETUP_READ);



Utilizando um buffer único para entrada e para saída:



FILE
uart_io = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);



Para preparar as nossas funções uart_putchar e uart_getchar para
serem usadas com os fluxos de entrada/saída, temos que mudar as definições um
pouco. 



void
uart_putchar(char c, FILE *stream) {

&nbsp;&nbsp;&nbsp; loop_until_bit_is_set(UCSRA, UDRE);

&nbsp;&nbsp;&nbsp; UDR = c;

}



char
uart_getchar(FILE *stream) {

&nbsp;&nbsp;&nbsp; loop_until_bit_is_set(UCSRA, RXC); /* Wait
until data exists. */

&nbsp;&nbsp;&nbsp; return UDR;

}



Agora podemos redirecionar ambos STDIN e STDOUT para a
Serial. Isso nos habilita a usar as funções de leitura e escrita da AVR Libc.



int
main(void) {

&nbsp;&nbsp;&nbsp; uart_init();

&nbsp;&nbsp;&nbsp; stdout = &amp;uart_output;

&nbsp;&nbsp;&nbsp; stdin&nbsp;
= &amp;uart_input;

&nbsp;&nbsp;&nbsp; char input;

&nbsp;&nbsp;&nbsp; while(1) {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; puts("Hello world!");

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input = getchar();

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("You wrote %c\n",
input);

&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp; return 0;

}



Registradores de Controle e Status



 

   
UCSRA Bit #

   
Nome

   
Descrição

  

 

  
bit 7

  
RXC

  
USART Recebimento Completo. Ligado quando tem dados disponíveis no registrador de dados que não foram lidos ainda.

 

  
bit 6

  
TXC

  
USART Transmissão Completa. Ligado quando todos os dados foram transmitidos.

 

  
bit 5

  
UDRE

  
USART Registro de Dados Vazio. Ligado quando o registrador UDR está vazio e novos dados podem ser transmitidos.

 

  
bit 4

  
FE

  
Erro de Frame. Ligado quando próximo byte no registrador de dados UDR possui erro de frame.

 

  
bit 3

  
DOR

  
Dados Atropelados. Ligado quando o registrador UDR não foi lido antes do próximo frame ter chegado.

 

  
bit 2

  
PE

  
USART Erro de Paridade. Ligado quando o próximo frame no registrador UDR possui erro de paridade.

 

  
bit 1

  
U2X

  
USART Velocidade Dupla de Transmissão. Quando ligado diminui o tempo de bit pelo dobro da velocidade.

 

  
bit 0

  
MPCM

  
Modo de Comunicação Multi-processamento. Quando ligado os dados recebidos são ignorados se não possuírem informação de endereço.

 




 

   
UCSRB Bit #

   
Nome

   
Descrição

  

 

  
bit 7

  
RXCIE

  
RX Ativação da Interrupção de Conclusão. Ligue para permitir interrupção quando recebimento completo.

 

  
bit 6

  
TXCIE

  
TX Ativação da Interrupção de Conclusão. Ligue para permitir interrupção quando transmissão completa.

 

  
bit 5

  
UDRIE

  
USART Registrador de Dados Vazio com Interrupção Ativa. Ligue para permitir interrupção quando registrador de dados vazio.

 

  
bit 4

  
RXEN

  
Recebimento Ativo. Ligue para ativar o recebimento.

 

  
bit 3

  
TXEN

  
Transmissão Ativa. Ligue para ativar a transmissão.

 

  
bit 2

  
UCSZ2

  
USART Tamanho do Caractere. Utilize em conjunto com&nbsp;UCSZ1 e UCSZ0 para configurar o tamanho do frame. Tamanhos disponíveis são&nbsp;5-bit (000), 6-bit (001), 7-bit (010), 8-bit
  (011) e 9-bit (111).



 

  
bit 1

  
RXB8

  
Receptor do Bit 8 de Dados. Quando usando modo de 9-bit, este registrador recebe o nono bit do recebimento do frame.

 

  
bit 0

  
TXB8

  
Transmissor do Bit 8 de Dados.&nbsp;Quando usando modo de 9-bit, este registrador envia o nono bit da transmissão do frame.

 




 

   
UCSRC Bit #

   
Nome

   
Descrição

  

 

  
bit 7

  
URSEL

  
Seletor de Registrador. Ative esse registro quando escrevendo no registrador UCSRC, pois esse registrador é compartilhado com o UBRRH.

 

  
bit 6


  
UMSEL


  
USART Seleção de Modo. O bit seleciona entre modos (0) assíncrono ou (1) síncrono.



 

  
bit 5
bit 4

  
UPM1
UPM0

  
USART Modo de Paridade. UPM1:0 selecionam o modo de paridade, sendo (00) nenhum, (10) par (11) ímpar.

 

  
bit 3

  
USBS

  
USART Seletor do Bit de Parada. Escolhe a quantidade de bits de parada, (0) 1 bit e (1) 2 bits.

 

  
bit 2
bit 1

  
UCSZ1
UCSZ0

  
USART Tamanho do Caractere. Utilize em conjunto com&nbsp;UCSZ2&nbsp;para configurar o tamanho do frame. Tamanhos disponíveis são&nbsp;5-bit (000), 6-bit (001), 7-bit (010), 8-bit (011) e 9-bit (111).

 

  
bit 0

  
UCPOL

  
USART Polaridade de Clock. Utilizado apenas em modo síncrono, sendo (0) borda de subida na transmissão e descida na recepção e (1) borda&nbsp;de descida na transmissão e subida na recepção.

 



Dúvidas como programar Arduino no AVR Studio? Veja as aulas abaixo:


Curso Arduino Express Aula 5 - Arduino vs. Atmel Studio 6, Parte 1/2 (seriallink.com.br)





Curso Arduino Express Aula 5 - Arduino vs. Atmel Studio 6, Parte 2/2 (seriallink.com.br)