RS-485 Library

RS-485 is a multipoint communication which allows multiple devices to be connected to a single bus. The mikroC PRO for ARM 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

Stellaris

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

STM32

The following variable must be defined in all projects using RS-485 Library: Description : Example :
extern sfr sbit RS485_rxtx_pin; Control RS-485 Transmit/Receive operation mode sbit RS485_rxtx_pin at GPIOA_ODR.B6;

Library Routines

RS485Master_Init

Prototype

void RS485Master_Init();

Description

Initializes MCU as a Master for RS-485 communication.

Parameters

None.

Returns

Nothing.

Requires

External dependencies of the library from the top of the page must be defined before using this function.
UART HW module needs to be initialized. See UARTx_Init.

Example

Stellaris

// RS485 module pinout
sbit  RS485_rxtx_pin           at GPIO_PORTA_DATA2_bit;  // set transcieve pin
sbit  RS485_rxtx_pin_direction at GPIO_PORTA_DIR2_bit;   // set transcieve pin direction
// end RS485 module pinout
...
UART1_Init(9600);                           // initialize UART1 module
RS485Master_Init();                         // intialize MCU as a Master for RS-485 communication

STM32

// RS485 module pinout
sbit  RS485_rxtx_pin           at GPIOA_ODR.B6;  // set transcieve pin
// end 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

void RS485Master_Receive(char *data_buffer);

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
char msg[8];
...
RS485Master_Receive(msg);
Notes

None.

RS485Master_Send

Prototype

void RS485Master_Send(char *data_buffer, char datalen, char slave_address);

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
char msg[8];
...
// send 3 bytes of data to Slave with address 0x12
RS485Master_Send(msg, 3, 0x12);
Notes

None.

RS485Slave_Init

Prototype

void RS485Slave_Init(char Slave_address);

Description

Initializes MCU as a Slave for RS-485 communication.

Parameters
  • Slave_address: Slave address
Returns

Nothing.

Requires

External dependencies of the library from the top of the page must be defined before using this function.
UART HW module needs to be initialized. See UARTx_Init.

Example

Stellaris

// RS485 module pinout
sbit  RS485_rxtx_pin           at GPIO_PORTA_DATA2_bit;  // set transcieve pin
sbit  RS485_rxtx_pin_direction at GPIO_PORTA_DIR2_bit;   // set transcieve pin direction
// end RS485 module pinout
...
UART1_Init(9600);                           // initialize UART1 module
RS485Slave_Init(160);                // intialize MCU as a Slave for RS-485 communication with address 160

STM32

// RS485 module pinout
sbit  RS485_rxtx_pin           at GPIOA_ODR.B6;  // set transcieve pin
// end 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

void RS485Slave_Receive(char *data_buffer);

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
char msg[8];
...
RS485Slave_Read(msg);
Notes

None.

RS485Slave_Send

Prototype

void RS485Slave_Send(char *data_buffer, char datalen);

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
char msg[8];
...
// send 2 bytes of data to the Master
RS485Slave_Send(msg, 2);
Notes

None.

Library Example

This is a simple demonstration of RS485 Library routines usage.

Master sends message to Slave with address 160 and waits for a response. The Slave accepts data, increments it and sends it back to the Master. Master then does the same and sends incremented data back to Slave, etc.

Master displays received data on GPIO_PORTE, while error on receive (0xAA) and number of consecutive unsuccessful retries are displayed on GPIO_PORTD. Slave displays received data on GPIO_PORTE, while error on receive (0xAA) is displayed on GPIO_PORTD.

RS485 Master code:

Stellaris

char dat[10];               // buffer for receving/sending messages
char i,j;

sbit  RS485_rxtx_pin           at GPIO_PORTA_DATA2_bit;  // set transcieve pin
sbit  RS485_rxtx_pin_direction at GPIO_PORTA_DIR2_bit;   // set transcieve pin direction

// Interrupt routine
void interrupt() iv IVT_INT_UART0 {
  RS485Master_Receive(dat);
  UART_ICR_RXIC_bit = 1;           // ensure interrupt not pending
}

void main(){
  long cnt = 0;

  GPIO_Digital_Output(&GPIO_PORTD, _GPIO_PINMASK_ALL);
  GPIO_PORTD_DATA = 0;

  GPIO_Digital_Output(&GPIO_PORTE, _GPIO_PINMASK_ALL);
  GPIO_PORTE_DATA = 0;

  UART0_Init(19200);        // initialize UART0 module
  Delay_ms(100);
  
  RS485Master_Init();       // initialize MCU as Master

  UART_IM_RXIM_bit = 1;          // enable uart rx interrupt
  NVIC_IntEnable(IVT_INT_UART0); // enable interrupt vector

  EnableInterrupts();            // enable core interrupts

  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;
  
  RS485Master_Send(dat,1,160);

  while (1){
                                       // upon completed valid message receiving
                                       //   data[4] is set to 255
    cnt++;
    if (dat[5])  {                     // if an error detected, signal it
      GPIO_PORTD_DATA = 0xAA;          //   by setting GPIO_PORTD to 0xAA
    }
    if (dat[4]) {                      // if message received successfully
      cnt = 0;
      dat[4] = 0;                      // clear message received flag
      j = dat[3];
      for (i = 1; i <= dat[3]; i++) {  // show data on GPIO_PORTE
        GPIO_PORTE_DATA = dat[i-1];
      }                                // increment received dat[0]
      dat[0] = dat[0]+1;               // send back to master
      Delay_ms(1);
      RS485Master_Send(dat,1,160);

    }
   if (cnt > 100000) {
      GPIO_PORTD_DATA ++;
      cnt = 0;
      RS485Master_Send(dat,1,160);
      if (GPIO_PORTD_DATA > 10)        // if sending failed 10 times
        RS485Master_Send(dat,1,50);    //   send message on broadcast address
     }
  }
}

STM32

char dat[10];               // buffer for receving/sending messages
char i,j;

sbit  RS485_rxtx_pin  at GPIOA_ODR.B6;  // set transcieve pin

// Interrupt routine
void interrupt() iv IVT_INT_USART1 ics ICS_AUTO {
  RS485Master_Receive(dat);
}

void main(){
  long cnt = 0;

  GPIO_Digital_Output(&GPIOC_BASE, _GPIO_PINMASK_ALL);
  GPIOC_ODR = 0;

  GPIO_Digital_Output(&GPIOD_BASE, _GPIO_PINMASK_ALL);
  GPIOD_ODR = 0;

  UART1_Init(19200);               // initialize UART1 module
  Delay_ms(100);
  
  RS485Master_Init();              // initialize MCU as Master

  USART1_CR1bits.RXNEIE = 1;       // enable uart rx interrupt
  NVIC_IntEnable(IVT_INT_USART1);  // enable interrupt vector

  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;

  RS485Master_Send(dat, 1, 160);

  while (1) {
                                       // upon completed valid message receiving
                                       //   data[4] is set to 255
    cnt++;
    if (dat[5]) {                      // if an error detected, signal it
      GPIOC_ODR = 0xAA;                //   by setting PORTC to 0xAA
    }
    if (dat[4]) {                      // if message received successfully
      cnt = 0;
      dat[4] = 0;                      // clear message received flag
      j = dat[3];
      for (i = 1; i <= dat[3]; i++) {  // show data on PORTD
        GPIOD_ODR = dat[i-1];
      }                                // increment received dat[0]
      dat[0] = dat[0]+1;               // send back to master
      Delay_ms(1);
      RS485Master_Send(dat,1,160);

    }
    if (cnt > 100000) {
      GPIOC_ODR ++;
      cnt = 0;
      RS485Master_Send(dat,1,160);
      if (GPIOC_ODR > 10)            // if sending failed 10 times
        RS485Master_Send(dat,1,50);    //   send message on broadcast address
    }
  }
}

RS485 Slave code:

Stellaris

char dat[9];      // buffer for receving/sending messages
char i;

sbit  RS485_rxtx_pin           at GPIO_PORTA_DATA2_bit;  // set transcieve pin
sbit  RS485_rxtx_pin_direction at GPIO_PORTA_DIR2_bit;   // set transcieve pin direction

// Interrupt routine
void interrupt() iv IVT_INT_UART0 {
  RS485Slave_Receive(dat);
  UART_ICR_RXIC_bit = 1;    // ensure interrupt not pending
}

void main() {
  GPIO_Digital_Output(&GPIO_PORTD, _GPIO_PINMASK_ALL);
  GPIO_PORTD_DATA = 0;

  GPIO_Digital_Output(&GPIO_PORTE, _GPIO_PINMASK_ALL);
  GPIO_PORTE_DATA = 0;

  UART0_Init(19200);        // initialize UART0 module
  Delay_ms(100);
  
  UART_IM_RXIM_bit = 1;          // enable uart rx interrupt
  NVIC_IntEnable(IVT_INT_UART0); // enable interrupt vector

  EnableInterrupts();            // enable core interrupts

  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;

  while (1) {
    if (dat[5])  {                    // if an error detected, signal it by
      GPIO_PORTD_DATA = 0xAA;         //   setting GPIO_PORTD to 0xAA
      dat[5] = 0;
    }
    if (dat[4]) {                     // upon completed valid message receive
      dat[4] = 0;                     //   data[4] is set to 0xFF
      for (i = 1; i <= dat[3];i++){
        GPIO_PORTE_DATA = 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
    }
  }
}

STM32

char dat[9];      // buffer for receving/sending messages
char i;

sbit  RS485_rxtx_pin  at GPIOA_ODR.B6;  // set transcieve pin

// Interrupt routine
void interrupt() iv IVT_INT_USART1 ics ICS_AUTO {
  RS485Slave_Receive(dat);
}

void main() {
  GPIO_Digital_Output(&GPIOC_BASE, _GPIO_PINMASK_ALL);
  GPIOC_ODR = 0;

  GPIO_Digital_Output(&GPIOD_BASE, _GPIO_PINMASK_ALL);
  GPIOD_ODR = 0;

  UART1_Init(19200);                // initialize UART1 module
  Delay_ms(100);
  
  USART1_CR1bits.RXNEIE  = 1;        // enable uart rx interrupt
  NVIC_IntEnable(IVT_INT_USART1);   // enable interrupt vector

  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;

  while (1) {
    if (dat[5])  {                  // if an error detected, signal it by
      GPIOC_ODR = 0xAA;             //   setting PORTC to 0xAA
      dat[5] = 0;
    }
    if (dat[4]) {                   // upon completed valid message receive
      dat[4] = 0;                   //   data[4] is set to 0xFF
      for (i = 1; i <= dat[3];i++){
        GPIOD_ODR = 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
    }
  }
}

Message format and CRC calculations

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

Copy Code To ClipboardCopy Code To Clipboard
START_BYTE = 0x96; // 10010110
STOP_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 ^ address;
crc_send ^= data[0];    // if exists
crc_send ^= data[1];    // if exists
crc_send ^= data[2];    // if exists
crc_send = ~crc_send;
if ((crc_send == START_BYTE) || (crc_send == STOP_BYTE))
   crc_send++;
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