RS-485 Library
RS-485 is a multipoint communication which allows multiple devices to be connected to a single bus. The mikroPascal PRO for PIC 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
50is the broadcast address for all Slaves (packets containing address50will be received by all Slaves except the Slaves with addresses150and169).
Important :
- 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 : |
|---|---|---|
var RS485_rxtx_pin : sbit; sfr; external; |
Control RS-485 Transmit/Receive operation mode | var RS485_rxtx_pin : sbit at RC2_bit; |
var RS485_rxtx_pin_direction : sbit; sfr; external; |
Direction of the RS-485 Transmit/Receive pin | var RS485_rxtx_pin_direction : sbit at TRISC2_bit; |
Library Routines
- RS485Master_Init
- RS485Master_Receive
- RS485Master_Send
- RS485Slave_Init
- RS485Slave_Receive
- RS485Slave_Send
RS485Master_Init
| Prototype |
procedure RS485Master_Init(); |
|---|---|
| Returns |
Nothing. |
| Description |
Initializes MCU as a Master for RS-485 communication. |
| Requires |
Global variables :
|
| Example |
// RS485 module pinout var RS485_rxtx_pin : sbit at RC2_bit; var RS485_rxtx_pin_direction : sbit at TRISC2_bit; // End of RS485 module pinout ... UART1_Init(9600); // initialize UART1 module RS485Master_Init(); // intialize MCU as a Master for RS-485 communication |
RS485Master_Receive
| Prototype |
procedure RS485Master_Receive(var data_buffer: array[5] of byte); |
|---|---|
| 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 |
var msg : array[20] of byte; ... RS485Master_Receive(msg); |
RS485Master_Send
| Prototype |
procedure RS485Master_Send(var data_buffer: array[20] of byte; datalen: byte; slave_address: 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 |
var msg : array[20] of byte; ... // send 3 bytes of data to Slave with address 0x12 RS485Master_Send(msg, 3, 0x12); |
RS485Slave_Init
| Prototype |
procedure RS485Slave_Init(slave_address: byte); |
|---|---|
| Returns |
Nothing. |
| Description |
Initializes MCU as a Slave for RS-485 communication. Parameters :
|
| Requires |
Global variables :
|
| Example |
// RS485 module pinout var RS485_rxtx_pin : sbit at RC2_bit; var RS485_rxtx_pin_direction : sbit at TRISC2_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 |
RS485Slave_Receive
| Prototype |
procedure RS485Slave_Receive(var data_buffer: array[20] of byte); |
|---|---|
| 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 |
var msg : array[20] of byte; ... RS485Slave_Read(msg); |
RS485Slave_Send
| Prototype |
procedure RS485Slave_Send(var data_buffer: array[20] of byte; datalen : 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 |
var msg : array[8] of byte; ... // 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 PORTB, while error on receive (0xAA) and number of consecutive unsuccessful retries are displayed on PORTD. Slave displays received data on PORTB, while error on receive (0xAA) is displayed on PORTD. Hardware configurations in this example are made for the EasyPIC6 board and 16F887.
RS485 Master code:
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 RC2_bit; // set transcieve pin
rs485_rxtx_pin_direction : sbit at TRISC2_bit; // set transcieve pin direction
// Interrupt routine
procedure interrupt();
begin
RS485Master_Receive(dat);
end;
begin
cnt := 0;
ANSEL := 0; // Configure AN pins as digital I/O
ANSELH := 0;
PORTB := 0;
PORTD := 0;
TRISB := 0;
TRISD := 0;
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);
RCIE_bit := 1; // enable interrupt on UART1 receive
TXIE_bit := 0; // disable interrupt on UART1 transmit
PEIE_bit := 1; // enable peripheral interrupts
GIE_bit := 1; // enable all interrupts
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.
RS485 Slave code:
program RS485_Slave_Example;
var dat : array[20] of byte; // buffer for receving/sending messages
i, j : byte;
var rs485_rxtx_pin : sbit at RC2_bit; // set transcieve pin
rs485_rxtx_pin_direction : sbit at TRISC2_bit; // set transcieve pin direction
// Interrupt routine
procedure interrupt();
begin
RS485Slave_Receive(dat);
end;
begin
ANSEL := 0; // Configure AN pins as digital I/O
ANSELH := 0;
C1ON_bit := 0; // Disable comparators
C2ON_bit := 0;
PORTB := 0;
PORTD := 0;
TRISB := 0;
TRISD := 0;
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
RCIE_bit := 1; // enable interrupt on UART1 receive
TXIE_bit := 0; // disable interrupt on UART1 transmit
PEIE_bit := 1; // enable peripheral interrupts
GIE_bit := 1; // enable all interrupts
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 PIC16F887 MCU to PIC16F887 MCU 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 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 := not crc_send;
if ((crc_send = START_BYTE) or (crc_send = STOP_BYTE)) then
Inc(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!




