Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

bind_interrupts マクロを展開する

先ほど使った bind_interrupts! マクロに戻りましょう。

#![allow(unused)]
fn main() {
bind_interrupts!(struct Irqs {
    TEMP => temp::InterruptHandler;
});
}

一見すると、この構文は少し奇妙に見えるかもしれません。特に struct の部分です。まるで struct をインスタンス化しようとしているように見えますが、そうではありません。これは単なるマクロへの入力です。マクロは内部で Irqs という名前のユニット struct を作成し、それに必要な割り込みバインディング用の trait を実装します。

ユニット struct はフィールドを持たない空の struct で、最も一般的にはマーカーとして使われます。また、そのサイズは 0 バイトです。

気になる場合は、cargo expand を使って生成されるコードを確認できます。おおよそ次のようになります。

#![allow(unused)]
fn main() {
use embassy_nrf::interrupt::typelevel;

struct Irqs;

// これは無視します: ... impl Copy and Clone for Irqs

#[allow(non_snake_case)]
#[no_mangle]
unsafe extern "C" fn TEMP() {
    <temp::InterruptHandler as typelevel::Handler<typelevel::TEMP,>>::on_interrupt();
}

unsafe impl typelevel::Binding<typelevel::TEMP, temp::InterruptHandler,> for Irqs {
}
}

ここでは、Irqs という名前のユニット struct が作られています。また、TEMP() という関数も作られます。これは、TEMP 割り込みが発生したときにハードウェアが実際に呼び出す割り込みハンドラです。その関数の中で、temp::InterruptHandleron_interrupt() を呼び出しています。その後、Irqs struct に対して Binding trait を実装しています。これによって Embassy に、TEMP 割り込みが私たちのハンドラに接続されていることを伝えます。

マーカー

Irqs struct の中には関数を何も定義していないのに、なぜその Irqs struct に対して Binding trait を実装するのか疑問に思うなら、その理由を理解するために Temp::new 関数を見る必要があります。

#![allow(unused)]
fn main() {
// let mut temp = Temp::new(p.TEMP, Irqs);

pub fn new(
    _peri: impl Peripheral<P = TEMP> + 'd,
    _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::TEMP, InterruptHandler> + 'd,
) -> Self {
    into_ref!(_peri);

    // 温度値を通知する割り込みを有効化する
    interrupt::TEMP.unpend();
    unsafe { interrupt::TEMP.enable() };

    Self { _peri }
}
}

ちょっと待ってください。2 番目の引数 _irq はまったく使われていません。では、これは何のためにあるのでしょうか。

直接使われていなくても、その型は重要です。Embassy はこれによって、TEMP 割り込み用のハンドラを設定済みかどうかを確認しています。_irq の型は、TEMP に対する Binding trait を実装していなければなりません。そうでなければ、コードはコンパイルできません。

たとえば、次のように書いたとします。

#![allow(unused)]
fn main() {
bind_interrupts!(struct Irqs {
    TWISPI0 => twis::InterruptHandler<peripherals::TWISPI0>;
});
}

マクロは TWISPI0 割り込みに対する Binding trait を、次のように生成します。

#![allow(unused)]
fn main() {
// マーカーを理解するために Binding trait に注目できるよう、これはコメントアウトしています
// #[allow(non_snake_case)]
// #[no_mangle]
// unsafe extern "C" fn TWISPI0() {
//     <twis::InterruptHandler<
//         peripherals::TWISPI0,
//     > as typelevel::Handler<
//         typelevel::TWISPI0,
//     >>::on_interrupt();
// }

unsafe impl typelevel::Binding<typelevel::TWISPI0,twis::InterruptHandler<peripherals::TWISPI0>,> for Irqs {

}
}

しかし、これは Temp::new が期待しているものとは一致しません。Temp::new が期待しているのは、TEMP 割り込みに対する Binding です。したがって、コンパイラはエラーを出します。