这款MODBUS-RTU数显模块的核心设计理念是打造一个高性价比、易扩展的工业通信终端设备。主控采用8g1k08单片机,这是一款资源丰富、性价比极高的国产MCU,特别适合工业控制场景。模块最突出的特点是双串口架构设计:
硬件设计上预留了丰富的扩展接口:
提示:PCB设计时特别注意了485接口的防护电路,加入了TVS二极管和自恢复保险丝,可有效防止现场静电和浪涌冲击。
模块的通信核心在于双串口的协同工作。8g1k08本身只有一个硬件串口,我们通过软件模拟实现了第二个串口:
c复制// 串口1硬件初始化(主通信口)
void UART1_Init(uint32_t baud) {
SCON = 0x50; // 模式1,允许接收
TMOD |= 0x20; // 定时器1模式2
TH1 = TL1 = 256 - FOSC/12/16/baud;
TR1 = 1;
ES = 1;
EA = 1;
}
// 串口2软件模拟(用于调试输出)
void UART2_SendByte(uint8_t dat) {
uint8_t i;
UART2_TX = 0; // 起始位
Delay_1bit();
for(i=0; i<8; i++) {
UART2_TX = dat & 0x01;
dat >>= 1;
Delay_1bit();
}
UART2_TX = 1; // 停止位
Delay_1bit();
}
RS485通信需要特别注意总线控制时序:
c复制void RS485_Send(uint8_t *buf, uint8_t len) {
RS485_DIR = 1; // 使能发送
Delay_ms(1); // 等待稳定
UART1_SendBuf(buf, len);
while(!TI); // 等待发送完成
TI = 0;
RS485_DIR = 0; // 切换回接收
}
注意:RS485总线必须采用终端匹配电阻(120Ω),在多设备组网时尤其重要,可有效抑制信号反射。
模块完整实现了MODBUS-RTU最常用的三个功能码:
协议处理流程如下:
c复制void MODBUS_Process(void) {
if(RX_Flag) { // 接收到完整帧
uint16_t crc = CRC16(RX_Buf, RX_Count-2);
if(crc == *(uint16_t*)(RX_Buf+RX_Count-2)) {
switch(RX_Buf[1]) { // 功能码
case 0x03: Handle_03h(); break;
case 0x06: Handle_06h(); break;
case 0x10: Handle_10h(); break;
default: Send_Exception(0x01);
}
}
RX_Flag = 0;
}
}
MODBUS使用CRC-16校验,这里采用查表法优化计算速度:
c复制const uint16_t crc_table[] = {0x0000, 0xCC01, 0xD801, ..., 0x8201};
uint16_t CRC16(uint8_t *buf, uint8_t len) {
uint16_t crc = 0xFFFF;
uint8_t i;
for(i=0; i<len; i++) {
crc = (crc >> 8) ^ crc_table[(crc ^ buf[i]) & 0xFF];
}
return crc;
}
采用状态机模型实现多层菜单系统:
c复制typedef struct {
uint8_t current;
uint8_t max;
void (*display)(void);
void (*action)(uint8_t key);
} MENU_ITEM;
MENU_ITEM menu[] = {
{1, 254, Show_Addr, Set_Addr}, // 地址设置
{0, 2, Show_Baud, Set_Baud}, // 波特率(0=4800,1=9600,2=19200)
{0, 2, Show_Parity, Set_Parity}, // 校验(0=None,1=Odd,2=Even)
// ...其他菜单项
};
void Menu_Handler(uint8_t key) {
static uint8_t level = 0;
if(key == KEY_SET) {
level = (level + 1) % MENU_LEVELS;
} else {
menu[level].action(key);
}
menu[level].display();
}
采用74HC595驱动数码管,实现动态扫描和特效:
c复制void Display_Refresh(void) {
static uint8_t pos = 0;
HC595_Write(~(1<<pos), Digit[pos]); // 位选+段选
pos = (pos + 1) % 4;
// 处理闪烁效果
if(blink_mask & (1<<pos)) {
if(blink_cnt < BLINK_HALF) HC595_Write(0xFF, 0);
}
}
采用页写入策略提高存储寿命:
c复制void Param_Save(void) {
uint8_t buf[16];
// 准备待写入数据
buf[0] = device_addr;
buf[1] = baud_rate;
// ...其他参数
// 擦除并写入
I2C_EE_WriteBytes(0, buf, sizeof(buf));
// 写入后立即校验
uint8_t verify[16];
I2C_EE_ReadBytes(0, verify, sizeof(verify));
if(memcmp(buf, verify, sizeof(buf))) {
// 存储失败处理
}
}
经验:EEPROM每个扇区有10万次写入寿命,应避免频繁写入相同数据。实际项目中可加入"数据变化检测"逻辑,只有参数确实改变时才执行存储操作。
无响应:
CRC校验错误:
数码管缺段:
显示闪烁:
功能扩展:
性能优化:
量产建议:
在实际项目中,这个模块已经成功应用于工业现场的温度监控系统,连续稳定运行超过2000小时无故障。通过这个开源项目,开发者不仅可以快速搭建MODBUS设备,更能深入理解工业通信协议的实现细节。