当前位置: 首页 > news >正文

STM32 上实现 Modbus-RTU

STM32 上实现 Modbus-RTU ,最稳妥的是“裸机驱动 + 状态机”(资源小、实时性强)。


一、方案选型(STM32 推荐)

方案 适用场景 评价
裸机状态机(推荐) 从站 / 简单主站 稳定、可控
FreeMODBUS 快速成型 但裁剪麻烦
libmodbus Linux / 高端 MCU ❌ 不适合小 RAM
自己写全套 学习 ⚠️ 易踩坑

工业现场:裸机 + 定时器 + UART 中断


二、硬件与串口配置

1、硬件连接(RS485)

STM32 USART2
TX  ->  MAX485 DI
RX  ->  MAX485 RO
PB8 ->  MAX485 DE/RE

2、CubeMX 配置

参数 设置
Baudrate 9600 / 19200 / 115200
Word Length 8 Bits
Parity None
Stop Bits 1
Mode Asynchronous
USART Interrupt Enable

三、Modbus-RTU 核心规则

帧结构

[地址][功能码][数据…][CRC低][CRC高]

帧结束判定(关键)

RTU 必须用 3.5 个字符静默时间

波特率 3.5字符时间
9600 ~4 ms
19200 ~2 ms
115200 ~0.35 ms

四、CRC16 校验(查表法 )

static const uint16_t crc_tab[] = {0x0000,0xC0C1,0xC181,0x0140,0xC301,0x03C0,0x0280,0xC241,// …(完整表略)
};uint16_t Modbus_CRC16(uint8_t *buf, uint16_t len)
{uint16_t crc = 0xFFFF;while (len--)crc = (crc >> 8) ^ crc_tab[(crc ^ *buf++) & 0xFF];return crc;
}

五、接收状态机

typedef enum {RX_IDLE,RX_RECEIVING,RX_DONE
} RxState;volatile RxState rx_state = RX_IDLE;
volatile uint8_t rx_buf[256];
volatile uint8_t rx_len = 0;
volatile uint16_t idle_cnt = 0;

UART 接收中断

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if (huart->Instance == USART2){rx_state = RX_RECEIVING;idle_cnt = 0;HAL_UART_Receive_IT(&huart2, (uint8_t *)&rx_buf[rx_len++], 1);}
}

1ms 定时器中断(帧结束判定)

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if (htim->Instance == TIM2){if (rx_state == RX_RECEIVING){if (++idle_cnt >= 4)   // 3.5字符{rx_state = RX_DONE;}}}
}

六、Modbus 从站处理(功能码 03 / 06)

uint16_t hold_reg[10] = {0};void Modbus_Process(void)
{if (rx_state != RX_DONE) return;uint16_t crc_rx = (rx_buf[rx_len-1]<<8) | rx_buf[rx_len-2];if (Modbus_CRC16((uint8_t*)rx_buf, rx_len-2) != crc_rx){rx_len = 0;rx_state = RX_IDLE;return;}if (rx_buf[0] != 0x01) goto exit;   // 地址不符switch (rx_buf[1]){case 0x03:  // 读保持寄存器Modbus_ReadHolding();break;case 0x06:  // 写单个寄存器Modbus_WriteSingle();break;}exit:rx_len = 0;rx_state = RX_IDLE;
}

七、功能码 03 示例(读寄存器)

void Modbus_ReadHolding(void)
{uint16_t addr = (rx_buf[2]<<8) | rx_buf[3];uint16_t cnt  = (rx_buf[4]<<8) | rx_buf[5];uint8_t tx[256];uint8_t i = 0;tx[i++] = 0x01;tx[i++] = 0x03;tx[i++] = cnt * 2;for (uint8_t j = 0; j < cnt; j++){tx[i++] = hold_reg[addr + j] >> 8;tx[i++] = hold_reg[addr + j] & 0xFF;}uint16_t crc = Modbus_CRC16(tx, i);tx[i++] = crc & 0xFF;tx[i++] = crc >> 8;HAL_UART_Transmit(&huart2, tx, i, 100);
}

参考代码 stm32 实现Modbus-rtu www.youwenfan.com/contentcnt/181948.html

八、调试与避坑指南

问题 原因
帧不完整 3.5字符时间不准
CRC 错 字节序反了
只收到一次 未重新开启接收中断
总线冲突 多从站地址重复
数据乱码 波特率不一致

调试工具

  • Modbus Poll
  • USB-RS485 转换器
  • 示波器看 3.5T 静默
http://www.jsqmd.com/news/675961/

相关文章:

  • 2026年COB小间距显示屏厂家权威方案分析:如何为高端场景匹配最佳选择 - 速递信息
  • 超越官方控制面板:NVIDIA Profile Inspector如何解锁显卡隐藏潜力?
  • SQL数据更新时如何减少锁表时间_合理控制事务边界与并发
  • AzurLaneAutoScript终极指南:快速掌握碧蓝航线全自动脚本
  • 别再只盯着FPS了!聊聊IA-SSD在RTX 2080Ti上85帧背后的显存与并行性玄学
  • 从‘认不出’到‘认得准’:face_recognition库中tolerance参数调优实战与避坑指南
  • 2026深圳小程序开发,本地靠谱服务商推荐榜单 - 品牌测评榜单
  • Wan2.2-I2V-A14B入门:JDK1.8环境下的Java SDK开发与调用示例
  • Pandas导入excel表中指定列
  • 终极魔兽争霸III优化指南:让经典游戏在现代电脑上完美运行
  • 购物卡回收技巧,京尔回收的实用方式上线 - 购物卡回收找京尔回收
  • 普通人逆袭的底层逻辑一定要掌握AI工具GEO - 速递信息
  • ESP8266 AT指令实战:5分钟搞定一个HTTP GET数据上报器(附完整单片机代码框架)
  • 别再傻傻分不清了!嵌入式开发中485、CAN、SPI、I2C到底怎么选?一个表格帮你搞定
  • 分享有大型工程合作经验的PE给水管厂家,性价比之选揭秘 - 工业品牌热点
  • 列表的截取
  • real-anime-z镜像免配置优势:省去Diffusers/Xformers/CLIP等手动安装环节
  • 从PostgreSQL迁移到华为云GaussDB?这份JDBC连接差异指南和代码适配要点请收好
  • 2026年亲测:油烟机风力变小,是电机老化还是该清洗? - 小何家电维修
  • 二次元游戏模组管理终极解决方案:如何用XXMI启动器统一管理6款热门游戏
  • 终极指南:3步实现微信平板模式,轻松突破安卓多设备登录限制
  • 解读发明AI专利检索服务,好用的品牌有哪些 - 工业设备
  • 2026年创意AI应用趋势:AI印象派艺术工坊入门必看指南
  • 别再只用历史负荷了!试试PyTorch LSTM融合多变量特征,让你的预测准确率提升(实战对比分析)
  • 2026年成都香港留学中介服务对比:五家优选指南 - 科技焦点
  • RVC语音分离+变声一体化教程:内置UVr干声处理实测分享
  • 别再只会用7805了!手把手教你用三极管搭建一个可调稳压电源(附电路图)
  • 重塑你的英雄联盟展示界面:非侵入式个性化工具深度探索
  • Win10/Win11下NVIDIA驱动死活卸载不掉?别急着重装系统,试试修复这个服务
  • 过滤机产品质量排名靠前的厂家有哪些,过滤机价格多少钱 - mypinpai