Programando Protocolo Serial

 

 

Descrição dos Pinos:

TAB 1

 1

Detecção de portadora

CD

2

Recepção de dados

RXD

3

Transmissão de dados

TXD

4

Terminal de dados pronto

DTR

5

Terra do sinal

GND

6

Data set pront

DSR

7

Solicitação de envio

RTS

8

Pronto para enviar

CTS

9

Indicador de chamada

RI

 

 

 

Comunicação serial é chamada de assíncrona porque os dados podem ser enviados ou recebidos no momento em que os dispositivos precisarem.

 

 

 

FUNÇÕES DOS REGISTRADORES 

            O registrador no endereço de base atribuído a cada porta serial é usado para comunicações de dados. Os bytes são movidos para a UART usando as instruções OUT e IN do microprocessador. Os seis endereços seguintes são usados por outros registradores de porta serial, os quais, em ordem, são os seguintes: Registrador de Identificação de Interrupção, Registrador de Controle da Linha, Registrador de Controle do Modem, Registrador de Status da linha e Registrador de Status do Modem. Outro registador, chamado Latch do Divisor, compartilha o endereço de base usado pelos registradores de Transmissão e Recepção, e os próximos registradores mais altos usados pelo registrador de ativação da interrupção. Ele é acessado alterando-se uma opção no registrador de controle de linha.

            Esse latch armazena o divisor que determina a velocidade operacional da porta serial. Qualquer valor carregado no latch é multiplicado por 16. O produto resultante é usado para dividir o sinal de clock fornecido ao chip do UART para determinar a velocidade em bps. Devido ao fator de multiplicação 16, a maior velocidade de operação da porta serial é 1/16 do sinal do clock fornecido, que é 1,8432 MHz. Definindo o latch com o seu valor mínimo, 1, a velocidade resultante é 115200

            Os registradores não apenas armazenam os valores utilizados pelo chip UART, mas também são usados para informar ao sistema como a transmissão serial está ocorrendo. Por exemplo, o registrador de Status da linha indica se um caráter carregado para transmissão foi realmente enviado. Também indica quando um novo caráter foi recebido.

            Embora podendo mudar os valores armazenados nesses registradores manualmente por meio do Debug ou de programas próprios, esses registradores em geral dão flexibilidade ao programador.

            Em vez de ter que usar DIP switches ou jumpers, a facilidade de endereçamento direto dos registradores permite que todos os parâmetros operacionais vitais sejam definidos pelo software. Por exemplo. Carregando os valores apropriados no registrador de controle da linha, se altera o tamanho da palavra, paridade e número de bits de parada usados em cada palavra serial.

 

 

Controle de Fluxo

É necessário regular o fluxo de dados quando são transferidos dados entre duas seriais.

 

Dois métodos são utilizados:

O primeiro por software e o outro por hardware utilizando o os sinais CTS e RTS.

Funciona da seguinte maneira:

àO CTS é setado pelo receptor quando estiver pronto para receber mais dados. Em contrapartida, o RTS é setado pelo emissor quando os dados estão prontos a serem enviados;

 

            O controle de fluxo feito pelo hardware é mais rápido, mas ele não é disponível para todos os SOs.

 

 

Acessando Portas Seriais

No Linux, o acesso para as portas seriais é feito por devices files. Para acessar basta abrir o arquivo correspondente no /dev.

 

System Port 1 Port 2

IRIX® /dev/ttyf1 /dev/ttyf2

HP−UX /dev/tty1p0 /dev/tty2p0

Solaris®/SunOS® /dev/ttya /dev/ttyb

Linux® /dev/ttyS0 /dev/ttyS1

Digital UNIX® /dev/tty01 /dev/tty02

 

Abrindo uma porta serial

 

#include <stdio.h> /* Definição padrão de I/O */

#include <string.h> /* Definição padrão de string */

#include <unistd.h> /* Funções padrões UNIX */

#include <fcntl.h> /* Controle de arquivos */

#include <errno.h> /* Definições de numero de erros*/

#include <termios.h>         /* Definições POSIX de controle de terminal

* A maioria dos sistemas suportam a POSIX terminal

* interface para configuração de baud rate,

* tamanho de caracter, etc...

*/

/*

* 'abre_pserial()' − Abre porta serial.

*

* Sucesso à  descriptor do arquivo

* Fracasso à 0

*/

 

Int abre_pserail(void)

{

int fd; /* Descrito do arquivo */

fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);

                /* 

* O_NOCTTY = diz ao UNIX que esse programa não precisa ser o terminal de

* controle.

                * O_NDELAY = diz ao UNIX que o programa não se importa em qual estado

* está o DCD, se desejar utilizar este sinal deve-se colocar em sleep()até que ele

* detecte algum * dispositivo na outra ponta.

*/

               

if (fd == −1)

{

/*

* Não foi possível abrir a porta.

*/

perror("open_port: Não foi possível abrir o arquivo /dev/ttyS0 − ");

}

else

fcntl(fd, F_SETFL, 0); /*Função de leitura da porta */

return (fd);

}

 

Escrevendo dados na porta

Para escrever na porta, basta utilizar a chamada de sistema write().

 

n = write(fd, "ATZ\r", 4);

if (n < 0)

fputs("write() de 4 bytes falhou :-/!\n", stderr);

 

CONFIGURANDO A UART

As duas funções mais importantes que configuram  a UART são  tcgetattr(3) and tcsetattr(3). São atributos de get e set respectivamente.  O acesso às opções é feito através de um ponteiro para as opções de comunicação serial disponíveis.

 

Descrição dos Membros

c_cflag Opções de controle

c_lflag Line options

c_iflag Input options

c_oflag Output options

c_cc Caracteres de controle

c_ispeed Input baud

c_ospeed Output baud

 

Opções de Controle -- c_cflag member controla o baud rate, numero de dados por bits, paridade, stop bits, e controle de fluxo por hardware.

Constantes para o c_cflag TAB 2

 

 

O  c_cflag   contem duas opções que devem ser ativadas, CLOCAL  e CREAD. Isso ira assegurar que o programa não será o “dono” da porta para transmissões esporádicas e sinais de desligamento do dispositivo e também que a interface serial leia os dados recebidos.

 

A constante de baud rate (CBAUD, B9600, etc.) são usadas para interfaces antigas que não possuem o c_ispeed e o c_ospeed.

Na próxima seção é mostrado como configurar a taxa de transmissão (baud rate).

 

Nunca inicialize o membro da c_cflag (ou outro flag) diretamente; utilize pelos operadores AND, OR, e NOT para setar ou limpar o conteúdo dos membros.

 

Configurar a taxa de transmissão (baud rate)

 

Interfaces antigas guardam o baud rate como um membro do  c_cflag  (tabela 4). Enquanto novas implementações, provem membros do c_ispeed e c_ospeed que contém o atual valor do baud rate.

As funções  cfsetospeed(3) e cfsetispeed(3) servem para setar o baud rate.

 

/*

* Conseguindo a opção atual da porta

*/

tcgetattr(fd, &options);

/*

* Setando baud rates para 19200...

*/

cfsetispeed(&options, B19200);

cfsetospeed(&options, B19200);

/*

* Abilitando o receptor e setando o modo local...

*/

options.c_cflag |= (CLOCAL | CREAD);

/*

* Setando novas opções para a porta

*/

tcsetattr(fd, TCSANOW, &options);

 

 

Opções do Baud Rate

 

O TCSANOW é uma constante que especifica que todas as mudanças devem ocorrer imediatamente sem esperar os dados que estão sendo enviados/recebidos serem concluídos antes que ocorra esta mudança.

 

Outras constantes

TCSANOW Modificações passam a valer na hora

TCSADRAIN Espera até que todos os dados sejam transmitidos

TCSAFLUSH Limpa buffers I/O e dai modifica.

 

Setando o tamanho dos Caracteres

 

O tamanho do caractere é especificado em bits:

options.c_cflag &= ~CSIZE; /* Mascara o tamanho do caractere em bits */

options.c_cflag |= CS8; /* Seleciona os 8 bits de dados*/

CONFIGURANDO PARIDADE

 

Sem paridade (8N1):

options.c_cflag &= ~PARENB

options.c_cflag &= ~CSTOPB

options.c_cflag &= ~CSIZE;

options.c_cflag |= CS8;

 

Mesma paridade (7E1):

options.c_cflag |= PARENB

options.c_cflag &= ~PARODD

options.c_cflag &= ~CSTOPB

options.c_cflag &= ~CSIZE;

options.c_cflag |= CS7;

 

Diferentes paritdades (7O1):

options.c_cflag |= PARENB

options.c_cflag |= PARODD

options.c_cflag &= ~CSTOPB

options.c_cflag &= ~CSIZE;

options.c_cflag |= CS7;

 

 

Paridade por espaço é setada da mesma maneira que sem paridade

options.c_cflag &= ~PARENB

options.c_cflag &= ~CSTOPB

options.c_cflag &= ~CSIZE;

options.c_cflag |= CS8;

Setando controle de fluxo por hardware

CTS (Clear To Send)

RTS (Request To Send)

 

àPara ativar o controle de fluxo por hardware:

options.c_cflag |= CNEW_RTSCTS;

àPara desativar...

options.c_cflag &= ~CNEW_RTSCTS;

 

Opções Locais

O modo local controla como os caracteres de entrada são gerenciados pelo driver serial. Geralmente o c_lflag  é configurado para rodar como canonico ou raw.

          TAB 3

 

 

Canonical Input (MODO CANONICO)

MODO CANÔNICO OU PADRÃO: Modo em que o programa recebe os dados apenas quando o usuário pressiona ENTER. Nesse modo, o usuário pode digitar, usar BackSpace para corrigir o que estiver errado, e quando estiver satisfeito, envia a linha para o programa.

 

Normalmente é selecionado para este modo os seguintes parâmetros:

options.c_lflag |= (ICANON | ECHO | ECHOE);

 

Raw Input (MODO CRU)

Entrada Raw não pode ser processada. Caracteres são passados exatamente como foram recebidos. Normalmente, é desce lecionado os parâmetros ICANON, ECHO, ECHOE, e ISIG.:

options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

 

Opções de entrada

Para estas opções é utilizado o membro  c_iflag. E como o c_cflag, o valor guardado em c_iflag é um combinação dos bits através do OR das opções desejadas.

TAB 4

BRKINT

Gerar uma interrupção quando um sinal de "break" é detectado na linha. (Para quem não sabe, o "break" é um sinal que pode ser mandado via porta serial, independentemente dos dados);

IGNBRK

Ignora quaisquer sinais de "break";

ICRNL

Converter CR recebidos em LF;

IGNCR

Descartar CR recebidos;

INLCR

Converter LF recebidos em CR;

IGNPAR

Descartar LF recebidos;

INPCK

Fazer checagem de paridade nos caracteres recebidos;

PARMRK

Marcar erros de paridade;

ISTRIP

Zerar o oitavo bit dos caracteres recebidos;

IXOFF

Desligar o controle de fluxo por software;

IXON

Ligar o controle de fluxo por software.

 

 

 

Setando a paridade na entrada

É necessário habilitar a checagem de paridade na entrada quando é abilitado paridade no membro (PARENB) do c_cflag.

As constants relevantes para paridade na entrada são INPCK, IGNPAR, PARMRK, e ISTRIP. Normalmente é selecionado  INPCK e ISTRIP para habilitar e desabilitar checagem:

options.c_iflag |= (INPCK | ISTRIP);

IGNPAR é perigoso pois diz ao driver serial que ele deve ignorar erros de paridade e deixar passar os dados com erros. Isto é muito utilizado para detectar a qualidade de uma rede.

PARMRK causa erros de paridade para ser sinalizado para entrada

Se IGNPAR esta habilitado, um caractere NUL (000 em octal) é mandado pelo programa antes de todo caracter com paridade indicando um erro.

 

Setando o controle de fluxo pelo software

Controle de fluxo por software é habilitado utilizando as constante IXON, IXOFF, e IXANY :

options.c_iflag |= (IXON | IXOFF | IXANY);

Para desabilitar…

options.c_iflag &= ~(IXON | IXOFF | IXANY);

O start bit (XON) e o stop bit (XOFF) são definidos pelo vetor c_cc descrito abaixo.

Opções de Saída

O  c_oflag contains opções de filtragem. Como demonstrado anteriormente na entrada de dados, pode ser escolhido os modos raw ou data output.

 

Constantes do c_oflag       TAB 5

 

 

 

 

Escolhendo modo de saída processado

Modo porcessado é selecionado setando a opção OPOST no membro c_oflag:

options.c_oflag |= OPOST;

 

Escolhendo modo de saída Raw

Selecionado resetando a opção OPOST no c_oflag:

options.c_oflag &= ~OPOST;

Quando o OPOST é desabilitado, todas as outras opções no bit c_oflag são ignoradas.

 

Caracteres de controle

Caracteres de controle são definidos pelo vetor c_cc que caracteres de controle assim como parâmetros de timeout.

 

TAB 6 Membro c_cc

 

 

Setting Software Flow Control Characters

The VSTART and VSTOP elements of the c_cc array contain the characters used for software flow control.

Normally they should be set to DC1 (021 octal) and DC3 (023 octal) which represent the ASCII standard

XON and XOFF characters.

 

Setando Timeout na leitura

 

Dois elementos do vetor c_cc são utilizados para determinar timeouts: VMIN e VTIME.

Timeouts são ignorados no modo canônico de entrada ou quando a opção NDELAY esta setada no arquivo via open ou fcntl.

 

Adicionalmente, há dois valores configuráveis para o modo não canônico:

VMIN(n)

Número mínimo de caracteres que o buffer deve acumular para então enviá-los ao programa;

VTIME(n)

Tempo máximo que o driver vai esperar até enviar o buffer de entrada ao programa, mesmo que a contagem de caracteres não tenha atingido VMIN. VTIME=0 significa que o driver vai esperar "ad infinitum" até coletar VMIN caracteres.

Os valores mais comumente utilizados para VMIN e VTIME são 1 e 0, respectivamente. Ou seja, o programa sempre vai receber imediatamente qualquer byte que for recebido. Porém a função read() não retorna até obter pelo menos um caractere do dispositivo...

 

Exemplos:

/*

PUCRS  Pontifica Universidade Catolica do Rio Grande do Sul

PPGEE  Programa de Pos-Graduacao em Engenharia Eletrica

GPARC  Grupo de Pesquisas Avancadas em Redes de Computadores

 

PROGRAMA:

                Leitura dos parametros lidos pelo monitor de eletrocardiograma MINISCOPE I - INSTRAMED

AUTOR:

                Heloisa Melo Hertzog

ULTIMA ATUALIZACAO:

                18/08/2004

*/

 

#include <stdio.h>

#include <unistd.h>

#include <fcntl.h>

#include <errno.h>

#include <termios.h>

#include <sys/types.h>

#include <sys/time.h>

 

int main()

{

            struct termios newtio, oldtio;

 

            int fd;                                      //descritor da serial

            char *portname;                                //nome da porta

            FILE *arq;                               //ponteiro p o arquivo

 

            int pto;                                    //armazena ptos lidos

        int i = 0;

            char buf[10];

 

            portname = "/dev/ttyS0";

 

            /*abre a porta serial p leitura e escrita, a flag

            O_NOCTTY indica q caracteres CTRL-C serao ignorados

            qdo recebidos*/

            fd = open(portname, O_RDWR | O_NDELAY | O_NOCTTY);

            if(!fd)

            {

                        printf("\nErro na abertura da porta %s\n", portname);

                        exit (1);

            }

 

            //salva a config atual da porta

            tcgetattr(fd, &oldtio);

            //inicializa a estrutura newtio

        bzero (&newtio, sizeof(newtio));

 

            /* Configuracao da Porta

            CS8 = 8n1 (8bit,sem paridade,1 stopbit)

            CREAD = habilita a recepcao dos caracteres

            ICRNL = mapeia CR´s to NL´s (faz com que um CR enviado por um

            computador windows tambem termine a string)

            */

            newtio.c_cflag = (CS8 | CREAD);

        newtio.c_lflag = 0;

        newtio.c_oflag = 0;

        newtio.c_iflag = ICRNL;

            cfsetispeed(&newtio, B115200);

            cfsetospeed(&newtio, B115200);

 

            //limpa os registradores da porta

            tcflush (fd, TCIFLUSH);

            //atualiza a config

        tcsetattr(fd, TCSANOW, &newtio);

 

            printf("\nAberta Porta %s\n", portname);

 

            //abre um arq txt e adiciona dados no seu final

            arq = fopen ("ptos", "a");

            if (!arq)

            {

                        printf("\nErro na abertura do arquivo.\n");

                        exit (1);

            }

 

            while(i < 2)

            {

                        sleep(1);

                        //escreve 0x02 na serial

                        buf[0] = 0x02;

                        write(fd, &buf[0], 1);

 

                        //enquanto tiver pontos le e grava em um arquivo

                        while(read(fd, &pto, 1) > 0)

                        {

                                    pto &= 255;

                                    printf("\npto : %i", pto);

                                    fprintf(arq, "%i", pto);

                        }

                        i++;

            }

 

            //restaura a config da porta

            tcsetattr (fd, TCSANOW, &oldtio);

 

            //fecha porta

            close(fd);

            //fecha arquivo

            fclose (arq);

 

            printf("\nPrograma Encerrado\n");

}

 

 

 

 

 

 

 

 

/*

This program shows how to setup the console and serial interface

to implement a dumb terminal in LINUX

 

What you type on the keyboard is sent to the serial interface;

The characters received by the serial interface are written on the screen

by Marcos Augusto Stemmer

This program is Free Software

*/

 

#include <stdio.h>

#include <unistd.h>

#include <fcntl.h>

#include <errno.h>

#include <termios.h>

#include <sys/types.h>

#include <sys/time.h>

 

int fd, kbd;    /* Descritor da porta */

struct termios mytty, kbdios;   /* Console settings */

 

/* Esta função existe para quando ocorre o fim do programa*/

/* Restaurar o modo normal da tty quando sair */

void back_to_normal(void)

{

fcntl(kbd, 0, FNDELAY);   /*Configurações do descritos kbd e setando a opção de FNDELAY ele não espera confirmação do DCD*/

tcsetattr(kbd, TCSANOW, &kbdios); /* Seta as novas opções para a porta TCSNOW->Modificacoes instantaneas*/

puts("\nTerminou\n");

}

 

int main(int argc, char **argv)

{

char letra, *portname;

char msg[80];

int endini;

fd_set descritores;     /* Set of i/o handles */

struct timeval timeout;

 

 

/* Abre porta serial */

portname=argc > 1? argv[1]: "/dev/ttyS0";

fd=open(portname, O_RDWR | O_NDELAY); /* nao espera pelo DCD */

if(fd==-1)

  {

  sprintf(msg, "\nImpossivel abrir a porta \"%s\"", portname);

  perror(msg);

  return -1;

  }

 

/* Set attributes of the serial interface to raw mode */

if(tcgetattr(fd, &mytty)==-1) /*Coletando atributos da configuração da UART para &mytty*/

  {

  perror(NULL);

  close(fd);

  return -1;

  }

 

/*Realiza modificações nas opções da configuração, mas ainda NÃO É VÁLIDO*/

/*OPCÕES DA TAB 4*/

mytty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); /*NEGA IGNORAR PAR*/

mytty.c_oflag &= ~OPOST;    /*função de filtragem*/

mytty.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);

mytty.c_cflag &= ~(CSIZE|PARENB);

mytty.c_cflag |= CS8;

 

/* Configurando o Baud Rate para 9600 baud */

            cfsetispeed(&mytty, B9600);

            cfsetospeed(&mytty, B9600);

 

/*Setando as configurações, tornando-as operacionais/validas */

if(tcsetattr(fd, TCSANOW, &mytty)==-1)

  {

  perror(NULL);

  close(fd);

  return -1;

  }

 

fprintf(stderr, "\nAberta \"%s\"\n"

 

"Aperta Ctrl-X para sair\n", portname);

 

/* Get keyboard handle */

            kbd=fileno(stdin);

 

/* Set keyboard to raw mode */

            tcgetattr(kbd, &kbdios);   /*Pega as configurações atuais da porta*/

            atexit(back_to_normal);        

            memcpy(&mytty, &kbdios, sizeof(struct termios));

            mytty.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);  /*ICANON->nega o canon else raw TAB3*/

            tcsetattr(kbd, TCSANOW, &mytty);

 

 

/* Essas chamadas fazem ler imediatamante mesmo que não tenha nenhum dado transmitido*/

            fcntl(fd, F_SETFL, FNDELAY);

            fcntl(kbd, F_SETFL, FNDELAY);

 

/* Now we may use the serial interface and keyboard for sending

  just one character at a time */

 

do

  {

/* Wait for any activitiy in the serial port (fd) or keyboard (kbd) */

  FD_ZERO(&descritores);  /* Inicializa a lista de handles */

  FD_SET(fd,  &descritores);

  FD_SET(kbd, &descritores);

  timeout.tv_sec=5;

  timeout.tv_usec=0;

  select(FD_SETSIZE, &descritores, NULL, NULL, &timeout);

 

  while(read(fd, &letra, 1) > 0) write(kbd, &letra, 1);

  letra=0;

  while(read(kbd, &letra,1) > 0) write(fd, &letra, 1);

  } while(letra!='X'-64);

close(fd);

return 0;

}

 

 

 

 

 

 

 

Tarefa

 

 

Transforme o Programa acima de modo “cru” (RAW) para canônico.

 

 

 

 

 

 

Programas Uteis

 

 

  • in.telnetd (servidor telnet);
  • xterm (emulador de terminal para modo gráfico)
  • minicom (programa de comunicação via porta serial).