RS-485 Library
RS-485 is a multipoint communication which allows multiple devices to be connected to a single bus. mikroBasic 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).
- 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
Stellaris
| 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 GPIO_PORTA_DATA2_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 GPIO_PORTA_DIR2_bit |
STM32
| 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 GPIOA_ODR.B6 |
Library Routines
- RS485Master_Init
- RS485Master_Receive
- RS485Master_Send
- RS485Slave_Init
- RS485Slave_Receive
- RS485Slave_Send
RS485Master_Init
| Prototype |
sub procedure 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.
|
| Example |
Stellaris' RS485 module pinout dim rs485_rxtx_pin as sbit at GPIO_PORTA_DATA2_bit ' set transcieve pin dim rs485_rxtx_pin_direction as sbit at GPIO_PORTA_DIR2_bit ' set transcieve pin direction ' End of RS485 module pinout ... UART1_Init(9600) ' initialize UART1 module RS485Master_Init() ' intialize MCU as a Master for RS-485 communication STM32' RS485 module pinout dim rs485_rxtx_pin as sbit at GPIOA_ODR.B6 ' set transcieve pin ' 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 |
sub procedure RS485Master_Receive(dim byref data as byte[10]) |
|---|---|
| Description |
Receives messages from Slaves. Messages are multi-byte, so this routine must be called for each byte received. |
| Parameters |
The routine automatically adjusts |
| Returns |
Nothing. |
| Requires |
MCU must be initialized as a Master for RS-485 communication. See RS485Master_Init. |
| Example |
dim msg as byte[8] ... RS485Master_Receive(msg) |
| Notes |
None. |
RS485Master_Send
| Prototype |
sub procedure RS485Master_Send(dim byref buffer as byte[20], dim datalen as byte, dim address as byte) |
|---|---|
| Description |
Sends message to Slave(s). Message format can be found at the bottom of this page. |
| Parameters |
|
| 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 |
dim msg as byte[8] ... ' send 3 bytes of data to slave with address 0x12 RS485Master_Send(msg, 3, 0x12) |
| Notes |
None. |
RS485Slave_Init
| Prototype |
sub procedure RS485Slave_Init(dim slave_address as byte) |
|---|---|
| Description |
Initializes MCU as a Slave for RS-485 communication. |
| Parameters |
|
| Returns |
Nothing. |
| Requires |
External dependencies of the library from the top of the page must be defined before using this function.
|
| Example |
Stellaris' RS485 module pinout dim rs485_rxtx_pin as sbit at GPIO_PORTA_DATA2_bit ' set transcieve pin dim rs485_rxtx_pin_direction as sbit at GPIO_PORTA_DIR2_bit ' set transcieve pin direction ' 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 STM32' RS485 module pinout dim rs485_rxtx_pin as sbit at GPIOA_ODR.B6 ' set transcieve pin ' 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 |
sub procedure RS485Slave_Receive(dim byref data as byte[20]) |
|---|---|
| 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 routine automatically adjusts |
| Returns |
Nothing. |
| Requires |
MCU must be initialized as a Slave for RS-485 communication. See RS485Slave_Init. |
| Example |
dim msg as byte[8] ... RS485Slave_Read(msg) |
| Notes |
None. |
RS485Slave_Send
| Prototype |
sub procedure RS485Slave_Send(dim byref data as byte[20], dim datalen as byte) |
|---|---|
| Description |
Sends message to Master. Message format can be found at the bottom of this page. |
| Parameters |
|
| 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 |
dim msg as byte[8] ... ' send 2 bytes of data to the Master RS485Slave_Send(msg, 2) |
| Notes |
None. |
Library Example
The example demonstrates 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 GPIO_PORTB while error on receiving (0xAA) and number of consecutive unsuccessful retries are displayed on GPIO_PORTD.
Stellaris
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 GPIO_PORTA_DATA2_bit ' set transcieve pin
rs485_rxtx_pin_direction as sbit at GPIO_PORTA_DIR2_bit ' set transcieve pin direction
' Interrupt routine
sub procedure interrupt() iv IVT_INT_UART0
RS485Master_Receive(dat)
UART_ICR_RXIC_bit = 1 ' ensure interrupt not pending
end sub
main:
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 (TRUE) ' upon completed valid message receiving
' data[4] is set to 255
Inc(cnt)
if (dat[5] <> 0) then ' if an error detected, signal it
GPIO_PORTD_DATA = 0xAA ' by setting GPIO_PORTD to 0xAA
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 GPIO_PORTE
GPIO_PORTE_DATA = dat[i-1]
next i
dat[0] = dat[0]+1 ' send back to master
Delay_ms(1)
RS485Master_Send(dat,1,160)
end if
if (cnt > 100000) then ' if in 100000 poll-cycles the answer
Inc(GPIO_PORTD_DATA) ' was not detected, signal
cnt = 0 ' failure of send-message
RS485Master_Send(dat,1,160)
if (GPIO_PORTD_DATA > 10) then ' if sending failed 10 times
RS485Master_Send(dat,1,50) ' send message on broadcast address
end if
end if
wend
end.
STM32
program RS485_Master_Example
dim dat as byte[20] ' buffer for receving/sending messages
i, j as byte
cnt as longint
dim rs485_rxtx_pin as sbit at GPIOA_ODR.B6 ' set transcieve pin
' Interrupt routine
sub procedure interrupt() iv IVT_INT_USART1
RS485Master_Receive(dat)
end sub
main:
cnt = 0
GPIO_Digital_Output(@GPIOD_BASE, _GPIO_PINMASK_ALL)
GPIOD_ODR = 0
GPIO_Digital_Output(@GPIOC_BASE, _GPIO_PINMASK_ALL)
GPIOC_ODR = 0
UART1_Init(19200) ' initialize UART1 module
Delay_ms(100)
RS485Master_Init() ' initialize MCU as Master
USART1_CR1.B5 = 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 (TRUE) ' upon completed valid message receiving
' data[4] is set to 255
Inc(cnt)
if (dat[5] <> 0) then ' if an error detected, signal it
GPIOC_ODR = 0xAA ' by setting portd to 0xAA
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 PORTD
GPIOD_ODR = dat[i-1]
next i
dat[0] = dat[0]+1 ' send back to master
Delay_ms(1)
RS485Master_Send(dat,1,160)
end if
if (cnt > 100000) then ' if in 100000 poll-cycles the answer
Inc(GPIOC_ODR) ' was not detected, signal
cnt = 0 ' failure of send-message
RS485Master_Send(dat,1,160)
if (GPIOC_ODR > 10) then ' if sending failed 10 times
RS485Master_Send(dat,1,50) ' send message on broadcast address
end if
end if
wend
end.
Stellaris
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 GPIO_PORTA_DATA2_bit ' set transcieve pin
rs485_rxtx_pin_direction as sbit at GPIO_PORTA_DIR2_bit ' set transcieve pin direction
' Interrupt routine
sub procedure interrupt() iv 21
RS485Slave_Receive(dat)
UART_ICR_RXIC_bit = 1 ' ensure interrupt not pending
end sub
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 (TRUE)
if (dat[5] <> 0) then ' if an error detected, signal it by
GPIO_PORTD_DATA = 0xAA ' setting GPIO_PORTD to 0xAA
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 GPIO_PORTE
GPIO_PORTE_DATA = 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.
STM32
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 GPIOA_ODR.B6 ' set transcieve pin
' Interrupt routine
sub procedure interrupt() iv IVT_INT_USART1
RS485Slave_Receive(dat)
end sub
main:
GPIO_Digital_Output(@GPIOD_BASE, _GPIO_PINMASK_ALL)
GPIOD_ODR = 0
GPIO_Digital_Output(@GPIOC_BASE, _GPIO_PINMASK_ALL)
GPIOC_ODR = 0
UART1_Init(19200) ' initialize UART1 module
Delay_ms(100)
USART1_CR1.B5 = 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 (TRUE)
if (dat[5] <> 0) then ' if an error detected, signal it by
GPIOC_ODR = 0xAA ' setting PORTC to 0xAA
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 PORTD
GPIOD_ODR = 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.
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 = crc_send not crc_send
if ((crc_send = START_BYTE) or (crc_send = STOP_BYTE)) then
crc_send = crc_send + 1
end if
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!




