以下程序的目的是定期在串行上输出数据帧。周期由定时中断定义,每秒钟。
代码适用于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() {
};
答案 0 :(得分:7)
有许多问题可能会或可能不会导致问题,但在任何情况下都应该修复。这些评论是一般性的;我并不熟悉Arduino或其库。
在中断处理程序(ISR)中发出Serial.write()
调用几乎肯定是不合适的。如果Serial对象是中断驱动的,它将具有相关的缓冲区。如果该缓冲区不足以占用所有数据,则该函数可能阻塞,这是中断处理程序中的 no no 。此外,如果定时器中断的优先级高于串行中断,则Serial.write()
阻塞时会导致死锁。 0x40(64字节)似乎是串行输出的可能缓冲区大小,因此这可能是主要原因。如果您可以增加可能使其工作的缓冲区大小,但在ISR中执行可能的阻塞操作仍然是一个坏主意。
即使串行输出被轮询而不是中断驱动,你的中断处理程序也会花费相当长的时间,这也是一个坏主意,但在这种情况下可能不是问题,但在 9600,n,8,1 ,64个字符需要67毫秒才能清除发送寄存器。
stateLED
和frame
是共享变量(在中断和主要上下文之间),因此应声明为volatile。
您的片段中未显示更新frame
的方式和位置,但由于中断将异步发生,因此frame
的任何更新都应位于 critical section < / em> - 至少禁用了timer1中断。
<强>更新强>
根据A.H.的回复,我下载了源代码并看了一眼。 Serial
是\ arduino-1.0 \ hardware \ arduino \ cores \ arduino \ hardwareSerial.cpp / .h中定义的类HardwareSerial
的静态对象。发送缓冲区长度确实是64字节,如果缓冲区已满,HardwareSerial::write()
函数会“忙等待”。您需要修改并重新构建源以扩展缓冲区或添加write()
的非阻塞版本。
然而这肯定是锁定的原因 - 缓冲区永远不会为空,因为在timer1中断运行时无法处理发送中断。
答案 1 :(得分:6)
因此,您的代码在1.0之前工作,因为HardwareSerial::write(uint8_t)
(这是所有输出的基础)没有缓冲区,只有在传输完字节后才返回。
我觉得这令人惊讶,Serial
{{1}}没有提到这种行为。