本帖最后由 yangjiaxu 于 2024-7-3 14:31 编辑
大家都知道STM32F103属于arm内核,因此需要让rust来支持arm才行,因此,需要安装,
rustup target install thumbv7m-none-eabi
rustup target install thumbv7m-none-eabi 是 Rust 工具链管理器 rustup 的一个命令,用于安装特定的目标(target)。这个目标 thumbv7m-none-eabi 是针对 ARM Cortex-M 系列处理器的,特别是那些支持 Thumb-2 指令集的处理器。
具体来说:
thumbv7m 表示目标架构是 Thumb-2 指令集的 ARMv7-M 架构。
none 表示没有操作系统(bare-metal)。
eabi 表示嵌入式应用程序二进制接口(Embedded Application Binary Interface)。
安装这个目标后,你可以使用 Rust 编译器 rustc 或构建工具 cargo 来编译代码,生成可以在 ARM Cortex-M 系列处理器上运行的二进制文件。这对于开发嵌入式系统、微控制器项目非常有用。
安装完之后需要创建一个工程,可以使用命令行的方式进行创建,如下图所示:
创建成功之后,需要修改Cargo.toml,这个主要修改的目的是添加一些依赖项,说实话我是不会添加这些,所以本着拿来主义就好。
[dependencies]
embedded-hal = "0.2.7"
nb = "1"
cortex-m = "0.7.6"
cortex-m-rt = "0.7.1"
# Panic behaviour, see https://crates.io/keywords/panic-impl for alternatives
panic-halt = "0.2.0"
[dependencies.stm32f1xx-hal]
version = "0.10.0"
features = ["rt", "stm32f103", "medium"]
接下来需要创建.cargo/config and memory.x,主要是配置编译链和目标芯片的存储方面的信息。
接下来就可以进行程序编写了,在上文创建好的main.rs中,将以下代码复制进去:
//! 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 stm32f1::stm32f103::gpioa;
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 gpioa = dp.GPIOA.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 = gpioa.pa8.into_push_pull_output(&mut gpioa.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();
}
}
这段代码是一个嵌入式 Rust 程序,用于在 STM32F1 微控制器上闪烁连接到 PA8 引脚的 LED。由于担心很多人可能会看不懂,逐行进行了代码解释,解释如下:
#![deny(unsafe_code)]:禁止使用 unsafe 代码。
#![no_std]:不使用标准库 std,而是使用嵌入式系统常用的 core 库。
#![no_main]:不使用标准的 main 函数入口,而是使用嵌入式系统常用的入口点。
use panic_halt as _;:导入 panic_halt 库,用于处理 panic 情况。
use nb::block;:导入 nb 库的 block 模块,用于阻塞等待操作。
use cortex_m_rt::entry;:导入 cortex_m_rt 库的 entry 宏,用于定义程序入口点。
use stm32f1xx_hal::{pac, prelude::*, timer::Timer};:导入 stm32f1xx_hal 库的相关模块。
#[entry]:定义程序的入口点。
fn main() -> !:定义一个无限循环的 main 函数。
let cp = cortex_m::Peripherals::take().unwrap();:获取 Cortex-M 内核的外设。
let dp = pac::Peripherals::take().unwrap();:获取 STM32F1 设备的外设。
let mut flash = dp.FLASH.constrain();:获取并约束 Flash 外设。
let rcc = dp.RCC.constrain();:获取并约束 RCC(复位和时钟控制)外设。
let clocks = rcc.cfgr.freeze(&mut flash.acr);:冻结时钟配置并获取时钟频率。
let mut gpioa = dp.GPIOA.split();:获取 GPIOA 外设。
let mut led = gpioc.pa8.into_push_pull_output(&mut gpioa.crh);:配置 PA8 引脚为推挽输出模式。
let mut timer = Timer::syst(cp.SYST, &clocks).counter_hz();:配置系统定时器。
timer.start(1.Hz()).unwrap();:启动定时器,设置定时器触发频率为 1 Hz。
loop { block!(timer.wait()).unwrap(); led.set_high(); block!(timer.wait()).unwrap(); led.set_low(); }:无限循环,等待定时器触发,切换 LED 状态。
这段代码通过配置 GPIO 和定时器,实现了在 STM32F1 微控制器上闪烁连接到 PA8 引脚的 LED。
当代码编辑好了之后,需要进行编译了
cargo build --features stm32f103 --example blinky
我是直接在例程的基础上进行的,所以当时名字没有修改,是blinky,在example文件夹下的blinky进行编译,编译好之后,会生成blinky文件,这个文件是不可执行文件,需要将其转换为bin可烧录的二进制文件,需要下面的命令:
rust-objcopy -O binary target/thumbv7m-none-eabi/debug/examples/blinky blinky.bin
这个命令具体来说是这样的:
rust-objcopy 是一个工具,通常是 llvm-objcopy 或 objcopy 的别名,用于处理对象文件(object files)。
-O binary 表示输出格式为二进制(binary)格式。
target/thumbv7m-none-eabi/debug/examples/blinky 是输入的 ELF 文件路径,这是 Rust 编译生成的可执行文件。
blinky.bin 是输出的二进制文件路径,这个文件可以直接烧录到目标硬件上。
这个命令的作用是将 Rust 编译生成的 ELF 文件 blinky 转换为二进制文件 blinky.bin,以便于将其烧录到 ARM Cortex-M 系列的微控制器中运行。
在嵌入式开发中,通常需要将编译生成的 ELF 文件转换为二进制文件,因为微控制器通常需要的是二进制格式的可执行文件,而不是 ELF 格式的文件。
当执行这个命令之后,就会生成一个bin文件
将该bin文件,地址是0x08000000,烧录到目标芯片即可。工作效果如下图所示。
至此,利用rust进行编写LED控制代码来让STM32F103执行就完成了,这系列的操作流程有几个难点:
第一就是针对Cargo.toml的编辑,主要是有那些依赖项,添加什么内容是比较难摸索的,需要有参考才可以。
第二是创建.cargo/config and memory.x,这个说实话可以找个好用的例程直接复制过来,但是如果找不到例程就需要自己来写了,网上可以找到,比如memory.x
接下来就是工具的问题,比如我第一次操作的时候,发现没安装rust-objcopy,导致找不到其命令,还有就是GCC编译链没安装,导致程序编译出错,不过通过对应的提示可以知道怎么操作,这个还算是简单一些。
最后就是针对GCC调试,这个我暂时还没实现,因为使用rust进行调试我还是不太习惯,而且调试感觉没有keil或者IAR这种单步调试来得直观。
总结:就是不是很建议使用rust开发STM32,哈哈,真的很麻烦,如果不熟悉的话,确实不是一个很适合的语言,不如C来的痛快,不过这两种语言和有优势,如果将rust用在网络/后台开发方面,应该更加方便和强大。