【问题标题】:Making the best of a bad "checksum" algorithm充分利用糟糕的“校验和”算法
【发布时间】:2011-04-05 18:12:30
【问题描述】:

我正在开发通过串行端口控制 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
  • 将单个命令分解为多个等效命令(例如,G1 X2 变为 G1 X1 G1 X2 假设最初 X=0)
  • 消除(或添加)多余的词(例如,T0 对大多数命令来说是没有意义的,如果你发送 F3000 一次它暗示将来可以选择发送或不发送)

如果我认为固件会分组丢弃字节,那么最重要的事情可能是避免像 00 这样的背靠背重复,这将(如果一起丢弃)是不可见的。

【问题讨论】:

  • 明确一点 - 您无法控制或修改 8 位 MCU 的固件以使用不同的校验和/或命令行解释器?
  • 都是开源的,所以这绝对是一种可能。对于那里的许多固件实例,使驱动程序尽可能健壮会很好。 (虽然更好的固件修改是更好的串行通信管理!)
  • 容差修改思路吓人!在复杂的序列中,公差可能会叠加,客户会发现在您的机器上制造的零件似乎永远不会很合适!
  • 关于容差修改思想的另一件事:“F2999.9”和“F3000”有同样的问题——连续三个'9'字符和一个连续三个'0'字符一样糟糕排。您必须执行“F2989.9”之类的操作,以避免连续删除 2 个相同的字符。似乎要引入很多错误。它仍然会丢失两个未检测到的九,尽管非连续丢失的字符可能更不常见。
  • 协议中是否有任何“回读”机制?即您能否要求机器回显您刚刚发送的进给率,以确保它是正确的?

标签: embedded serial-port driver checksum


【解决方案1】:

您可以尝试的一件事是将主机上的 UART 配置为发送 2 个停止位而不是 1 个(这可能是您当前正在使用的)。 MCU 接收器不会注意到任何东西,只是字符之间有一个额外的空闲位时间。在下一个字符移入之前,将字符移出接收寄存器的时间大约增加了 10%。

通常情况下,UART 在接收数据时不会查找超过 1 个停止位,即使 UART 配置为 2 个停止位(没有理由在接收时强制执行额外的停止位),因此 MCU仍将仅发送一个停止位,不会在接收设备响应时造成任何问题。

如果您处于高数据速率,这不会增加太多时间,因此可能无济于事(但这取决于溢出的根本原因是什么)。如果我的加密是正确的,如果您以 38400 bps 运行链路,MCU 可以再花 25 微秒来避免溢出。

这是一个很长的尝试,但这是一个便宜的更改,除了主机端的串行端口配置之外不需要任何修改。

【讨论】:

    【解决方案2】:

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

    我会说简单的校验和可能完全适合数据长度、格式和处理器性能。如果您已经在丢弃字符,您几乎不想通过 CRC 增加更多的 CPU 负载,对吗?

    您在此协议中有多道防线。数据必须按顺序排列良好,并通过校验和,它似乎也具有相当有限的有效字符集。因此,一起检查语法、序列和校验和可能会非常健壮。此外,您可以检查参数值是否在界限内,当然,如果您选择使用 UART,您的 UART 将进行基本的奇偶校验检查。

    UART Rx 寄存器溢出问题最好通过测试 UART 的溢出标志来解决。 UART 总是具有硬件溢出检测和溢出错误产生的中断。如果您的串行输入是中断驱动的,那么您可能没有启用和处理溢出,或者您可能忽略它并将其视为正常接收中断。如果您没有出现溢出,则问题出在 FTDI 设备上,并且在到达 UART 之前就发生了数据丢失。下面的最后两段讨论了该问题的可能解决方案。

    此链接运行的波特率是多少?在大多数情况下,如果您在典型的 UART 数据速率上丢弃字符,那么实现是有缺陷的。您可能会不恰当地关闭中断太长时间,在中断级别上做太多工作,或者中断优先级选择不当。您需要解决根本原因,而不是试图在协议级别解决根本的实现问题;旨在应对嘈杂的数据链路,而不是糟糕的软件。

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

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

    【讨论】:

    • 您的观点都是有效的,但我的问题是如何使 驱动程序 与现有固件更健壮。至少存在 3 种固件“品系”以及每种固件的衍生品,并且大多数人都使用自定义选项构建以匹配其确切配置。能够与“损坏”的固件进行稳健通信是很有价值的。
    • 我喜欢战略性地引入延迟的想法 - 特别是如果您可以将 MCU 上的溢出描述为在某种大小的数据爆发后更为常见。但是,我认为当校验和算法无法检测到两个丢失的字符时,它就足够了。 Cripes - 提出协议的人至少应该在协议中添加行长或将字符位置混合到校验和计算中。
    • 啊,我喜欢用延迟拆分重复项的想法。至少有一个固件似乎忽略了控制字符,所以我可以发送F30^A^A^A^A0^A^A^A0... 我会检查其他固件是否有类似的技巧。
    • @Ben:你说的 driver 是什么意思还不清楚。您是指与嵌入式目标通信的 PC 客户端应用程序吗?关于延迟,我实际上是指“延迟”,而不是虚拟字符;这可能只会加剧问题,特别是如果我怀疑溢出发生在 FTDI 设备而不是微控制器中。消息之间的简单 Sleep() 调用确定就足够了吗?
    • @Michael:你可能是对的,但删除字符是一个实现缺陷而不是环境条件。该协议不应该处理损坏的软件。
    【解决方案3】:

    如果您无法更改固件,那么您可以在 PC 上执行哪些操作以提高链接的稳健性,您的选择将非常有限。只是几种可能性:

    • 如果固件允许,请降低数据速率(减少丢失字节的机会)。
    • 如果协议或功能有任何灵活性允许,请尝试构造不包含零字节或重复字节的数据包。
    • 多次发送消息(这使设备更有可能收到一条好的消息;但这无助于减少“误报”)。

    如果您可以更改固件,那么您有更大的改进潜力:实施适当的 CRC(即使是 8 位 CRC 也会有很大的改进,但 16 位会更好) .

    您最好在 PC 驱动程序中实现自动协商,这样它就可以同时处理“旧”和“新”协议,并确定它正在与哪种类型的设备通信。

    【讨论】:

    • 降低数据速率确实有帮助,但系统可能会受到带宽限制(许多短工具移动),因此需要权衡。在使驱动程序在面对 10% 的 xmit 失败时变得健壮后,我达到了“误报”是主要危险的地步。
    • 鉴于固件的限制,我喜欢尝试构建最短的数据包以避免重复字符的想法。使用行重新编号代码来避免行号与数据匹配的情况。我也同意固件升级是更好的选择。
    猜你喜欢
    • 2011-06-05
    • 2021-05-11
    • 2014-09-07
    • 1970-01-01
    • 2012-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多