segunda-feira, 16 de maio de 2016

Serial do Arduino Usando AVR Libc


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. 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 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:

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)


Um comentário:

  1. Gostei Renato, está bem feito seu trabalho. Parabéns
    Alvaro.site90.com/alvieletronica.html
    alvaroluiz2@yahoo.com.br

    ResponderExcluir