【问题标题】:Arduino locks upArduino 锁定
【发布时间】:2012-05-12 16:07:25
【问题描述】:

下面程序的目的是定期在串行上输出一个数据帧。该周期由一个定时中断定义,每秒一次。

代码在 Arduino IDE 版本 0022 上工作,但在 1.0 上我无法让它工作。当使用定时器例程并且maxFrameLength 设置为0x40 或更高时,控制器锁定。使用 0x39 或更低时,程序继续运行(由闪烁的 LED 指示)。

这里出了什么问题,为什么?它是一个错误吗?我做错了吗?

我将http://code.google.com/p/arduino-timerone/downloads/detail?name=TimerOne-v9.zip 用于 Mega1280 上的计时器例程。

#include "TimerOne.h"

#define LED 13
#define maxFrameLength 0x40

boolean stateLED = true;
byte frame[ maxFrameLength ];

void sendFrame() {
  digitalWrite( LED , stateLED );
  stateLED = !stateLED;
  Serial.write( frame, maxFrameLength ); // ptr + bytes to send
}

void setup() {
  pinMode( LED , OUTPUT );
  Timer1.initialize( 1000000 );  // initialize timer1 with 1 second period
  Timer1.attachInterrupt( sendFrame );
  Serial.begin( 9600 );
};

void loop() {
};

【问题讨论】:

  • 串口的流控设置是什么?硬件?软件?没有?
  • 在初始化计时器之前你不应该打电话给Serial.begin吗?
  • @AlanStokes:正确,但不能解决问题。谢谢

标签: c++ embedded arduino


【解决方案1】:

有许多问题可能会或可能不会导致问题,但无论如何都应该解决。这些 cmets 本质上是通用的;我不熟悉 Arduino 或其库。

在中断处理程序 (ISR) 中发出 Serial.write() 调用几乎可以肯定是不合适的。如果串行对象是中断驱动的,它将有一个关联的缓冲区。如果该缓冲区不够大,无法接收所有数据,则函数可能会阻塞,这是中断处理程序中的no no。此外,如果定时器中断的优先级高于串行中断,则Serial.write() 阻塞时会导致死锁。 0x40(64 字节)似乎是串行输出的可能缓冲区大小,因此这可能是主要原因。如果您可以增加可能使其工作的缓冲区大小,但在 ISR 中执行潜在的阻塞操作仍然是一个坏主意。

即使轮询串行输出而不是中断驱动,您的中断处理程序也会花费相当长的时间,这也是一个坏主意,但在这种情况下可能不是问题,而是在 9600,n,8,1 ,64 个字符需要 67 毫秒才能清除发送寄存器。

stateLEDframe 是共享变量(在中断和主上下文之间),因此应声明为 volatile

您的片段中没有显示frame 的更新方式和位置,但由于中断将异步发生,因此对frame 的任何更新都应该在 critical section 中 - 使用 at至少禁用 timer1 中断。


更新

鉴于 A.H. 的回复,我下载了源代码并进行了查看。 Serial 是在 \arduino-1.0\hardware\arduino\cores\arduino\hardwareSerial.cpp/.h 中定义的 HardwareSerial 类的静态对象。传输缓冲区长度确实是 64 字节,如果缓冲区已满,HardwareSerial::write() 函数会执行“忙等待”。您将需要修改并重新构建源以扩展缓冲区或添加write() 的非阻塞版本。

然而,这肯定是锁定的原因 - 缓冲区永远不会清空,因为在 timer1 中断运行时无法处理发送中断。

【讨论】:

  • “它没有显示在你的片段中如何以及在哪里更新框架,”我试图找到最小的代码片段,我可以重现这个问题,结果证明没有必要添加那个片段的代码。您提出了关于禁用 Timer1 中断的有效观点。我也需要弄清楚“易失性”部分,我记得读过它,但我不确定 Arduino 是否支持它。
  • @jippie:我的回答中的 volatile 一词是关于该主题的 embedded.com 文章的链接。请注意,我已将编辑中的链接更改为我最初打算但当时找不到的文章。因此,如果您之前关注过它,请再看一遍。
  • @jippie:A.H. 的回答表明 1.0 的行为正如我在第二段中所建议的那样,之前在第三段中也是如此。两者都是不受欢迎的行为 - ISR busy-waitingblocking。新库并没有破坏您的代码,而是以更明显的方式暴露了现有缺陷。
  • 不知道我在哪里读到的,但不久前我读到 Arduino 的串行缓冲区是 128 字节。那一定是旧版本的文档。无论如何,这个更新看起来像是在指出问题,至少我现在知道为什么我的代码会失败,以及我需要在什么方向重新考虑我的设计。
  • @Jippie:我相信在 1.0 之前只有接收被缓冲并且该缓冲区是 128 字节。在 1.0 代码中,Tx 和 Rx 都被缓冲,并且是 16 到 64,具体取决于可用目标 RAM 的数量。请注意,我从未使用过 Arduino;我所知道的就是我为这个问题所做的研究。我敦促您自己查看源代码:arduino.googlecode.com/files/arduino-1.0-src.tar.gz
【解决方案2】:

The release notes for 1.0 告诉你:

  • 串行传输现在是异步的 - 即调用 Serial.print() 等将数据添加到传输的输出缓冲区 在后台。此外, Serial.flush() 命令已重新调整用途 等待传出的数据被传输,而不是丢弃 收到传入数据。

因此,您的代码在 1.0 之前可以工作,因为 HardwareSerial::write(uint8_t)(这是所有输出的基础)没有缓冲区,并且仅在传输字节后才返回。

令我惊讶的是,the reference page for Serial 没有提到这种行为。

【讨论】:

  • 这是一个有趣的发现,我需要更详细地检查。谢谢
  • 好地方。我下载了源代码以确认我的假设并相应地添加到我的答案中。
猜你喜欢
  • 1970-01-01
  • 2014-06-20
  • 1970-01-01
  • 1970-01-01
  • 2015-01-02
  • 2019-01-04
  • 1970-01-01
  • 2021-12-23
  • 1970-01-01
相关资源
最近更新 更多