Freemodbus 移植疑难杂症:从发不出应答帧到周期性不应答的血泪史
Freemodbus 移植疑难杂症:从发不出应答帧到周期性不应答的血泪史
移植完 Freemodbus 之后,发现 485 接口能收到数据,但是却发不出来应答帧。一开始能够发送应答帧,但是缺少最后一个字节,发现和串口的 TC、TXE 有关。具体问题的解决请参考 (https://www.cnblogs.com/cage666/p/8529885.html)。
串口发送相关标志位:
特征 | TXE (Transmit Data Register Empty) | TC (Transmission Complete) |
---|---|---|
含义 | 发送数据寄存器空闲,可以接收下一个数据。 | 整个数据帧(包括停止位)已经完全发送出去。 |
时机 | 数据从 TDR 转移到移位寄存器后置位。 | 移位寄存器中的最后一个位(停止位)发送完成后置位。 |
表示状态 | 可以写入下一个字节。 | 整个发送操作已完成。 |
数据位置 | 数据可能还在移位寄存器中。 | 数据已经完全离开移位寄存器。 |
用途 | 控制下一个字节的发送,触发 TXE 中断批量发送。 | 确保所有数据发送完成,控制 RS485 方向,进入低功耗,触发 TC 中断。 |
然后修改之后,能够完整发送应答帧了,心里美滋滋,发现每次复位之后,在主机(串口助手模拟)已经发送了询问帧,每次轮询间隔 1s 左右的时候,从机却不应答,手动复位后也是一样。
我这里首先切换了串口助手,设置为 3s 轮询一次,发现可以进行应答了。我以为是中断处理不了那么高频次的prvvUARTRxISR()
和 prvvUARTTxReadyISR()
函数都是在中断函数中处理的。随后我又通过标志位的形式,把 prvvUARTRxISR()
移到主循环,发现其结果并无二异。于是我仔细研读 Freemodbus 的结构,层级关系,发现自己所处理的都是一样的,找不到解决的问题。进行调试后发现,尽管主机周期轮询,还是断断续续的接收到,甚至接收不到。诶,奇了怪了,为什么会这样。反正最终也没找到哪里的问题,但是能够确定的是,要么是串口,要么是定时器。
于是,我问 cursor,cursor 先生,串口处理不了来自主机发送的大量询问帧是因为什么?
他说:可能是因为 prvvUARTRxISR()
或者其它程序导致中断处理延迟,另外就是因为接收速率过高。
于是本人为尊敬的串口先生加了一个环形 buffer, AUV! 结果您猜怎么着,没什么卵用。
点击查看小丑的表情
^_^
接着 cursor 先生叫我增大 buffer,结果还是没用。随后经过调研中断的整个程序执行过程,发现在怎么着,也不可能 3s 才处理完吧。我又问他,是不是和定时器有关。
他说~
Assistant
您好!从当前的日志和问题(接收到第一个字节后立即发生 Overrun Error,后续字节丢失)来看,这个问题与 Modbus 的 T3.5 定时器(TIM4)本身没有直接关系。
为什么与 T3.5 定时器关系不大?
主要原因在于 Overrun Error (ORE) 的发生时机 和 T3.5 定时器的作用 是不同的。
-
Overrun Error (ORE) 的发生时机:
- ORE 发生在 字节级别 的接收过程中。它表明当 UART 的接收数据寄存器 (DR) 已经满了,而 CPU 未能在下一个字节完全到达并准备好接收之前,将其读走时发生了数据覆盖。
- 您的日志显示,ORE 是在您成功接收并处理 第一个字节
0x01
之后立即 发生的。这意味着在第二个字节(主机发送的0x04
)到达时,接收硬件没有准备好接收,可能是由于第一个字节0x01
没有被及时、正确地从硬件寄存器中读取并处理,或者后续的接收流程因为像之前讨论的 Linter 错误导致的宏失效等问题而中断。
-
T3.5 定时器的作用:
- T3.5 定时器用于判断 一帧 Modbus 数据的结束。它的计时是在接收到数据流中的 最后一个字节之后,或者在两个 Modbus 帧之间的静默期开始的。
- 当 T3.5 定时器超时发生时,它会调用
prvvTIMERExpiredISR()
->pxMBPortCBTimerExpired()
函数,通知 Modbus 协议栈认为当前的一帧数据已经接收完毕,可以进行解析处理了。 - 在您遇到的问题中,ORE 发生在 Modbus 帧的 早期(在接收第二个字节时),这远远早于 T3.5 定时器应该判断帧结束的时机。实际上,由于 ORE 导致后续字节丢失或接收错误,Modbus 协议栈可能并没有接收到构成完整帧所需的所有字节,因此 T3.5 定时器可能没有在接收到“完整帧”后正确超时,而是如您之前的日志所示,在只收到一个(可能错误的)字节后就超时了,这反而是 ORE 导致后续问题的一个表现,而不是 ORE 本身的原因。
总结来说,ORE 是一个 UART 硬件接收错误,发生在单个字节接收层面。T3.5 定时器是一个软件或硬件定时器,用于判断整个数据帧的边界。ORE 的发生是先于 T3.5 定时器需要判断帧结束的时机。因此,您遇到的 Overrun Error 更可能与 UART 的接收处理、中断优先级、或者像之前讨论的宏定义或代码逻辑问题有关,而不是 T3.5 定时器本身的功能异常。
因为本人学识尚浅,真被他带进去了,就没管定时器了,一直在研究串口。但是我测试进行普通收发一直都是可以的,为什么到这里又不行了。于是在我确认 Freemodbus 协议栈没有错误之后,转身把矛头刺向串口初始化,不过也是一直没问题的,天王老子来了串口也没问题,而且 485 接口测试我也进行了,包是能用的。于是不得不把目光转向定时器,重点放在 arr 和 psc 这两个值上,追溯到底发现也是没问题的,这定时器原汁原味,不可能变异。经过一番瞎折磨,对比网上各种资料,发现定时器初始化的时候有个自动重装载设置成了允许。
您猜怎么着,改成 disable 后,烧录复位,串口助手轮询请求帧,哇,像极了吃了泻药的便秘患者,复位就能用!
把串口助手轮询时间改成 1s、500ms、甚至 100ms,都能接收。
整个心情愉快~~~hhh。
于是我本着刨根问底的原则,发现使能自动重装载有个叫影子寄存器的东东。它不能立即把 usTim1Timerout50us 的值给到定时器,而是要等待一个定时器周期后,才会把 usTim1Timerout50us 的值重新赋给定时器。我这里 arr 设置是默认 65535,折合整个定时器周期约为 3s 多,确实符合要 3s 左右才能应答的情况,要超过 3s 才算的上一帧。之前为什么不能应答呢,因为 Freemodbus 是在串口接收到数据的时候,一直使能定时器,重置定时器。当最后一个字节发送完毕后,定时器超时,引起中断,这一帧就判断结束了。不懂的小伙伴搜一搜 T3.5 和 T1.5 时间的概念,用于 Modbus 协议的。而我这里需要超过 3s 才会判断完结束一帧,可主机轮询的间隔小于 3s,被认为是一直处于一帧的状态,没办法超时,所以才应答不了。当你第一字节发送完成后,需要等待 3s 左右,也就是把默认 arr 的值(65535)给到影子寄存器后用完,下一个周期才会用 usTim1Timerout50us 的值。所以现象是,发送第一次询问帧后,等待 3s,后面才可以以大于 usTim1Timerout50us(我这里是 79)一个周期的时间去发送请求帧。
所以千万不要使能自动重装载,把它关掉。手动更新去中断标志。
总结一下问题的来源:
-
对定时器的理解不够深入:自动重装载的原理没有搞懂,尤其是影子寄存器的机制。
-
Keil 的调试有待加强:现在我其实不是很懂如何调试,像这种定时器问题,不知道如何有效地跟踪。有时候能接收到,有时串口又接收不到,现象不稳定。友妹友懂的盆友~,教我一下呗☺
-
串口的标志位不懂:没有深入学习,尤其是 TXE 和 TC。TC 广泛应用于 485 这样的电路中,尤其没有自收发功能的芯片。
-
AI 不能完全相信:思路需要亲自决定,自己出设计,AI 来编程。不懂的可以问 AI,但是不懂的知识盲区问了 AI 还是会被带偏,所以还是要打好基础。
本文来自博客园,作者:春赏百花冬观雪,转载请注明原文链接:https://www.cnblogs.com/haoxianghuang/p/18869073