春赏百花冬观雪の小窝

Loading...

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 定时器的作用 是不同的。

  1. Overrun Error (ORE) 的发生时机

    • ORE 发生在 字节级别 的接收过程中。它表明当 UART 的接收数据寄存器 (DR) 已经满了,而 CPU 未能在下一个字节完全到达并准备好接收之前,将其读走时发生了数据覆盖。
    • 您的日志显示,ORE 是在您成功接收并处理 第一个字节 0x01 之后立即 发生的。这意味着在第二个字节(主机发送的 0x04)到达时,接收硬件没有准备好接收,可能是由于第一个字节 0x01 没有被及时、正确地从硬件寄存器中读取并处理,或者后续的接收流程因为像之前讨论的 Linter 错误导致的宏失效等问题而中断。
  2. 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)一个周期的时间去发送请求帧。

所以千万不要使能自动重装载,把它关掉。手动更新去中断标志。

总结一下问题的来源:

  1. 对定时器的理解不够深入:自动重装载的原理没有搞懂,尤其是影子寄存器的机制。

  2. Keil 的调试有待加强:现在我其实不是很懂如何调试,像这种定时器问题,不知道如何有效地跟踪。有时候能接收到,有时串口又接收不到,现象不稳定。友妹友懂的盆友~,教我一下呗☺

  3. 串口的标志位不懂:没有深入学习,尤其是 TXE 和 TC。TC 广泛应用于 485 这样的电路中,尤其没有自收发功能的芯片。

  4. AI 不能完全相信:思路需要亲自决定,自己出设计,AI 来编程。不懂的可以问 AI,但是不懂的知识盲区问了 AI 还是会被带偏,所以还是要打好基础。

posted @ 2025-05-10 00:03  春赏百花冬观雪  阅读(256)  评论(0)    收藏  举报