Rustで組み込みプログラミング for STM32(3) – Lチカ

さぁ、それではLチカをやっていきましょう!

Lチカのソースコード

いきなりですが、ソースコードを示します。低レベルな記述が好きな僕ですが、Rust でそれをやると unsafe な物となってしまうので、今回ばかりは HAL を使います。。。

fn main() {
    let cp = cortex_m::Peripherals::take().unwrap();
    let p = hal::stm32f30x::Peripherals::take().unwrap();

    let mut flash = p.FLASH.constrain();
    let mut rcc = p.RCC.constrain();
    let clocks = rcc.cfgr.freeze(&mut flash.acr);
    // Systickベースでディレイするオブジェクト
    let mut delay = Delay::new(cp.SYST, clocks);

    // PA5 for Output
    let mut gpioa = p.GPIOA.split(&mut rcc.ahb);
    let mut pa0 = gpioa.pa5.into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper);

    loop {
        pa0.set_high();
        delay.delay_ms(500_u16);
        pa0.set_low();
        delay.delay_ms(100_u16);
    }
}

ざっと説明

各ブロックで説明をします。僕自身理解が不十分ですが…

let cp = cortex_m::Peripherals::take().unwrap();
let p = hal::stm32f30x::Peripherals::take().unwrap();

こちらで cortex_m のコア部と STM32F303 用の HAL の設定用インスタンスを生成します。変数はイミュータブルでいいっぽいです。

let mut flash = p.FLASH.constrain();
let mut rcc = p.RCC.constrain();
let clocks = rcc.cfgr.freeze(&mut flash.acr);
// Systickベースでディレイするオブジェクト
let mut delay = Delay::new(cp.SYST, clocks);

ごちゃごちゃやっていますが、このブロックは Systick タイマを用いて Delay をさせることを目的としています。そのために色々と必要なのです。なおここでは動作クロックをデフォの 8MHz としています。

// PA5 for Output
let mut gpioa = p.GPIOA.split(&mut rcc.ahb);
let mut pa0 = gpioa.pa5.into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper);

ここでは GPIOA の情報を取得、gpioa.pa5.into_push_pull_output で PA5 をプッシュプルの出力動作としています。いずれも変更を加えるものですので、ミュータブルな参照が必要となっています。

loop {
    pa0.set_high();
    delay.delay_ms(500_u16);
    pa0.set_low();
    delay.delay_ms(100_u16);
}

こちらがプログラムのメイン部です。動作は見てわかると思いますが、delay_ms の引数渡すべき型は unsigned でなければいけません。よって末尾に _u16 を付けています。付け忘れると「そんな型に対応した関数なんかねぇよ」って思いっきり怒られます。

生成バイナリの書き込み

プロジェクト内の target/thumbv7em-none-eabihf/debug/ に ELF バイナリがあるはず。こちらを OpenOCD やシリアル書き込みによってターゲットに焼きます。
例えば OpenOCD なら次のコマンドとか。

$ echo target/thumbv7em-none-eabihf/debug/blinky | xargs -I {} openocd -f interface/cmsis-dap.cfg -f target/stm32f3x.cfg -c "adapter_khz 5000;program {};reset;exit"

お疲れ様でした。動いた方はおめでとうございます。あとはドキュメント読んでね!!!!!!(丸投げ)

LED ボヤァもやってみたのでソースコード

チカチカするだけでは野暮ったいと思い疑似 PWM のLEDのボヤァを実装した所、見事にハマりましたので呈示します。ご査収ください。なお動画は上手く取れなかったので取ってません。

#![no_std]
extern crate cortex_m;
extern crate stm32f30x_hal as hal;
use hal::prelude::*;

// Systickを用いたディレイ
use hal::delay::Delay;

const I_MAX: u16 = 30;

fn main() {
    let cp = cortex_m::Peripherals::take().unwrap();
    let p = hal::stm32f30x::Peripherals::take().unwrap();

    let mut flash = p.FLASH.constrain();
    let mut rcc = p.RCC.constrain();
    let clocks = rcc.cfgr.freeze(&mut flash.acr);
    // Systickベースでディレイするオブジェクト
    let mut delay = Delay::new(cp.SYST, clocks);

    // PA5 for Output
    let mut gpioa = p.GPIOA.split(&mut rcc.ahb);
    let mut pa0 = gpioa.pa5.into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper);

    // let mut i: u16 = 100;
    let mut i: u16 = 0;
    loop {
        pa0.set_high();
        delay_ms(&mut delay, i);
        pa0.set_low();
        delay_ms(&mut delay, I_MAX - i);
        i += 1;
        i %= I_MAX;
    }
}

// For avoiding lock up on ms == 0.
fn delay_ms(delay: &mut hal::delay::Delay, ms: u16) {
    if ms != 0_u16 {
        (*delay).delay_ms(ms);
    }
} 

何を変えたか

delay_ms なんですが、どうやら引数に0を渡すとロックアップするっぽいです。ですのでコード下部の関数を作って workaroud としました。これを言いたかったがためにLEDボヤァのコードを載せました。以上。

まだ割り込みが出来ていないのでやりたい。