今回は GPIO の入力を操作してみます。
今回もベアメタルと言われているレジスタにアクセスする方法を使います。
開発環境は以下の通りです。
PC:Windows10 OS
Board:STM32Nucleo-F401RE
デバイス:STM32F401RE
エディタ:VSCode
言語:Rust
ボードの情報は こちら からご覧いただけます。
環境構築については こちら をご覧になってください。
仕様と回路
スイッチと抵抗を用意して、Nucleoボードにつなぎ、スイッチが押されている時にLD2を点灯させて、離したら消灯するようにプログラムします。
入力に使うポートはLD2を操作するGPIOA5の隣の GPIOA6 (PA6)を使うことにしました。
回路は以下の通りです。
抵抗は手持ちの10kΩを使うことにしました。
スイッチがなければジャンバーワイヤーを使ってもよいでしょう。
loop()で回して、PA6が High なら PA5 に Low を出力し、 Low ならスイッチが押されたと判断して PA5 に High を出力します。
PA5 = High で LD2 が点灯するので、この論理で良さそうです。
プロジェクトをつくる
コマンドプロンプトを起動し、cargo generate と git を使ってSTM32F401用のプロジェクトを作成します。
cargo generate –git https://github.com/rust-embedded/cortex-m-quickstart.git と入力しプロジェクト名を聞いてくるので stm32f401-gpio2 と入力します。
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-gpio2
C:\Users\xxxxx\stm32f401-gpio2 というフォルダができていることを確認します。
VSCodeを起動する
以下のようにコマンドを入力することで、stm32f401-gpio2をカレントディレクトリとしてエディタ(VSCode)が起動します。
cd stm32f401-gpio2
code .
C:\Users\xxxxx>cd stm32f401-gpio2
C:\Users\xxxxx\stm32f401-gpio>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"
編集が終わったら 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-gpio2\.vscode に保存します。
ソースコード
main()関数の外側は、組み込み特有の記述になります。
このように書くものだと考えておけば良いと思います。
#![no_std]
#![no_main]
use panic_halt as _; // you can put a breakpoint on `rust_begin_unwind` to catch panics
use cortex_m_rt::entry;
use stm32f4::stm32f401; // (1)
#[entry]
fn main() -> ! {
let dp = stm32f401::Peripherals::take().unwrap(); // (2)
dp.RCC.ahb1enr.modify(|_, w| w.gpioaen().enabled()); // (3)
let gpioa = &dp.GPIOA; // (4)
gpioa.moder.modify(|_, w| w.moder5().output()); // (5)
loop {
if gpioa.idr.read().idr6().is_low() { // (6)GPIOA6を読む
gpioa.odr.modify(|_, w| w.odr5().high()); // (7)
}
else {
gpioa.odr.modify(|_, w| w.odr5().low()); // (8)
}
}
}
コード概要
今回は解説する必要がないくらい簡単ですね。
PA6はモードの初期値が入力になっているのでモードレジスタを操作する必要がありません。
ポートの状態を読むには、GPIO の IDR を使います。
(6)で GPIOA の IDR の IDR6 を読み、PA6の状態を調べています。
それでは F5キーでプログラムを起動した後、entry で停止したらもう一度F5キーを押してプログラムを動作させてみます。
スイッチを押すと、LD2(緑色)が点灯し、離すと消えます。
デバッグモードは OpenOCD に設定する必要があるので、エラーが出る場合には確認してみてください。
いかがでしたか?
皆さんの環境では、うまく動作しましたか?
今回のプロジェクトを stm32f401-gpio2 におきましたので、よろしければ参考になさってください。
お疲れさまでした。