RustのRTFM (Real Time For the Masses)を試してみる④

はじめに

組込みRust界の伝説japaric氏が実装しているReal Time For the Masses (RTFM) framework for ARM Cortex-M microcontrollersを試してみます。

github.com

面白いので、引き続き触っていきます。

リソース管理続き

RTFMstatic変数は、通常のstatic変数と異なり、実行時に初期化できます。 このように実行時に初期化するリソースをlate resourcesと呼んでいます。 補足すると、lazy_staticとは、また異なる仕組みのようです。Optionで包んで云々、ということはやっていません。 リソースの初期化は、init関数で行う必要があり、init関数は、初期化したリソースを戻り値にしなければなりません。

何を言っているのかわからないと思うので、サンプルコードを見てみましょう。

use heapless::{
    consts::*,
    spsc::{Consumer, Producer, Queue},
};

#[app(device = lm3s6965)]
const APP: () = {
    // Late resources `()`を初期値にします
    static mut P: Producer<'static, u32, U4> = ();
    static mut C: Consumer<'static, u32, U4> = ();

    #[init]
    fn init() -> init::LateResources {
        // NOTE: we use `Option` here to work around the lack of
        // a stable `const` constructor
        static mut Q: Option<Queue<u32, U4>> = None;

        *Q = Some(Queue::new());
        let (p, c) = Q.as_mut().unwrap().split();

        // late resourcesを初期化して、関数の戻り値とします
        init::LateResources { P: p, C: c }
    }

    #[idle(resources = [C])]
    fn idle() -> ! {
        loop {
            if let Some(byte) = resources.C.dequeue() {
                hprintln!("received message: {}", byte).unwrap();

                debug::exit(debug::EXIT_SUCCESS);
            } else {
                rtfm::pend(Interrupt::UART0);
            }
        }
    }

    #[interrupt(resources = [P])]
    fn UART0() {
        resources.P.enqueue(42).unwrap();
    }
};

まず、()で初期化されたlate resourcesを宣言します。

    static mut P: Producer<'static, u32, U4> = ();
    static mut C: Consumer<'static, u32, U4> = ();

次に、init関数のシグネチャが、init::LateResourcesを返すものに変わります。 このinit::LateResourcesは、late resourcesPCとのタプルです。

    #[init]
    fn init() -> init::LateResources {

idleはC、UART0はPを、それぞれ専有して利用するため、ロックなしでリソースを利用することができます

このサンプルでは、`heaphessクレートのqueueが使われています。 このheaplessクレートは、文字通り、ヒープがない環境でコレクションを使うためのクレートです。 queueは、single producer / single consumerのモデルになっています。 このqueueの初期化をinitで行い、ProducerとConsumerのオブジェクトを共有リソースとして管理することで、うまいこと安全な機構になっているように見えます。

リソースがただのstaticで、可変参照にならない場合も、ロックなしでリソースにアクセスできます。 これは、late resourcesにも適用されます。

#[app(device = lm3s6965)]
const APP: () = {
    static KEY: u32 = ();

    #[init]
    fn init() -> init::LateResources {
        rtfm::pend(Interrupt::UART0);
        rtfm::pend(Interrupt::UART1);

        init::LateResources { KEY: 0xdeadbeef }
    }

    #[interrupt(resources = [KEY])]
    fn UART0() {
        hprintln!("UART0(KEY = {:#x})", resources.KEY).unwrap();

        debug::exit(debug::EXIT_SUCCESS);
    }

    #[interrupt(priority = 2, resources = [KEY])]
    fn UART1() {
        hprintln!("UART1(KEY = {:#x})", resources.KEY).unwrap();
    }
};