どーもー、Reveです。
今日は久しぶりに電子工作ネタです。
Arduinoを扱っている関係で、AVRマイコンを直接いじることもあるのですが、
今まで触ったことが無かったウォッチドッグタイマーを動かすことができたので、備忘録を兼ねてその手法を記していこうかと。
【AVRマイコン】
Atmel社が出しているマイコンのシリーズで、Arduinoでも(ATMega328P)使用されています。
特に、ATTiny13A-PUというマイコンは秋月電子でなんと50円という超低価格の割に機能も豊富で便利なので、簡単な電子工作ではとても重宝します。
当方もこのATTiny13Aをよく利用するのですが、今回はそのウォッチドッグタイマー機能を簡単に利用する方法を書いていきます。
【ウォッチドッグタイマー】
このウォッチドッグタイマー(以下、WDT)とはシステムの監視を行うためのタイマーで、通常では異常事態が発生した際にリセットをかけてシステムを再スタートさせるのですが、通常のタイマーとしての利用も可能です。
実はマイコンのスリープ機能(休止モード)で、メインクロックが停止したときもWDTは動作するので、スリープからの復帰に使えたりします。あとは他のタイマーをすでに使っているときとか
もちろん、ATTiny13Aにも搭載されているのですが、今日はこの機能をレジスタをいじることなく使用して行きます。
なお、WDTをタイマーとして使う場合、最大で8秒までしか計測できないため、それ以上長い時間は変数を設けるなど工夫する必要があります。
【使い方】
では、どうすればWDTを使えるかというと、開発環境が提供するライブラリを駆使します。
Atmel社が提供している公式の開発環境(Atmel Studio 7)がVisual Studioベースでなかなか使いやすく、ライブラリも豊富(今回使うライブラリも含む)なので、こちらを導入しましょう。
下のリンクからインストーラをダウンロードして実行すればOKです。
http://www.atmel.com/Microsite/atmel-studio/
開発環境を入れたら、コードの書き方に入っていきましょう。
順を追って解説していきます。
1. ライブラリの導入(インクルード)
avr/wdt.hというライブラリをインクルードします。
これにはWDTを使うための関数が3つ用意されています。
・wdt_reset() : WDTの設定をリセットするための関数
・wdt_enable(value) : WDTを有効にする関数。valueに定数を入れて時間を設定する
・wdt_disable() : WDTを無効にする関数
ライブラリと関数については、このリンク先に詳細が載っています。
http://cega.jp/avr-libc-jp/group__avr__watchdog.html
(6/29追記)
関数wdt_enableでWDTを起動した場合、タイマーの設定時間が経過するとリセットがかかるようです。
通常のタイマーのように使う手法は、手順3.に記載しています。
2. WDT発動後に再起動を繰り返す問題の対策
どうやら最近のAVRマイコンは、WDTの発動後もタイマーが継続するので、次回以降は最速のタイミング(15ms)でリセットを繰り返すという動作が起きてしまうそうで、意図しない誤作動を防ぐために入れています。
(参照) 電子牛乳様 AVR がウォッチドッグタイマ発動後に再起動を繰り返す問題
http://milkandlait.blogspot.jp/2014/06/avr_3.html
3. WDTの割り込み処理
WDTの設定時間が経過した際の割り込み処理を記述します。
AVRマイコンでは割り込み処理をISRという名前の関数に実装します。
ISRの引数は、割り込みのイベントを指定するもので、例えばタイマーの時間経過やスイッチ入力などがありますが、今回はWDTなのでWDT_vectを入れます。
あとは関数内に好きな処理を実装するのですが、wdt_disableを入れておくとWDTを一時停止できるので、WDT発動後の意図しないリセットを防ぐことができます。
(6/29追記)
前述のとおり、wdt_enableでは割り込み処理後にリセットがかかります。
通常のタイマー割り込みとして使うためには関係するレジスタ(WDTCR)を設定する必要があります。
WDTCRにタイマー割り込みの設定をするには、レジスタ内の
・WDCEを1にする
・WDTIEを1にして、時間を設定するための定数を入れる
と、通常のタイマーのようにWDTを使うことができます。
なお、設定の直前に割り込みを無効にし、直後に再び割り込みを無効にするのが推奨されているようです。
以上の設定が簡単にできるよう、自作の関数を定義すると良いでしょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
; title="WDT_start"]void WDT_start(int wt) { //disable interrupt cli(); //watchdog timer reset wdt_reset(); //watchdog timer change enable, use for interruption WDTCR |= (1 << WDCE) | (1 << WDTIE); //interruption interval WDTCR |= wt; //enable interrupt sei(); } |
4. WDTの実行
以上の準備が終わったら、任意のタイミングでWDTが起動するように関数を記述します。
wdt_enableを必要な場所に記述することでその場所からWDTが起動します。
時間の設定は、引数に指定の定数を入れることで可能になります。
下に書き方をソースで載せました(WDTの実装のみなので、入出力ピンなどの処理は追加してください)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
; title="test.c"]//1. ライブラリの導入 //他に必要なライブラリは適宜インクルードする #include <avr/wdt.h> //2. WDT発動後に再起動を繰り返す問題の対策 //詳しくはこのリンクを参照 //http://milkandlait.blogspot.jp/2014/06/avr_3.html uint8_t mcusr_mirror __attribute__ ((section (".noinit"))); void get_mcusr(void) __attribute__((naked)) __attribute__((section(".init3"))); void get_mcusr(void) { mcusr_mirror = MCUSR; MCUSR = 0; wdt_disable(); } //3. WDTの設定 //WDTの設定と起動 void WDT_start(int wt) { //disable interrupt cli(); //watchdog timer reset wdt_reset(); //watchdog timer change enable, use for interruption WDTCR |= (1 << WDCE) | (1 << WDTIE); //interruption interval WDTCR |= wt; //enable interrupt sei(); } //WDTのタイマー時間経過時の割り込み処理 ISR(WDT_vect) { //処理内容を記述 //WDTの無効 wdt_disable(); } //4. WDTの起動(メイン処理内) int main(void) { while (1) { //WDTの許可 //WDTの時間はライブラリ内の定数で指定(WDTO_1Sで1秒) WDT_start(WDTO_1S); //設定した時間が経過した際にリセットをかけたい場合は、この関数を使う //wdt_enable(WDTO_1S); } } |
と、こんな感じでライブラリを駆使すればすぐにWDTが使えます。
ぜひやってみようw
そういえば、公式に付属していたCDで開発環境を導入しようとしたら意外と苦労したのもいい思い出…((´・ω・`;))
まあ、今ではインストーラ一発ですがw
コメント