libmodbus的modbus_read_registers在Raspbian 10下RS485读取超时咨询
排查思路
结合你列的所有现象,优先按下面的顺序逐个验证,定位概率从高到低:
- 首先核对寄存器地址逻辑
你代码里传的读取起始地址是50001,这个值是工业现场常用的“保持寄存器逻辑地址”,对应实际Modbus协议下发的PDU地址需要减40000,也就是实际请求访问的是偏移为1的寄存器地址。老版本libmodbus(比如Yocto Dizzy带的3.0之前的版本)会自动帮你做这个地址偏移,所以在Yocto设备上传50001是能正常读到数据的;但Raspbian buster源里装的libmodbus是3.1以上的新版本,默认完全按照Modbus协议规范传参,不会自动做偏移换算,你传50001就会真的下发一个请求去访问从站的50001号寄存器——绝大多数从站的寄存器空间根本到不了这个地址,自然不会回包,直接触发读取超时。
而你用pymodbus测试时,pymodbus默认也是不做自动地址偏移的,如果你之前的pymodbus代码是直接传10或者对应正确的PDU地址,就能正常通信,这刚好匹配你遇到的现象。
验证方式很简单,把代码里的读取参数改成实际PDU地址试试:// 原代码:modbus_read_registers(ctx, 50001, 5, reg); // 替换成真实协议地址 int num = modbus_read_registers(ctx, 10, 5, reg); - 其次检查串口硬件流控配置
Raspbian buster的udev规则对常见的USB转RS485芯片(CH340、PL2303等),默认会打开串口的硬件RTS/CTS流控配置。libmodbus初始化串口时会直接沿用系统的termios配置,在打开硬件流控的状态下,libmodbus发送RTU帧时会一直等待CTS引脚的电平触发,而USB转RS485适配器根本不会回传CTS信号,导致请求帧根本发不出去。
而pymodbus打开串口时,会主动关闭硬件流控,所以同个端口同个链路下pymodbus能正常发数据。
验证方式:在modbus_connect之后,手动强制关闭串口的硬件流控:if (modbus_connect(ctx) == -1) { printf("Unable to connect: %s\n", modbus_strerror(errno)); modbus_free(ctx); } // 新增:关闭硬件流控 struct termios tios; tcgetattr(modbus_get_port(ctx), &tios); tios.c_cflag &= ~CRTSCTS; tcsetattr(modbus_get_port(ctx), TCSANOW, &tios); - 再检查响应超时阈值
老版本libmodbus的RTU模式默认响应超时是500ms,树莓派的USB转串口本身有一定的传输延迟,再加上市面上很多RS485从站的响应处理延迟就有几百毫秒,很容易出现“从站已经回了数据,但libmodbus已经超时退出”的假超时。
对应的修改方式是手动拉长超时时间:
这点可以和pymodbus的配置对比:pymodbus默认的响应超时是3s,所以不会触发这类假超时。modbus_set_response_timeout(ctx, 2, 0); // 等待2秒响应 - 最后检查串口残留锁文件
如果之前跑pymodbus程序时没有正常退出,会在系统的锁目录下留下ttyUSB0的残留锁文件,libmodbus打开串口时检测到锁虽然不会在connect阶段报错,但后续读写时会因为串口被占用直接抛出超时错误。
验证方式是删掉所有串口残留锁:sudo rm -f /var/lock/LCK..ttyUSB0
内容的提问来源于stack exchange,提问作者user12154135
相关产品推荐
相关产品推荐

