割り込み
割り込みはさまざまな点で例外とは異なりますが、その動作と使用方法はおおむね似ており、同じ割り込みコントローラによって処理されます。例外が Cortex-M アーキテクチャによって定義されるのに対し、割り込みは名称と機能の両面で、常にベンダー(多くの場合はチップでさえも)固有の実装です。
割り込みは大きな柔軟性を持つため、高度な方法で使用しようとする場合にはその点を考慮する必要があります。本書ではそうした使い方は扱いませんが、次の点を覚えておくとよいでしょう。
- 割り込みにはプログラム可能な優先度があり、それによって各ハンドラの実行順序が決まります
- 割り込みはネストおよびプリエンプトできるため、ある割り込みハンドラの実行中に、より高い優先度の別の割り込みによって中断されることがあります
- 一般に、割り込みを発生させた原因はクリアする必要があり、そうしないと割り込みハンドラに際限なく再突入してしまいます
実行時における一般的な初期化手順は、常に次のとおりです。
- 望んだタイミングで割り込み要求を生成するようにペリフェラルを設定する
- 割り込みコントローラで割り込みハンドラの望ましい優先度を設定する
- 割り込みコントローラで割り込みハンドラを有効にする
例外と同様に、cortex-m-rt クレートは割り込みハンドラを宣言するための interrupt 属性を公開しています。ただし、この属性は device フィーチャが有効な場合にのみ利用できます。とはいえ、この属性を直接使用することは意図されておらず、そのようにするとコンパイルエラーになります。
代わりに、device クレート(通常は svd2rust を使って生成されます)が提供する interrupt 属性の再エクスポート版を使用するべきです。これにより、コンパイラはその割り込みが対象デバイス上に実際に存在することを検証できます。利用可能な割り込みの一覧と、それらが割り込みベクタテーブル内で占める位置は、通常、svd2rust によって SVD ファイルから自動生成されます。
use lm3s6965::interrupt; // device クレートから再エクスポートされた属性
// Timer2 割り込みのハンドラ
#[interrupt]
fn TIMER2A() {
// ..
// 生成された割り込み要求の原因をクリアする
}
割り込みハンドラは、例外ハンドラと同様、見た目は通常の関数に似ています(引数がない点を除いて)。ただし、特別な呼び出し規約があるため、ファームウェアのほかの部分から直接呼び出すことはできません。ただし、ソフトウェアで割り込み要求を生成し、制御を割り込みハンドラへ移すことは可能です。
例外ハンドラと同様に、割り込みハンドラの内部で static mut 変数を宣言して、安全に 状態を保持することもできます。
#[interrupt]
fn TIMER2A() {
static mut COUNT: u32 = 0;
// `COUNT` は `&mut u32` 型であり、安全に使用できる
*COUNT += 1;
}
ここで示した仕組みについてより詳しい説明は、exceptions section を参照してください。