Comunicação Serial

A principal característica da comunicação serial é o processo de enviar dados um bit de cada vez, de forma sequencial, através de um canal de comunicação ou barramento. Diferente da comunicação paralela, em que todos os bits de cada símbolo são enviados juntos.

Com a finalidade de possibilitar a conversão, transmissão e recepção de dados de forma serial, sendo estes originalmente dispostos de maneira paralela, surge o formato UART, acrônimo de Universal Asynchrounous Receiver/Transmiter ou Receptor/Transmissor Universal Assíncrono.

O termo «Universal» refere-se a característica do formato dos dados e velocidade serem configuráveis, enquanto «Assíncrono» diz respeito a forma de comunicação em série ocorre, em que os dispositivos não estão continuamente sincronizadas por um sinal de clock comum.

Nota

Mais detalhes sobre a comunicação UART podem ser vistos em Introduction to UART Communication - microcontrollerslab.com.

No caso do Overo, temos três sistemas de Comunicação UART implementados por hardware à nossa disposição. O que faz desnecessário qualquer implementação manual através de software utilizando GPIO.

Agora vamos entender como funciona o protocolo de comunicação UART. Essa comunicação funciona através da conexão do transmissor (TX) de um dispositivo com o receptor (RX) de outro dispositivo, no caso, apenas o TX realiza alterações no nível de tensão da linha, sendo assim, a comunicação é, para cada conexão, uma via de mão única. Logo, para realizar uma comunicação de mão dupla iremos utilizar duas conexões, uma será a ligação de RX do dispositivo 1 com o TX do dispositivo 2 e a outra ligação será o oposto, RX do dispositivo 2 com TX do dispositivo 1, similar a imagem abaixo.

../../../_images/UART_bus.png

Podemos analisar a situação da comunicação a nível de bit. Por exemplo, para uma comunicação UART do tipo «8N1» (8 bits de dados, 0 bits de paridade e 1 bit de parada) teremos o canal em estado IDLE, que significa «não operando», representado pelo nível de tensão estático em alto. Logo, quando pretende-se iniciar a comunicação é enviado um pulso de nível baixo e em seguida são enviados os oito bits de dados que será acompanhado de um bit de parada em estado alto.

É importante destacar que a função do bit de parada é realizar uma pausa na transmissão para algum processamento interno dos dispositivos, não é necessário, portanto, nenhum tempo adicional entre os dados transmitidos.

Como essa é uma comunicação assíncrona, é essencial que a velocidade da comunicação seja pré-determinada. Essa velocidade geralmente é dada em Baud rate, uma unidade de medida para a quantidade de unidades de sinal enviadas por segundo.

Nota

O termo «Baud rate» é utilizado como medida de velocidade de transmissão de dados entre dispositivos. Um baud é uma medida de velocidade de sinalização e representa o número de mudanças na linha de transmissão (seja em frequência, amplitude, fase etc…) ou eventos por segundo.

A figura a seguir apresenta um esquemático que ilustra o funcionamento da comunicação UART. Na ilustração a velocidade de comunicação é de 10.000.000 baud/s.

../../../_images/UART_8N1.png

Particularidades da Gumstix Overo

Por padrão, a placa de expansão Tobi disponibiliza apenas dois dos UARTs disponíveis no computador em modulo Overo para uso através dos seus pinos. Como pode ser visto na imagem abaixo, a UART1 está conectada aos pinos 10 e 9 e a UART3 está conectada aos pinos 22 e 21. Ainda é importante dizer que a porta serial UART3 é o mesmo pino utilizado pelo «USB console», ou seja, é o mesmo pino que usamos para controlar a Gumstix pelo computador, ou seja, em configuração padrão as mensagens do sistema e as mensagens para o sistema são enviadas por esta porta.

../../../_images/Pinos_Tobi.png

Diagrama dos pinos da placa de expansão tobi.

Caso as duas portas seriais já mencionadas não sejam suficientes, existe ainda a porta serial UART2. Porém, por padrão, está não está disponível em nenhum dos pinos da placa Tobi para nossa utilização. Na verdade, ela foi reservada para comunicar-se com o Bluetooth, contudo apenas versões posteriores do computador embarcado Overo por nos utilizado possuem Bluetooth, portanto podemos, caso necessário, exportar a UART2 para os pinos e utiliza-los. Para utilizar esta porta serial é necessário modificar o u-boot de modo a multiplexar sua função ao GPIO 146/147 que, como mostrado na figura anterior, estão ligados aos pinos 29 e 27. Portanto para fazê-lo é necessário modificar o arquivo «overo.h» removendo as linhas de comando referentes ao modo de GPIO dos pinos 146 e 147, remover as linhas que desabilitam a UART2 e adicionar as linhas que habilitam a comunicação serial pela UART2.

Para entender, detalhadamente, o que precisa ser feito e quais registradores serão alterados deve-se consultar a seção de comunicação serial do Technical Reference Manual (TRM) do processador. Contudo existe um tópico no Fórum de Discussões da Gumstix que indica diretamente quais alterações devem ser feitos no «u-boot» para que se possa utilizar a UART2, apesar disso a solução apresentada nesse fórum não foi testada durante este trabalho.

Configuração da UART

Como já comentado anteriormente, o computador embarcado possui um hardware específico para comunicação UART, ou seja, não é necessário realizar uma implementação manual, para utilizar a comunicação UART basta escrever em alguns registradores para enviar a mensagem.

Na verdade, em nosso caso é ainda mais simples pois o sistema operacional instalado já traz configurado drivers para a aplicação da comunicação serial. Portanto, não é necessário acessar a memória física do dispositivo, precisamos apenas escrever no driver o que deve ser transmitido.

Os drivers de comunicação serial são arquivos do tipo caractere com nome «ttyOx», em que «x» é um número exclusivo para cada uma das UARTs. Esses drivers estão localizados em «/dev» e funcionam como comunicação em terminal.

Por exemplo, o driver «ttyO2» é o driver de comunicação serial da porta «USB Console» a mesma que conectamos ao computador, ou seja, ao escrever nessa porta escreveremos no computador conectado à Gumstix e ao ler essa porta estaremos lendo o computador. Em outras palavras, escrever ou ler nesse driver terá o mesmo resultado final de chamar, respectivamente, a função printf() ou scanf(), quando um computador estiver conectado a essa porta com o terminal aberto.

A configuração das portas seriais pode ser feita de duas maneiras, por linhas de comando no terminal Linux ou por um código que altere as configurações do hardware. A mais simples e, novamente, mais limitada ou menos eficiente é a configuração por meio de linhas de comando, a configuração por esse modo costuma ser usada apenas quando feita por um usuário humano em tempo real.

Para realizar a configuração por meio do terminal Linux devemos utilizar o comando stty, já que esse comando possui uma enorme quantidade de parâmetros que permite estabelecer a comunicação serial da forma desejada.

Nota

Para visualizar todos os parâmetros do comando stty basta executar stty –help no terminal.

Se, por exemplo, for executada a linha de comando stty -F /dev/ttyO0 -a serão impressas todas as configurações da comunicação serial UART1 do dispositivo. Para imprimir apenas as principais configurações, deve-se suprimir o -a, a última opção do comando. Caso a alteração da velocidade seja desejável, ela pode ser alterada simplesmente acrescentando a velocidade desejada ao final da linha de comando.

A figura abaixo apresenta um exemplo de configuração da UART1 por meio do terminal de comandos Linux.

../../../_images/config_uart1.png

A outra maneira de configurar a comunicação serial feita por esses drivers sem alterar manualmente o conteúdo do endereço físico da memória é com o auxílio da biblioteca «termios.h». Essa biblioteca possui uma ampla variedade de funções que configuram a comunicação serial com base nos parâmetros de uma estrutura «termios», também definida nesta biblioteca.

Nota

Mais informações sobre a biblioteca «termios.h» podem ser encontrados em termios.h - Linux manual page.

São dois os parâmetros da comunicação UART, além dos mencionados anteriormente, que se destacam, o número mínimo de bits que se espera ler em cada tentativa de leitura e o tempo máximo de espera por um novo caractere após a transmissão do último caractere e após o número mínimo de caracteres ser atingido.

O número mínimo de bits que se espera ser lido e o tempo máximo de espera pelo próximo bit em décimos de segundo podem ser configurados com os seguintes comandos termios.c_cc[VMIN] = e termios.c_cc[VTIME] =, em que termios é o nome de sua estrutura. Para a configuração de velocidade recomenda-se usar a função cfsetspeed().Já a função cfmakeraw() configura, além de outros parâmetros, o funcionamento sem bit de paridade e com 8 bits de dados. Após realizados os ajustes na estrutura é necessário ainda executar a função cfsetattr() para que as alterações sejam feitas na UART.

Abaixo encontra-se o código utilizado para configurar a comunicação serial dos computadores Overo. Observe que nessa função de configuração não foi utilizada a flag «O_NONBLOCK» na função «open()» e foi definido como 1 o número mínimo de caracteres a serem retornados após uma tentativa de leitura, portanto caso o código seja executado e nenhuma informação seja enviada para este canal o processador aguardará eternamente por esse caractere. A contagem de tempo, definida como 0,1 segundo, só inicia após o número mínimo de caracteres ser atingido.

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>

void main()
{
    struct termios cUART1;
    int UART1 = open("/dev/ttyO0", O_RDWR);

    if(tcgetattr(UART1,&cUART1))
        printf("Erro tcgetattr");
    cfmakeraw(&cUART1);
    cfsetspeed(&cUART1,B115200);
    cUART1.c_cflag &= ~CSTOPB;

    cUART1.c_cc[VMIN] = 1;
    cUART1.c_cc[VTIME] = 1;
    if (tcsetattr(UART1, TCSANOW, &cUART1))
        printf("Erro tcsetattr");

}

Download do código comentado

A figura a seguir apresenta um exemplo de configuração da UART1 por meio do codigo de configuração acima.

../../../_images/config_uart1.png

Nota

Com a finalidade simplificar a configuração do UART dentro de um outro código, foram efetuadas algumas modificações no código anterior para converte-lo em uma função para configuração de comunicação serial, como pode ser visto abaixo:

int configUART1()
{
    struct termios cUART1;
    int UART1 = open("/dev/ttyO0", O_RDWR);

    if(tcgetattr(UART1,&cUART1))
        printf("Erro tcgetattr");
    cfmakeraw(&cUART1);
    cfsetspeed(&cUART1,B115200);
    cUART1.c_cflag &= ~CSTOPB;

    cUART1.c_cc[VMIN] = 1;
    cUART1.c_cc[VTIME] = 1;
    if (tcsetattr(UART1, TCSANOW, &cUART1))
        printf("Erro tcsetattr");

    return UART1;
}

Uma vez feita a configuração, foi implementado também o código a seguir com a finalidade de testar a comunicação entre dois computadores. No teste, um dispositivo envia uma mensagem para o outro dispositivo que responde com uma mensagem semelhante para o primeiro dispositivo, em seguida ambos os dispositivos imprimem a mensagem recebida.

int main()
{

    int UART1 = configUART1();  // call the UART configuration function
    char dis[2], out[100], string[100];

    printf("Que dispositivo eu sou?");
    scanf("%c", &dis[0]);
    dis[1] = 0;
    string[0] = 0;
    strcat(string, "Ola! Essa e uma mensagem do dispositivo ");
    strcat(string, dis);

    // testa UART
    write(UART1, string, strlen(string));
    sleep(1);
    read(UART1, out, 100);
    printf("Mensagem lida pelo dispositivo %s: %s\n", dis, out);
    close(UART1);
    return 0;
}

Download do código completo

Como os dois dispositivos são idênticos, será necessário conectar o pino 10 de um dispositivo com o pino 9 do outro dispositivo e vice-versa. Utilizando esse código como base é possível enviar qualquer mensagem de até 100 caracteres de um dispositivo ao outro.

A figura a seguir apresenta o resultado do teste dos códigos apresentados. Nessa figura podemos ver dois terminais do Linux, cada um vinculado a um computador embarcado, e ambos chamam a mesma função, logo em seguida vemos a mensagem lida por cada um dos dispositivos.

Referências