Fork me on GitHub

15/12/13

TUTORIAL: RS-485 com PIC

TUTORIAL: RS-485 com PIC



"É um padrão de comunicação serial, também denominado EIA-485."

"O padrão RS-485 é baseado na transmissão diferencial de dados, através de um par de fios, que é ideal para transmissão em altas velocidades, longas distâncias e em ambientes propícios a interferência eletromagnética. Ele permite a comunicação entre vários elementos( escravos ) em uma mesma rede de dados. A transmissão diferencial de dados funciona da seguinte forma: qualquer transmissor RS-485 possui dois canais independentes conhecidos como A e B, que transmitem níveis de tensão iguais, porém com polaridades opostas."



O compilador MikroC PRO oferece suporte para a comunicação RS-485. Possui 6 funções:

RS485Master_Init() - Inicializa o MCU como Master( Mestre ).

RS485Master_Receive( char *dat ) - Recebe os dados do escravo.
dat - 7 bytes recebidos e que contem várias informações:
dat[0..2] - Conteúdo da mensagem;
dat[3] - Número de bytes da mensagem.
dat[4] - Flag. Recebeu uma mensagem( dat[4] = 255 ). Deve ser limpa via software.
dat[5] - Flag. Erro ( dat[5] == 255 ). Deve ser limpa via software.
dat[6] - Endereço do escravo que enviou a mensagem.

RS485Master_Send( char *dat, char length, char address ) - Envia uma mensagem a um determinado escravo.
dat - 3 bytes a serem enviados ao escravo.
length - O tamanho da mensagem. 1 a 3 bytes.
address - O endereço do escravo.

RS485Slave_Init( char address ) - Inicializa o MCU como Slave( Escravo ).
address - Define um endereço para o escravo.

RS485Slave_Receive( char *dat ) - Recebe os dados do mestre.
dat - 6 bytes recebidos e que contém várias informações:
dat[0..2] - Conteúdo da mensagem;
dat[3] - Número de bytes da mensagem.
dat[4] - Flag. Recebeu uma mensagem( dat[4] = 255 ). Deve ser limpa via software.
dat[5] - Flag. Erro ( dat[5] == 255 ). Deve ser limpa via software.

RS485Slave_Send( char *datchar length ) - Envia uma mensagem ao mestre.
dat - 3 bytes a serem enviados ao escravo.
length - O tamanho da mensagem. 1 a 3 bytes.

Obs.:
  • Mensagem contendo o endereço 50 será recebido por todos os escravos, exceto os escravos com endereços 150 e 169.
  • A biblioteca UARTx deve ser inicializada.
EXEMPLO: Master
sbit  rs485_rxtx_pin  at RD0_bit;
sbit  rs485_rxtx_pin_direction at TRISD0_bit;

char dat[8];

void interrupt()
{
  RS485Master_Receive( dat );
}

void main()
{
    Uart1_Init( 9600 ); //Inicializa o modulo UART
    RS485Master_Init(); //inicializa o MCU como master
    
    Soft_Uart_Init( &PORTD, 6, 7, 9600, 0 );  
    
    RCIE_bit = 1; //Habilita interrupção por recepcao
    TXIE_bit = 0; //Desabilita interrupção por transmissao
    PEIE_bit = 1; //Habilita interrupção dos periféricos
    GIE_bit = 1; //Habilita a interrupção global
    
    dat[4] = 0; //limpa flag
    dat[5] = 0; //limpa flag
    
    while(1)
    {
      dat[0] = 0x22;
      dat[1] = 0x67;
      dat[2] = 0x40; //Mensagem a ser enviada
      //Envia a mensagem para o escravo com o endereço 'C'
      RS485Master_Send( dat, 3, 'C' ); 
      //Aguarda o recebimento da mensagem do escravo
      while( dat[4] != 255 );
      
      dat[4] = 0; //limpa a flag
      //Envia a mensagem recebida para o PC
      Soft_Uart_Write( dat[0] );
      Soft_Uart_Write( dat[1] );
      Soft_Uart_Write( dat[2] );

      Delay_ms( 1000 );
      
      //Recepe o processo novamente só que para outro escravo
      dat[0] = 0x01;
      dat[1] = 0x22;
      dat[0] = 0x22;
      RS485Master_Send( dat, 3, 'T' );

      while( dat[4] != 255 );

      dat[4] = 0;
      Soft_Uart_Write( dat[0] );
      Soft_Uart_Write( dat[1] );
      Soft_Uart_Write( dat[2] );

      Delay_ms( 1000 );

    }
}

EXEMPLO: Slave
sbit  rs485_rxtx_pin  at RB0_bit;
sbit  rs485_rxtx_pin_direction at TRISB0_bit;

char dat[8];
unsigned ADC_Rd;

void interrupt()
{
 RS485Slave_Receive( dat );
}

void main()
{
    Uart1_Init( 9600 );
     //inicializa como escravo e endereço = 'T'
    RS485Slave_Init( 'T' );
    
    RCIE_bit = 1; //Habilita interrupção por recepcao
    TXIE_bit = 0; //Desabilita interrupção por transmissao
    PEIE_bit = 1; //Habilita interrupção dos periféricos
    GIE_bit = 1; //Habilita a interrupção global
    
    dat[4] = 0; //limpa flag
    dat[5] = 0; //limpa  flag
    
    while(1)
    {
        if( dat[4] ) //Recebeu alguma mensagem
        {
            dat[4] = 0; //limpa a flag
            //Supondo q o MCU possui canal analógico
            ADC_Rd = ADC_Read(0); //Faz a leitura do canal analógico
            dat[0] = 0; //Informa a leitura do canal 0
            dat[1] = ((char*)&ADC_Rd)[0];
            dat[2] = ((char*)&ADC_Rd)[1];
            RS485Slave_Send( dat, 3 ); //Envia 3 bytes ao master
        }
    }

}

14 comentários:

  1. Boa tarde Tiago você teria algum exemplo de comunicação rs485 entre dois pic's mestre e escravo tipo eu preciono um botão no mestre ele envia para o escravo o escravo executa e informa ao mestre qual o seu estado.
    Você teria algo desse tipo agradeço a ajuda.

    ResponderExcluir
    Respostas
    1. O codigo seria semelhante ao de cima, so adicione os tratamentos do botoes.

      Excluir
  2. Boa noite você teria como me falar onde devo colocar os botões.

    ResponderExcluir
  3. ESTOU TRABALHANDO NUM PROJETO PARA LIGAR O PIC EM UMA HMI TOUCH, MAS COMO ELA TEM O PADRAO RS485 PARA ACIONAR AS PORTAS DIGITAIS B10001 A B19999 COMO EU PODERIA DECLARAR ESTAS VARIAVEIS NO PIC ?

    ResponderExcluir
  4. Boa tarde, tenho uma aplicação onde tenho 4 circuitos comunicando em 485 e preciso adquirir a mesma informação nesses 4 circuitos e exibir a soma delas. Fiquei com um pouco de dúvida no código, se utilizar um pic, ele deve enviar um byte para "ligar" cada escravo? Eu endereço o byte por exemplo para o escravo 3, quando receber uma resposta eu verifico na variavel correspondente ao endereço do escravo se foi o 3 que respondeu e a partir disso faço o tratamento das outras informações também recebidas? Obrigado, parabéns pelo blog.

    ResponderExcluir
  5. Olá! uma dúvida.... pq no whille tem o de (dat[4] != 255) para aguardar a resposta do slave? sendo que assim que o master recebe pela interrupção, a flag dat[4] é setada 255, da forma que está o código não fica preso no whille, ele segue rodando pq a flag ainda é 0... não seria, whille(dat[4] == 255); ? para o código ficar preso e esperar a interrupção jogar o dat[4] para 255? Abraços e parabéns pelo blog! muito bom!

    ResponderExcluir
    Respostas
    1. Seu código funciona perfeitamente, fiz um esquema no proteus e deu tudo certo... acho que estou confuso ainda na lógica da biblioteca RS485 do MIKROC. Abrços

      Excluir
    2. Ele só vai sair do while quando dat[4] == 255, ou seja, enquanto não for 255, ele fica no loop.

      Excluir
  6. Olá, boa tarde!

    Tenho dificuldades em encontrar o MAX 485 no Proteus. Devo baixar alguma biblioteca para inseri-lo?

    Obrigado

    ResponderExcluir
  7. Boa tarde Galera, onde devo colocar os botões saídas de sinal?

    ResponderExcluir
  8. Qual configuração de fuses e cristal você recomenda? Estou usando um de 20Mhz e na prática o master reseta sozinho depois de alguns dados enviados. Ele tem outras funções além dessa do 485, eu apenas me baseei no seu código. O engraçado é com outras configurações de cristal que coloco o slave não recebe, mas também o master não reseta sozinho. Tem alguma idéia do que possa ser?

    ResponderExcluir
  9. Olá! Com essa biblioteca eu só consigo enviar dados para os Slaves? Eu estou precisando coletar os dados que estão sendo executados nas entradas analógicas dos escravos... Até que eu consegui, porem depois de alguns instantes os Master trava no while.... é como se tivesse acontecido alguma falha de comunicação... Alguém pode me ajudar? Eu estou precisando muito de uma luz...

    ResponderExcluir
    Respostas
    1. Olá dê uma olhada aqui: http://www.rogercom.com/CursoOnlineUSB/ModuloUnicoAula018.htm

      Excluir

Postagens Relacionadas!!