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

  • Não importa

Modo Síncrono

  • 1 = Modo Master (clock gerado por BRG)

  • 0 = Modo Slave (clock gerado externamente pelo Master)


bit 6 - TX9 – Transmissão com 9 bits

  • 1 – Seleciona 9 bits na transmissão

  • 0 – Seleciona 8 bits na transmissão


bit 5 - TXEN – Habilita transmissão

  • 1 = Transmissão habilitada

  • 0 = Transmissão desabilitada


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

  • Não usado nesse modo


bit 1 - TRMT – Status do registro de transmissão

  • 1 = buffer vazio

  • 0 = buffer cheio (transmissão em andamento)


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

  • 1 = Porta serial habilitada

  • 0 = Porta serial desabilitada


bit 6 - RX9 – Recepção com 9 bits

  • 1 – Seleciona 9 bits na recepção

  • 0 – Seleciona 8 bits na recepção


bit 5 - SREN – Habilita recepção simples

Modo assíncrono

  • Não usado nesse modo

Modo síncrono master

  • 1 = Habilitada recepção simples

  • 0 = desabilita transmissão simples

Modo síncrono slave

  • Não usado nesse modo


bit 4 - CREN – Modo de recepção contínuo

Modo assíncrono

  • 1 = Habilita modo contínuo

  • 0 = desabilita modo contínuo

Modo síncrono

  • 1 = Habilitada recepção continua até que CREN seja limpo

  • 0 = desabilita modo contínuo


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

  • Não usado nesse modo

Modo síncrono

  • não usado nesse modo


bit 2- FERR – Flag de erro de frame

  • 1 = Ocorreu um erro de frame durante a recepção

  • 0 = nenhum erro de frame


bit 1 - OERR –Flag de overrun

  • 1 = Ocorreu um erro de overrun

  • 0 = sem erro 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:

  1. Que tal estudar a tabela ASCII para começar a entender a mesma?!
  2. 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).



Copyright deste conteúdo reservado para Márcio José Soares e protegido pela Lei de Direitos Autorais LEI N° 9.610, de 19 de Fevereiro de 1998. É estritamente proibida a reprodução total ou parcial do conteúdo desta página em outros pontos da internet, livros ou outros tipos de publicações comerciais ou não, sem a prévia autorização por escrito do autor.