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

发布于 2024-10-30 20:57:32 字数 1459 浏览 1 评论 0原文

我正在开发一个通过串行端口控制 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 这样的连续重复(如果放在一起)将不可见。

I'm working on an existing driver that controls an 8-bit MCU over a serial port. There are many different flavors of firmware for the MCU but they all share a common method of trying to ensure link integrity. This method is not very robust and I am looking for ideas about how the driver could modify its behavior to get the most out of it.

Commands are gcode with a line number and a checksum:

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

The line number must be sequential (but can be reset with M110). If the checksum doesn't match or the line number is out of sequence the firmware will reply Resend: nnn where nnn is the last successful N plus 1. The "checksum" is extremely primitive:

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

The main problem is that the primary error mechanism is dropped bytes due to interrupts being held off leading to overflows in the 1-byte MCU FIFO. The actual serial bus is a few cm between an FTDI (or similar) USB serial bridge and the MCU, so bit errors are unlikely. I've never observed a bit error in a reply from the firmware.

As you can see, the algorithm above would detect one dropped byte, but if you dropped two of the same byte (anywhere!) the result would still match. Thus F3000.0 (feedrate 3000mm/min) could be transmuted to F30.0 and still match. Plus, the character set is very small so certain bits are never going to be involved.

Is there anything the driver can do to make a given line more robust?

  • Add or drop trailing (or even leading) zeroes
  • Add or drop spaces
  • Re-order words (X1 Y1 is the same as Y1 X1)
  • Add or drop spaces
  • Make "insignificant" modifications to values within some tolerance (eg F2999.9 instead of F3000)
  • Reset the line number to get some specific N for a given line
  • Break a single command into multiple equivalent commands (eg G1 X2 becomes G1 X1 G1 X2 assuming X=0 initially)
  • Eliminate (or add) superfluous words (eg T0 is meaningless for most commands, and if you send F3000 once it is implied in the future so it can be optionally sent or not)

If I believe that the firmware drops bytes in groups then the most important thing could be to avoid back-to-back duplicates like 00 which would (if dropped together) be invisible.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

眼眸 2024-11-06 20:57:32

您可以尝试的一件事是将主机上的 UART 配置为发送 2 个停止位而不是 1 个(您当前可能正在使用该停止位)。除了字符之间存在额外的空闲位时间之外,MCU 接收器不会注意到任何其他情况。在移入下一个字符之前,大约需要 10% 的时间将字符从接收寄存器中取出。

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

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

虽然可能性不大,但这是一个廉价的改变,除了主机端的串行端口配置之外不需要任何修改。

One thing you could try is to configure the UART on the host to send 2 stop bits instead of 1 (which is what you're probably currently using). The MCU receiver will notice nothing except that there's an extra bit-time of idle between characters. That's about 10% more time to get the character out of the receive register before the next character is shifted in.

Typically, a UART doesn't look for more than 1 stop bit on receive data, even if the UART is configured for 2 stop bits (there's no reason to enforce extra stop bits on receive), so the fact that the MCU will still send only a single stop bit shouldn't cause any problems on receiving responses from the device.

If you're at a high data rate this doesn't add much time, so it probably won't help (but that depends on what's the underlying cause of the overruns is). If my ciphering is right, it works out to another 25 microseconds for the MCU to avoid an overrun if you're running the link at 38400 bps.

It's a long shot, but it's a cheap change that should require no modifications other than the serial port configuration on the host side.

一枫情书 2024-11-06 20:57:32

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

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

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

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

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

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

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

We may not all be familiar with G-Code, a link is always helpful for domain specific technology.

I would say that the simple checksum is probably adequately suited to the data length, format, and the processor performance. If you are already dropping characters, you hardly want to add more CPU load with a CRC do you?

You have multiple lines of defence in this protocol. The data has to be well formed, in sequence, and pass a checksum, it also appears to have a fairly limited valid character set. So checking syntax, sequence, and checksum together will likley be pretty robust. Additionally you might check that parameter values are in bounds, and of course your UART will have basic parity checking if you choose to use it.

The problem of UART Rx register overrun is better dealt with by testing the UART's overrun flag. UARTs invariably have hardware overrun detection, and interrupt generation on overrun error. If your serial input is interrupt driven, then it seems likely that you are either not enabling and processing the overrun, or that perhaps you are ignoring it and treating it as a normal receive interrupt. If you are not getting an overrun, then the problem is with the FTDI device and the data loss is occurring before it gets to the UART. The last two paragraphs below deal with possible solutions to that problem.

What baud rate does this link run at? In most cases if you are dropping characters on a typical UART data rate then the implementation is flawed. You are may be switching off interrupts inappropriately for too long, doing too much work at the interrupt level, or have inappropriate interrupt priority selection. You need to fix the root cause, and not try to fix a fundamental implementation problem at the protocol level; that is intended to cope with noisy data links, not bad software.

One other possible issue is the FTDI device. I have seen issues with multiple FTDI drivers conflicting and causing data drop-outs. The solution in that case was to use FTDI's FTClean utility to remove the drivers, and then reinstall the latest driver. FTClean appears to be absent from their site though you can obtain it indirectly through a Google search. FTDI's site does have a different removal tool which I guess superseded FTClean. Do you get the same problems with are real serial port? Also I have found USB serial devices using Prolific devices and drivers to be particularly prone to data loss, even at moderate data speeds.

Finally, I have found a number of data drop-out problems using various USB-Serial devices can be solved by "cadencing" the output. Some devices have rather small internal buffers. You may have noticed the character drop-outs start occurring after about 128 characters or whatever the USB device's internal buffer size may be. Inserting short delays (say 10ms) into the data stream can solve this issue. In this case you could simply do that at the end of each line. Another way of 'cadencing' is to poll the transmit buffer in the PC application and wait until it is empty before adding more data, and then only adding data in small chunks, in your case a single line perhaps. I have found that this usually solves data loss problems with no observable loss in data transfer performance.

你怎么这么可爱啊 2024-11-06 20:57:32

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

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

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

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

If you can't change the firmware, your options are quite limited for what you can do on the PC to improve the robustness of the link. Just a few possibilities:

  • Lower the data rate if the firmware allows for that (reduce chance of dropped bytes).
  • Try to construct packets that don't contain zero or duplicate bytes, if the protocol or functionality has any flexibility to allow for that.
  • Send messages several times (this makes it more likely the device will receive a good message; this doesn't help to reduce "false positives" however).

If you can change the firmware, then you have much greater potential for improvement: implement a proper CRC (even an 8-bit CRC would be a significant improvement, but 16 bits would be better).

You would be best to implement auto-negotiation in the PC driver so it can talk both the "old" and "new" protocols, and figure out which type of device it's talking to.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文