充分利用糟糕的“校验和”算法

9

我正在处理一个现有的驱动程序,该程序通过串口控制8位MCU。这个MCU有许多不同版本的固件,但它们都采用了一种通用的方法来确保链路完整性。然而这种方法并不是非常健壮,因此我正在寻找如何修改驱动程序行为以达到最佳效果的想法。

命令是带有行号和校验和的gcode:

N3 T0*57
N4 G92 E0*67
N5 G28*22
N6 G1 F1500.0*82
N7 G1 X2.0 Y2.0 F3000.0*85
N8 G1 X3.0 Y3.0*33

行号必须是连续的(但可以通过M110重置)。如果校验和不匹配或者行号不连续,固件将返回Resend:nnn,其中nnn是最后一个成功的N加1。 "校验和"非常原始:

        // Calc checksum.
        byte checksum = 0;
        byte count = 0;
        while(instruction[count] != '*')
                checksum = checksum^instruction[count++];

主要问题在于主要的错误机制是由于中断被挂起导致的丢失字节,从而导致1字节MCU FIFO溢出。实际的串行总线是在FTDI(或类似设备)USB串行桥和MCU之间的几厘米距离内,因此比特错误不太可能发生。我从未观察到来自固件的比特错误。
如您所见,上面的算法将检测到一个丢失的字节,但如果您丢失了两个相同的字节(任何位置!),结果仍将匹配。因此,F3000.0(进给速率3000mm / min)可以转换为F30.0并仍然匹配。此外,字符集非常小,因此某些位永远不会涉及。
驱动程序能否做些什么使给定的行更加稳健?
  • 添加或删除尾随(甚至前导)零
  • 添加或删除空格
  • 重新排序单词(X1 Y1Y1 X1相同)
  • 添加或删除空格
  • 对某些公差内的值进行“不重要”的修改(例如F2999.9代替F3000
  • 重置行号以获得给定行的某些特定N
  • 将单个命令分成多个等效命令(例如,假设X = 0,则G1 X2变为G1 X1 G1 X2
  • 消除(或添加)不必要的单词(例如T0对于大多数命令来说是无意义的,并且如果您发送了F3000一次,则在未来会隐含它,因此可以选择发送或不发送)
如果我认为固件会以组的形式丢失字节,那么最重要的事情可能是避免连续的重复项,例如00,这将(如果一起丢失)是不可见的。

请明确一点 - 您无法控制或修改8位MCU的固件以使用不同的校验和和/或命令行解释器,对吗? - Michael Burr
这都是开源的,所以这绝对是可能的。为了适应众多固件实例,使驱动程序尽可能健壮将是不错的选择。(虽然更好的修改固件会有更好的串行通信管理!) - Ben Jackson
1
修改公差的想法真是令人害怕!在复杂的序列上,公差可能会叠加,客户会发现在你的机器上制造的零件似乎永远不太合适! - Clifford
1
关于公差修改的想法,还有一件事情: "F2999.9" 与 "F3000" 有相同的问题 - 连续三个 '9' 字符与连续三个 '0' 字符一样糟糕。你必须做类似于 "F2989.9" 的事情来避免丢失两个相同的字符。这似乎引入了很多错误。而且仍然存在丢失两个九的风险,尽管非连续的丢失字符可能更不常见。 - Michael Burr
在协议中是否有“读回”机制?即,您是否可以要求机器回显刚刚发送的进给速率,以确保它是正确的? - AShelly
此外,务必极力推动固件升级。我一直在一个项目上工作,我们已经忍受偶尔的串行数据丢失(没有校验和)多年了,现在已经到了花费大量时间开发解决方案来检测、修复或忽略任何错误记录的地步。如果我们一开始就修复了发射器,我们将节省大量精力,但我们直到为时已晚才将问题提高到足够重要的优先级。 - AShelly
3个回答

5
你可以尝试将主机上的UART配置为发送2个停止位,而不是你当前使用的1个停止位。MCU接收器除了字符之间会多出一个额外的空闲时间外,不会注意到任何变化。这样就有大约10%的时间来从接收寄存器中获取字符,然后在下一个字符被移入之前处理该字符。
通常,即使UART配置为2个停止位(对于接收数据),UART也不会寻找超过1个停止位(没有理由强制执行附加的停止位)。因此,MCU仍然只发送单个停止位对于从设备接收响应不应该造成任何问题。
如果你的数据速率很高,则此操作不会增加多少时间,所以它可能不会有所帮助(但这取决于缓冲区溢出的潜在原因是什么)。如果我的计算正确,如果你将链路运行速率设置为38400 bps,则另外需要25微秒才能避免MCU溢出。
虽然机会渺茫,但这是一个廉价的更改,除了在主机端串口配置之外,不需要进行任何修改。

4
如果无法更改固件,您在PC上改善连接稳定性的选项相当有限。以下是一些可能的选择:
- 如果固件允许,降低数据传输速率(减少丢失字节的机会)。 - 尝试构建不包含零或重复字节的数据包,如果协议或功能允许的话。 - 发送多个消息(这使设备更有可能接收到正确的消息;但不能帮助减少“错误的正例”)。
如果可以更改固件,则具有更大的改进潜力:实现适当的CRC(即使是8位CRC也将显着提高,但16位更好)。
最好在PC驱动程序中实现自动协商,以便可以使用“旧”和“新”协议进行通信,并确定正在与哪种类型的设备进行通信。

降低数据速率确实有所帮助,但系统可能会受到带宽限制(许多短工具移动),因此这是一个权衡。在使驱动程序能够在面对10%的传输失败时更加健壮之后,我达到了“误报”成为主要危险的点。 - Ben Jackson
鉴于固件的限制,我喜欢尝试构建避免重复字符的最短可能数据包的想法。使用行重新编号代码以避免行号与数据匹配的情况。我也同意升级固件是更好的选择。 - AShelly

4

我们可能对G代码并不熟悉,链接对于特定领域的技术总是很有帮助。

我认为简单的校验和可能已经足够适用于数据长度、格式和处理器性能。如果你已经丢失了字符,你肯定不想增加CRC的CPU负载,对吧?

在这个协议中,你有多条防线。数据必须格式良好、按顺序传输,并通过校验和,它还似乎具有相当有限的有效字符集。因此,检查语法、顺序和校验和一起使用可能会非常强大。此外,你可以检查参数值是否在范围内,当然,如果你选择使用,你的UART将具有基本的奇偶校验。

UART的Rx寄存器溢出问题最好通过测试UART的溢出标志来解决。UART通常具有硬件溢出检测和中断生成功能。如果您的串行输入是中断驱动的,则似乎您要么未启用和处理溢出,要么忽略它并将其视为正常接收中断。如果您没有遇到溢出,则问题可能出在FTDI设备上,并且数据丢失发生在到达UART之前。以下最后两段内容介绍了可能的解决方案。
此链接的波特率是多少?在大多数情况下,如果在典型的UART数据速率下丢失字符,则实现存在缺陷。您可能会不适当地关闭中断太长时间,在中断级别执行太多工作,或者选择不合适的中断优先级。您需要修复根本原因,而不是尝试在协议级别修复基本实现问题;该问题旨在应对嘈杂的数据链路,而不是错误的软件。

另一个可能存在的问题是 FTDI设备。我曾经遇到过多个FTDI驱动程序冲突并导致数据丢失的情况。在这种情况下的解决方案是使用FTDI的FTClean实用程序来删除驱动程序,然后重新安装最新的驱动程序。尽管你可以通过Google搜索间接获取FTClean,但它似乎已从他们的网站中消失了。FTDI的网站确实有一个不同的卸载工具,我想它已取代了FTClean。你是否会遇到相同的问题,如果使用真正的串行端口?此外,我发现使用Prolific设备和驱动程序的USB串行设备在中等数据速度下特别容易出现数据丢失。

最后,我发现使用各种USB-串口设备时会遇到许多数据丢失问题可以通过“节奏化”输出来解决。一些设备拥有相当小的内部缓冲区。您可能已经注意到,在大约128个字符或USB设备的内部缓冲区大小之后,字符丢失开始发生。将短延迟(例如10毫秒)插入数据流中可以解决此问题。在这种情况下,您可以在每行末尾简单地执行该操作。另一种“节奏化”的方法是在PC应用程序中轮询传输缓冲区,并在添加更多数据之前等待其为空,然后只以小块添加数据,在您的情况下可能是单行。我发现,这通常可以解决数据丢失问题,而不会在数据传输性能上造成明显的损失。

你提出的所有观点都是正确的,但我的问题是如何在现有固件的基础上使驱动程序更加健壮。目前至少存在三种“变种”固件及其衍生版本,大多数人都使用自定义选项来匹配其确切的配置。能够与“损坏”的固件进行稳健通信是非常有价值的。 - Ben Jackson
我喜欢有策略地引入延迟的想法 - 特别是如果你可以将MCU上的超限特征化为在某些大小的数据爆发后更常见。但是,我认为说校验和算法足够好是一种宽容的说法,因为它无法检测到两个丢失的字符。天哪 - 想出这个协议的人至少应该在协议中添加一个行长度或将字符位置混合到校验和计算中。 - Michael Burr
啊,我喜欢使用延迟来拆分重复项的想法。至少有一个固件似乎忽略控制字符,所以我可以传输F30^A^A^A^A0^A^A^A0... 我会检查其他固件是否有类似的技巧。 - Ben Jackson
@Clifford:我强烈怀疑是MCU出了问题。UART中断的优先级低于定时器中断。定时器中断正在执行(模拟)浮点数运算。相同的硬件(我的测试系统)运行其他固件可以处理双倍的波特率。因此,“^A”填充很有趣,因为单个额外字符会添加子毫秒延迟。目标是强制连续丢失的字符成为校验和失败。您的睡眠解决方案旨在简单地避免在第一时间丢失字节,但我担心操作系统粒度的睡眠会降低带宽。 - Ben Jackson
@Michael:说得对,但在中断服务子程序中使用浮点运算?谁写的这玩意儿?这简直是无能。我想知道为什么需要使用浮点数? - Clifford
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接