はじめに
以前GR-PEACH × RustでLチカしてみよう【とにかくやってみる編】という記事を書いたのですが、その時「今度はスタートアップからスクラッチで書いてみたい」ということを書きました。
この記事は上記作業の備忘録となります。備忘録なので記載が雑な部分がありますがご容赦ください。
ちなみにボードはGR-PEACHのROMがシリアルフラッシュだったりとスタートアップの工程がめんどくさそうだったので、まずは簡単なSTMのNucleoボードでチャレンジしました。
本記事の環境は以下にまとめてあります。
環境
- OS
- Windows 10 Pro 2004(64bit)
- rustc・cargo
- 1.55.0-nightly
- rustup
- 1.24.3
- Eclipse
- Pleiades All in One Eclipse 2020-12(Platform)
- Rust DT
- 0.8.1.v201612291856
- OpenOCD
- v0.11.0-1
- GNU Arm Embedded Toolchain
- Version 9-2019-q4-major
- ボード
環境構築
Eclipseのインストール
以下URLから取得します。
JavaでもC++でもないのでパッケージはPlatform
でOKです。私はちょっと古い2020-12
版を使っていますが、最新版(私が確認したときは2021-04版)はダークテーマに背景も文字も真っ黒になってしまうバグがあり使っていないだけなので、おそらく最新版でも問題ないはずです。
RustDTのインストール
以下URLを参考にインストールします。
GNU Arm C/C++ OpenOCD Debuggingのインストール
Rust用のOpenOCDデバッグ環境はなさそうなので、C/C++用をインストールします。
最新のEclipse Embedded CDT
はhttps://download.eclipse.org/embed-cdt/releases/6.1.2/p2/にあるのですが、ここからインストールしようとしたら、RustDTと相性が悪いようでRustDTをアンインストールするよう促されたので、仕方なくhttp://sourceforge.net/projects/gnuarmeclipse/files/Eclipse/updatesから(古めのやつ?を)インストールしました。
インストール方法は先程のRustDTと同じく新規ソフトウェアのインストール...
から行います。OpenOCDでデバッグしたいだけなので、インストールするのはGNU Arm C/C++ OpenOCD Debugging
だけでOKです。
OpenOCDのインストール
推奨インストール方法はxpm
によるインストールのようですが、めんどくさかった(MSYS2がインストールされていなかった)ので公式サイトにリンクされていた以下の非公式バイナリパッケージ
を取得しました。インストーラではないので適当なところに展開してください。
余談ですが、過去にGR-PEACHのデバッグにv0.10.0
を使っていたのでそれを使いまわそうとしたのですが、エラーで起動しなかったので最新版をインストールしました。
GNU Arm Embedded Toolchainのインストール
GDBを使うのでインストールします。
ST-Linkドライバのインストール
以下URLからドライバを取得します。
プロジェクト作成
プロジェクト・エクスプローラーで右クリック → 新規作成 → プロジェクト → Rust → Rust Cargo Project
からプロジェクトを作成します。
コーディング
以下のようなLチカコードを書きました。
#![no_main]
#![no_std]
#![feature(llvm_asm)]
#[link_section = ".vector_table.reset_vector"]
#[no_mangle]
pub static RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset;
#[no_mangle]
pub unsafe extern "C" fn Reset() -> ! {
let rcc_ahb1enr = 0x4002_3830 as *mut usize;
let gpioa_moder = 0x4002_0000 as *mut usize;
let gpioa_odr = 0x4002_0014 as *mut usize;
set_bit(rcc_ahb1enr, 0, true);
set_bit(gpioa_moder, 10, true);
let mut flg: bool = true;
loop {
flg = !flg;
set_bit(gpioa_odr, 5, flg);
for _ in 0..24_000 {
llvm_asm!("" :::: "volatile")
}
}
}
unsafe fn set_bit( addr: *mut usize, bit: usize, is_set: bool ) {
if is_set {
*addr |= 1 << bit;
}
else {
*addr &= !(1 << bit);
}
}
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_panic: &PanicInfo<'_>) -> ! {
loop {}
}
ビルド
ビルドコマンドの変更
プロジェクトを右クリック → プロパティ → Rust Build Target → ビルド・ターゲット(build)
からビルドコマンドを変更できます。デフォルトだと${CARGO_TOOL_PATH} test --no-run --message-format=json
(テスト用のコマンド)となっているので、cargo build
に書換えます。
ターゲットの追加
以下のコマンドでターゲットを追加します。
> rustup target add thumbv7em-none-eabihf
STM32F401RE
マイコンのコアはCortex-M4F
なので、thumbv7em-none-eabihf
を追加します。他のコアのマイコンを使用している場合は
- thumbv6m-none-eabi → M0, M0+
- thumbv7m-none-eabi → M3
- thumbv7em-none-eabi → M4, M7
- thumbv7em-none-eabihf → M4F, M7F
を追加してください。
リリースチャネルをNightlyに変更
Eclipseのプロジェクトを作成したディレクトリで以下のコマンドを実行しNightlyに変更します。
> rustup override add nightly
configファイルの作成
Eclipseのプロジェクトを作成したディレクトリに.cargo
というディレクトリを作成し、その中に以下のconfig
ファイルを作成します。
[target.thumbv7em-none-eabihf]
rustflags = [
"-C", "link-arg=-Tlinker.ld",
]
[build]
target = "thumbv7em-none-eabihf"
リンカ・スクリプトの作成
Eclipseのプロジェクトを作成したディレクトリ直下に以下のlinker.ld
ファイルを作成します。
MEMORY {
FLASH : ORIGIN = 0x08000000, LENGTH = 512K
RAM : ORIGIN = 0x20000000, LENGTH = 96K
}
ENTRY(Reset);
EXTERN(RESET_VECTOR);
SECTIONS {
.vector_table ORIGIN(FLASH) : {
LONG(ORIGIN(RAM) + LENGTH(RAM)); /* スタックポインタ */
KEEP(*(.vector_table.reset_vector)); /* リセットベクタ */
} > FLASH
.text : {
*(.text .text.*);
} > FLASH
.rodata : {
*(.rodata .rodata.*);
} > FLASH
/DISCARD/ : {
*(ARM.exidx .ARM.exidx.*);
}
}
STM32F401RE
マイコンのROMは0x08000000
から512KB、RAM0x20000000
から96KBです。
やっとビルド
Eclipseで作成したプロジェクトのBuild Targets
にbuild
check
clean
という項目が並んでいると思います。build
をダブルクリックするだけでビルドは完了します。Cのビルドに慣れていると一瞬で終わりすぎて、ちゃんとビルドできたのかとちょっと焦ります
書込み及びデバッグ
メニューから実行 → デバッグ構成
を選択し、デバッグ構成ウィンドウを開きます。
次にGDB OpenOCD Debuggingを右クリック → 新規作成
でデバッグ構成を作成します。
各タブの設定内容は以下のとおりです。
- メイン
- プロジェクト
- 通常なら紐付けるプロジェクトが選択できるのですが、C/C++プロジェクトではないのため選択肢がありません。したがって空欄にします。
- C/C++ アプリケーション
- ELFファイルを指定します。プロジェクトディレクトリ内の
target/thumbv7em-none-eabihf/debug/nucleo_blinky
にあって、拡張子はありません。ファイル名はプロジェクト名によって異なります。
- ELFファイルを指定します。プロジェクトディレクトリ内の
- プロジェクト
- デバッグ
- OpenOCD Setup
- 実行可能ファイル
- OpenOCDの実行ファイルを指定します。
- 私の場合は
${openocd_path}/bin/${openocd_executable}
です。変数の値が期待してるものと違ってた気がするので、うまく実行できない場合は見直してください(多分絶対パスで指定するのが確実)。
- Config options
- ターゲットボードのコンフィグファイルを指定します。
- 私の場合は
-s ${openocd_path}/scripts -f board/st_nucleo_f4.cfg
です。パスについては上記と同様です。
- 実行可能ファイル
- GDB Client Setup
- 実行可能ファイル
- GDBの実行ファイルを指定します。
- 元々何かの変数が指定してあった気がしますが、なんかうまくいかなかったので私は絶対パスを指定してしまっています。
- 実行可能ファイル
- OpenOCD Setup
- 始動
- Run/Restart Commands
- Set breakpoint at
-
main
が指定されていますが今回main関数は無いのでチェックを外しておきます。
-
- Set breakpoint at
- Run/Restart Commands
以上で設定は完了(のはず)です。
デバッグ
を押すとデバッグが開始され、Reset
関数の先頭でブレークしているはずです。F8
で実行します。
おわりに
以前の記事から1年近く経ってしまいましたがようやくスタートアップからスクラッチでLチカすることができました。
相変わらず割込み等使ってませんが、ようやく組込みRustっぽくなってきた気がします。cortex-m-quickstart
テンプレートやstm32f4
クレートを使えば一発なんだとは思いますが、一つ一つ理解しながらやっていこうと思います。
記事を書いている途中でRustDT
はもう古い(更新が止まってる)ことに気付いたり、Eclipseの変数が全然使えなかったりと色々ありましたが、また時間ができたらアップデートしていこうと思います。
次回は割込みに挑戦しようと思います。