资讯动态

MODBUS现场调试与移植实战:从RS485故障到FreeModbus

发布时间:2026/9/8 9:16:05 来源:尧图企业网站定制
1. 现场总线也有“普通话”MODBUS为什么能一路活到今天干嵌入式这些年调试过的现场设备不算少。你问我见最多的通信协议是哪个不是CAN不是工业以太网也不是各种家电里用的私有协议而是MODBUS。水处理厂的PLC、光伏逆变器、电机保护器、环境监测终端、智能电表……几乎每个设备出厂都默认留一个MODBUS接口。哪怕现在TSN、OPC UA这些新名词满天飞MODBUS依然是工控现场最通用的“普通话”。我第一次正儿八经调MODBUS是在一个污水厂的自控项目里。现场温度变送器、液位计、流量计来自五六个品牌控制器五花八门。如果没有MODBUS想把这么多设备的通信统一起来工程量会大得吓人。MODBUS强在哪强在它足够简单、完全开放不需要授权费报文格式清晰到可以手算CRC。这意味着哪怕你用的只是一颗几块钱的MCU、一份几百行的裸机C代码也能在一天之内把主从通信跑起来。这种“下限极低”的特性决定了它在资源有限的嵌入式设备里永远有一席之地。这篇文章不是教科书式的协议翻译而是我在实际项目里反复翻车之后沉淀下来的笔记。内容包括协议本身的数据模型和功能码RTU与TCP两种传输模式怎么选以及一个非常典型的现场故障——主机从机单独测都正常、连在一起就不通的排查全过程。最后我会给出一个基于STM32和FreeModbus的完整移植示例文末附一份可以直接抄的避坑清单。适合正在做单片机开发、嵌入式Linux项目或者经常跟串口调试工具和上位机联调的工程师阅读。不管你是刚入行还是干了几年总有几条能对上号。2. 先把报文拆干净MODBUS的寄存器模型与功能码2.1 四种数据对象线圈、离散输入、输入寄存器、保持寄存器MODBUS对从站数据的抽象非常粗暴——就四张表。这四张表分别对应开关量输入输出和模拟量输入输出只要理解了这四张表整个协议的数据访问逻辑就通了一半。数据对象英文名读写属性位/字宽对应功能码典型用途线圈Coil可读可写位01/05/0F继电器、电磁阀、指示灯离散输入Discrete Input只读位02限位开关、按钮、干接点输入寄存器Input Register只读字(16bit)04AD采样值、传感器测量值保持寄存器Holding Register可读可写字(16bit)03/06/10设定值、运行参数、累计值特别容易搞混的是输入寄存器和保持寄存器。很多人在写从机程序时会问为什么读温度用04写设定值用06因为从MODBUS的角度看温度是设备“输入”进来的测量量而设定值是允许外部“保持”修改的参数。这个区分在PLC的地址映射里体现得更明显3x开头是输入寄存器4x开头是保持寄存器1x是离散输入0x是线圈。了解这些命名习惯后面用Modbus Poll这类工具时就不会对着地址发懵。2.2 功能码速查不用全背但这几个必须刻在脑子里MODBUS功能码从01到127有一大堆但实际工程中翻来覆去就那么几个。我列一张常用功能码表建议你收藏调试时对照着看。功能码名称操作对象报文特点01读线圈线圈返回位拼接成的字节02读离散输入离散输入返回位拼接成的字节03读保持寄存器保持寄存器返回寄存器值每个寄存器2字节04读输入寄存器输入寄存器同上05写单个线圈线圈固定写0xFF00或0x000006写单个寄存器保持寄存器写一个16位值0F写多个线圈线圈批量写位10写多个寄存器保持寄存器批量写16位值新手最容易犯的错是要修改一个保持寄存器时下意识用了06结果发现设备没反应查了半天才发现该用10或者10时不支持。其实06和10的区别很直接——06只写一个寄存器10可以连续写多个。但有些从机实现里只做了06或只做了10所以上位机适配时必须先看从机的手册确认支持哪种功能码。2.3 RTU报文拆解与CRC16的土办法MODBUS RTU的报文没有复杂的帧头帧尾每帧就是“从站地址 功能码 数据 CRC16”。我拿最常见的“读保持寄存器”举例。主机想读从站1、起始地址0、连续2个保持寄存器发出的请求帧是01 03 00 00 00 02 C4 0B逐字节拆开看01从站地址。范围1~2470是广播地址248~255保留03功能码读保持寄存器00 00起始寄存器地址高字节在前00 02寄存器数量高字节在前C4 0BCRC16校验值这里要注意——低字节在前所以报文里C4是CRC的低字节0B是高字节如果从站正常响应会返回这样一帧01 03 04 00 01 00 02 D5 4A其中04是数据字节数2个寄存器 × 2字节00 01和00 02分别是两个寄存器的值D5 4A是CRC。如果从站出错了功能码的最高位会被置1比如03变成83后面跟一个异常码。03功能码对应的异常码有01非法功能、02非法地址、03非法数据几种调试时看到功能码大于0x80就可以判断这是异常响应而不是正常数据。CRC16的计算是MODBUS里最让人头疼又绕不开的部分。标准算法是多项式0xA001的反向CRC16计算时初始值为0xFFFF。我直接贴一段可以用的C代码uint16_t crc16_modbus(uint8_t *data, uint16_t len) { uint16_t crc 0xFFFF; while (len--) { crc ^ *data; for (int i 0; i 8; i) { if (crc 0x0001) { crc (crc 1) ^ 0xA001; } else { crc 1; } } } return crc; }这段代码算出来的CRC是一个16位值。发送的时候要先发低字节、再发高字节这个字节序坑了我整整一个下午。当时我在逻辑分析仪上看到报文数据明明是对的但设备就是不回后来才发现CRC的高低字节装反了。工业干扰严重的环境里一个错的CRC就能救一台设备——如果CRC校验不过而仍然执行数据继电器、电机这些执行机构就可能做出完全错误的动作后果不堪设想。3. RTU和TCP的同与不同选型前先搞清楚这几点3.1 换了个壳的MODBUS本质没变MODBUS RTU跑在串口上MODBUS TCP跑在以太网上。很多人以为TCP版是把RTU报文原封不动塞进TCP包其实不是。TCP版在RTU的基础上套了一个MBAP头又去掉了CRC。TCP请求帧长这样事务ID(2字节) 协议ID(2字节) 长度(2字节) 单元ID(1字节) 功能码 数据事务ID同一帧请求和响应对应用于TCP连接上多帧并发时区分协议ID固定为0长度后续字节数单元ID类似RTU的从站地址但TCP场景下更多用于网关后面连接的串口从站路由对比一下更直观对比项MODBUS RTUMODBUS TCP物理层RS232/RS485以太网端口/地址从站地址1~247TCP端口502单元ID帧间隔要求有严格的3.5字符间隔无校验CRC16无依赖TCP/IP链路层报文头无MBAP头7字节最大从站数理论上247理论上更多RTU对时序有要求帧与帧之间必须保持至少3.5个字符时间的静默间隔帧内字节间隔不能超过1.5个字符时间。这一点在裸机串口中断程序里特别容易被忽略——如果接收中断处理不及时两字节间间隔超过1.5字符时间协议栈会认为这是两帧数据然后丢弃一帧。而TCP就没有这个烦恼TCP本身有分包重传机制这也是TCP移植比RTU省心的一个重要原因。3.2 怎么选不是越新越好而是越合适越好很多项目在规划阶段就来回来去纠结该用RTU还是TCP。我的经验是先用最朴素的判断标准别被“上以太网显得高级”这种想法带偏。如果你的设备安装距离短几米到几十米、节点少一主几从、设备MCU资源也紧张RS485 MODBUS RTU基本是性价比最高的选择。STM32F103这种Cortex-M3核心串口加一个定时器就能把从机跑起来硬件成本可能不到10块钱。而如果设备要接入上位机管理系统、多台设备需同时被多个客户端访问、或者传输距离已经超过RS485的合理范围那就直接上MODBUS TCP。接线一条网线搞定不用考虑A/B极性、终端电阻、共地这些RS485的毛病。不过要提醒一句MODBUS TCP虽然传输层可靠但应用层仍然是主从模型。一个TCP Server端从机默认情况下只能被一个Client主机建立会话多客户端同时访问时要么用网关把多个连接映射到同一个从机要么在从机程序设计上做多会话支持。这个坑在设备选型阶段容易被忽视等调试时才发现多个上位机同时连不上只能临时加网关或改程序工期一下就紧张了。3.3 轮询周期和总线利用率一主多从的节奏感MODBUS是一主多从的协议主机是一个接一个地发请求从机不能主动上报只能被动响应。这就意味着整个系统的实时性上限取决于主机轮询一圈的时间。假设波特率96008数据位1停止位每秒大约960字节1起始位8数据1停止位。一帧“读4个寄存器”的请求约8字节响应约11字节加上帧间隔和从机处理时间一主5从轮询一圈大概需要8113× 5 × 11ms约120ms。这个时间对于温湿度采集、液位监测完全够用但如果从机数量超过30个或者要求20ms以内刷新一个参数RTU就不太合适了要么提高波特率要么换TCP。调试时如果发现响应时快时慢我建议打开主站工具的耗时统计功能直接看每一帧的往返延迟。正常情况下延迟是稳定的个位数毫秒如果延迟忽高忽低多半是总线上有冲突重发或者某个从机处理逻辑里有阻塞延时。在Modbus Poll主界面下方有个Tx、Rx和Error计数框再配合右边的指示条能很直观地看到收发时间和错误帧比例。4. 设备单测正常、联机就翻车一次RS485故障的完整排查4.1 故障现象单独怎么测都正常连在一起就哑火“485 modbus 主机 从机 分别测试都正常 主机连接从机就不正常”这个搜索热词简直戳中了无数调试员的肺管子。我就在一个泵站项目里遇到过一模一样的情况用USB转485接电脑电脑上跑Modbus Slave模拟从机PLC主站轮询完全正常随后把电脑切到Modbus Poll当主站去轮询现场的从机仪表也正常。但是当PLC通过485总线直接连接那台仪表时却一帧都收不到。这种问题最迷惑人的地方在于每一个单点都是好的但组合在一起就坏了。很多新手会以为是设备坏了或者怀疑PLC有问题于是反复重启设备、反复换模块折腾半天毫无进展。其实问题的根源通常不在设备本身而在总线链路的某个“裕量”上——单测时走的是一对一短链路PC侧的USB转485驱动器性能强、电平干净掩盖了现场的接线和信号完整性问题。一旦换成现场设备和真实PLC链条上任何一个薄弱环节都会被放大。4.2 排查第一步先别碰代码用万用表看AB线静态电平出现这种“单测正常、联机失败”的问题我的排查习惯是从物理层开始一层层往上查。第一步必做的是用万用表量一下A、B线之间的电压。RS485在空闲状态下A-B的电压差应该是负的通常在-1.5V到-6V之间逻辑为1即Mark状态发送数据时A-B为正逻辑为0Space状态。如果量到的电压是0V左右说明总线上没有任何设备在驱动或者有设备处于故障状态。如果量到的电压是5V附近且纹丝不动说明总线可能一直处于发送状态或者A/B极性接反了。那一次我在现场量A-B电压发现是4.8V左右而且不管PLC发不发数据都不变。这说明总线被某个设备“霸占”在发送状态了。再顺着查发现是某台仪表在不通电时内部的RS485芯片仍然在给总线供电导致整个总线一直被拉死在高电平。把这个仪表的485隔离器供电处理掉后总线就能正常翻转了。这种问题用示波器看会更清楚但现场没有示波器的话万用表也能帮我们判断大方向。4.3 最容易踩的物理层三连坑A/B接反、公共地缺失、终端电阻A/B接反是485联机失败的第一大原因。你可能会说A接A、B接B不就行了问题是不同厂商对A/B的命名习惯不一样。有些设备用“D”和“D-”D对应BD-对应A有些设备直接标“”“-”实际对应B和A。如果你发现两台设备单独跟PC通信都正常但两台设备直接相连不通先怀疑A/B极性。公共地缺失是第二个隐性杀手。RS485虽然是差分信号理论上不需要共地但收发器的共模输入范围是有限的典型值是-7V到12V。当两台设备使用不同开关电源供电且电源之间没有共地参考时两地之间的电位差可能高达几十伏。一旦这个电位差超出收发器的共模范围信号就会彻底失效。所以多节点485总线我强烈建议把各个设备的信号GND用一根线串起来。很多新手以为485只需A、B两根线漏接了GND导致通信时好时坏、跟温度湿度都有关系。终端电阻是第三个坑。规范做法是在总线首尾两端各接一个120Ω电阻作用是吸收信号反射。两个节点距离小于10米时不接也能凑合但距离拉长到几十米或者节点数多了以后没有终端电阻会让波形出现过冲和振铃表现在现象上就是通信不稳定、偶发超时。我在泵站那次排查中把所有设备都整齐接好后最后一个动作就是在总线两端各并了一个120Ω电阻通信立刻稳定了。实测下来终端电阻对波形的改善是立竿见影的。4.4 下一步用示波器看波形快速定位是驱动能力还是时序问题万用表能判断“有没有信号”但看不清“信号好不好”。如果总线电压正常、A/B接线也正确通信还是失败就该上示波器了。把示波器探头接到A和B之间用差分方式观察波形。正常的一帧数据应该是空闲时在负电平发送起始位后切换到正电平然后一串方波脉冲帧结束后回到负电平。注意看两个指标一是幅值A-B差分的摆幅应该在1.5V到5V之间二是边沿,上升沿和下降沿是否陡峭。有一次我调一个电表设备发现波形幅值只有0.8V左右明显偏低。这种波形如果不是在很短距离内根本传不过去。原因后来查到是那个从机设备用了廉价的带限流电阻的485收发方案驱动能力不足。解决办法是在该从机的A、B线上额外加一个偏置电阻网络把总线空闲电平抬高到标准范围同时降低对驱动能力的要求。还有一种情况是波形幅值正常但发送完后总线恢复空闲状态的时间特别长超过了几十个微秒。这会导致紧随其后的响应帧被主机判定为“帧间隔不足”从而被丢弃。这个问题通常出在485芯片的收发切换控制上——从机程序在发送完数据后如果DIR引脚的电平没有及时拉低收发器会继续占用总线下一帧数据就得等很久。这类问题用示波器在看波形时能明显看到发送结束位置的电平“拖尾”。4.5 根因总结硬件问题远多于软件问题那次泵站项目最后查到的根因是两个方面叠加第一是仪表A/B端子和PLC的接线极性确实定义不一致第二是总线缺了一根公共地。两个问题单独看都不至于完全不通但叠加起来就一点信号都传不过去。我把这类问题的排查顺序做成一张表之后每次调试都按这个顺序走效率高很多排查项方法判定标准A/B接线极性万用表测空闲电平空闲应负电平发送时应正电平公共地检查所有节点GND是否相连各设备地电位差接近0V终端电阻总线两端各并120Ω波形无过冲无振铃静态偏置量A-B电压空闲应小于-1V驱动能力示波器看幅值差分摆幅≥1.5V收发切换示波器看帧结束处无明显拖尾波特率/帧格式串口工具监听主机从机配置一致从站地址/功能码Modbus Poll读地址不冲突功能码受支持这里要特别强调第四列“判定标准”里的空闲电平。RS485在空闲时A相对于B是负电平这个“负”是逻辑1也是线路默认状态。如果总线空闲时电平为正说明总线上有收发器把空间态拉反了。判断A/B有没有接反最直接的方法就是用万用表测空闲电压负的就是对的正的就是反了。5. 从零跑通FreeModbusSTM32上的MODBUS RTU实战5.1 为什么用FreeModbus而不是自己写协议栈有关“stm32f103标准库通过rs232串口基于FreeModbus v1.6移植实现modbus rtu”这个热门搜索词说明很多人都在用FreeModbus。确实FreeModbus是目前嵌入式圈子里最流行的开源MODBUS实现它把MODBUS的应用层处理、异常码生成、广播地址处理、01/03/05/06/0F/10等多个功能码全部实现了你只需要做四件事串口收发、定时器、寄存器回调、主循环调用。自己从零写MODBUS协议栈当然可以如果只用到03读保持寄存器这一个功能码自己写确实更快几十分钟就能搞定。但实际项目里往往需要多个功能码协同工作如果从机还要处理寄存器边界校验、非法地址、非法数据这些异常情况自己写的协议栈很容易出纰漏。FreeModbus的好处在于这些边界情况都被处理好了你只要把寄存器回调填对协议栈会自动回复异常码。我移植过几次踩过的坑几乎都在移植层应用层一次没出过问题。5.2 移植四件事串口、定时器、事件、回调FreeModbus的移植是按端口层来组织的核心文件是portserial.c和porttimer.c。先说串口部分。FreeModbus依赖四个串口函数xMBPortSerialInit初始化串口配置波特率、数据位、校验位vMBPortSerialEnable使能/禁止收发中断xMBPortSerialPutByte发送一个字节xMBPortSerialGetByte接收一个字节串口中断里要做的就是把收到的字节调用pvMBFrameStartCur或prvvMBFrameReceived等协议栈回调丢进去。简单写法是void USART1_IRQHandler(void) { if (USART_GetITStatus(USART1, USART_IT_RXNE) ! RESET) { uint8_t byte USART_ReceiveData(USART1); // 把字节交给协议栈 xMBPortSerialGetByte(byte); // 不是这样正确是调 prvvUARTRxISR } }如果用的是标准库最省事的方式是参考FreeModbus官方demo里的结构它用的是“接收一个字节放进缓冲区由事件层统一处理”的机制。关于中断函数里具体调用哪个函数不同版本的FreeModbus API名字略有差异v1.6版本接收中断里调用prvvUARTRxISR()发送完成中断里调用prvvUARTTxReadyISR()。然后是定时器。FreeModbus用定时器来计量帧间隔判断一帧是否结束。RTU要求帧间静默3.5字符时间所以定时器中断周期设置为“3.5个字符的传输时间”在9600波特率下大致是3.5ms左右。定时器中断里调用vMBPortTimersTick()并向协议栈声明“一个tick过去了”。定时器配置代码的核心是这样static void TIM2_Config(void) { TIM_TimeBaseInitTypeDef TIM_Init; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 以72MHz为例设3.5字符时间对应的定时周期 uint16_t usTimerT35 (uint16_t)(35 * 1000000 / baud_rate / 10); // 粗略建议查FreeModbus移植手册 TIM_Init.TIM_Period usTimerT35; TIM_Init.TIM_Prescaler 72 - 1; ... }网上很多移植教程直接说“定时器周期设3.5个字符”但实际算出来之后不同波特率下对应的时间不一样。我建议直接用FreeModbus源码里的移植宏定义它会根据波特率自动计算你只需要把定时器时钟频率配置正确。第三件事是事件机制。FreeModbus内部用事件状态机来驱动协议处理主循环里不断调用eMBPoll()协议栈会检查是否有新帧、是否超时、是否要发送响应。你要保证主循环不能长时间阻塞否则串口接收缓冲溢出或者定时器事件处理不及时报文就丢了。在裸机开发中主循环里有延时函数或者长耗时操作时要特别注意打开FreeModbus的接收超时和事件处理。第四件事是寄存器回调。以保持寄存器为例你需要实现这个回调函数eMBErrorCode eMBRegHoldingCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode) { // usAddress 是寄存器地址协议里的地址从0开始 // eMode 是 MB_REG_READ 还是 MB_REG_WRITE if (eMode MB_REG_READ) { for (int i 0; i usNRegs; i) { pucRegBuffer[i * 2] holding_regs[usAddress i] 8; pucRegBuffer[i * 2 1] holding_regs[usAddress i] 0xFF; } } else { for (int i 0; i usNRegs; i) { holding_regs[usAddress i] (pucRegBuffer[i * 2] 8) | pucRegBuffer[i * 2 1]; } } return MB_ENOERR; }这里最关键的是字节序。MODBUS规定寄存器数据高字节在前大端如果你的设备内部存储是小端必须在这里做高低字节交换。我见过不少人调试时数据老是错位就是忘了这个交换。5.3 移植完成后的验证流程代码写完烧进去不要直接接PLC先用电脑验证。打开Modbus Poll新建连接设置串口号、波特率、数据位、校验位从站地址填1功能码选03起始地址填0数量填10然后点连接。如果一切顺利看到的是连续刷新的寄存器列表。如果连不通把Modbus Slave也打开用另一路USB转485接上一个当主机一个当从机先确认彼此能不能通。如果两个工具之间都不通说明问题在串口驱动或接线如果工具之间能通、接设备就不通重点查从站地址和寄存器映射。我在一次在线升级项目中遇到过一种更隐蔽的情况Modbus Poll读寄存器正常但用Modbus Slave模拟从机、再由PLC去轮询时PLC偶尔报超时。后来排查发现是因为我的STM32从机中断优先级设置太低主循环里有Flash擦写操作导致串口中断被长时间抢占帧间隔超了1.5字符时间。解决办法是把串口中断优先级提到最高Flash操作拆成分片执行确保每隔一段时间就让出CPU。这个经验是常规移植文档里不会写的但实际项目中非常关键。6. 调试工具的配置细节与我的避坑清单6.1 Modbus Poll和Modbus Slave的正确打开方式Modbus Poll是主站模拟工具Modbus Slave是从站模拟工具这两个配合使用基本能覆盖大多数调试场景。先说Modbus Poll的用法。打开后最上面一排是功能码、从站地址、起始地址和数量设置。很多人问我为什么我点了连接后数据区域全是红色超时大概率是地址范围设大了或者从站根本没在响应。先把数量设小一点比如读4个寄存器如果通了再调大。再有一个容易被忽略的细节Modbus Poll界面左边会显示Tx:xxx和Error:xxx的计数器。如果Error一直在涨双击错误列可以看到具体的异常码比如03是非法数据02是非法地址。这个信息能帮你快速定位是从站协议实现有问题还是请求参数不对。至于Modbus Slave的配置关键在“Setup Definition”里设置寄存器的起始地址和数量以及每个寄存器的初始值。调试时我习惯把从站地址固定为1功能码选03寄存器起始地址设0数量设10。然后勾选“Display as”里的signed或unsigned方便观察数值。需要模拟写操作时可以直接双击寄存器单元格修改数值主站那边会立刻读到变化。工具可以免费使用但功能有限制比如长时间连续运行会卡住或者禁用某些功能。如果项目周期长可以考虑开源替代品比如qModMaster一个免费开源的Modbus主站工具或者modpoll命令行工具都是稳定的选择。6.2 串口调试助手的辅助定位先看完底层再谈应用层很多工程师调试MODBUS时上来就打开Modbus Poll看通信超时就觉得是从机程序错了。我的习惯顺序恰恰相反先把串口调试助手挂上用被动监听模式看总线上的原始字节流。挂一个USB转485的监听器在A、B线上电脑开一个9600波特率的串口调试助手。当主机发出报文时串口助手会把这帧16进制数据显示出来。对照报文格式手册一眼就能看出请求帧里的功能码、地址、数据段是否合理。如果主机发了CRC而你从机的CRC校验总是不通过在串口助手里对比一下你计算的CRC和实际收到的CRC会立刻发现高低字节顺序的问题。串口调试助手的另一个用途是验证从机的发送行为。如果从机处理完请求后没有发出响应帧那问题大概率在从机侧比如功能码没实现、回调函数没触发。如果从机发出了响应但主机报超时那问题可能出在响应帧格式上。总之先用串口助手确认物理层和链路层的字节流是对的再进入应用层功能调试。这个顺序能节省大量排查时间。6.3 我反复踩过的几个坑汇总成一张避坑清单最后把我这些年调MODBUS时反复踩过的坑统一整理一下写在这里。每一条都是我实际项目中真实遇到过的不是照抄手册。CRC高低字节搞反这是最容易犯的错。CRC计算出来的16位值发送时先送低字节再送高字节。用上面那段代码算出的值是左对齐的但发送顺序必须反一下。我建议在写代码时顺手加一条注释“发送时先低后高”不然过两个月再看自己写的代码又会搞混。寄存器字节序没交换MODBUS寄存器默认高位在前但设备内部存储可能高位在后。在FreeModbus的回调里自己写字节交换时一定要小心读和写方向的交换要一致。否则会出现“写进去的是01 02读出来是02 01”这种诡异问题。串口配置不一致波特率、数据位、校验位、停止位任何一个不一致都会导致完全无法通信。RS485通常用8数据位、1停止位、无校验。如果设备手册写的是“8E1”那就要把校验位设成偶校验。调试串口工具时记得留意状态栏上的参数显示。轮询周期太短导致从机来不及处理主站轮询周期设得太短从机刚收到请求还没处理完下一帧就来了。FreeModbus内部有缓冲区保护但过于频繁的轮询仍会降低系统效率。一般建议最小轮询周期不要低于10ms否则低端MCU很容易出现响应超时。广播地址不回帧地址0是广播地址从机收到广播帧后要执行动作但不需要响应。调试时如果用地址0去测试从机Modbus Poll会一直报超时这其实是正常现象不代表协议栈出错。功能码只实现了一部分有些设备手册写得含糊实际只实现了03和06。调试时如果用到10写多寄存器发现设备没反应先用06单寄存器试试看设备是否支持。如果支持06不支持10就得让上位机把写多寄存器的请求拆成多个06请求。从站地址冲突一条总线上挂了多个从机时地址不能重复。排查方法很简单把其他从机断开只留一个改一个不冲突的地址再试。线太长但不加终端电阻超过30米或者节点数超过4个强烈建议加终端电阻。实测效果立竿见影不加的话传输距离越远波形反射越严重。调MODBUS这事说难不难说简单也不简单。协议本身很透明难的是物理链路上的各种不确定因素。我最后再分享一个小习惯每次拿到一个从机设备我会先用Modbus Poll把所有寄存器都读一遍然后手动改几个值把设备的行为摸透再写代码。这个步骤看着费时间实际能省下后面联调时的大量排障时间。当然如果是给别人做的设备记得先看设备手册确认寄存器含义别乱改设定值尤其是那些可能影响安全运行的参数。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价