3

以下のプログラムの目的は、データフレームを定期的にシリアルに出力することです。期間は、毎秒の時限割り込みによって定義されます。

コードはArduinoIDEバージョン0022で機能しましたが、1.0では機能しません。タイマールーチンを使用し、以上にmaxFrameLength設定されている場合、コントローラーはロックアップします。0x400x39以下を使用すると、プログラムは実行を継続します(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やそのライブラリに特に精通していません。

Serial.write()割り込みハンドラ(ISR)で呼び出しを発行することはほぼ間違いなく不適切です。Serialオブジェクトが割り込み駆動型の場合、関連するバッファがあります。そのバッファがすべてのデータを取得するのに十分な大きさでない場合、関数はブロックする可能性があります。これは、割り込みハンドラではノーノーです。さらに、タイマー割り込みがシリアル割り込みよりも優先度が高い場合、ブロック時にデッドロックが発生します。0x40(64バイト)はシリアル出力のバッファサイズのように思われるため、これが主な原因である可能性があります。バッファサイズを大きくして機能させることができるが、ISRでブロック操作を実行することはお勧めできません。Serial.write()

シリアル出力が割り込み駆動ではなくポーリングされる場合でも、割り込みハンドラーにはかなり時間がかかります。これも悪い考えですが、この場合はおそらく問題ではありませんが、9600、n、8,1、64文字では67文字になります。送信レジスタをクリアするためのミリ秒。

stateLEDおよびは(割り込みコンテキストとメインコンテキストの間で)共有変数であるため、揮発性frameとして宣言する必要があります。

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 に答える