割り込みハンドラは短く書く
| 設計内容分類 | 組み込みソフトウェアにおけるC言語記述 |
|---|
実現したい仕様・課題
割り込みハンドラ(ISR)の中に重い処理を書くと、その間ほかの割り込みやメイン処理が待たされ、通信データの取りこぼしや制御周期の乱れなど、システム全体の応答性・リアルタイム性を損ないます。割り込みを使いながら、システムの応答性を保ちたいというのが課題です。
設計のポイント
割り込みハンドラの役割は「事象の受け付けと最小限の記録」に限定し、時間のかかる処理はメインループ(またはRTOSのタスク)側で行う、という分担を基本とします。ISR内で行うのは、割り込み要因フラグのクリア、受信データのバッファへの格納、イベントフラグのセットといった短時間で終わる処理のみとし、データの解析・計算・応答の組み立てなどは、ISRが立てたフラグやバッファをメインループが監視して実行します。この「フラグ+メインループ処理」のパターンにより、割り込みの応答時間(レイテンシ)が短く安定し、多重割り込みの設計も単純になります。ISR内で避けるべき記述として、sprintf等の時間のかかる関数、動的メモリ確保(malloc)、終了を待つ通信・ポーリング、長いループがあります。また、リエントラント(再入)を考慮していない関数をISRとメインの双方から呼ぶことも危険です。ISRとメインで共有する変数にはvolatileを付け、複数バイトのデータや複合操作の受け渡しには、割り込み禁止区間による保護か、リングバッファのような割り込み前提の構造を使います。処理時間の見積りとして、最も頻度の高い割り込みの周期内に、ISRの実行時間が十分収まることを設計段階で確認し、余裕がない場合は処理の分割やDMAの活用を検討します。
【コード例】
volatile uint8_t rx_buf[RX_SIZE];
volatile uint16_t rx_wp = 0;
volatile uint8_t rx_event = 0;
void uart_rx_isr(void) { // ISR:格納とフラグのみ
rx_buf[rx_wp++] = UART_DR;
if (rx_wp >= RX_SIZE) rx_wp = 0;
rx_event = 1;
}
void main_loop(void) { // 重い処理はメイン側で
if (rx_event) { rx_event = 0; parse_rx_data(); }
}
マイコンの機能設計の観点から割り込み動作を短くする考え方は「割り込み動作の短時間化で動作レスポンス向上」、多重割り込み時の優先度設計は「優先レベルの設定で、多重割り込み時の正常動作を確保」もあわせてご覧ください。
補足情報
設計のポイント 一覧
組み込みソフトウェアにおけるC言語記述のポイント
- リングバッファで受信データを取りこぼさない
- switch-caseによるステートマシンの実装
- 割り込みハンドラは短く書く
- constを活用する(ROM配置と書き換え防止)
- ビット演算によるレジスタ・フラグ操作
- 固定幅整数型(stdint.h)を使う
- volatile修飾子を正しく使う
- 構造体の宣言にpackedを指定する
- ソースコードのエンディアン変換
- sizeofを使用する
- 特殊文字の記述にエスケープ文字を使用する
- 変数の初期化漏れを防ぐ
- 外部バスの使用
- sprintf関数を使う
- 列挙型(enum)を使う
- 構造体を使う
- 標準関数を使う
- 条件演算子を使う
- インクリメント演算子を使う
- C言語記述の際はアプリ層とドライバ層に分ける



