rs485 com pic

8
RS485 com PIC16F690 Na IDE que utilizo para programar para PIC (MikroC) encontram-se muitas bibliotecas, tal como em Arduino e por padrão, muitas mais já instaladas. Há algum tempo trabalhei em um projeto cuja comunicação em rede era RS485. Em Arduino, escrevi uma pequena prova de conceito nesse post. Como não 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 não deve passar de 300 metros, deve-se evitar áreas de interferência eletromagnéticas e coisas do tipo. Esse é um padrão de mercado e deve ser respeitado. Por exemplo, não é fundamental os resistores de 4.7k, mas o padrão prevê o máximo de proteção para a rede. O código implementado será exposto a seguir, com os respectivos comentários. Código //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;

Upload: francisco-josivan

Post on 20-Jul-2016

9 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Rs485 Com Pic

RS485 com PIC16F690 Na IDE que utilizo para programar para PIC (MikroC) encontram-se muitas

bibliotecas, tal como em Arduino e por padrão, muitas mais já instaladas. Há algum

tempo trabalhei em um projeto cuja comunicação em rede era RS485. Em Arduino,

escrevi uma pequena prova de conceito nesse post. Como não 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 não deve passar de 300 metros, deve-se evitar áreas de

interferência eletromagnéticas e coisas do tipo.

Esse é um padrão de mercado e deve ser respeitado. Por exemplo, não é

fundamental os resistores de 4.7k, mas o padrão prevê o máximo de proteção para

a rede.

O código implementado será exposto a seguir, com os respectivos comentários.

Código

//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;

Page 2: Rs485 Com Pic

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 variável

char message[10];

char i;

char ok = 0;

short int SEND = 0;

short int RECEIVE = 3;

//sbit são 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';

}

Page 3: Rs485 Com Pic

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 não

haja colisão é baseada

// no ID do dispositivo em questão

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 não são

broadcast são 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]. Não implementado

}

else if (message[1] == 'D'){

//abaixa pino output[5]

}

Page 4: Rs485 Com Pic

}

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

interrupção, o BUZZER pare de tocar. Isso acontece porque no

main()

o pino está sendo levantado a cada 50ms. Se houver uma

interrupção

nesse intervalo, o som pode parar.

O caso mais comum é não haver intermitência do BUZZER durante

uma

interrupção, pelo mesmo motivo.

Solução: 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

Page 5: Rs485 Com Pic

else if (message[0] == 'G'){

//-------------------- gravação

/*

A gravação 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 são ID e os 2 seguintes são 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 configuração, 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{

Page 6: Rs485 Com Pic

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();

Page 7: Rs485 Com Pic

// 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 não for B e sim ‘[', então o

segundo e terceiro byte são avaliados. Qualquer dispositivo que não seja o 09 irá

ignorar a mensagem. O dispositivo 09 então lerá o primeiro byte do campo da

mensagem. 'S' representa o sensor, e '1', o sensor 1. Então o dispositivo monta a

mensagem [ID:S1VALOR] e responde ao server.

O buzzer é para localização 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.

Page 8: Rs485 Com Pic

Enfim, o código pode ser facilmente modificado agora para suas necessidades. Não

fiz video porque eu não tenho nenhum hardware comigo infelizmente, mas esse

código é o suficiente para sua comunicação RS485.

Aqui foi utilizado o recurso de interrupções, ou seja, quando chega um dado serial

o programa para completamente para atender a interrupção, precedendo qualquer

tratamento com o desligamento das interrupções, afim de ‘represar’ qualquer novo

dado entrante.

Se precisar de mais conceitos sobre interrupções ou outros recursos utilizados aqui,

procure aqui no site, pois há referência para tudo o que foi utilizado.

Boa diversão!