chakokuのブログ(rev4)

テック・コミック・ごくまれにチャリ

STM32/RustでHALを使わずレジスタを直接操作してLチカ

組み込みRustでは、ハードウエア抽象化したライブラリHALを使ってプログラミングするのが正統派なんでしょうが、自分の場合、Rustの正しい所有権の書き方がイマイチわかっておらず、どうやってもペリフェラル操作をmainからモジュール側に出すことができなかった。そんなこんなで、、まずはペリフェラルを操作できないとプログラミングが進まないので、HALを使わず自分でレジスタ操作することにした。勉強が進んだらまた、HALライブラリに置き換えるとして・・
先人のサンプルを参考に、NucleoのL476RGのボードの緑のLEDを点滅させるソースは以下。WAITもタイマを使わずループで待たせている。
GPIOを設定しただけで、本当はクロックとかも正しい値を書きこまないといけないのだろうけど、、そこまでまだ読めてなくて、今はGPIOのGPIOA PA5の出力をL/Hさせているだけ。 だけなんだが、、まぁ泥臭くレジスタを操作しまくればペリフェラルが制御できるので、このやり方を続ければUARTとI2Cも制御できるはず。こんな実装をしてたらRustの恩恵をまったく受けられないのだが。。

pub const GPIOA_BASE: u32 = 0x4800_0000;   //APB2PERIPH_BASE + 0x0800;
pub const MODER_OFFSET: u32 = 0x0;  // GPIOA
pub const BSRR_OFFSET: u32 = 0x18;

pub const RCC_BASE: u32 = 0x4002_1000;
pub const AHB2ENR_OFFSET : u32 = 0x4C;   // RCC_AHB2ENR
pub const GPIO_PIN_5: u32 = 5;


pub fn setup(){

  let reg    = (RCC_BASE + AHB2ENR_OFFSET)  as *mut u32;
  unsafe { core::ptr::write_volatile(reg, 1) };

  let addr = (GPIOA_BASE + MODER_OFFSET)  as *mut u32;
  let set_mode5  = 0x01 << (GPIO_PIN_5 * 2); // 0x01 ... GPIO OUTPUT MODE
  unsafe { 
	let current = core::ptr::read_volatile(addr) ;
        let mut val = current & 0xFFFFF3FF;
        val |= set_mode5;
	core::ptr::write_volatile(addr, val) ;
  }
  let bsrr    = (GPIOA_BASE + BSRR_OFFSET)  as *mut u32;
  loop {
     unsafe { core::ptr::write_volatile(bsrr, 1 << GPIO_PIN_5) };   //on
     let _ = my_wait(0x10);
     unsafe { core::ptr::write_volatile(bsrr, (1 << GPIO_PIN_5) << 16)}; //off
     let _ = my_wait(0x10);
   }
}

pub fn my_wait(count: u64) -> u128{

   let mut zzz:u128 = 0x0;
   for _i in 0..count {
       for _j in 0..0xff {
             zzz +=1;
       }     
   }
   zzz   
}