Modbus Communication Architecture
Modbus implementation requires distinct approaches for slave and master devices:
Slave Device Operation
Slave devices maintain constant recieve readiness. The RS485 transceiver remains in receive mode, storing incoming data in a buffer. When a complete message is detected, the packet is parsed and appropriate responses are generated.
Master Device Operation
Masters receive requests via USART1, forward commands through RS485 to slaves, and relay responses back to the originator.
Slave Implementation
Main Application Loop
int main(void) {
SystemInit();
NVIC_SetPriorityGrouping(0x02);
Timer_Init(100, 8400); // 10ms interval
UART_Setup(4800);
RS485_Setup(4800);
while(1) {
if(completion_flag) {
ParseModbusFrame();
completion_flag = 0;
}
}
}
Timer Configuration
void TIM3_IRQHandler(void) {
if(TIM_GetITStatus(TIM3, TIM_IT_Update)) {
if(idle_counter < 60000) idle_counter++;
if((idle_counter > 10) && (rx_length != 0)) {
completion_flag = 1;
}
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}
}
Modbus Frame Proecssing
void ParseModbusFrame() {
uint16_t crc = CalculateCRC(rx_buffer, rx_length - 2);
if(crc == (rx_buffer[rx_length-2] << 8 | rx_buffer[rx_length-1])) {
if(device_id == rx_buffer[0]) {
switch(rx_buffer[1]) {
case MB_READ_HOLDING:
// Construct response
tx_buffer[0] = device_id;
tx_buffer[1] = MB_READ_HOLDING;
tx_buffer[2] = register_count * 2;
// Populate data
AddCRCChecksum(tx_buffer, response_size);
TransmitRS485(tx_buffer, response_size);
break;
}
}
}
rx_length = 0;
}
CRC-16 Implementation
uint16_t ComputeModbusCRC(uint8_t *data, uint16_t len) {
uint16_t crc = 0xFFFF;
for(uint16_t i = 0; i < len; i++) {
crc ^= data[i];
for(uint8_t j = 0; j < 8; j++) {
if(crc & 0x0001) {
crc = (crc >> 1) ^ 0xA001;
} else {
crc >>= 1;
}
}
}
return ((crc & 0xFF) << 8) | ((crc >> 8) & 0xFF);
}
Master Implementation
Main Application Logic
int main(void) {
// Initialization
while(1) {
if(rs485_flag) {
ForwardToUART(rs485_rx, rs485_len);
rs485_len = 0;
}
else if(uart_flag) {
ForwardToRS485(uart_rx, uart_len);
uart_len = 0;
}
}
}
Dual-Port Forwarding
void ForwardToRS485(uint8_t *data, uint16_t len) {
RS485_ENABLE_TX();
for(uint16_t i = 0; i < len; i++) {
while(!USART_GetFlagStatus(USART2, USART_FLAG_TXE));
USART_SendData(USART2, data[i]);
}
RS485_ENABLE_RX();
}
UART Interrupt Handling
void USART1_IRQHandler(void) {
if(USART_GetITStatus(USART1, USART_IT_RXNE)) {
uart_rx[uart_len++] = USART_ReceiveData(USART1);
idle_counter = 0;
}
}
RS485 Hardware Control
void RS485_Init(void) {
GPIO_InitTypeDef gpio;
gpio.GPIO_Pin = GPIO_Pin_8;
gpio.GPIO_Mode = GPIO_Mode_OUT;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOG, &gpio);
RS485_ENABLE_RX(); // Default receive mode
}