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 <util/setbaud.h>
void
uart_init(void) {
UBRRH = UBRRH_VALUE;
UBRRL = UBRRL_VALUE;
#if USE_2X
UCSRA |= _BV(U2X);
#else
UCSRA &= ~(_BV(U2X));
#endif
UCSRC = _BV(URSEL) | _BV(USBS) | _BV(UCSZ1)
| _BV(UCSZ0); // format 8N1
UCSRB = _BV(RXEN) | _BV(TXEN); /* 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) {
loop_until_bit_is_set(UCSRA, UDRE); /* Wait
until data register empty. */
UDR = c;
}
Opção 2, através do registrador TXC:
void
uart_putchar(char c) {
UDR = c;
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) {
loop_until_bit_is_set(UCSRA, RXC); /* Wait
until data exists. */
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) {
loop_until_bit_is_set(UCSRA, UDRE);
UDR = c;
}
char
uart_getchar(FILE *stream) {
loop_until_bit_is_set(UCSRA, RXC); /* Wait
until data exists. */
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) {
uart_init();
stdout = &uart_output;
stdin
= &uart_input;
char input;
while(1) {
puts("Hello world!");
input = getchar();
printf("You wrote %c\n",
input);
}
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 UCSZ1 e UCSZ0 para configurar o tamanho do frame. Tamanhos disponíveis são 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.
|
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 UCSZ2 para configurar o tamanho do frame. Tamanhos disponíveis são 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 de descida na transmissão e subida na recepção.
|
Dúvidas como programar Arduino no AVR Studio? Veja as aulas abaixo:
Gostei Renato, está bem feito seu trabalho. Parabéns
ResponderExcluirAlvaro.site90.com/alvieletronica.html
alvaroluiz2@yahoo.com.br