比較的長い時間の制御をおこないます。タイマ割り込みできっかけを作り処理自体は Loop()などのフォアグラウンド処理で行います。Peripheral MCU のタイマ割り込みは周辺機器に関係する処理で使用します。
◀ この記事の前に: タイマ割り込み
▶ この記事の次に:
定時タイマ割り込みのしくみ
組み込みシステムは外部と直接関係を持つため、外部の状況を早く捉えたり、周辺回路が要求するタイミングに合わせたりする必要があります。それを実現するために時間(タイミング)を重視します。その時間を正確に管理するための機能がタイマです。
MCU には多くの場合、タイマを搭載しています。MCU内部の CPU はプログラムを逐次実行しています。プログラムの実行中に必要な時刻で処理を行う必要があります。そのときに使用する機能がタイマ割り込みです。タイマ割り込みはあらかじめ決めておいた時刻になると CPU に信号を発します。CPU はその信号を受けると逐次実行していたプログラムを一旦停止して、優先的にその信号に合わせた処理を実行します。その処理がタイマ割り込み処理です。処理が終了すると元々実行していた処理を再開します。
タイマの実際の動作は、カウンタにあらかじめ設定した値から一定の時間毎に1づつ減じて( カウントダウン )0になったときに割り込みを発生する場合と、カウンタに0を設定して一定の時間毎に1づつ増して( カウントアップ )あらかじめ設定した値になった場合に割り込みを発生する場合があります。この「一定の時間」をタイマのクロックと呼びます。クロックの周波数でタイマの分解能が決まります。クロックは MCU 内の各種の信号から選択します。
インターバルの生成
インターバルの生成は定時的にタイマで割り込みを発生する部分と、その割り込みをカウントして必要とするインターバルを作る部分で構成します。タイマのクロックは多くの供給源から選択することができます。ここではシステムクロックにしました。プリスケーラはタイマの前段で分周を行いクロック周波数を変更する機能です。プリスケーラの値を変更することによりタイマの分解能を変更することができます。
タイマで割り込みを発生する部分は、 100 ms の定時割り込みを発生します。その割り込みをカウントして必要とするインターバルを作る部分は、複数あるフォアグラウンド処理が、それぞれの処理で必要なインターバルを生成しています。前者が基準となりますので、後者の処理分解能は 100 ms となります。
汎用タイマに関する設定方法は、ESP32 Arduino Core のバージョンにより異なります。ここでは 3.xを使用しています。1.x, 2.x を使用した場合はコードの説明( 後述 )で示します。
データの構造
データの構造は以下のとおりです。
グローバル定義
グローバル定義は以下のとおりです。個々のモジュールや関数に関わるグローバル定数は、それぞれの機能の説明に記載しています。
名 称 | 型 | 値 | 説 明 |
---|---|---|---|
TI_CLOCK | int | 10,000 | タイマのクロック 10kHz( 0.01 ms ) |
TI_INIT_INTERVAL | int | 10,000 | タイマのオーバーフロー値 100 ms = 0.01 × 10,000 |
グローバル変数
名 称 | 型 | 初期値 | 説 明 |
---|---|---|---|
gInterval | volatile bool * | false | 汎用タイマの割り込み発生時のフラグ |
*:割り込みに関する変数は volatile を指定する場合があります。( 以下参照 )
Technical Considerations volatile 修飾子
割り込みは変数の値が意図しないタイミングで変化する可能性があります。コンパイラは最適化を行う際に変数の値が変化しないと判断すると、不要なメモリアクセスを削除したり、レジスタに値をキャッシュしたりすることにより、本来の変数でない場所へのアクセスを行う可能性が発生します。
volatile 修飾子が適用された変数は、対象とする変数へのアクセス(読み取りや書き込み)は毎回メモリから直接行われます。割り込みは不意に発生しますので、変数が不規則に変更される可能性があることをコンパイラに伝えます。そのため、アクセス(読み取りや書き込み)は毎回メモリから直接行われ、常に正しく変数にアクセスできます。
関数の概要
関数は以下のとおりです。
内部関数
外部から直接アクセスできずモジュール( 特定のスコープ内 )で使用する関数です。
コード
コードは以下のとおりです。
オブジェクト
ハードウエアタイマを使用します。ESP32 は4本の汎用タイマを搭載しています。
//---------------------------------------------------------------------
// object
//---------------------------------------------------------------------
//------------------------------
// Timer
//------------------------------
hw_timer_t* TItimer = NULL;
内部関数
外部から直接アクセスできずモジュール( 特定のスコープ内 )で使用する関数です。
setupTMTIInt()
分 類:
初期化( setup() )
機 能:
汎用タイマを初期化する。
関係する関数:
上位関数: setup()
/*----------------------------------------------------------------------------*/
/* Initialize Timer
/* param : -
/* return : -
/*----------------------------------------------------------------------------*/
void setupTCInt()
{
// ボードマネージャ: esp32 by Espressif Systems ver.3.2.0
TItimer = timerBegin( TI_CLOCK );
timerAttachInterrupt( TItimer, &onTimer );
timerAlarm( TItimer, TI_INIT_INTERVAL, true, 0 );
// ボードマネージャ: esp32 by Espressif Systems ver.2.0.17
// TItimer = timerBegin( 0, 80, true );
// timerAttachInterrupt( TItimer, &onTimer, true );
// timerAlarmWrite( TItimer, 1000000, true );
}
ESP32 Arduino Core バージョンにより異なります。
ver. 1.x または ver.2.x はタイマの選択とプリスケーラの設定で初期化します。
ver. 3.x はクロック周波数の設定で初期化します。

timerAlarmWrite() で指定する値は、ver. 3.x では割り込み周期としては機能しない場合があり、内部で 周波数に基づいて自動調整されるため、esp_timer(マイクロ秒単位で動作)を使った方が柔軟性があります。
タイマはペリフェラル周波数を使用します。これは CPU周波数と異なります。CPU周波数が 80MHz以上のとき( 120MHz、240MHz )のペリフェラル周波数は 80MHzになります。
onTimer()
分 類:
割り込み
機 能:
フラグを介してフォアグラウンド処理に伝える。( 割り込み内の処理を低減しています。 )
関係する関数:
ありません。
//==============================================================================
// Interrupt
//==============================================================================
/*----------------------------------------------------------------------------*/
// Timer interrupt / Interval
// Timer interrupt setting for main loop
// param : -
// return : void
/*----------------------------------------------------------------------------*/
void IRAM_ATTR onTimer()
{
gInterval = true;
}
Technical Considerations IRAM_ATTR とは
IRAM_ATTR は関数を高速な IRAM上に読み込ませる指示です。このオプションが無い場合、フラッシュ上に配置される可能性があります。その場合、低速動作になり問題を起こすことがあります。
ESP32の SRAMは、IRAM( Instruction RAM )と DRAM( Data RAM )に分かれています。IRAM は命令領域、DRAM はデータ領域に使われます。 IRAM は CPUのキャッシュに固定的に割り当てられます。DRAM はアプリケーションのデータに動的メモリ割り当てが行われます。
loop()内
分 類:
フォアグラウンド処理( メインループ )
機 能:
タイミング(時間)に関係する各フォアグラウンド処理を実行する。
関係する関数:
各フォアグラウンドでタイミング(時間)に関係する処理
例) procTOF(): 測距センサ
//=====================================================================
// Main loop
//=====================================================================
void loop() {
//.... local ....
int nDistance;
//-----------------------------------------------------
// Timer interval 100 ms
//-----------------------------------------------------
if( gInterval == true ){
gInterval = false;
//.... UTOF ....
procTOF();
//●● debug
nDistance = getDistanceTOF();
printDisp( String( nDistance ), 2, 1, 10, 1, 0xFFFFF, 0x000000 );
}
}
参考情報
参考になる情報は以下のとおりです。
このホームページ内
- 複数のMCUをつなぐ: このロボットには複数の MCUが搭載されています。その MCU間の接続の概要です。
- タイマ割り込み( ソフトウエア / Body MCU )
- 定時タイマ割り込み( ソフトウエア / Body MCU )
他のWebサイト
- General Purpose Timer ESP32のマニュアル / ESP-IDF Programming Guide
- ESP32の高精度タイマー割り込みを調べる タイマの概要をわかりやすく説明 / Lang-ship
- ESP32のタイマー割り込みの精度、応答時間 精度に関する検証など / ラジオペンチ
- ESP32のメモリレイアウトの理解と最適化 メモリに関する詳説 / Zenn パラドイア