ページ

2012-02-03

[arduino]Arduino Mega ADK or Arduino Megaでタイマ割り込みを使う

ATmega2560が搭載された、Arduino Megaでタイマ割り込みを使用する方法をご紹介します。

本題に入る前に少しお話があります。

以前、一定周期で処理を実行するためのスケッチ(ソースコード)を掲載したことがありますが、あのスケッチはやっつけなのでかなり問題があります。


delayを使用して時間調整を行なっていたため周期の精度が悪かったり、オーバーフローが発生するとおかしな挙動が発生したりします。
オーバーフローの方は簡単な演算で解決可能なのですが、精度の悪さはどうにも解決できませんでした。

フィードバックをかけて誤差を少なくしようと色々やってみたのですが、誤差を20マイクロ秒以下に抑えることができませんでした。

タイマ割り込みを使用せずに頑張った理由としては、外部のライブラリを使用せず、Arduinoの開発環境に標準で付いている機能のみでやってやろうという発想からでした。

ふと気が付いてみたら時間調整を行う処理だけで、それなりに行数を食ってしまいなんとも微妙な状態になってしまいました。

そこで、Arduinoの公式サイトにも掲載されているタイマ割り込みライブラリを使用して、タイマ割り込みを利用した周期処理を行うことにしました。

MsTimer2というライブラリで、以下のリンクに詳細が載っています。

ダウンロードしてArduinoの"libraries"フォルダに突っ込めば準備完了です。
サンプルコード(FlashLed")も付いているので、開発環境を立ち上げ早速実行してみます。

500msec毎にLEDが付いたり消えたりするはずです。。。はずでした。

試してみると分かりますが、ATmega2560が搭載された新しいArduino MegaではMsTimer2は使えません(2012/02/03現在)。

何故なのかというと、MsTimer2のソースコード"MsTimer2.cpp"の中で、CPU毎に処理が書かれているのですが、悲しいことにArmega2560の処理が無いからです。

しかし、ちょっといじるだけで使えるようになります。

"MsTimer2.cpp"の
#if defined (__AVR_ATmega168__) || defined (__AVR_ATmega48__) || defined (__AVR_ATmega88__) || defined (__AVR_ATmega328P__) || (__AVR_ATmega1280__)
となっている箇所全てを
#if defined (__AVR_ATmega168__) || defined (__AVR_ATmega48__) || defined (__AVR_ATmega88__) || defined (__AVR_ATmega328P__) || (__AVR_ATmega1280__) || (__AVR_ATmega2560__)
view raw MsTimer2.cpp hosted with ❤ by GitHub

と書き換えるだけです。
見にくいので、変更する場所だけを言うと
|| (__AVR_ATmega2560__)
を追記するだけです。

これで、Armega2560が搭載されたArduino Megaでもタイマ割り込みの処理が実行されます。

何故これだけで良いかというと、Armega2560はATmega1280のメモリが増えただけなので、レジスタの変更などは有りません。したがって、処理の内容はATmega1280のままで良いことになります。

再度、サンプルコードを実行してみると、今度はちゃんとLEDが付いたり消えたりするのが確認できると思います。

周期の精度はタイマ割り込みなので、言うこと無しです。

一定間隔で処理をする時は、こんな感じで書きます。

#include <MsTimer2.h>
/* タイマ割り込み間隔 */
#define TIMER2_INTERVAL (unsigned long)500
/* タイマ割り込み */
void timer2interrupt() {
/* 何か処理 */
}
void setup() {
Serial.begin(115200);
MsTimer2::set(TIMER2_INTERVAL, timer2interrupt);
MsTimer2::start();
}
void loop() {
/* 何もしない */
}
view raw timer1.cpp hosted with ❤ by GitHub
こんな書き方も出来ます。
#include <MsTimer2.h>
/* タイマ割り込み間隔 */
#define TIMER2_INTERVAL (unsigned long)500
#define LOCK 1
#define UNLOCK 0
volatile int lock = 0;
/* タイマ割り込み */
void timer2interrupt() {
lock = UNLOCK; /* ロック開放 */
}
void setup() {
Serial.begin(115200);
MsTimer2::set(TIMER2_INTERVAL, timer2interrupt);
MsTimer2::start();
}
void loop() {
if( lock == UNLOCK ){
/* 何か処理 */
lock = LOCK;
}
}
view raw timer2.cpp hosted with ❤ by GitHub

2 件のコメント:

  1. タイマー割り込みが使えなく、困っていたところ偶然見つけました。
    おかげさまでMEGAでも動作するようになり、助かりました。
    本当にありがとうございます。

    返信削除
  2. 分かりやすかったです。
    ありがとうございます。

    返信削除