RustのRTFM (Real Time For the Masses)を試してみる④
はじめに
組込みRust界の伝説japaric
氏が実装しているReal Time For the Masses (RTFM) framework for ARM Cortex-M microcontrollersを試してみます。
面白いので、引き続き触っていきます。
リソース管理続き
RTFMのstatic
変数は、通常の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 resourcesのP
とC
とのタプルです。
#[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(); } };