初心者が組み込みRustやってみる - その4
初心者が組み込みRustやってみる - その3 - 雨垂れ石を穿つ
のつづき。
8. LED、再び
tomoyuki-nakabayashi.github.io
これまでは初期設定済みのペリフェラルが提供されていたが、今度は自分で初期設定する。
ほとんどのペリフェラルは、電源が入っていない状態で起動する。そのため、RCC (Reset and Clock Control) を使ってペリフェラルの電源をオンにしないといけないらしい。
さらに、GPIOは電源を入れた後の初期状態だとデジタル入力として設定されている。デジタル出力として使うよ、の設定をしないといけない。
マニュアルやrustdocと格闘して、なんとか解答例を見ずに自力でLEDを点灯させることができた。
今は「マニュアルのここに書いてあるよ」が与えられているが、それも自分で探すのは相当大変そう。。。
でも苦労の末、思った通りに動くと楽しい。
9. クロックと時間
tomoyuki-nakabayashi.github.io
今まで与えられていたdelay関数を自分で実装する。
9.1. forループで遅延
- forループによる遅延は、マイクロコントローラ内のプロセッサのクロック数に依存する。
- 空のfor文はコンパイラによる最適化で削除されるため、releaseモードで実行すると遅延しなくなってしまう。
9.2. NOP
- 最適化によってdelay関数が壊れないようにするには、
cortex_m
クレートの cortex_m::asm::nop を呼び出す。
9.3. ワンショットタイマ
マニュアルを読みつつ、分からないところは手順に従いつつ、最終的なコードは以下。
#![no_main] #![no_std] use aux9::{entry, tim6}; #[inline(never)] fn delay(tim6: &tim6::RegisterBlock, ms: u16) { // ms tick後にオフになるようにタイマを設定 tim6.arr.write(|w| w.arr().bits(ms)); // タイマを有効化 tim6.cr1.write(|w| w.cen().set_bit()); // 更新イベントが発生するまで待つ while !tim6.sr.read().uif().bit_is_set() {} // 更新イベントフラグをクリアする tim6.sr.modify(|_, w| w.uif().clear_bit()); } #[entry] fn main() -> ! { let (mut leds, rcc, tim6) = aux9::init(); // TIM6の電源を入れる rcc.apb1enr.modify(|_, w| w.tim6en().set_bit()); // OPM (One Pulse Mode) に設定する tim6.cr1.write(|w| w.opm().set_bit().cen().clear_bit()); // カウンタが1KHzで動作するようにプリスケーラを設定する let psc = 7999; tim6.psc.write(|w| w.psc().bits(psc)); let ms = 50; loop { for curr in 0..8 { let next = (curr + 1) % 8; leds[next].on(); delay(tim6, ms); leds[curr].off(); delay(tim6, ms); } } }