RS-485 Library
RS-485 is a multipoint communication which allows multiple devices to be connected to a single bus. The mikroBasic PRO for AVR 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).
- START byte value =
150
- STOP byte value =
169
- Address
50
is the broadcast address for all Slaves (packets containing address50
will be received by all Slaves except the Slaves with addresses150
and169
).

- The library uses the UART module for communication. The user must initialize the appropriate UART module before using the RS-485 Library.
- For MCUs with multiple UART modules it is possible to initialize them and then switch by using the UART_Set_Active routine.
Library Dependency Tree

External dependencies of RS-485 Library
The following variable must be defined in all projects using RS-485 Library: | Description : | Example : |
---|---|---|
dim RS485_rxtx_pin as sbit sfr external |
Control RS-485 Transmit/Receive operation mode | dim RS485_rxtx_pin as sbit at PORTD2_bit |
dim RS485_rxtx_pin_direction as sbit sfr external |
Direction of the RS-485 Transmit/Receive pin | dim RS485_rxtx_pin_direction as sbit at DDD2_bit |
Library Routines
- RS485Master_Init
- RS485Master_Receive
- RS485Master_Send
- RS485Slave_Init
- RS485Slave_Receive
- RS485Slave_Send
RS485Master_Init
Prototype |
sub procedure RS485Master_Init() |
---|---|
Returns |
Nothing. |
Description |
Initializes MCU as a Master for RS-485 communication. |
Requires |
Global variables :
|
Example |
' RS485 module pinout dim RS485_rxtx_pin as sbit at PORTD2_bit dim RS485_rxtx_pin_direction as sbit at DDD2_bit ' End of RS485 module pinout ... UART1_Init(9600) ' initialize UART module RS485Master_Init() ' intialize MCU as a Master for RS-485 communication |
RS485Master_Receive
Prototype |
sub procedure RS485Master_Receive(dim byref data_buffer as byte[20]) |
---|---|
Returns |
Nothing. |
Description |
Receives messages from Slaves. Messages are multi-byte, so this routine must be called for each byte received. Parameters :
The function automatically adjusts |
Requires |
MCU must be initialized as a Master for RS-485 communication. See RS485Master_Init. |
Example |
dim msg as byte[20] ... RS485Master_Receive(msg) |
RS485Master_Send
Prototype |
sub procedure RS485Master_Send(dim byref data_buffer as byte[20], dim datalen as byte, dim slave_address as byte) |
---|---|
Returns |
Nothing. |
Description |
Sends message to Slave(s). Message format can be found at the bottom of this page. Parameters :
|
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 |
dim msg as byte[20] ... ' send 3 bytes of data to slave with address 0x12 RS485Master_Send(msg, 3, 0x12) |
RS485Slave_Init
Prototype |
sub procedure RS485Slave_Init(dim slave_address as byte) |
---|---|
Returns |
Nothing. |
Description |
Initializes MCU as a Slave for RS-485 communication. Parameters :
|
Requires |
Global variables :
|
Example |
Initialize MCU as a Slave with address 160: ' RS485 module pinout dim RS485_rxtx_pin as sbit at PORTD2_bit dim RS485_rxtx_pin_direction as sbit at DDD2_bit ' End of RS485 module pinout ... UART1_Init(9600) ' initialize UART module RS485Slave_Init(160) ' intialize MCU as a Slave for RS-485 communication with address 160 |
RS485Slave_Receive
Prototype |
sub procedure RS485Slave_Receive(dim byref data_buffer as byte[20]) |
---|---|
Returns |
Nothing. |
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 :
The function automatically adjusts |
Requires |
MCU must be initialized as a Slave for RS-485 communication. See RS485Slave_Init. |
Example |
dim msg as byte[5] ... RS485Slave_Read(msg) |
RS485Slave_Send
Prototype |
sub procedure RS485Slave_Send(dim byref data_buffer as byte[20], dim datalen as byte) |
---|---|
Returns |
Nothing. |
Description |
Sends message to Master. Message format can be found at the bottom of this page. Parameters :
|
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 |
dim msg as byte[8] ... ' send 2 bytes of data to the master RS485Slave_Send(msg, 2) |
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 P0, while error on receive (0xAA) and number of consecutive unsuccessful retries are displayed on P1. Slave displays received data on P0, while error on receive (0xAA) is displayed on P1. Hardware configurations in this example are made for the EasyAVR5A board and ATmega16.
RS485 Master code:
program RS485_Master_Example dim dat as byte[10] ' buffer for receving/sending messages i, j as byte cnt as longint dim rs485_rxtx_pin as sbit at PORTD2_bit ' set transcieve pin rs485_rxtx_pin_direction as sbit at DDD2_bit ' set transcieve pin direction ' Interrupt routine sub procedure interrupt() org 0x16 RS485Master_Receive(dat) end sub main: cnt = 0 PORTA = 0 ' clear PORTA PORTB = 0 ' clear PORTB PORTC = 0 ' clear PORTC DDRA = 0xFF ' set PORTA as output DDRB = 0xFF ' set PORTB as output DDRC = 0xFF ' set PORTB as output UART1_Init(9600) ' initialize UART1 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 RS485Master_Send(dat,1,160) SREG_I_bit = 1 ' enable global interrupt RXCIE_bit = 1 ' enable interrupt on UART receive while TRUE Inc(cnt) if (dat[5] <> 0) then ' if an error detected, signal it PORTC = dat[5] ' by setting PORTC end if if (dat[4] <> 0) then ' if message received successfully cnt = 0 dat[4] = 0 ' clear message received flag j = dat[3] for i = 1 to dat[3] ' show data on PORTB PORTB = dat[i-1] next i dat[0] = dat[0]+1 ' increment received dat[0] Delay_ms(1) ' send back to slave RS485Master_Send(dat,1,160) end if if (cnt > 100000) then ' if in 100000 poll-cycles the answer Inc(PORTA) ' was not detected, signal cnt = 0 ' failure of send-message RS485Master_Send(dat,1,160) if (PORTA > 10) then ' if sending failed 10 times PORTA = 0 RS485Master_Send(dat,1,50) ' send message on broadcast address end if end if wend end.
RS485 Slave code:
program RS485_Slave_Example dim dat as byte[20] ' buffer for receving/sending messages i, j as byte dim rs485_rxtx_pin as sbit at PORTD2_bit ' set transcieve pin rs485_rxtx_pin_direction as sbit at DDD2_bit ' set transcieve pin direction ' Interrupt routine sub procedure interrupt() org 0x16 RS485Slave_Receive(dat) end sub main: PORTB = 0 ' clear PORTB PORTC = 0 ' clear PORTC DDRB = 0xFF ' set PORTB as output DDRC = 0xFF ' set PORTB as output UART1_Init(9600) ' initialize UART1 module Delay_ms(100) RS485Slave_Init(160) ' Intialize MCU as slave, address 160 dat[4] = 0 ' ensure that message received flag is 0 dat[5] = 0 ' ensure that message received flag is 0 dat[6] = 0 ' ensure that error flag is 0 SREG_I_bit = 1 ' enable global interrupt RXCIE_bit = 1 ' enable interrupt on UARTs receive while TRUE if (dat[5] <> 0) then ' if an error detected, signal it by PORTC = dat[5] ' setting PORTC dat[5] = 0 end if if (dat[4] <> 0) then ' upon completed valid message receive dat[4] = 0 ' data[4] is set to 0xFF j = dat[3] for i = 1 to dat[3] ' show data on PORTB PORTB = dat[i-1] next i dat[0] = dat[0]+1 ' increment received dat[0] Delay_ms(1) RS485Slave_Send(dat,1) ' and send it back to master end if wend end.
HW Connection
Example of interfacing PC to ATmega16 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?
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.
What do you think about this topic ? Send us feedback!