对话
#define bit_true(x,掩码) (x |= 掩码) | ||
#define bit_false(x,掩码) (x &= ~掩码) | ||
#define bit_toggle(x,掩码) (x ^= 掩码) | ||
#define bit_true(x,mask) {uint8_t sreg = SREG; cli(); (x) |= (掩码); SREG = sreg; } |
这段代码不是我很熟悉的东西。我不知道运行这组代码意味着什么。一般来说,我不喜欢 Grbl 特定处理器的代码,除非出于移植原因很容易变通。我不是其他处理器的专家,所以我不能说是否所有其他微控制器都可以做这样的事情。但是,如果他们可以,我不会感到惊讶。
此外,cli() 命令:我不确定这会对系统中的其他所有内容产生什么影响。cli() 命令是否有可能清除或跳过串行中断、步进脉冲中断或引脚更改中断?
这似乎是一种在内存字上强制执行原子读取-修改-写入的有效方法。Atmel Mega 数据表2549P–AVR–10/2012有与此类似的示例。(参见第 7.8 节的示例)。 一种更快、更有效的方法可能是利用芯片的通用 IO 寄存器(第 9.3 节),它们具有原子设置位和清除位指令。使用此功能将避免必须禁用中断。
并将 sys.execute 替换为代码中其他任何地方的 SYS_EXEC,avr-gcc 编译器将在代码具有or的任何地方生成原子 |
嗯.. 仍然不清楚当 cli() 处于活动状态时其他中断会发生什么。如果步骤 ISR 与 cli() 同时触发,它是否排队等待全局中断标志重新启用?还是完全跳过它直到下一步 ISR?如果是这样,那么这将是一个交易破坏者。 我认为有更好的方法可以做到这一点,而不必弄乱全局中断标志。很可能只是通过引入一个新的 ISR-only 标志变量,主程序将从中读取。 |
不同优先级的中断将在再次使能中断时处理。他们将根据优先级开火。当调用 isr 时,中断被禁用,并在处理例程退出时被启用。因此,使用 cli/sei 提供原子访问应该是安全的。在 ISR 内部中断已经被禁用,所以访问多字节变量是安全的。 希尔森凯蒂尔
|
链接文档的第 19 页:
但我同意,您不必这样做,至少在大多数情况下不必这样做。 |
关于可移植性:每个平台都有自己的方式来确保对变量的原子访问。此外,可移植性可能会受到有关并发性和内存访问的其他特定于平台的因素的影响——例如,8 位访问可能是原子的,但 16 位可能不是。或者,您可能必须插入内存屏障以确保线程之间的缓存一致性(尽管这主要用于具有缓存的多处理器系统)。即使没有特定于平台的代码,平台相关的错误也会发生,就像在这种情况下一样。 关于中断:禁用中断以确保原子执行是受支持的标准技术。不应通过长时间禁用中断来滥用它,但对于设置/清除标志之类的短代码,它可以正常工作。如果在禁用中断的情况下触发中断,则处理程序将在重新启用中断后运行。这是在单 CPU 系统上确保原子性的最简单(但在本例中是足够的)方法。 关于不涉及平台特定代码的替代解决方案:我已经能够通过简单地将 EXEC_CYCLE_STOP 标志从 sys.execute 移到一个单独的变量中来可靠地工作。但是,我没有对主程序和中断处理程序可能修改哪些其他标志进行全面分析。如果主程序和中断处理程序都在更改同一个变量,则使用 bit_true/bit_false/bit_toggle 的其他代码可能会在其他地方发生相同的行为。 使用 GPIO 寄存器可能确实是最好的方法 – 只要这些寄存器尚未被 grbl 或标准库用于其他目的。此外,设置多个标志可能仍然不安全——据我所知,原子、单指令访问仅设置单个标志(需要通过 AVR 文档确认)。 |
感谢大家的澄清。这打消了我的顾虑。至于设置 sys.execute 标志的其他地方,它们几乎在每个 ISR 中设置,从串行、步进器和引出线。其他人在潜在的冲突中应该非常非常罕见,除了连续剧,但我认为如果这是一个问题,我们应该已经看到了一些关于此的问题报告。 我很想看看你想出了什么@kfoltman作为具有单独变量的解决方案。与此同时,我将在本周末测试您的代码,看看它是否存在任何性能问题。正如你所说,我不应该遇到任何事情,我希望我不会。 |
你想要我用来触发问题的 Python 脚本吗?它可能需要一些修改,但至少它是一个还算不错的起点。 |
是的!那将是最有帮助的。我很好奇为什么这个步进 ISR 标志是导致这个特定问题的标志,而另一个 sys.execute 在其他 ISR 中写入不这样做。也许这只是关闭阶段期间一切的时间安排,或者其他原因。 |
“在潜在冲突中非常罕见”- 可能仍然意味着偶尔会破坏工作(或什至工具),尤其是对于 PCB 等长期、复杂的工作。我希望我们能摆脱所有的比赛条件,这对我来说当然听起来不容易,但并非完全不可能。:) 请不要在实际机器上运行它,它可能会由于大量运动而损坏步进器或机械装置! 我用来测试的脚本是:
请注意,出于某种原因,我的 arduino 克隆不会在每次打开时都重置,因此您可能必须将初始睡眠时间从 1 秒增加到 3 秒或更多。该脚本所做的是发出许多小距离的 G0 命令并等待 grbl 响应。我计算发送的命令和收到的“ok”响应之间的差异,以限制未完成请求的数量,这样输入缓冲区就不会溢出。 在未打补丁的版本中,它最终会停止接收“ok”消息。通常在一分钟左右后,具体取决于运行情况。 将 G0 替换为其他命令以验证其他地方不会出现相同的问题(探测等)可能会很有趣。 我将尝试编写另一个这样的脚本,它会发送一些位置变化(绝对或相对)并查看累积位置是否与命令一致。 |
@kfoltman: 谢谢!消除所有竞争条件一直是当务之急,很多时候需要从头开始重写固件。我很高兴你能发现这个。我猜这个特殊问题在安装新的规划器和步进器算法时变得更加明显。您无法将较旧的 Grbl 固件推送到接近此的任何位置。我一直将此类问题归因于未受保护的计划程序缓冲区,但也许其中有一点是这个原子标志设置问题。 无论如何,因为人们已经为非常非常长的工作(数百万行)运行 Grbl,所以我认为正常操作不会有崩溃的风险。这可能是由于代码的编写方式导致它对其余原子标志写入的问题不敏感。但是,这并不是说我们不需要解决这个问题。我非常非常高兴我们可能已经识别出它并采取了重要措施来修复它。(主要是,我很高兴这不是状态机的问题,因为我很难找到它的任何问题。我很困惑。) |
周末我一直在玩“固定”版本——看起来不错,没有遇到任何错误/挂起/可疑行为。 |
@kfoltman: 对不起!我无法在周末调查这个。我一直忙于其他工作和家庭事务。请给我一周的时间来深入研究这个问题。这是美国的假期周末,所以我想我会抽出一些时间。 |
@mschorer: 感谢举报!我个人不使用 UGS,所以我没有看到状态更新问题,但这进一步验证了这个错误修复。状态更新通过串行中断使用相同的标志机制。同样的问题可能会间歇性地错过状态更新。 另外,我同意专用的 bit_true_atomic 宏。我们在其他不需要原子访问的地方使用它。浏览并更新它们对我来说并不难。 让我们知道您的测试情况如何! |
@mschorer我同意 _atomic 后缀,对于局部变量,使用原子版本没有任何意义。然而,也许原来的 bit_true / bit_false 也应该重命名为 bit_true_nonatomic / bit_false_nonatomic – 这样他们就不会被不熟悉代码的新人意外地用在错误的上下文中。至少根据我的经验,这些偏执的“防御性做法”往往效果很好,即使我用它们来防止或发现我自己将来的错误时也是如此:) |
好的,很高兴知道!我还不能用我的设置触发它,但我打算铣削一些 PCB,所以我有很好的动机在尝试之前查看任何剩余的崩溃。:) 您可以发布您的修改和设置吗?我认为这个问题应该可以在没有物理机器的情况下重现,我可以尝试调整我的测试脚本中的时间并让它长时间运行。 |
@kfoltman:代码在 github 的 mschorer/grbl/tree/dev 中。
|
你用什么来编译它?该分支上的 Makefile 在 OBJECTS 中缺少 status.o 和 i2c_master.o – 我添加了它并运行了我的脚本,并进行了一些修改,但无法重现该问题。 |
我将 eclipse 与来自http://www.baeyens.it/eclipse/的 V2 avr-arduino 插件一起使用- 编译工作正常。但很好! |
@mschorer你能在某处发布你编译的 grbl.hex 和机器配置吗?也许它更棘手(依赖于编译器版本,或者仅在某些加速度或最大速度范围内发生)。不太可能但并非不可能。 我目前正在运行一个不同的脚本,一旦状态返回接近上一个移动结束的位置,它就会发出一个新的点动命令 (G91 G0)。令人恼火的是,还没有崩溃。另外,我假设您使用的是 UGS 0.7?我的脚本无法触发的前端/grbl 交互中可能存在一些微妙的时间。 |
这是我的第二个测试脚本——尝试了不同的阈值和延迟值,以及不同的加速设置,但还没有一次崩溃。
|
做了一点 git 魔术,新结帐,重新编译……比以往任何时候都更好。不能让它失速?然而?? |
.hex 是魔法前版本还是魔法后版本?:) |
(你做 git 魔法之前的版本对我来说更有趣,从“能够用脚本重现问题”的角度来看) |
十六进制是“post”。 |
这似乎修复了导致 Grbl 在某些操作期间挂起的错误,
尤其是慢跑。