SNSへはこちら

Rustで組み込みプログラミング for STM32(2) – プロジェクト自作

(1)の続きです。

参考サイト

自作プロジェクト

コピペで出来ます。やってみましょう(できるようになるまで丸3日かかった...)。今回ターゲットにしたチップはいつもの STM32F303K8T6 です。とはいってもチップが違うのならリンカの所を変えればいいだけですが。

まずはコマンドを打撃して構造を作ります。

$ xargo new blinky --bin # blinkyというプロジェクト作成
$ cd blinky
$ mkdir .cargo # なんか必要なディレクトリ

続いてファイルの構成です。

Cargo.toml

[package]
authors = ["Hoge Tarou <hogehoge@example.com>"]
name = "blinky"
version = "0.1.0"
[profile.dev]
codegen-units = 1
incremental = false

[profile.release]
lto = true
debug = true
[dependencies]
stm32f30x = "*"
[dependencies.cortex-m]
version = "0.4.1"

[dependencies.cortex-m-rt]
version = "0.3.12"
features = ["abort-on-panic"]
[dependencies.stm32f30x-hal]
version = "0.1.1"
features = ["rt"]
[dependencies.num-traits]
version = "0.2"
default-features = false

Xargo.toml

[dependencies.core]
stage = 0

[dependencies.compiler_builtins]
features = ["mem"]
stage = 1

build.rs

Makefile みたいなやつで、Rust のソースコードファイル以外に変更があったらビルドを再度してくれるように指示するためのファイルです。以下ファイル全体がおまじないですね。

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

fn main() {
    // Put the memoryer script somewhere the memoryer can find it
    let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
    File::create(out.join("memory.x"))
        .unwrap()
        .write_all(include_bytes!("memory.x"))
        .unwrap();
    println!("cargo:rustc-memory-search={}", out.display());

    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-changed=memory.x");
}

memory.x

リンカスクリプトです。ROM ではなく FLASH としている点にご注意。

MEMORY
{
  RAM (xrw)     : ORIGIN = 0x20000000, LENGTH = 12K
  FLASH (rx)        : ORIGIN = 0x8000000, LENGTH = 64K
}

.cargo/config

[target.thumbv7em-none-eabihf]
runner = 'arm-none-eabi-gdb'
rustflags = [
  "-C", "link-arg=-Tlink.x",
  "-C", "linker=arm-none-eabi-ld",
  "-C", "linker-flavor=ld",
    "-Z", "no-landing-pads",
    "-C", "opt-level=0",
]

[build]
target = "thumbv7em-none-eabihf"

これで終わりです。お疲れ様でした。以下に完成後のディレクトリツリーを示します。

$ tree . -L 2 -a
[master]
.
├── .cargo
│   └── config
├── Cargo.toml
├── Xargo.toml
├── build.rs
├── memory.x
└── src
    └── main.rs

ソースファイルの編集

というわけで src/main.rs をひな形にします。既存のファイルを削除して置き換えてください。

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

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

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-based delay
    let mut delay = Delay::new(cp.SYST, clocks);

    loop {
        // TODO: Insert codes here
    }
}

なおビルド時には以下のコマンドを実行してください。

$ xargo build

ハマりどころ集

僕がハマってもがき苦しんでいたので是が非でも書きます。build 時のエラーになります。

can't find crate for core と言われる

cargo じゃなくて xargo ですよ!

arm-none-eabi-ld に「libc.a ねぇよ」って怒られる

面倒だからとリンカスクリプトを IDE が生成したものとしてそのまま memory.x にコピペすると怒られます。これは内部の /DISCARD/ セクションの中で libc.a libgcc.a libm.a を呼び寄せていますので、Rust でやる場合はこのような記述はあってはなりません(多分)。
また、下に述べる現象も同時に発生するため、上のように MEMORY の所のみを記載するようにしてください。

リンク時に「サイズが足りない」「Reset_Handlerないよ」って怒られる

IDE が生成したリンカスクリプトをそのまま memory.x にコピペすると起こる現象です。上と同じく MEMORY の所のみを記載するようにしてください。

今回はここまで。Lチカはもう出来ているのですが、このまま続けたら記事は長くなるし、僕自身眠くなってきたのでここでおしまいにします。
ビルドは stm32f30x クレートが非常に時間掛かると思いますが、首を長くして待ってやってくださいな。初回だけですから。