5-2. no_stdクレート
no_stdや組込みで利用可能なクレートを紹介します。Rustのクレートが登録されているcrates.io
には、crates.io No standard libraryカテゴリがあります。2019/5/16現在、840ものクレートが登録されています。
組込みに関連するものは、awesome-embedded-rustに主要なものがまとめられています。
rt (runtime) クレート
rtクレートは、ターゲットアーキテクチャ用の最小限のスタートアップ / ランタイムを提供するクレートです。cortex-m-rt、msp430-rt、riscv-rtの3つのターゲットアーキテクチャに対して実装が存在しています。
これらのクレートは、以下の機能を提供します。
.bss
と.data
セクションの初期化- FPUの初期化
- プログラムのエントリポイントを指定するための
#[entry]
アトリビュート static
変数が初期化される前に呼ばれるコードを指定するための#[pre_init]
アトリビュート- 一般的なターゲットアーキテクチャ用のリンカスクリプト
- ヒープ領域の開始アドレスを表す
_sheap
シンボル
このクレートを使用することで、次のようにアプリケーションのmainコードからプログラムを記述することができます。
#![no_std]
#![no_main]
extern crate panic_halt;
use cortex_m_rt::entry;
// `main`をこのアプリケーションのエントリポイントであるかのように利用できます。
// `main`は戻れません
#[entry]
fn main() -> ! {
// ここに処理を書きます
loop { }
}
Embedonomiconは、このようなrt
クレートの実装方法を解説しています。
embedded HAL
embedded HALは、組込みRustで共通して利用できるtrait
を定義しているクレートです。例えば、SPIやシリアル、といったトレイトが定義されています。
このクレートの抽象を利用して、デバイスドライバを書くことで、アプリケーションの再利用性が向上します。組込みRustの多くのプロジェクトが、このembedded HALを利用しています。
その他
lazy_static
lazy_staticは、実行時にしか初期化できない (new()
関数でのみオブジェクトが構築できる) ような、複雑なオブジェクトのstatic
変数を作るために使います。通常、new()
関数でオブジェクトを作るような構造体は、コンパイル時に値が計算できないため、static
変数の初期化には使えません。また、lazy_static
は、static
変数を1度だけ初期化する機能も提供します。lazy_static
マクロで作られたstatic
変数は、その変数が実行時に最初に使用される時に、初期化されます。
例えば、Writing an OS in RustのVGA Text mode Lazy Staticsでは、VGAにテキストを描画するグローバルインタフェースWRITER
の実装で使用しています。
#![allow(unused)] fn main() { use lazy_static::lazy_static; use spin::Mutex; lazy_static! { pub static ref WRITER: Mutex<Writer> = Mutex::new(Writer { column_position: 0, color_code: ColorCode::new(Color::Yellow, Color::Black), buffer: unsafe { &mut *(0xb8000 as *mut Buffer) }, }); } }
Mutex<Writer>
という初期化が非常に複雑なオブジェクトの参照がstatic
変数になっていることがわかります。このように実行時にしか構築できない値もstatic
変数にできる上、どこで初期化するかに悩まなくて済みます。
bitflags
bitflagsは、型安全なビットマスクフラグを提供するクレートです。型安全であることがポイントで、誤ったビット操作を起こしにくいです。AndやOrのオペレータも実装されており、bits()
メソッドで生の値を取り出すことができます。
#[macro_use] extern crate bitflags; bitflags! { struct Flags: u32 { const A = 0b00000001; const B = 0b00000010; const C = 0b00000100; const ABC = Self::A.bits | Self::B.bits | Self::C.bits; } } fn main() { let e1 = Flags::A | Flags::C; let e2 = Flags::B | Flags::C; assert_eq!((e1 | e2), Flags::ABC); // union assert_eq!((e1 & e2), Flags::C); // intersection assert_eq!((e1 - e2), Flags::A); // set difference assert_eq!(!e2, Flags::A); // set complement assert_eq!(e1.bits(), 5u32); // get raw value }
bit_field
bit_fieldは、ビットフィールドへのアクセスを簡単にするためのクレートです。BitField
トレイトを提供しており、i8
, u8
, usize
などの整数型が、トレイトを実装しています。
ビットフィールドへのアクセスは、次のように書けます。
let mut value: u32 = 0b110101;
assert_eq!(value.get_bit(1), false);
assert_eq!(value.get_bit(2), true);
assert_eq!(value.get_bits(2..6), 0b1101);
value.set_bit(2, true);
assert_eq!(value, 0b110111);
value.set_bits(0..2, 0b00);
assert_eq!(value, 0b110100);
bitfield
bitfieldは、ビットフィールドを定義するマクロを提供するクレートです。bit_field
とクレート名が似ていますが、別物です。
下のように、ビットフィールドを定義します。
bitfield! {
#[derive(Clone, Copy, Debug)]
pub struct PinSelect(u32);
pub connected, set_connected: 31;
reserved, _: 30, 6;
pub port, set_port: 5;
pub pin, set_pin: 4, 0;
};
fn main() {
let mut reg = PinSelect(0);
reg.set_pin(5);
reg.set_port(0);
reg.set_connected(1);
assert_eq!(0x1000_0005, reg.all_bits());
}
micromath
micromathは、軽量な数値計算ライブラリです。三角関数などがあります。加速度計など、センサドライバでの計算に利用できます。
register-rs
register-rsは、Rust製のRTOSであるTock
で利用されているMMIO / CPUレジスタインタフェースです。読み書き可能、読み込み専用、書き込み専用、を表現するジェネリック構造体を提供します。
volatile_register
volatile_registerは、メモリマップドレジスタへのvolatileアクセスを提供します。register-rsの簡易版、と言った印象です。
use volatile_register::RW;
// メモリマップドレジスタブロックを表現するstructを作ります
/// Nested Vector Interrupt Controller
#[repr(C)]
pub struct Nvic {
/// Interrupt Set-Enable
pub iser: [RW<u32>; 8],
reserved0: [u32; 24],
/// Interrupt Clear-Enable
pub icer: [RW<u32>; 8],
reserved1: [u32; 24],
// .. more registers ..
}
// ベースアドレスをキャストしてアクセスします
let nvic = 0xE000_E100 as *const Nvic;
// unsafeブロックが必要です
unsafe { (*nvic).iser[0].write(1) }
embedded-graphics
embedded-graphicsは、2Dグラフィックを簡単に描画するためのクレートです。次の機能を提供します。
- 1ビット / ピクセルの画像
- 8ビット / ピクセルの画像
- 16ビット / ピクセル画像
- プリミティブ
- 行、四角、丸、三角
- テキスト
このクレートは、メモリアロケータも事前の巨大なメモリ領域確保も必要としません。