Arduinoのタイマー割り込みをロボットで試してみる

2021年8月14日

プログラミングのなかで、絶対にほかの処理に影響を与えられないで確実に一定間隔で動作をしてほしいという場合、タイマー割り込みという機能を使います。

Arduinoでは、IDEをダウンロードした素のままの状態ではこの機能を使うことができませんが、Arduinoの公式サイトでは、このタイマー割り込みを使えるようにするための拡張ライブラリーが提供されています。

Arduinoでタイマー割り込みライブラリーを使う

Arduino公式サイトには、MsTimer2FlexiTimer2というライブラリーが用意されています。
使い方は全く同じなのですが、対応しているCPUによってどちらを使うか選択するようです。
クムクム[QX-001R3J]は、ATMEGA32U4(ArduinoLeonardo)を使用しているため FlexiTimer2を使用してプログラミングを行います。

各タイマー割り込みの特徴とライブラリが対応しているCPU

各タイマーライブラリーの特徴と対応しているCPUは下記のとおりです。

FlexiTimer2

FlexiTimer2は、Arduinoボード上で使用されるタイマーライブラリの一つであり、高度なタイミング制御を可能にする便利な機能を提供します。

対応CPU

ATmega8,ATmega128,ATmega168,ATmega48,ATmega88,ATmega328P,ATmega1280,ATmega2560,AT90USB646,AT90USB1286,ATmega32U4

特徴:
  1. シンプルなインターフェース: FlexiTimer2は、シンプルなインターフェースを備えており、コード内でのタイマーのセットアップと制御が容易です。
  2. 複数のタイマーのサポート: FlexiTimer2は、複数のタイマーをサポートし、それぞれのタイマーに独自のカスタム割り込みルーチンを設定することができます。
  3. 高度なタイミング制御: FlexiTimer2は、マイクロ秒単位での精密なタイミングをサポートし、精確な時間ベースのイベントの制御が可能です。
2つのLEDを交互に光らせるサンプルプログラム
#include <FlexiTimer2.h>

#define LED_PIN1 2  // LED1のピン番号
#define LED_PIN2 3  // LED2のピン番号

int ledState1 = LOW;  // LED1の現在の状態
int ledState2 = LOW;  // LED2の現在の状態

void setup() {
  pinMode(LED_PIN1, OUTPUT);
  pinMode(LED_PIN2, OUTPUT);

  // タイマーの設定
  FlexiTimer2::set(1000, toggleLEDs); // 1000ミリ秒ごとにtoggleLEDs()関数を呼び出す
  FlexiTimer2::start(); // タイマーをスタート
}

void loop() {
  // ここに何らかの処理を追加する場合は、非常に短い時間で終了するようにしてください
}

void toggleLEDs() {
  // LEDの状態を切り替える
  ledState1 = !ledState1;
  ledState2 = !ledState2;

  // LEDの状態に応じて、LEDを点灯または消灯する
  digitalWrite(LED_PIN1, ledState1);
  digitalWrite(LED_PIN2, ledState2);
}

このプログラムでは、FlexiTimer2を使用して1秒ごとにtoggleLEDs()関数を呼び出し、LED1とLED2の状態を交互に切り替えています。LED1とLED2はそれぞれLED_PIN1LED_PIN2で指定されたピンに接続されており、digitalWrite()関数を使用してLEDの状態を制御しています。

注意点として、FlexiTimer2のset()関数で設定するタイマーの周期は、ミリ秒単位で指定します。また、FlexiTimer2のタイマー割り込みは、プログラムのタイミングを変更するため、他の処理との競合に注意して使用してください。

MsTimer2

ATmega8,ATmega128,ATmega168,ATmega48,ATmega88,ATmega328P,ATmega1280

MsTimer2は、Arduinoのライブラリの1つで、マイクロ秒単位のタイマー割り込みを利用して、定期的に関数を実行するための機能を提供します。以下はMsTimer2の特徴です

特徴
  1. シンプルな使用: MsTimer2はシンプルなインターフェースを持ち、使いやすいため、初心者にも扱いやすいです。
  2. タイマーの精度: MsTimer2はマイクロ秒単位のタイマー割り込みを利用するため、非常に高い精度でタイミングを制御できます。
  3. 低リソース消費: MsTimer2はハードウェアタイマーを使用してタイマー割り込みを実現しており、CPUの負荷が低いため、他の処理との競合を最小限に抑えることができます。
  4. 多機能: MsTimer2はタイマー割り込みを利用して、関数の定期実行だけでなく、ディレイ処理やPWM制御など、さまざまな用途に利用することができます。
  5. フリーウェア: MsTimer2はオープンソースのライブラリであり、自由に利用することができます。
MsTimer2で2つのLEDを光らせるサンプルプログラム
#include <MsTimer2.h>

// LEDのピン番号
const int ledPin1 = 2;
const int ledPin2 = 3;

// LEDの状態
boolean ledState1 = false;
boolean ledState2 = false;

// LEDの点滅間隔(ミリ秒)
const int blinkInterval = 500;

void setup() {
  // LEDのピンをOUTPUTに設定
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);

  // MsTimer2の設定
  MsTimer2::set(blinkInterval, toggleLEDs);  // タイマー割り込みの間隔とコールバック関数を設定
  MsTimer2::start();  // タイマー割り込みを開始
}

void loop() {
  // ここには何も書かない
}

// タイマー割り込みで実行される関数
void toggleLEDs() {
  // LED1の状態を反転
  ledState1 = !ledState1;
  // LED2の状態を反転
  ledState2 = !ledState2;

  // LED1とLED2を交互に点滅
  digitalWrite(ledPin1, ledState1);
  digitalWrite(ledPin2, ledState2);
}

のプログラムでは、MsTimer2を使用して、定期的にtoggleLEDs()という関数を実行しています。toggleLEDs()関数では、ledState1ledState2という2つの変数を使用して、2つのLEDの状態を交互に反転させ、digitalWrite()関数を使用してLEDを点滅させています。LEDのピン番号や点滅間隔は、自分の環境に合わせて適宜変更してください。

クムクムロボットでっ使ってみる

クムクムのBEEP音を鳴らしながら、顔や手のモータをー非同期で動かすプログラムをFlexTimerを使って実際に書いてみます。

ライブラリーの準備

まずはライブラリーをダウンロードしArduinoIDEの環境で使えるようにします。

ダウンロードしたzipファイルをArduinoIDEのメニュー[スケッチ]-ライブラリーをインクルード-.zip形式のライブラリーをインストールを選択しダウンロードしたファイルをインストール

インストールが終わるとメニュの中にライブラリーが表示されますので、そのライブラリをクリックすることで、IDEソースコードエディタに #include 文が挿入されます。

私の場合は、ダウンロードされたzipファイル名が wimleers-flexitimer2-v1.1-0-g7338cfb.zip だったので、ライブラリ一覧には[wimleers-flexitimer2-v1.1-0-g7338cfb]と表示されていました。

プログラム

//
//タイマー割り込みを使ってモータと音とLEDを非同期で動かしてみる
//

#include "FlexiTimer2.h"
#include "Servo.h"

Servo sv;

//定期的に実行されるピコピコ
void PicoPico() {
  static boolean output = HIGH;  // プログラム起動前に1回だけHIGH(1)で初期化される
  digitalWrite(A0, output);      // 13番ピン(LED)に出力する(HIGH>ON LOW>OFF)
  output = !output;              // 現在のoutput内容を反転(HIGH→LOW/LOW→HIGH)させoutputにセットする
  if(output==HIGH){
    tone(12,880,100) ;
  }else{
    tone(12,440,100) ;
  }
}
//セットアップ
void setup() {
  //ポートの設定
  pinMode(A0, OUTPUT); // クムクムの赤LED
  pinMode(12, OUTPUT); // クムクムのBEEP
  pinMode(7, OUTPUT);  // クムクムの顔のモータ
  pinMode(11,OUTPUT) ; // クムクムのモータ電源制御
  //モータの設定
  sv.attach(7);          // 顔モータ割り当て
  digitalWrite(11,HIGH); //電源供給 
  //タイマー割り込み駆動
  FlexiTimer2::set(500, PicoPico); 
  FlexiTimer2::start();             
}
//メインループ
void loop() {
  sv.write(180) ; //180度の位置に回転
  delay(1000) ;   //1秒まつ
  sv.write(0) ;   //0度の位置に回転
  delay(1000) ;   //1秒まつ
}

動き

このプログラムは、顔を0~180度(右から左)まで1秒間隔で回転をさせる間に、その動きとは全く関係なくLEDの赤色と440Hz,880HzのBEEP音がしっかり500msec単位で点滅と発音を繰り返しています。

メインのloopの中で、顔を動かしたあとに delay(1000)で1秒間メインループはプログラムの動きを一時停止しているにも関わらず、PicoPicoという関数は500msecごとに定期的に動作してるところがポイントです。

関数仕様

FlexiTimer2 :: set(unsigned long units、double Resolution、void(* f)())
この関数は、unitsresolutionで分解能を指定することでより細かいタイマー時間を設定するjことができます。
たとえばunits=1resolution = 1.0/3000f1秒間に3000回呼び出されますが、units=2としたの場合は1秒間に1500回しか呼び出されません。
void(* f)には時間ごとに呼び出す関数の名前を指定します。
例)FlexiTimer2::set(500, 1.0/1000000, flash) ; // 500us毎に割込み発生

//ライブラリのインクルード: スケッチの先頭でFlexiTimer2ライブラリをインクルードしま//
#include <FlexiTimer2.h>
//タイマーの設定: タイマーを設定するために、FlexiTimer2のbegin()関数
FlexiTimer2::set(1000, functionName); // 1000ミリ秒のインターバルで関数を呼び出す
//タイマーの開始: タイマーを開始するには、FlexiTimer2のstart()関数
FlexiTimer2::start();
//タイマー割り込みの処理: タイマー割り込みが発生すると、指定した関数が自動的に呼び出されます。
void functionName() {
  // タイマー割り込みの処理をここに記述する
}
タイマーの停止: タイマーを停止するには、FlexiTimer2のstop()関数
FlexiTimer2::stop()
タイマーの再開: 停止したタイマーを再開するには、FlexiTimer2のstart()関数
FlexiTimer2::start();

2023.04.21 更新