Sunteți pe pagina 1din 8

RS485 com PIC16F690

Na IDE que utilizo para programar para PIC (MikroC) encontram-se muitas
bibliotecas, tal como em Arduino e por padro, muitas mais j instaladas. H algum
tempo trabalhei em um projeto cuja comunicao em rede era RS485. Em Arduino,
escrevi uma pequena prova de conceito nesse post. Como no escrevi nada para
PIC, resolvi disponibilizar o protocolo desenvolvido na poca.
Esquema
O circuito deve ser montado como no esquema a seguir:

Lembrando que o cabo no deve passar de 300 metros, deve-se evitar reas de
interferncia eletromagnticas e coisas do tipo.
Esse um padro de mercado e deve ser respeitado. Por exemplo, no
fundamental os resistores de 4.7k, mas o padro prev o mximo de proteo para
a rede.
O cdigo implementado ser exposto a seguir, com os respectivos comentrios.
Cdigo



//ID do dispositivo local
char ID[] = "01";
char extID[3];
int fromEEPROM[2];
char sensorN[2];
char serial[2];

//Aqui deveria ser unsigned char, mas errei quando escrevi
unsigned short int answer = 1;
unsigned short int sleepTime;

short int freq;
short int ERR;

char sensorToStr[6];
float sensorResult;

char resposta[12];
//os splits da mensagem devem ser feitas nessa varivel
char message[10];
char i;
char ok = 0;
short int SEND = 0;
short int RECEIVE = 3;

//sbit so aliases para o tris e o pino
sbit TRANS_TRIS at TRISC5_bit; // TRIS !!!
sbit TRANS_PINO at RC5_bit; // PINO !!!

sbit LED_TRIS at TRISC4_bit; //tris do led
sbit LED at RC4_bit;

sbit BUZZER_TRIS at TRISC6_bit;
sbit BUZZER at RC6_bit;

char output[10];
char receiveAbyte;

//flag para tocar o buzzer
short int playBuzzer = 1;

//limpar array. Desse modo economiza-se processamento, pois o tamanho
j est definido
void clear(char *var,short int size){
for (i=0;i<size;i++){
var[i] = '\0';
}
}

//conversao de hexa para decimal, da maneira mais simples possivel,
para int e char
short int hex2dec(char b){
if (b >57 && b <71){
return b-55;
}
else if (b>47 && b<58){
return b-48;
}
}

//leitura da EEPROM
void myEEPROM(short int SorI){
fromEEPROM[0] = 0 + EEPROM_Read(0x00+SorI);
fromEEPROM[1] = 0 + EEPROM_Read(0x01+SorI);

if (SorI == 0){
if (fromEEPROM[0] == 255 && fromEEPROM[1] == 255){
ID[0] = '0';
ID[1] = '1';
}
else{
ID[0] = (char) fromEEPROM[0];
ID[1] = (char) fromEEPROM[1];
}
}
else{
if (fromEEPROM[0] == 255 && fromEEPROM[1] == 255){
serial[0] = '0';
serial[1] = '0';
}
}
}

//o formato da mensagem ID:bdc. O tempo de resposta para que no
haja coliso baseada
// no ID do dispositivo em questo
void commandAnalyser(){
//BROADCAST - se recebe um broadcast (mensagem = B):
//envia o ID:bdc baseando o tempo no ID.
i = output[0];
if (i == 'B'){
sleepTime = atoi(ID);
for (i=0;i<sleepTime;i++){
Delay_ms(10);
}
clear(resposta,12);
strcpy(resposta,ID);
strncat(resposta,":BDC\n",5);
answer = 1;
return;
}

//string em output
clear(message,10);
//2 bytes sao o ID
clear(extID,3);
extID[0] = output[0];
extID[1] = output[1];

//se nao for ID identico, ignorar. Todas as msgs que no so
broadcast so tratadas assim
if (strcmp(extID,ID)){
clear(resposta,12);
clear(message,10);
clear(output,10);
answer = 0;
return;
}
//parse da mensagem
clear(message,10);
for (i=3;i<strlen(output);i++){
message[i-3] = output[i];
}
//AA:BCD
if (message[0] == 'P'){
if (message[1] == 'U'){
//levanta pino output[5]. No implementado
}
else if (message[1] == 'D'){
//abaixa pino output[5]
}
}
else if (message[0] == 'T'){
//Tom no buzzer RC6
/* A T E N A O
O funcionamento esta perfeito. Pode acontecer que, ao iniciar
uma
interrupo, o BUZZER pare de tocar. Isso acontece porque no
main()
o pino est sendo levantado a cada 50ms. Se houver uma
interrupo
nesse intervalo, o som pode parar.
O caso mais comum no haver intermitncia do BUZZER durante
uma
interrupo, pelo mesmo motivo.
Soluo: Pode-se simplesmente baixar o pino para o BUZZER
tocar, sem
interagir no loop do main().
*/
playBuzzer = message[1] - 49;
clear(resposta,12);
resposta[0] = 'O'; resposta[1] = 'K'; resposta[2] = '\n';
resposta[3] = 0;
}
else if (message[0] == 'L'){
//liga ou desliga (L0/1)
LED = message[1] - 49;
clear(resposta,12);
resposta[0] = 'O'; resposta[1] = 'K'; resposta[2] = '\n';
resposta[3] = 0;
}
else if (message[0] == 'S'){
//Sensor
//i para economizar memoria
i = hex2dec(message[1]);
//TODO: a amostra vem do pino. fazer array de sensor/pino (ERRADO
ABAIXO)
freq = ADC_Get_Sample(hex2dec(message[1]));
//1,2,3 supondo LM35
if (i <4){
sensorResult = (5.0 * freq * 100.0)/1024.0;
}
clear(resposta,12);
strcpy(resposta,ID);
strncat(resposta,":S",2);
//1 =B, 2=D, 3=C
sensorN[0] = message[1];
sensorN[1] = ' ';
strncat(resposta,sensorN,1);
strncat(resposta,"=",1);
clear(sensorToStr,6);
ERR = FloatToStr(sensorResult,sensorToStr);
if (ERR == 0){
strncat(resposta,sensorToStr,5);
}
else{
strncat(resposta,"0.00",5);
}
strncat(resposta,"\n",1);
}

//GRAVAR ADDR
else if (message[0] == 'G'){
//-------------------- gravao
/*
A gravao ser da serial (2 bytes) e do ID (2 bytes)
message[1] deve ser igual a S (serial) ou I (id), seguido dos 2
bytes.

Os primeiros 2 bytes da eeprom so ID e os 2 seguintes so serial.
O dispositivo pode receber qualquer formato, decimal ou hexa.
*/
//-----------------------------
if (message[1] == 'I'){
EEPROM_Write(0x00,0x00 + (int) message[2]);
Delay_ms(50);
EEPROM_Write(0x01,0x00 + (int) message[3]);
Delay_ms(50);
ID[0] = message[2];
ID[1] = message[3];
}
else if (message[1] == 'S'){
EEPROM_Write(0x02,0x00 + (int) message[2]);
Delay_ms(50);
EEPROM_Write(0x03,0x00 + (int) message[3]);
Delay_ms(50);
serial[0] = message[2];
serial[1] = message[3];
}

clear(resposta,12);
clear(message,10);
for (i=4;i<strlen(output);i++){
message[i-4] = output[i];
}
strcpy(resposta,ID);
strncat(resposta,":",1);
strncat(resposta,message,strlen(output)-4);

//feita a configurao, deve efetuar um reset
/*
asm{
GOTO 0x0000;
}
*/
}
//quando se consulta o ID na eeprom, ele armazenado em ID mesmo!
else if (message[0] == '?'){
if (message[1] == 'I'){
myEEPROM(0);
}
else{
myEEPROM(2);
}
//agora monta a resposta
clear(resposta,12);
strcpy(resposta,ID);
strncat(resposta,":",1);
if (message[1] == 'S'){
strncat(resposta,serial,2);
strncat(resposta,"\n",1);
}
else{
strncat(resposta,ID,2);
strncat(resposta,"\n",1);
}
}
else{
answer = 0;
}
}

void reader(){
for (i=0;i<10;i++){
output[i] = '\n';
}
receiveAbyte = UART1_Read();
if (receiveAbyte == '['){
i = 0;
while (receiveAbyte != ']'){
if (UART1_Data_Ready() == 1){
receiveAbyte = UART1_Read();
output[i] = receiveAbyte;
i++;
}
}
output[i-1] = 0;
ok = 1;
}
}

void interrupt() {
GIE_bit = 0;
if (RCIF_bit == 1){
reader();
RCIF_bit = 0;
}
GIE_bit = 1;
}

void sendOrReceive(int condition){
// O TRANSCIEVER o pino DE e RE, nao confundir com TX/RX .
// no esquema da board esta utilizando rc0
//
if (condition == SEND){
//transmitir
TRANS_PINO = 1;
Delay_ms(10);
}
else{
//receber
TRANS_PINO = 0;
Delay_ms(10);
}
Delay_ms(50);
}

void main() {
TRANS_TRIS = 0;
LED = 1;
Delay_ms(10);
UART1_Init(9600); // initialize UART1 module
Delay_ms(100); // ensure that error flag is 0
//ADC_Init();
// RCIE___./ .___PEIE___./ .___GIE
RCIE_bit = 1; // enable interrupt on UART1 receive
TXIE_bit = 0; // disable interrupt on UART1 transmit
(default)
PEIE_bit = 1; // enable peripheral interrupts
GIE_bit = 1; // enable all interrupts

ANSEL = 0;
ANSELH = 0;

C1ON_bit = 0; // Turn off comparators
C2ON_bit = 0;

LED_TRIS = 0; //aterrando no pic
BUZZER_TRIS = 0; //aterrando no pic

myEEPROM(0);
Delay_ms(50);

while (1) {
BUZZER = playBuzzer;
//passa para escrita (transciever)
sendOrReceive(RECEIVE);
if (ok == 1){
sendOrReceive(SEND);
commandAnalyser();
if (answer == 1){
//UART1_Write_Text(resposta);
UART1_Write_Text(resposta);
}
else{
answer = 1;
}
ok = 0;
}
Delay_ms(50);
BUZZER = 1;
Delay_ms(50);
}
}
A mensagem deve ter um formato assim:
[ID:MSG]
Por exemplo, ler o sensor 1 no dispositivo 09:
[09:S1]
Todos os dispositivos recebem a mensagem. Se o byte 0 no for B e sim [', ento o
segundo e terceiro byte so avaliados. Qualquer dispositivo que no seja o 09 ir
ignorar a mensagem. O dispositivo 09 ento ler o primeiro byte do campo da
mensagem. 'S' representa o sensor, e '1', o sensor 1. Ento o dispositivo monta a
mensagem [ID:S1VALOR] e responde ao server.
O buzzer para localizao do dispositivo. Por exemplo, tenho 50 dispositivos, qual
o 09? Teria quer ler identificador por identificador, mas se tiver um buzzer
tocando, posso ir diretamente ao dispositivo.
Enfim, o cdigo pode ser facilmente modificado agora para suas necessidades. No
fiz video porque eu no tenho nenhum hardware comigo infelizmente, mas esse
cdigo o suficiente para sua comunicao RS485.
Aqui foi utilizado o recurso de interrupes, ou seja, quando chega um dado serial
o programa para completamente para atender a interrupo, precedendo qualquer
tratamento com o desligamento das interrupes, afim de represar qualquer novo
dado entrante.
Se precisar de mais conceitos sobre interrupes ou outros recursos utilizados aqui,
procure aqui no site, pois h referncia para tudo o que foi utilizado.
Boa diverso!

S-ar putea să vă placă și