ENVIANDO DADOS
PARA OUTRO DISPOSITIVO USANDO RS232
A USART DO MICROCONTROLADOR PIC16F628A
A USART ou Universal Synchronous
Assynchronous Receiver Transmitter (Receptor Transmissor
Universal Síncrono e Assíncrono) do microcontrolador
PIC16F628A, permite que este se comunique com outros
dispositivos através do padrão RS-232. Os níveis de
tensão na USART estão dentro do padrão TTL (0 - 5VDC) e
devem, portanto, ser convertidos para os níveis de
tensão do padrão RS-232 (+/- 15 VDC).
OBS.: A conversão, na placa, dos
níveis de tensão TTL da USART do PIC para o padrão
RS-232 é feito por CI3.
REGISTRADORES IMPORTANTES
REGISTRADOR
TXSTA
Esse registrador possui os bits de
controle e status para transmissão da USART.
Escrita
Leitura
|
Escrita
Leitura
|
Escrita
Leitura
|
Escrita
Leitura
|
Lido sempre 0
|
Escrita
Leitura
|
Escrita
Leitura
|
Escrita
Leitura
|
CSRC
|
TX9
|
TXEN
|
SYNC
|
--
|
BRGH
|
TRMT
|
TX9D
|
bit 7
|
|
bit 0
|
bit 7 – CSRC – Seleção do clock
Modo Assíncrono
Modo Síncrono
bit 6 - TX9 – Transmissão com 9 bits
bit 5 - TXEN – Habilita transmissão
bit 4 - SYNC – Modo da USART
-
1 = Modo síncrono
-
0 = Modo Assíncrono
bit 3 – não implementado – lido sempre como 0
bit 2- BRGH – Baud rate High
Modo Assíncrono
-
1 = Alta velocidade
-
0 = Baixa velocidade
Modo Síncrono
bit 1 - TRMT – Status do registro de transmissão
bit 0 – TX9D – Nono bit da transmissão, que pode ser o
bit de paridade
REGISTRADOR RXSTA
Esse registrador possui os bits de
controle e status para recepção da USART.
Escrita
Leitura
|
Escrita
Leitura
|
Escrita
Leitura
|
Escrita
Leitura
|
Escrita
Leitura
|
Escrita
Leitura
|
Escrita
Leitura
|
Escrita
Leitura
|
SPEN
|
RX9
|
SREN
|
CREN
|
ADEN
|
FERR
|
OERR
|
RX9D
|
bit 7
|
|
bit 0
|
bit 7 – SPEN – Seleciona porta serial
bit 6 - RX9 – Recepção com 9 bits
bit 5 - SREN – Habilita recepção simples
Modo assíncrono
Modo síncrono master
Modo síncrono slave
bit 4 - CREN – Modo de recepção contínuo
Modo assíncrono
Modo síncrono
bit 3 – ADEN – Detecção de endereço
Modo assíncrono 9 bits
-
1 = Habilita detecção de endereço,
habilita int e lê buffer de recepção quando RSR
estiver setado
-
0 = desabilita detecção de endereço,
todos os bytes são recebidos e o nono bit pode ser
usado como paridade
Modo assíncrono 8 bits
Modo síncrono
bit 2- FERR – Flag de erro de frame
bit 1 - OERR –Flag de overrun
bit 0 – RX9D – Nono bit da recepção, que pode ser o bit
de paridade
Para
utilizar a USART do PI16F628A é necessário configurar
alguns registradores. São eles:
TRISB = xxxx x01x
onde:
RB1 (RX) – entrada
RB2 (TX) – saída
Em seguida
o registrador SPBRG deve receber o valor do baud
rate. Esse valor pode ser obtido através da
fórmula:
Divisor = (Frequência do oscilador /
(16 * Baud rate desejado) – 1)
Ou ainda através de uma tabela presente no datasheet
do fabricante.
Em seguida é necessário configurar mais
dois registradores: TXSTA e RXSTA.
O bit TXEN do registrador TXSTA deve ser
setado. O bit BRGH deve ser setado ou não de
acordo com a velocidade desejada (alta ou baixa). Todos
os demais presentes nesse registrador devem ser zerados.
TXSTA = 0x20;
Já em RXSTA, precisamos setar os bits: SPEN
e CREN. Todos os demais devem ser zerados.
RXSTA = (0x80 |
0x10);
Os próximos registradores a serem configurados são PIE1
e PIR1 para o controle das interrupções
relacionadas a USART. Em PIE1 habilitamos o bit
RXIE. Cuidado para não interferir com outros
bits. Use aqui a lógica OU.
PIE1 |= 0x20 →
PIE1 = PIE1 ou 0010 000b
Em PIR1 limpamos o flag da recepção RCIF.
Cuidado para não interferir com outros bits. Use aqui a
lógica AND.
PIR1 &= ~0x20
→ PIR1 = PIR1 and 1101 1111b
Com isso, habilitamos a USART em modo assíncrono, 8
bits, sem paridade, 1 stop bit, com interrupção apenas
para dado recebido.
Vamos ver isso na prática mais a frente
na durante a experiência (teste).
Obs.: Existem
muitas formas de conexão entre equipamentos DTE e
DCE, e a norma EIA RS-232C determina como as mesmas
devem ser feitas. Este tema não será abordado aqui,
restringindo neste trabalho a comunicação RS-232
entre um PC (DTE) e o microcontrolador (DCE) sem
qualquer controle de fluxo (hardware ou software).
Recomendo buscar mais a respeito na Internet.
Até agora todas as
experiências foram feitas apenas com a placa. Para essa
experiência vamos precisar de um acessório a mais que é
um adaptador USB→RS-232 com conector DB09 macho como o
apresentado na figura abaixo. Ele servira para conectar
a placa ao seu PC.
Alguns PC’s mais
antigos possuem pelo menos uma porta serial RS-232
disponível na sua parte traseira, identificada por seu
conector DB09 Macho. Se você já usou essa porta em algum
momento poderá optar por usá-la agora. Para isso você
precisara apenas de um cabo como a figura abaixo.
Após resolver o
“hardware” a ser utilizado, basta escolher um programa
para recepção dos dados como o Hyperterminal do
Windows, o Termite também para Windows, o Putty
com versões para Windows e Linux e o minicom
apenas para Linux. Qualquer um desses poderá ser
utilizado. Os links para download estão a
seguir:
Hyperterminal – presente nas instalações mais
antigas do Windows
Termite - https://www.compuphase.com/software_termite.htm
Putty - https://www.putty.org/
minicom – use
“sudo apt install minicom” e em seguida “sudo
useradd -G dialout usuário”
Conecte o cabo à placa e ao PC. Feche os jumper’s JP1,
JP2 e JP3 na placa. Crie um projeto e insira no arquivo
main.c, pic_comm.c e pic_ints.c suas listagens
referentes (abaixo). Compile e grave o microcontrolador.
Listagem main.c
//****************************************************************************** // Projeto comm para placa PE-PIC16F628A - teste da USART // Permite controle dos LEDs via USART // // Desenvolvido por: Eng. Márcio José Soares // // V1.0 - 06/09/2022 // // Compilador: XC8 v2.45 // IDE : MPLABX 6.0 Linux // Plataforma: placa PE-PIC16F628A com PIC16F648A // Gravador : PICKIT3 // // Pinos utilizados: // RB1 : RX // RB2 : TX // RB4 : LED4 // RB5 : LED3 // RB6 : LED2 // RB7 : LED1 // //******************************************************************************
//****************************************************************************** // O Copyright deste programa está reservado para Márcio José Soares e seu // conteúdo está protegido pela Lei de Direitos Autorais LEI Nº 9.610, de // 19 de Fevereiro de 1998. É estritamente proibida a reprodução total ou // parcial dos conteúdos aqui apresentados sem prévia autorização, por escrito, // do autor. //******************************************************************************
//****************************************************************************** // Bits de configuração #pragma config FOSC = INTOSCIO // Bits de seleção do oscilador (Oscilador interno: RA6 e RA7 como I/O) #pragma config WDTE = OFF // WDT disabilitado #pragma config PWRTE = ON // Power-up Timer disabilitado #pragma config MCLRE = ON // MCLR = I/O RA5 #pragma config BOREN = OFF // Brown-out Detect BOD disabilitado #pragma config LVP = OFF // Low-Voltage Programming desabilitado - I/O RB4 #pragma config CPD = OFF // Data EE Memory Code Protection desabilitado #pragma config CP = OFF // Flash Program Memory Code Protection desabilitado
//****************************************************************************** // Se nenhuma freqüência foi definida, assume 4MHz // Esta definição é exigida para calibrar as funções __delay_us() e __delay_ms() #define _XTAL_FREQ 4000000
//****************************************************************************** // Inclui arquivos #include <xc.h> #include <pic16f648a.h> #include <string.h>
//****************************************************************************** // Definições importantes do módulo #define TRUE 1 #define LEDS PORTB #define TEMPO 1000
//****************************************************************************** // Funções externas do módulo extern void init_usart(void); extern void putch(unsigned char mbyte); extern void putch_str(char *mystr);
//****************************************************************************** // Variáveis globais do módulo char TxBuf[64];
//****************************************************************************** // Função config pic - configura pinos de I/O e outros // // Entradas - nenhuma // Saídas - nenhuma //****************************************************************************** void config_pic(void){ PORTA = 0; // zera ports PORTB = 0; INTCON = 0x00; // Todas as ints desligadas CMCON = 0x07; // desliga comparadores TRISA = 0xFF; // Configura PORT A - entrada TRISB = 0x02; // Configura PORT B - saída, exceto RB1 init_usart(); //inicia USART }
//****************************************************************************** // Função principal // // Entradas - nenhuma // Saídas - nenhuma //****************************************************************************** void main(void) { uint8_t i; char p[3]; config_pic(); //configura pic //************************************************************************** // Flags de controle das interrupções INTCON |= 0x40; // PEIE=1 - habilita int dos periféricos // (timers, comparadores, usart, etc) INTCON |= 0x80; // GIE=1 - habilita interrupções globais. __delay_ms(1000); //************************************************************************** // Prepara mensagem inicial strcpy(TxBuf, "\n\r\n\rTeste USART placa PE-PIC16F628A"); putch_str(TxBuf); strcpy(TxBuf, "\n\rby Eng. Marcio Jose Soares - Arne\n\rTecle:"); putch_str(TxBuf); //************************************************************************** // Loop for para montagem do menu for(i=0;i<5;i++){ p[0] = (char)(i+0x31); //número da opção a ser teclada p[1] = '\0'; //finaliza string strcpy(TxBuf, "\n\r\t"); //inicia com nova linha e tab strcat(TxBuf, p); //insere número da opção strcat(TxBuf," - liga/desliga "); //insere o que faz if(i <=3) //se dentro da faixa dos LEDs strcat(TxBuf,"LED"); //insere led else strcat(TxBuf,"RELE"); //senão é rele putch_str(TxBuf); //envia para tela if(i <=3) //é LED? putch((unsigned char)(i+0x32)); //envia para tela nr do LED } strcpy(TxBuf, "\n\rEntre com sua opcao:"); //encerra menu pedindo opção putch_str(TxBuf); LEDS = 0x00; //zera LEDs while(TRUE){ ; //tudo feito pela INT da USART } return; }
|
Listagem pic_comm.c
//****************************************************************************** // Arquivo pic_comm.c - tratamento da USART do PIC by Arne // Desenvolvido por Eng. Márcio José Soares //******************************************************************************
//****************************************************************************** // O Copyright deste programa está reservado para Márcio José Soares e seu // conteúdo está protegido pela Lei de Direitos Autorais LEI Nº 9.610, de // 19 de Fevereiro de 1998. É estritamente proibida a reprodução total ou // parcial dos conteúdos aqui apresentados sem prévia autorização, por escrito, // do autor. //******************************************************************************
//****************************************************************************** // Arquivos incluídos no módulo //****************************************************************************** #include <xc.h> #include <pic16f648a.h>
//****************************************************************************** // Definições importantes do módulo //******************************************************************************
#ifndef byte #define byte uint8_t #endif
#ifndef LEDS #define LEDS PORTB #endif
//****************************************************************************** // Define serial em uso //****************************************************************************** #ifndef _SERIAL_H_ #define _SERIAL_H_ #endif
//****************************************************************************** // Define baud rate, frequencia do oscilador e número de bits //****************************************************************************** #define BAUD 9600 #define FOSC 4000000L #define NINE 0 //1 se usar 9 bits, 0 se usar 8 - bit TX9 em TXSTA
//****************************************************************************** // Prepara divisor para calculo do valor no registrador SPBRG //****************************************************************************** #define DIVIDER ((int)((FOSC/(16UL * BAUD)) -1))
//****************************************************************************** // Configura High speed - bit BRGH em TXSTA //****************************************************************************** #define HIGH_SPEED 1
//****************************************************************************** // Prepara nr de bits - influencia em TXSTA //****************************************************************************** #if NINE == 1 #define NINE_BITS 0x40 #else #define NINE_BITS 0x00 #endif
//****************************************************************************** // Prepara high speed - incluencia em TXSTA //****************************************************************************** #if HIGH_SPEED == 1 #define SPEED 0x04 #else #define SPEED 0x00 #endif
//****************************************************************************** // Funções do módulo //******************************************************************************
//****************************************************************************** // Função - init_UART - Inicia a usart do PIC, conforme definições em pic_comm.h // // Entradas - nenhuma // Saídas - nenhuma //****************************************************************************** void init_usart(void){
TRISB1 = 1; //RX 16F6X8 TRISB2 = 0; //TX 16F6X8 SPBRG = DIVIDER; //seta velocidade TXSTA = (SPEED|NINE_BITS|0x20); //numero de bits RCSTA = (0x80|0x10); //habilita recepção PIE1 |= 0x20; //habilita int recep. PIR1 &= ~0x20; //limpa flag de recepção }
//****************************************************************************** // Função - putchar - saída de apenas um único byte // // Entradas - nenhuma // Saídas - nenhuma //****************************************************************************** void putch(unsigned char mbyte){
while(!TXIF); //aguarda registrador estar vazio TXREG = mbyte; //envia byte
}
//****************************************************************************** // Função - putchar str - envia uma string // // Entradas - nenhuma // Saídas - nenhuma //****************************************************************************** void putch_str(char *mystr){ while(*mystr){ putch((unsigned char)*mystr); mystr++; } }
//****************************************************************************** // Função - getchar - retorna um único byte // // Entradas - nenhuma // Saídas - nenhuma //****************************************************************************** unsigned char getch(void){
while(!RCIF); //espera até que registrador tenha um byte return RCREG; //retorna byte recebido }
//****************************************************************************** // Função - getchar - retorna um único caracter // // Entradas - nenhuma // Saídas - nenhuma //****************************************************************************** unsigned char getche(void){
unsigned char c;
putch(c = getch()); //pega caracter return c; //retorna caracter recebido }
//***************************************************************************** // Função trata_dado - testa e responde interrupção de recepção da USART // // Entradas - nenhuma // Saídas - caracter recebido //***************************************************************************** void trata_dado(byte dado){ switch(dado){ case 0x31: LEDS ^= 0x80; break; case 0x32: LEDS ^= 0x40; break; case 0x33: LEDS ^= 0x20; break; case 0x34: LEDS ^= 0x10; break; case 0x35: //rele ligado em RB3 - mesmo port que os LEDS LEDS ^= 0x08; break; } }
//***************************************************************************** // Função trata int comm - testa e responde interrupção de recepção da USART // // Entradas - nenhuma // Saídas - caracter recebido //***************************************************************************** void trata_int_comm(void){
byte c; while(!RCIF); //aguarda receber c = RCREG; //pega dado trata_dado(c); //trata dado recebido while(!TXIF); //aguarda registrador estar vazio TXREG = c; //eco while(!TXIF); //aguarda registrador estar vazio TXREG = 0x08; //apaga último teclado
}
|
Listagem pic_ints.c
//***************************************************************************** // Arquivo my_ints.c - tratamento das interrupções no PIC by Arne // Desenvolvido por Eng. Márcio José Soares //*****************************************************************************
//****************************************************************************** // O Copyright deste programa está reservado para Márcio José Soares e seu // conteúdo está protegido pela Lei de Direitos Autorais LEI Nº 9.610, de // 19 de Fevereiro de 1998. É estritamente proibida a reprodução total ou // parcial dos conteúdos aqui apresentados sem prévia autorização, por escrito, // do autor. //******************************************************************************
//****************************************************************************** // Arquivos incluídos no módulo //****************************************************************************** #include <xc.h> #include <pic16f648a.h>
//****************************************************************************** // Definições importantes no módulo //****************************************************************************** #ifndef byte #define byte uint8_t #endif
//***************************************************************************** // Funções externas do módulo //***************************************************************************** extern void trata_int_comm(void); extern void putch(unsigned char mbyte);
//***************************************************************************** // Funções do módulo //*****************************************************************************
//***************************************************************************** // Função para tratar o vetor de interrupções no PIC // // Este é o tratamento de interrupçõe no PIC. Como este microcontrolador possui // apenas um único vetor de interrupção (0x0004), toda e qualquer interrupção será // tratada aqui. Se uma única interrupção for utilizada, o tratamento pode ser direto, // sem a necessidade de se testar qual int ocorreu. Porém se mais de uma estiver // em uso, a função deverá testar os flags das ints para saber qual ocorreu, extamente // como é feito em assembly. // O uso da diretriz "interrupt 0" é importante, pois é assim que o SDCC reconhece que // esta função é na verdade um serviço para tratamento das interrupções. Para PICs usar // apenas nível 0. // // Entradas - nenhuma // Saídas - nenhuma //***************************************************************************** //static void isr(void) interrupt 0{ void __interrupt() pic_isr(void){
INTCON &= ~0x80; //desabilita ints
//analisa se é int do timer0 //if((TMR1IF == 1) && (TMR1IE == 1)){ // é int do timer1 // trata_int_timer1(); // trata int //} //analisa se é int externa //if((INTF == 1) && (INTE == 1)){ //é int externa // trata_int_ext(); //trata int //}
//analisa se é int da usart if((RCIF == 1) && (RCIE == 1)){ // é int de recepção da USART trata_int_comm(); // trata int } INTCON |= 0x80; // habilita novamente as ints
}
|
Esse programa pode
parecer um pouco mais complexo, mas não se assuste. Ele
possui 3 módulos para facilitar o entendimento do
programa como um todo. É uma boa prática modularizar
seus programas, ou seja, cada periférico ganha um
arquivo. Dessa forma você vai montando sua própria
biblioteca já que estes arquivos poderão ser utilizados
em outros projetos e até com outros microcontroladores
da linha PIC, família 16F.
O programa inicia
como todos os demais apresentados até aqui. Os bits
fuses são configurados, algumas definições importantes
são feitas e as funções externas, presentes em outros
módulos, declaradas. Nesse main pode-se notar que temos
uma sub-rotina para configurar o PIC. Evitamos assim que
a função main fique grande e confusa. Após a função main
enviar para o PC (programa terminal) a mensagem conforme
a figura abaixo, você pode controlar os LEDs e o relé
presente na placa via programa terminal. Ou seja, o PIC
está agora “ouvindo” a porta serial RS-232 e após
receber um byte via interrupção pic_isr() em pic_ints.c
faz o seu tratamento através de trata_int_comm() e
trata_dado() em pic_comm.c. Note que trata_dado() recebe
um byte e em seguida altera ou não (dependendo do valor)
a parte mais significativa do PORTB (LEDS). Veja:
se:
byte recebido = 0x31
→ “1” ASCII → LEDS ^⁼ 0x80 → LEDS = LEDS XOR 80H (1000
0000b)
byte recebido = 0x32
→ “2” ASCII → LEDS ^⁼ 0x40 → LEDS = LEDS XOR 40H (0100
0000b)
byte recebido = 0x33
→ “3” ASCII → LEDS ^⁼ 0x20 → LEDS = LEDS XOR 20H (0010
0000b)
byte recebido = 0x34
→ “4” ASCII → LEDS ^⁼ 0x10 → LEDS = LEDS XOR 10H (0001
0000b)
byte recebido = 0x35
→ “4” ASCII → LEDS ^⁼ 0x08 → LEDS = LEDS XOR 08H (0000
1000b)
Perceba que estamos
trabalhando com valores ASCII. No teclado do seu PC as
teclas 0 a 9 são representadas pelos códigos ASCII 30H a
39H, que são diferentes dos valores 0H a 9H que não
estão presentes nos teclados, mas que podem ser
utilizados em uma comunicação RS232 sem nenhum problema.
Desafios:
- Que tal estudar a tabela ASCII
para começar a entender a mesma?!
- Altere o programa para que ele
acenda ou apague os LEDs utilizando os valores ASCII
das teclas A, S, D, F e G ou a, s, d, f e g
(maiúsculas e minúsculas devem ser aceitas).
|