RS-485 Library

RS-485 is a multipoint communication which allows multiple devices to be connected to a single bus. mikroPascal PRO for PIC32 provides a set of library routines for comfortable work with RS485 system using Master/Slave architecture. Master and Slave devices interchange packets of information. Each of these packets contains synchronization bytes, CRC byte, address byte and the data. Each Slave has unique address and receives only packets addressed to it. The Slave can never initiate communication.

It is the user’s responsibility to ensure that only one device transmits via 485 bus at a time.

The RS-485 routines require the UART module. Pins of UART need to be attached to RS-485 interface transceiver, such as LTC485 or similar (see schematic at the bottom of this page).

Library constants:
  Important :

Library Dependency Tree

RS485 Library Dependency Tree

External dependencies of RS-485 Library

The following variable must be defined in all projects using RS-485 Library: Description : Example :
var RS485_rxtx_pin : sbit; sfr; atomic; external; Control RS-485 Transmit/Receive operation mode var RS485_rxtx_pin : sbit at RF2_bit;
var RS485_rxtx_pin_direction : sbit; sfr; atomic; external; Direction of the RS-485 Transmit/Receive pin var RS485_rxtx_pin_direction : sbit at TRISF2_bit;

Library Routines

RS485Master_Init

Prototype

procedure RS485Master_Init();

Description

Initializes MCU as a Master for RS-485 communication.

Parameters

None.

Returns

Nothing.

Requires Global variables :
  • RS485_rxtx_pin - this pin is connected to RE/DE input of RS-485 transceiver(see schematic at the bottom of this page). RE/DE signal controls RS-485 transceiver operation mode.
  • RS485_rxtx_pin_direction - direction of the RS-485 Transmit/Receive pin.
must be defined before using this routine.


UART HW module needs to be initialized. See UARTx_Init.

Example
// RS485 module pinout
var RS485_rxtx_pin : sbit at RF2_bit;         
var RS485_rxtx_pin_direction : sbit at TRISF2_bit;
// End of RS485 module pinout
...
UART1_Init(9600);                           // initialize UART1 module
RS485Master_Init();                         // intialize MCU as a Master for RS-485 communication
Notes

None.

RS485Master_Receive

Prototype

procedure RS485Master_Receive(var data : array[10] of byte);

Description

Receives messages from Slaves. Messages are multi-byte, so this routine must be called for each byte received.

Parameters
  • data_buffer: 7 byte buffer for storing received data. Data will be stored in the following manner:

  • data_buffer[0..2]: message content
  • data_buffer[3]: number of message bytes received, 1–3
  • data_buffer[4]: is set to 255 when message is received
  • data_buffer[5]: is set to 255 if error has occurred
  • data_buffer[6]: address of the Slave which sent the message

The routine automatically adjusts data[4] and data[5] upon every received message. These flags need to be cleared by software.

Returns

Nothing.

Requires

MCU must be initialized as a Master for RS-485 communication. See RS485Master_Init.

Example
var msg : array[8] of byte;
...
RS485Master_Receive(msg);
Notes

None.

RS485Master_Send

Prototype

procedure RS485Master_Send(var buffer : array[20] of byte; datalen : byte; slave_address : byte);

Description

Sends message to Slave(s). Message format can be found at the bottom of this page.

Parameters
  • data_buffer: data to be sent
  • datalen: number of bytes for transmition. Valid values: 0...3.
  • slave_address: Slave(s) address
Returns

Nothing.

Requires

MCU must be initialized as a Master for RS-485 communication. See RS485Master_Init.

It is the user’s responsibility to ensure (by protocol) that only one device sends data via 485 bus at a time.

Example
var msg : array[8] of byte;
...
// send 3 bytes of data to Slave with address 0x12
RS485Master_Send(msg, 3, 0x12);
Notes

None.

RS485Slave_Init

Prototype

procedure RS485Slave_Init(slave_address : byte);

Description

Initializes MCU as a Slave for RS-485 communication.

Parameters
  • Slave_address: Slave address
Returns

Nothing.

Requires Global variables :
  • RS485_rxtx_pin - this pin is connected to RE/DE input of RS-485 transceiver(see schematic at the bottom of this page). RE/DE signal controls RS-485 transceiver operation mode. Valid values: 1 (for transmitting) and 0 (for receiving
  • RS485_rxtx_pin_direction - direction of the RS-485 Transmit/Receive pin.
must be defined before using this routine.


UART HW module needs to be initialized. See UARTx_Init.

Example

Initialize MCU as a Slave with address 160:

// RS485 module pinout
var RS485_rxtx_pin : sbit at RF2_bit;     // transmit/receive control set to PORTC.B2
var RS485_rxtx_pin_direction : sbit at TRISF2_bit;
// End of RS485 module pinout
...
UART1_Init(9600);                   // initialize UART1 module
RS485Slave_Init(160);                // intialize MCU as a Slave for RS-485 communication with address 160
Notes

None.

RS485Slave_Receive

Prototype

procedure RS485Slave_Receive(var data_buffer : array[20] of byte);

Description

Receives messages from Master. If Slave address and Message address field don't match then the message will be discarded. Messages are multi-byte, so this routine must be called for each byte received.

Parameters
  • data_buffer: 6 byte buffer for storing received data, in the following manner:

  • data_buffer[0..2]: message content
  • data_buffer[3]: number of message bytes received, 1–3
  • data_buffer[4]: is set to 255 when message is received
  • data_buffer[5]: is set to 255 if error has occurred

The routine automatically adjusts data[4] and data[5] upon every received message. These flags need to be cleared by software.

Returns

Nothing.

Requires

MCU must be initialized as a Slave for RS-485 communication. See RS485Slave_Init.

Example
var msg : array[8] of byte;
...
RS485Slave_Read(msg);
Notes

None.

RS485Slave_Send

Prototype

procedure RS485Slave_Send(var data : array[20] of byte; datalen : byte);

Description

Sends message to Master. Message format can be found at the bottom of this page.

Parameters
  • data_buffer: data to be sent
  • datalen: number of bytes for transmition. Valid values: 0...3.
Returns

Nothing.

Requires

MCU must be initialized as a Slave for RS-485 communication. See RS485Slave_Init. It is the user’s responsibility to ensure (by protocol) that only one device sends data via 485 bus at a time.

Example
var msg : array[8] of byte;
...
// send 2 bytes of data to the Master
RS485Slave_Send(msg, 2);
Notes

None.

Library Example

The example demonstrates working with the PIC32 as a Master node in RS-485 communication. Master sends message to Slave with address 160 and waits for a response. After the response is received, the first byte of received data is incremented and sent back to the Slave. The received data is displayed on PORTB while error on receiving (0xAA) and number of consecutive unsuccessful retries are displayed on PORTD. Hardware configurations in this example are made for the LV-32MX v6 board and PIC32MX460F512L.

Copy Code To ClipboardCopy Code To Clipboard
program RS485_Master_Example;

var dat : array[10] of byte;   // buffer for receving/sending messages
    i, j : byte;
    cnt : longint;

var rs485_rxtx_pin  : sbit at RF2_bit;               // set transcieve pin
    rs485_rxtx_pin_direction : sbit at TRISF2_bit;   // set transcieve pin direction

// Interrupt routine
procedure interrupt(); iv IVT_UART_2; ilevel 7; ics ICS_SRS;
begin
  RS485Master_Receive(dat);
  U2RXIF_bit := 0;             // ensure interrupt not pending
end;

  begin
    cnt := 0;

    CHECON  := 0x32;
    AD1PCFG := 0xFFFF;

    PORTB := 0;
    PORTD := 0;
    TRISB := 0;
    TRISD := 0;

    UART2_Init(19200);         // initialize UART2 module
    Delay_ms(100);

    RS485Master_Init();        // initialize MCU as Master

    dat[0] := 0xAA;
    dat[1] := 0xF0;
    dat[2] := 0x0F;
    dat[4] := 0;               // ensure that message received flag is 0
    dat[5] := 0;               // ensure that error flag is 0
    dat[6] := 0;

    U2IP0_bit := 1;            // set interrupt
    U2IP1_bit := 1;            // priority
    U2IP2_bit := 1;            // to 7

    URXISEL1_U2STA_bit := 0;   // 0x = Interrupt flag bit is set when a character is received
    U2RXIF_bit := 0;           // ensure interrupt not pending

    MVEC_bit := 1;             // Interrupt controller configured for multi vectored mode
    asm 
      ei R0;                   // Enable all interrupts
    end;

    U2RXIE_bit := 1;           // enable intterupt

    RS485Master_Send(dat,1,160);

    while (TRUE) do
      begin                              // upon completed valid message receiving
                                         //   data[4] is set to 255
        Inc(cnt);
        if (dat[5] <> 0) then            // if an error detected, signal it
          PORTD := 0xAA;                 //   by setting portd to 0xAA
        if (dat[4] <> 0) then            // if message received successfully
          begin
            cnt := 0;
            dat[4] := 0;                 // clear message received flag
            j := dat[3];
            for i := 1 to dat[3] do      // show data on PORTB
              PORTB := dat[i-1];
            dat[0] := dat[0]+1;          // send back to master
            Delay_ms(1);
            RS485Master_Send(dat,1,160);
          end;

        if (cnt > 100000) then                // if in 100000 poll-cycles the answer
          begin
            Inc(PORTD);                       //   was not detected, signal
            cnt := 0;                         //   failure of send-message
            RS485Master_Send(dat,1,160);
            if (PORTD > 10) then              // if sending failed 10 times
              begin
                RS485Master_Send(dat,1,50);   //   send message on broadcast address
              end;
          end;
      end;
  end.

Copy Code To ClipboardCopy Code To Clipboard
program RS485_Slave_Example;

var dat : array[20] of byte;                           // buffer for receving/sending messages
    i, j : byte;

var rs485_rxtx_pin : sbit at RF2_bit;                  // set transcieve pin
    rs485_rxtx_pin_direction : sbit at TRISF2_bit;     // set transcieve pin direction

// Interrupt routine
procedure interrupt(); iv IVT_UART_2; ilevel 7; ics ICS_SRS;
begin
  RS485Slave_Receive(dat);
  U2RXIF_bit := 0;                      // ensure interrupt not pending
end;

begin
  CHECON  := 0x32;
  AD1PCFG := 0xFFFF;

  TRISB := 0;
  TRISD := 0;
  PORTB := 0;
  PORTD := 0;

  UART2_Init(19200);         // initialize UART2 module
  Delay_ms(100);

  RS485Slave_Init(160);     // Intialize MCU as slave, address 160

  dat[0] := 0xAA;
  dat[1] := 0xF0;
  dat[2] := 0x0F;
  dat[4] := 0;               // ensure that message received flag is 0
  dat[5] := 0;               // ensure that error flag is 0
  dat[6] := 0;

  U2IP0_bit := 1;            // set interrupt
  U2IP1_bit := 1;            // priority
  U2IP2_bit := 1;            // to 7

  URXISEL1_U2STA_bit := 0;   // 0x = Interrupt flag bit is set when a character is received
  U2RXIF_bit := 0;           // ensure interrupt not pending

  MVEC_bit := 1;             // Interrupt controller configured for multi vectored mode
  asm 
    ei R0;                // Enable all interrupts
  end;

  U2RXIE_bit := 1;                      // enable intterupt

  while (TRUE) do
    begin
      if (dat[5] <> 0) then           // if an error detected, signal it by
        begin
          PORTD := 0xAA;              //   setting portd to 0xAA
          dat[5] := 0;
        end;
      if (dat[4] <> 0) then           // upon completed valid message receive
        begin
          dat[4] := 0;                //   data[4] is set to 0xFF
          j := dat[3];

          for i := 1 to dat[3] do     // show data on PORTB
            PORTB := dat[i-1];

          dat[0] := dat[0]+1;         // increment received dat[0]
          Delay_ms(1);
          RS485Slave_Send(dat,1);     //   and send it back to master
        end;
    end;
end.

HW Connection

Example of interfacing PC to PIC32 MCU via RS485 bus with LTC485 as RS-485 transceiver

Example of interfacing PC to PIC32 MCU via RS485 bus with LTC485 as RS-485 transceiver

Message format and CRC calculations

Q: How is CRC checksum calculated on RS485 master side?

Copy Code To ClipboardCopy Code To Clipboard
const START_BYTE : byte = 0x96;  // 10010110
const STOP_BYTE  : byte = 0xA9;  // 10101001


PACKAGE:
-------- 
START_BYTE 0x96
ADDRESS
DATALEN
[DATA1]            // if exists
[DATA2]            // if exists
[DATA3]            // if exists
CRC
STOP_BYTE  0xA9


DATALEN bits
------------
bit7 = 1  MASTER SENDS
       0  SLAVE  SENDS
bit6 = 1  ADDRESS WAS XORed with 1, IT WAS EQUAL TO START_BYTE or STOP_BYTE
       0  ADDRESS UNCHANGED
bit5 = 0  FIXED
bit4 = 1  DATA3 (if exists) WAS XORed with 1, IT WAS EQUAL TO START_BYTE or STOP_BYTE
       0  DATA3 (if exists) UNCHANGED
bit3 = 1  DATA2 (if exists) WAS XORed with 1, IT WAS EQUAL TO START_BYTE or STOP_BYTE
       0  DATA2 (if exists) UNCHANGED
bit2 = 1  DATA1 (if exists) WAS XORed with 1, IT WAS EQUAL TO START_BYTE or STOP_BYTE
       0  DATA1 (if exists) UNCHANGED
bit1bit0 = 0 to 3 NUMBER OF DATA BYTES SEND


CRC generation :
----------------
crc_send := datalen  xor address;
crc_send := crc_send xor data[0];    // if exists
crc_send := crc_send xor data[1];    // if exists
crc_send := crc_send xor data[2];    // if exists
crc_send := crc_send not crc_send;
if ((crc_send = START_BYTE) or (crc_send = STOP_BYTE)) then
   crc_send := crc_send + 1;
NOTE: DATALEN<4..0> can not take the START_BYTE<4..0> or STOP_BYTE<4..0> values.
Copyright (c) 2002-2012 mikroElektronika. All rights reserved.
What do you think about this topic ? Send us feedback!
Want more examples and libraries? 
Find them on LibStock - A place for the code