前言
通过前面6章,我们已经手写完了:
Modbus RTU 完整协议帧解析
CRC16 标准算法
03/06/10 寄存器读写
05/0F 线圈开关量读写
全套标准异常码反馈
真机故障排查、总线干扰处理
Modbus TCP 基础原理
但是!之前的代码是教学式分散代码。
真正量产绝对不能堆在一个文件里。
本章我们完成可用的 Modbus 工程架构重构:
彻底模块化分层(配置层、协议层、设备层、业务层)
实现一键切换 主机/从机 模式
整理完整寄存器、线圈架构
裸机高稳定框架 + FreeRTOS 多线程改造方案
解决粘包、断连、卡死、总线抢占问题
最终产出:可直接开产品的 Modbus 模板工程
读完本章,你以后所有项目的 Modbus 代码,全部可以直接套用这套架构。
1. 为什么你之前的代码不稳定?(架构病根)
新手写的 Modbus 普遍有以下致命问题:
所有代码堆在主循环/中断里,耦合严重,无法复用
宏定义、参数、业务逻辑混在一起,改一个参数全盘报错
主机从机两套代码冲突,不能灵活切换
没有分层,底层串口改了,协议层直接崩
裸机轮询卡死,没有状态机调度
工业级稳定的核心不是算法,而是架构分层。
2. 最终量产工程标准架构(必记)
我们将工程分为四层,层层隔离、互不干扰:
第一层:配置层(modbus_config.h)
所有可修改参数全部放这里:模式、地址、波特率、超时、寄存器数量。
第二层:协议层(modbus_protocol.c)
纯协议实现:CRC、帧组装、帧解析、异常码、功能码处理。不包含任何硬件、不包含业务。
第三层:设备驱动层(modbus_port.c)
串口收发、485 DE/RE 切换、定时器超时。只做硬件搬运,不做协议解析。
第四层:业务数据层(modbus_data.c)
寄存器、线圈、数据刷新、参数校验、读写拦截。
四层架构最大优势:改硬件不动协议,改业务不动驱动,改参数不动代码。
3. 完整工程文件结构
Modbus/ ├─ modbus_config.h // 全局配置(模式、地址、波特率、超时) ├─ modbus_protocol.c // 协议核心、CRC、帧解析、功能码 ├─ modbus_port.c // 串口硬件、485控制、收发底层 └─ modbus_data.c // 寄存器、线圈、业务数据管理4. 第一层:全局配置文件(modbus_config.h)
真正的一键配置,无需改业务代码,即可切换主机/从机。
#ifndef __MODBUS_CONFIG_H #define __MODBUS_CONFIG_H // 模式选择:1=主机 0=从机 #define MODBUS_MASTER_MODE 0 // 基础通信配置 #define MODBUS_BAUDRATE 115200 #define MODBUS_TIMEOUT_MS 100 #define MODBUS_POLL_INTERVAL 100 // 设备从机地址 #define MODBUS_SLAVE_ADDR 0x01 // 缓冲区大小 #define MODBUS_TX_BUF_MAX 64 #define MODBUS_RX_BUF_MAX 64 // 寄存器配置 #define REG_HOLD_MAX 30 #define REG_RD_ONLY_END 9 #define REG_WR_START 10 // 线圈配置 #define COIL_MAX_NUM 16 #endif5. 第二层:协议核心层(纯协议、无硬件)
5.1 CRC16 核心函数(协议层通用)
uint16_t Modbus_CRC16(uint8_t *data, uint16_t len) { uint16_t crc = 0xFFFF; uint16_t i,j; for(i = 0; i < len; i++) { crc ^= data[i]; for(j = 0; j < 8; j++) { if(crc & 1) { crc >>= 1; crc ^= 0xA001; } else { crc >>= 1; } } } return crc; } void Modbus_Append_CRC(uint8_t *buf, uint8_t *len) { uint16_t crc = Modbus_CRC16(buf, *len); buf[*len] = crc & 0xFF; buf[*len+1] = (crc >> 8) & 0xFF; *len += 2; }5.2 异常帧生成(协议层统一封装)
void Modbus_Slave_Return_Exception(uint8_t *tx_buf, uint8_t *tx_len, uint8_t func, uint8_t err_code) { *tx_len = 0; tx_buf[(*tx_len)++] = MODBUS_SLAVE_ADDR; tx_buf[(*tx_len)++] = func | 0x80; tx_buf[(*tx_len)++] = err_code; Modbus_Append_CRC(tx_buf, tx_len); }6. 第三层:硬件移植层(串口、485驱动)
所有硬件相关全部隔离在此文件,换芯片、换引脚只改这里。
// 485 收发模式切换 void Modbus_TX_EN(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET); } void Modbus_RX_EN(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET); } // 串口发送 void Modbus_Send_Data(uint8_t *buf, uint8_t len) { Modbus_TX_EN(); HAL_UART_Transmit(&huart1, buf, len, 50); Modbus_RX_EN(); }核心优势:协议层完全不用管485怎么切换,底层硬件变更不影响协议。
7. 第四层:业务数据层(寄存器+线圈管理)
统一管理所有可读写数据,集中刷新、集中拦截。
// 保持寄存器 uint16_t modbus_hold_reg[REG_HOLD_MAX] = {0}; // 线圈缓存 uint8_t modbus_coil_buf[(COIL_MAX_NUM+7)/8] = {0}; // 线圈设置 void Modbus_Coil_Set(uint16_t addr, uint8_t sta) { if(addr >= COIL_MAX_NUM) return; uint16_t byte_id = addr / 8; uint8_t bit_id = addr % 8; if(sta) modbus_coil_buf[byte_id] |= (1<<bit_id); else modbus_coil_buf[byte_id] &= ~(1<<bit_id); } // 业务数据刷新 void Modbus_Data_Update(void) { // 模拟传感器数据刷新 modbus_hold_reg[0] = (uint16_t)(temperature * 10); modbus_hold_reg[1] = (uint16_t)(voltage * 10); modbus_hold_reg[2] = motor_speed; }8. 状态机调度(彻底解决裸机卡死)
之前的裸机代码是死等应答,极易卡死。
量产必须用状态机非阻塞调度。
typedef enum { MODBUS_IDLE, MODBUS_SEND_REQ, MODBUS_WAIT_ACK, MODBUS_PARSE_ACK, MODBUS_TIMEOUT_ERR }Modbus_StateTypeDef; Modbus_StateTypeDef modbus_state = MODBUS_IDLE; void Modbus_Master_Schedule(void) { switch(modbus_state) { case MODBUS_IDLE: // 定时发起轮询 if(HAL_GetTick() - poll_tick > MODBUS_POLL_INTERVAL) { poll_tick = HAL_GetTick(); Modbus_Master_Read_Reg(MODBUS_SLAVE_ADDR, 0, 3); modbus_state = MODBUS_WAIT_ACK; } break; case MODBUS_WAIT_ACK: // 超时判断 if(HAL_GetTick() - modbus_tick > MODBUS_TIMEOUT_MS) { modbus_state = MODBUS_TIMEOUT_ERR; } // 收到应答 else if(rx_len > 0) { modbus_state = MODBUS_PARSE_ACK; } break; case MODBUS_PARSE_ACK: Modbus_Parse_Frame(3); rx_len = 0; modbus_state = MODBUS_IDLE; break; case MODBUS_TIMEOUT_ERR: // 超时故障处理 fault_flag = 1; rx_len = 0; modbus_state = MODBUS_IDLE; break; } }核心提升:全程非阻塞,永不卡死!
9. FreeRTOS 多线程终极适配(工业量产标准)
裸机适合简单设备,复杂工控必须 RTOS 分层任务。
9.1 任务拆分原则
数据采集任务:10ms 刷新传感器、电压、转速
Modbus 通信任务:100ms 轮询、状态机调度
故障检测任务:检测掉线、丢包、异常计数
9.2 通信任务伪代码
void Modbus_Task(void *pvParameters) { while(1) { Modbus_Master_Schedule(); vTaskDelay(pdMS_TO_TICKS(10)); } }RTOS 优势:
通信卡顿不影响控制逻辑
不会因为总线干扰卡死整机
可以精准控制轮询节拍
10. 量产终极优化清单(必看)
10.1 软件优化
所有读写寄存器增加越界判断、数值范围拦截
连续3次超时判定设备掉线,锁定故障状态
禁止主机连续发包,严格一问一答状态机
接收缓冲区做溢出保护,防止数组崩溃
10.2 硬件优化
485 增加 TVS 防静电、防浪涌
长距离必须屏蔽双绞线、单端接地
多设备组网必须手拉手链式拓扑
高频干扰环境使用隔离485、隔离电源
11. 总结
你从 0 基础学到了工业量产全套能力:
读懂 Modbus 所有帧结构、协议底层原理
手写 CRC、寄存器、线圈、异常处理全套代码
掌握主机、从机双模式开发
搞定真机99%通信故障排查
掌握工业四层架构、状态机调度、RTOS适配
拥有一套可以直接商用的 Modbus 工程模板
从此以后,任何传感器、工控板、电机设备、PLC通信,你都可以独立开发、独立调试、独立量产。