Programando Protocolo Serial
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.
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
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.
É 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.
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);
}
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);
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.
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.
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);
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.
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*/
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;
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.
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
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;
}
Transforme o Programa acima
de modo “cru” (RAW) para canônico.