BGM再生用に作成したArduino UNO R4用タイマライブラリの解説
前回の記事 で、tone()
と delay()
による楽譜再生の課題と FspTimer クラス
や既存ライブラリの調査結果について書きました。
今回は、それらを元に作成したタイマライブラリ CallbackTimerR4
を解説します。本ライブラリと、バックグラウンドで tone()
を鳴らすライブラリ BackgroundMusicR4
を組み合わせれば、「Lチカしながら音楽再生」が出来るようになります。
CallbackTimerR4
楽譜中には全音符以外にも、数秒間同じ音を繋げて鳴らす記号 タイ が度々現れます。例えば BackgroundMusicR4
に添付の イングランド民謡 Greensleeves には最長 2571msec の音符が現れます。この時間をきっちり計り、かつ CPU リソースの使用を小さく抑えたのがこのライブラリの特徴です。
仕組みの大雑把な話は「自作のタイマ系ライブラリと BGM 再生ライブラリ」の章を見てもらうとして、ここでは使い方とインストール方法、および正確性の確認方法を説明します。
スケッチのサンプル
Arduino UNO R4 の LED をフワッとさせながら Minima は TX、RX を、WiFi は TX を Lチカさせるサンプルです。もちろん、わざわざタイマーを使わなくても Blink Without Delay を応用して、仮想的な2つのタスクで実現できますが、あくまで今回のサンプルということで。

#include "Arduino.h"
#include "CBTimer.h"
void callback_func(void) {
static int n = 0;
#if defined(ARDUINO_MINIMA)
if (n++ % 2) {
digitalWrite(LED_TX, HIGH);
digitalWrite(LED_RX, LOW);
} else {
digitalWrite(LED_TX, LOW);
digitalWrite(LED_RX, HIGH);
}
#elif defined(ARDUINO_UNOWIFIR4)
// P109 Port Output Data
if (n++ % 2) {
R_BSP_PinWrite(BSP_IO_PORT_01_PIN_09, BSP_IO_LEVEL_HIGH);
} else {
R_BSP_PinWrite(BSP_IO_PORT_01_PIN_09, BSP_IO_LEVEL_LOW);
}
#endif
}
void setup() {
// put your setup code here, to run once:
#if defined(ARDUINO_MINIMA)
pinMode(LED_TX, OUTPUT);
pinMode(LED_RX, OUTPUT);
#elif defined(ARDUINO_UNOWIFIR4)
// setup P109 functions as GPIO output pin (PDR:1, PMR:0) for TX LED
R_BSP_PinWrite(BSP_IO_PORT_01_PIN_09, BSP_IO_LEVEL_LOW);
#endif
static CBTimer timer;
timer.begin(350 /* msec cycle */, callback_func);
}
void loop() {
// put your main code here, to run repeatedly:
for (int i = 0; i < 256; i++) {
analogWrite(LED_BUILTIN, i);
delay(2);
}
for (int i = 255; i >= 0; i--) {
analogWrite(LED_BUILTIN, i);
delay(2);
}
delay(100);
}
メンバ関数の説明
コンストラクタ/デストラクタ
new
は使わず、クラス名 CBTimer
用いてインスタンス化するのが Arduino 的かと思います。
CBTimer 変数名;
コンストラクタは特に何もしていません。デストラクタは begin()
で FspTimer クラス
のインスタンスが確保したタイマを解放し、再利用可能な状態にします。
またそのインスタンス自体は CBTimer
クラス内で静的に保持されているため、残念ながら(というか、敢えて)次のように複数を宣言しても1つしか動作しません。
CBTimer 変数名1, 変数名2; // 変数名1と変数名2でFspTimerのインスタンスが共通となってしまう
初期化
bool begin(int period_ms, void (*callback)(void), bool start = true);
-
int period_ms
タイマによるイベントの発火周期をミリ秒単位で指定します。 -
void (*callback)(void)
タイマイベントの発火周期ごとに呼び出すコールバック関数を指定します。 -
bool start
すぐにタイマのカウントを開始する場合はtrue
(デフォルト、省略可能)を指定します。 -
戻り値
初期化に成功し、FspTimer
が確保できればtrue
が、出来なければfalse
を返します。
また次の様に timer_mode
を指定することもできます。
bool begin(timer_mode_t timer_mode, int period_ms, void (*callback)(void), bool start = true);
-
timer_mode_t
タイマイベントを周期的に発火させるには列挙定数のTIMER_MODE_PERIODIC
を、1回限りの場合はTIMER_MODE_ONE_SHOT
を指定します。列挙定数timer_mode_t
は階層の深いところ(UNO R4 Minima、UNO R4 WiFi)で定義されています。
その他のパラメータと戻り値は前項と同じです。
開始、停止、終了
bool start(void);
bool stop(void);
void end(void);
初期化関数 begin()
の start
に false
を指定した場合は start()
でタイマのカウントを開始できます。また stop()
はカウントの停止、end()
は FspTimer クラス
中の GPT もしくは AGT インスタンスを解放し、再利用可能な状態にします。
コールバック関数
タイマイベントの発火と共に呼び出すコールバック関数ですが、「割り込みハンドラの怪」にも書いた通り、少なくとも UNO R4 Minima と UNO R4 WiFi では millis()
も micros()
も使えちゃいます。
void callback_func(void) {
uint32_t time = millis();
...
}
インストール方法


まず Github の CallbackTimerR4 リポジトリ から画面右側にある Releases をたどり、移動先のページから最新版の Source code (zip) をダウンロードします。
続いて Arduino IDE メニューの「スケッチ → ライブラリをインクルード → .ZIP形式のライブラリをインストール…」から、先にダウンロードした .zip ファイルを読み込ませれば、ライブラリフォルダにインストールされます。
Examples の説明
ライブラリのインストール後は、メニューの「ファイル → スケッチ例 → カスタムライブラリのスケッチ例」から例題のスケッチを参照することができます。
例題 dimming_and_blinking
は上記 スケッチのサンプル と同じなので、ここではもう1つの例題 check-timer-accuracy
について説明します。
check-timer-accuracy
タイマイベント発火周期の正確性(精度)をソフトウェアで観測するスケッチです。ライブラリがちゃんと働いているかを確認するために作ったスケッチで、正確性の基準はあくまで millis()
や micros()
ですので、参考程度のものです。
実行時はシリアルモニタを開き、ボーレートを 115200bps に設定してください。
スケッチ中の下記定義で、観測の基準を millis()
か micros()
かを切り替えます。
#if 1
#define TIME_FUNCTION millis // Check in milliseconds
#define TIME_SCALE 1
#else
#define TIME_FUNCTION micros // Check in microseconds
#define TIME_SCALE 1000
#endif
また TIME_PERIOD_MS
でタイマ周期を、TIME_MEASUREMENT
で観測時間を設定します。
#define TIME_PERIOD_MS 10 // 10, 50, 100, 500, 1000, 1500, 2000, 3000, ...[msec]
#define TIME_MEASUREMENT (60000 * TIME_SCALE) // measurement for 1 minute
実行結果はシリアルモニタに出力されます。下の例は、TIME_PERIOD_MS
を 10ms に、TIME_MEASUREMENT
を1分に設定した時の結果で、タイマの開始から user_callback
で観測された 経過時間 と 周期、および 割り込み回数の総計 を出力します。
...
15:12:38.076 -> 59970 (10)
15:12:38.076 -> 59980 (10)
15:12:38.076 -> 59990 (10)
15:12:38.076 -> 60000 (10)
15:12:38.076 -> Number of interrupts = 6000
またこのスケッチ単体では GPT と AGT のうち前者が割り当てられるハズですが、下記のようにライブラリフォルダにある CBTimer.cpp
の#include "CBTimer.h
直前に #deifne CBTIMER_FORCE_AGT
を挿入すれば、強制的に AGT を割り当てて観測することが可能です。
#define CBTIMER_FORCE_AGT
#include "CBTimer.h"
あとがき(という名の言い訳…)
今回作成したライブラリは単に FspTimer クラス
をラップしているに過ぎませんが、長周期のタイマを少ない割り込みのフットプリントで実行させられるのが特徴です(もっとも、数時間おきに何かさせたいならリアルタイムクロック(RTC)を使うべきですが…)
ただしインスタンスは1個限りという制約があります。僕の目的からすればこれで十分というのが理由ですが、通常はタイマ1つで複数のイベントを発火させることができるので、これを使わずして貴重なタイマ資源を消費するプログラムを作るのをためらったというのがもう一つの理由です。
複数のタスクをキックしたいなら「ArduinoでTimerを使わずに割り込み処理をする」で紹介されている方法が、直感的かつシンプルで良いと思います。
むしろ次に紹介予定の BackgroundMusicR4
のように、何かと組み合わせて内部に隠蔽してしまうのが適した使い方でしょう。
ということで、次回はその BackgroundMusicR4
を紹介したいと思います