STM32 Rust ベアメタルにウォッチドッグタイマーを使ってみる

  • 2022.05.21
  • WDT
STM32 Rust ベアメタルにウォッチドッグタイマーを使ってみる

今回はウォッチドッグタイマーを使ってみます。
ベアメタルと言われているレジスタにアクセスする方法を使います。

開発環境は以下の通りです。

PC:Windows10 OS
Board:STM32Nucleo-F401RE
デバイス:STM32F401RE
エディタ:VSCode
言語:Rust

ボードの情報は こちら からご覧いただけます。
環境構築については こちら をご覧になってください。

ウォッチドッグタイマーとは

番犬タイマーと呼べば良いでしょうか、WDTとかWDGなどと省略して呼ばれています。
カウンタを監視していて、オーバーフローするとマイコンにリセットがかかるというものです。
WDTは例えばプログラムが暴走して動かなくなった場合にリセットをかけて正常に動作させる役割を持ちます。
通常はリセットがかからないようにカウンタをリフレッシュしてあげます。

ひと昔前までは、電源電圧の低下を監視する機能なども含んだ専用のICが主流でしたが今ではマイコンに内蔵しているものが多くなっています。
コスト的にも、こちらを使わない手はありません。

STM32では2種類のウォッチドッグタイマーが存在します。

IWDG:
独立型ウォッチドッグタイマー

先頭の I は independence の略ですね。
どちらかと言えば、こちらが一般的なのではないかと思います。
IWDGはカウンタを動かしてオーバーフローした時にリセットをかけるものです。
独立型と言われるのは、タイマーを動かすクロックがマイコンのメインクロックとは別にあるからです。

今回は独立型ウォッチドッグタイマーの挙動について確認してみます。

プロジェクトをつくる

コマンドプロンプトを起動し、cargo generate と git を使ってSTM32F401用のプロジェクトを作成します。

cargo generate –git https://github.com/rust-embedded/cortex-m-quickstart.git と入力しプロジェクト名を聞いてくるので stm32f401-spi と入力します。

cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart.git
 Unable to load config file: C:\Users\xxxxx\.cargo\cargo-general.toml
 Project Name : stm32f401-wdt

C:\Users\xxxxx\stm32f401-wdt というフォルダができていることを確認します。

VSCodeを起動する

以下のようにコマンドを入力することで、stm32f401-wdtをカレントディレクトリとしてエディタ(VSCode)が起動します。

cd stm32f401-wdt
code .

C:\Users\xxxxx>cd stm32f401-wdt
C:\Users\xxxxx\stm32f401-wdt>code .

config.tomlの編集

gdbの指定と Cortex-M4F をターゲットに指定します。

ツリーの >.cargo の >部分をクリックすると V .cargo となり、そのツリー下に config.toml というファイルが見えるのでこれをクリックして開きます。

(1)こちらの環境では 8行目に # runner = “arm-none-eabi-gdb -q -x openocd.gdb” があるので、先頭の # をはずします。
(2)こちらの環境では35行目に target = “thumbv7m-none-eabi” # Cortex-M3 があるので、先頭に # をつけます。
前後の行に合わせて # target = … のように記述しておきます。
(3)こちらの環境では37行目に # target = “thumbv7me-none-eabihf” # Cortex-M4F and Cortex-M7F (with FPU) があるので、先頭の # をはずします。

# のある行はコメント(無効)になります。

編集が終わったら Ctrl + s で保存しておきます。

launch.jsonの編集

ここではデバイスの指定を行います。
ツリーの >.vscode の >部分をクリックすると V .vscode となり、そのツリー下に launch.json というファイルが見えるのでこれをクリックして開きます。

“configurations”: の中に {},{} で2つの項目が区切られています。
上の方は “name” が “Debug (QEMU)” となっていて、下の “name” が “Debug (OpenOCD)” です。
QEMUはエミュレーターで、今回は OpenOCD による Debug を行いますので、下の {} 内だけを編集します。

(1)こちらの環境では35行目に #device”: “STM32F303VCT6” とあるので “STM32F401RET6” に変更します。
(2)こちらの環境では38行目に “target/stm32f3x.cfg” とあるので “target/stm32f4x.cfg” に変更します。
(3)こちらの環境では40行目に “svdFile”: “${workspaceRoot}/.vscode/STM32F303.svd” とあるので最後の 303 を 401 に変更します。

編集が終わったら Ctrl + s で保存しておきます。

Cargo.tomlの編集

ツリーの下の方に Cargo.toml があります。

今回はhalを使わずに stm32f4 というクレートを使いますので、それを指定しておきます。
クレートとはライブラリのようなものです。

23~25行を以下のように編集します。
dependencies は依存関係の意味で、こういうクレートの、このバージョンのものを使いますよ とRustに教えてあげます。

今回は HAL を使わないので stm32f4クレートを指定します。

[dependencies.stm32f4]
features = ["stm32f401", "rt"]
version = "0.14"

(注意)今回の追加

8行目に cortex-m = “0.6.0” があるので Delayを使うために “0.7” にバージョンを上げておきます。

[dependencies]の部分を以下のように書き換えます。

[dependencies]
cortex-m = "0.7"
cortex-m-rt = "0.7"
panic-halt = "0.2.0"

編集が終わったら Ctrl + s で保存しておきます。

クレートは cretes.io から探すことができます。

memory.xの編集

ツリーの下の方に memory.x があります。
デバイスによってFlashメモリーとRAMの容量が異なるので、ここで指定します。

6, 7行目を以下のように編集します。

  FLASH : ORIGIN = 0x08000000, LENGTH = 512K
  RAM : ORIGIN = 0x20000000, LENGTH = 96K

編集が終わったら Ctrl + s で保存しておきます。

svdファイル

svdファイルはデバッグに必要なファイルです。

svdファイルは こちら からダウンロードできます。

解凍して、data\STMicroの中にある STM32F401.svd を C:\Users\xxxxx\stm32f401-spi\.vscode に保存します。

ソースコード

#![no_std]
#![no_main]

const WDT_START_CODE: u32 = 0xcccc;
const WDT_ACCESSAVLE_CODE: u32 = 0x5555;
const WDT_REFRESH_CODE: u32 = 0xaaaa;

use panic_halt as _; // you can put a breakpoint on `rust_begin_unwind` to catch panics
use cortex_m_rt::entry;
use cortex_m::delay;    // Delayを使う
use stm32f4::stm32f401;

#[entry]
fn main() -> ! {
    let dp = stm32f401::Peripherals::take().unwrap();   // デバイス用Peripheralsの取得
    let cp = cortex_m::peripheral::Peripherals::take().unwrap();    // cortex-m Peripheralsの取得
    let mut delay = delay::Delay::new(cp.SYST, 84000000_u32);   // Delayの生成
    clock_init(&dp);    // クロック関連の初期化
    wdt_init(&dp);   // WDTの初期化
    loop {
        delay.delay_ms(4000_u32);           // delay
        wdt_refresh(&dp);
    }
}

fn clock_init(dp: &stm32f401::Peripherals) {

    // PLLSRC = HSI: 16MHz (default)
    dp.RCC.pllcfgr.modify(|_, w| w.pllp().div4());      // P=4
    dp.RCC.pllcfgr.modify(|_, w| unsafe { w.plln().bits(336) });    // N=336
    // PLLM = 16 (default)

    dp.RCC.cfgr.modify(|_, w| w.ppre1().div2());        // APB1 PSC = 1/2
    dp.RCC.cr.modify(|_, w| w.pllon().on());            // PLL On
    while dp.RCC.cr.read().pllrdy().is_not_ready() {    // 安定するまで待つ
        // PLLがロックするまで待つ (PLLRDY)
    }

    // データシートのテーブル15より
    dp.FLASH.acr.modify(|_,w| w.latency().bits(2));    // レイテンシの設定: 2ウェイト

    dp.RCC.cfgr.modify(|_,w| w.sw().pll());     // sysclk = PLL
    while !dp.RCC.cfgr.read().sws().is_pll() {  // SWS システムクロックソースがPLLになるまで待つ
    }
//  SYSCLK = 16MHz * 1/M * N * 1/P
//  SYSCLK = 16MHz * 1/16 * 336 * 1/4 = 84MHz
//  APB2 = 84MHz (SPI1 pclk)
}

fn wdt_init(dp: &stm32f401::Peripherals) {

    dp.IWDG.kr.write(|w| unsafe { w.bits(WDT_START_CODE) });    // Keyレジスタにスタートコードを書いて動作開始
    dp.IWDG.kr.write(|w| unsafe { w.bits(WDT_ACCESSAVLE_CODE) });   // アクセス保護の解除

    dp.IWDG.pr.modify(|_, w| unsafe { w.bits(3) });    // プリスケーラを 1/32 に設定
    dp.IWDG.rlr.modify(|_, w| unsafe { w.bits(4095) }); // リロード値を 4095 に設定

    while dp.IWDG.sr.read().rvu().bit() {
    }
    dp.IWDG.kr.write(|w| unsafe { w.bits(WDT_REFRESH_CODE) });  // keyレジスタにリフレッシュコードを書く
}

fn wdt_refresh(dp: &stm32f401::Peripherals) {
    dp.IWDG.kr.write(|w| unsafe { w.bits(WDT_REFRESH_CODE) });  // keyレジスタにリフレッシュコードを書く
}

コードの解説

詳しくはマイコンのリファレンスマニュアルをご覧になってください。

初期化の後は定期的にIWDGをリフレッシュしてあげる必要があります。

クロックソースが32kHzなのでプリスケーラで32分周するように設定しました。
タイムアウトの時間 = (1/32k) * 32 * 4095 = 4.095秒 に設定しています。

動作させてみる

それでは F5キーでプログラムを起動した後、entry で停止したらもう一度F5キーを押してプログラムを動作させてみます。

デバッグモードは OpenOCD に設定する必要があるので、エラーが出る場合には確認してみてください。

wdt_init()あたりにブレークポイントを貼っておきます。

delay.delay_ms(4000_u32) の引数を変更して、タイムアウト時間より長くするとRunさせた後に再びブレークポイントにやってくるのでマイコンがリセットしたことを確認できます。

タイムアウト時間より短くするとブレークポイントには最初の1度だけやってきます。

いかがでしたか?
皆さんの環境では、うまく動作しましたか?

今回のプロジェクトを stm32f401-wdt におきましたので、よろしければ参考になさってください。

お疲れさまでした。

WDTカテゴリの最新記事