Arduino タイマー割り込みで制御する

前回から使っている状態表示は1秒周期で表示されるはずなのですが、時々LEDのディレイやシリアル通信の処理の影響で何秒か飛ぶことがありました。

ですので、今回はタイマー割り込みを使ってその不具合を解消します。

タイマー割り込みの動作


通常プログラムを動作させるとloopの最後まで実行したら、また始めから処理をします。
下記はArduino nano アナログ テストプログラムを例としてloopの動きとなります。

timing

これでなぜ表示が飛ばされてしまうのか簡単に説明します。

下記、画像の上段がloop1回の処理となります。
赤の①がシリアルを受信した時の処理、次に青がアナログ取得、②が1秒毎に状態を表示する処理(←問題点)、③はLEDの制御となります。①と②は条件上、実行されない時もあります。

timing_2

時間を長くしてみると(実際、1秒間のloop実行数はもっと多いです。)
(1)の0秒目は問題なく処理をしています。
(2)の1秒目も間に合い表示させることが出来ています。
(3)ではLEDのディレイ時間が1秒以上に変更されました。
(4)では(3)で実行されたLEDディレイが継続されて、2秒目の表示が出力できませんでした。
(5)でようやく表示が出力されますが2秒を飛ばして3秒目が表示されてしまいます。

これを回避するために表示用の割り込みを試してみました。
結果としてはこのようになるのかなと。

timing_3.png

追加された緑は表示用の割り込み処理の動作です。
見ての通り1秒毎にloop処理を「中断」させて時間表示を行います。

中断されたということは、loopに戻った時に少なくとも時間表示のシリアル送受信処理分は遅延することになると思います。

使用するタイマー割り込み


使用するタイマー関数はArduinoコミュニティにユーザーより投稿された「FlexiTimer2」。

使用できる関数は以下の通り

  • FlexiTimer2::set(unsigned long units, double resolution, void (*f)()) ※1
    unsigned long units: 単位を指定する。
    double resolution: 分解能を指定する。
    units=1、resolution=1.0/3000とするとvoid (*f)を毎秒3000回実行する。
    units=500なら毎秒6回実行する。
    void (*f)():実行する関数
  • FlexiTimer2::set(unsigned long ms, void (*f)()) ※1
    unsigned long ms:void (*f)()の実行をms毎に指定できます。
    void (*f)():実行する関数
  • FlexiTimer2::start()
    タイマー実行
  • FlexiTimer2::stop()
    タイマー停止

※1:両方は不可。どちらかを使用する。

このタイマー関数のライブラリへ追加する方法は、

  1. 先ほどのリンク「FlexiTimer2」のソースコード項目にあるダウンロードリンクよりzipファイルを入手。
  2. 任意の場所に解凍してフォルダの中にある「wimleers-flexitimer2-7338cfb」をArduino IDEのライブラリの中へ移動する。
    初期設定のライブラリの場所は「C:\Users\ユーザー名\Documents\Arduino\libraries」
  3. 後はプログラムに加えるだけ。
    但し、使用する時は#include を記述して呼び出してあげる。

タイマー割り込みを加えたArduino nano アナログ テストプログラムが以下の通りです。

#include <FlexiTimer2.h>

/*~~~省略~~~*/

void setup() {
  Serial.begin(9600);
  while (!Serial)

/*~~~省略~~~*/

  FlexiTimer2::set(500, SerialCNT); // 500ms period
  FlexiTimer2::start();
}


void loop() {    
  //シリアル受信処理
  if (Serial.available() > 0) {
    inByte = Serial.read();
    LEDstat = inByte - '0';
    Serial.print("Mode key= ");
    Serial.println(LEDstat);
    LED = 0;
  }
  
  for(int i=0; i<=5 ; i++){value1 = analogRead(0);}

/*~~~LED制御省略~~~*/

}

void SerialCNT() {
    A0in = REF_DEF * value1 / 1024;
    Serial.print((millis()/1000));
    Serial.print("s: ");
    Serial.print(value1);
    Serial.print(" : A0= ");
    Serial.print(A0in, 4);
    Serial.write("V");
    Serial.write("V\r");
    Serial.write("\r");
}

setup()の最後で500msのタイマー割り込みをセットしでいます。
また、次の行でタイマーを実行しています。

タイマー割り込みで実行するプログラムはloopとは別に宣言します。
プログラム中の最下段SerialCNT()が割り込みで実行されます。

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中