3

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

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

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

我在Mega1280 上使用http://code.google.com/p/arduino-timerone/downloads/detail?name=TimerOne-v9.zip作为定时器例程。

#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() {
};
4

2 回答 2

7

有许多问题可能会或可能不会导致问题,但无论如何都应该解决。这些评论是一般性的;我不熟悉 Arduino 或其库。

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

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

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

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


更新

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

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

于 2012-05-13T09:31:30.557 回答
6

1.0的发行说明告诉您:

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

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

我发现令人惊讶的是,参考页面Serial没有提到这种行为。

于 2012-05-13T10:08:04.767 回答