Rust on ARM
前言 ☕
之前在学习 ARM 芯片的开发的时候,教学是采用了 Keil 在 Win 下的开发环境。由于系统,以及 IDE 等都有很大的限制,故对此非常反感。之后深入学习了解后,发现使用 ARM dev 的官网提供的编译工具可以解决编译器的部分,代码就参考 ST 的 Manual, 下载则使用 stflash 工具。至此,所有工具流程完备,便开始了吭哧吭哧的寄存器写代码环节。。。
一直到前一段时间,个人非常喜欢 Rust 的设计理念,虽然没网没开发的特点不是很喜欢,但是相比于优点来说可以忽略。在学习 Rust 过程中,了解到了可以用来开发 ARM 。 最近疫情刚好有时间,就捣鼓了一下,发现 crates 包代码也还算简洁,同时生成的二进制文件又可以足够小,再者就是不用自己使用寄存器了,也算一大优势。所以果断在此记录下。
环境准备 🍩
这里对于环境准备等的情况就不过多介绍了。自行安装。
-
软件准备:
OS Compiler flash tool any rust st-flash -
硬件准备:
- stm32f103c8t6 开发板子
- stlink 下载器
代码编写 📚
在代码编写之前,首先需要添加 stm32f103c8t6 的编译平台:
rustup target add thumbv7m-none-eabi
首先,这里先总括下涉及到什么文件:
-rw-r--r-- 1 staff staff 78 May 9 16:00 .cargo/config
-rw-r--r-- 1 staff staff 659 May 9 16:23 Cargo.toml
-rw-r--r-- 1 staff staff 139 May 9 16:24 memory.x
-rw-r--r-- 1 staff staff 1811 May 9 16:22 src/main.rs
以上文件分别涉及编译选项配置、依赖配置、芯片空间指定、主要代码编写。
先看下编译选项的配置:
[build]
target = "thumbv7m-none-eabi"
rustflags = ["-C", "link-arg=-Tlink.x"]
其次是芯片参数配置:
/* Linker script for the STM32F103C8T6 */
MEMORY {
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
RAM : ORIGIN = 0x20000000, LENGTH = 20K
}
以上的芯片参数可以从 Mannul 或则 st-flash 探测查看。当然通过已有的 HAL 库等的代码文件查看也可以得到。
重点在依赖的一些配置选项:
[package]
name = "stm32f103-blink"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.release]
opt-level = 'z' # turn on maximum optimizations
lto = true # link-time-optimizations for size reduction
[dependencies]
nb = "1.0.0" # Minimal and reusable non-blocking I/O layer
cortex-m = "0.7.4" # access to the generic ARM peripherals
cortex-m-rt = "0.7.1" # startup code for ARM core
embedded-hal = "0.2.7" # access to generic embedded functions
panic-halt = "0.2.0" # panic handler
[dependencies.stm32f1xx-hal]
features = ["stm32f103", "rt", "medium"]
version = "0.9.0"
在上述代码中,package 块是当前项目的基本信息,profile 块则是关于编译优化等选项。
在 dependencies 块中,nb 是 IO 封装,cortex-m 则是关于 Crotex-M 的一些底层封装。类似于 C 语言开发中,一些汇编的初始化工作:涉及中断,内存初始化等。cortex-m-rt 则是关于 Cortex-m 的内核级别的代码,设计 BusFault、UsageFault 等等。这个的话初学者是接触不到的。embedded-hal 则是 HAL 层的一些标准封装。panic-halt 则是一些错误处理的封装。
最下面的代码块则是一些基本的信息选项。
最后则是我们的主要代码模块了:
//! Blinks an LED
//!
//! This assumes that a LED is connected to pc13 as is the case on the blue pill board.
//!
//! Note: Without additional hardware, PC13 should not be used to drive an LED, see page 5.1.2 of
//! the reference manual for an explanation. This is not an issue on the blue pill.
#![deny(unsafe_code)]
#![no_std]
#![no_main]
use panic_halt as _;
use nb::block;
use cortex_m_rt::entry;
use stm32f1xx_hal::{pac, prelude::*, timer::Timer};
#[entry]
fn main() -> ! {
// Get access to the core peripherals from the cortex-m crate
let cp = cortex_m::Peripherals::take().unwrap();
// Get access to the device specific peripherals from the peripheral access crate
let dp = pac::Peripherals::take().unwrap();
// Take ownership over the raw flash and rcc devices and convert them into the corresponding
// HAL structs
let mut flash = dp.FLASH.constrain();
let rcc = dp.RCC.constrain();
// Freeze the configuration of all the clocks in the system and store the frozen frequencies in
// `clocks`
let clocks = rcc.cfgr.freeze(&mut flash.acr);
// Acquire the GPIOC peripheral
let mut gpioc = dp.GPIOC.split();
// Configure gpio C pin 13 as a push-pull output. The `crh` register is passed to the function
// in order to configure the port. For pins 0-7, crl should be passed instead.
let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
// Configure the syst timer to trigger an update every second
let mut timer = Timer::syst(cp.SYST, &clocks).counter_hz();
timer.start(1.Hz()).unwrap();
// Wait for the timer to trigger an update and change the state of the LED
loop {
block!(timer.wait()).unwrap();
led.set_high();
block!(timer.wait()).unwrap();
led.set_low();
}
}
整体代码这里就不做过多介绍了。有兴趣的话可以去 stm32f1xx-hal 自行学习。
通过以上代码编译完成后,还需要转换成 bin 文件,目前我才用的 ARM dev 网站的工具集中的 arm-none-eabi-objcopy
arm-none-eabi-objcopy -O binary target/thumbv7m-none-eabi/release/stm32f103-blink stm32f103-blink.bin
除此之外还没找到一个更好的办法。转换完成后,直接使用 stflash 工具进行下载就可以了。