RS485 com PIC16F690 fácil
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, mas hoje veremos RS485 com PIC16F690.
Esquema

O circuito deve ser montado como no esquema a seguir:
Seu alcance é de até 1200 metros com tolerância a pertubações eletromagnéticas.
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; 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] = ''; } } //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 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,":BDCn",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] } } 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 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{ 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 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.
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.
Se gostou, não deixe de compartilhar; dê seu like no video e inscreva-se no nosso canal Do bit Ao Byte Brasil no YouTube.
Prefere seguir-nos pelo facebook? Basta curtir aqui.
Prefere twitter? @DobitAoByte.
Próximo post a caminho!
1 comentário