說明
在傳統的C編寫STM32代碼時,有兩種使用方式:寄存器版本和HAL庫版本。
使用Rust編寫時也一樣,你需要考慮使用寄存器版本或者HAL庫版本。
toolchain與STM32對應關係
使用Rust編寫實質上是將Rust代碼編譯爲無STD庫的arm平臺代碼(二進制,彙編爲arm指令集),然後將其中的代碼段根據嵌入式硬件的要求,寫入到芯片中,從而實現在STM32中跑RUST的效果。
我們這裏使用的芯片STM32F407ZGT6 是一顆Cortex-M4架構的芯片,其對應RUST toolchain關係爲:
# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
# target = "thumbv7m-none-eabi" # Cortex-M3
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
準備工作
本章節以LED閃爍爲目標,嘗試編寫代碼。
其中,IO使用GPIOF的Pin9和Pin10。
使用Cargo工具創建一個bin項目,名字爲stm32app, 然後在根目錄添加一個文件memory.x
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
FLASH : ORIGIN = 0x08000000, LENGTH = 512K
RAM : ORIGIN = 0x20000000, LENGTH = 100K
}
特別注意FLASH和RAM值的大小,只能比真實的小,不能比真實的大。
這裏的memory.x是爲未來使用的一個庫cortex-m-rt
準備的。
編譯時,使用如下命令行:
cargo build --release --target=thumbv7em-none-eabihf -C link-arg=Tlink.x
其中,target可以是v7m或者v7em中任何一個,但是不能是v6m.
就和使用C寫代碼一樣,我們完全可以全手工構造寄存器映射關係,然後進行讀寫,但是爲了方便,我們使用開源的庫加快我們的代碼速度。
其中,寄存器版本主要依賴 stm32f4 這個庫,而HAL版本依賴stm32f4xx-hal。
他們沒有本質上的區別,只是寄存器版本庫對寄存器操作簡單明瞭,而HAL版本集成程度更高一點,你可以根據自己的情況進行選擇。
寄存器版本
Cargo.toml文件
[dependencies]
cortex-m = "*"
cortex-m-rt = "*"
cortex-m-semihosting = "*"
panic-halt = "*"
[dependencies.stm32f4]
features = ["stm32f407", "rt"]
version = "*"
main.rs
#![no_std]
#![no_main]
extern crate stm32f4;
extern crate panic_halt;
extern crate cortex_m_rt;
use cortex_m_rt::entry;
use stm32f4::stm32f407;
// use `main` as the entry point of this application
#[entry]
fn main() -> ! {
// get handles to the hardware
let peripherals = stm32f407::Peripherals::take().unwrap();
let pf = &peripherals.GPIOF;
let rcc = &peripherals.RCC;
// enable system clock RCC for gpiof
rcc.ahb1enr.write(|w|{
w.gpiofen().set_bit()
});
// config GPIOF pin9 and pin10 for led
{
// 1. mode 01 通用輸出
pf.moder.write(|w|{
w.moder9().output()
.moder10().output()
});
// 2. otype 0 推輓輸出
pf.otyper.write(|w|{
w.ot9().push_pull()
.ot10().push_pull()
});
// 3. ospeed 10, 50MHz high speed
pf.ospeedr.write(|w|{
w.ospeedr9().high_speed()
.ospeedr10().high_speed()
});
// 4. pup 01, 上拉
pf.pupdr.write(|w|{
w.pupdr9().pull_up()
.pupdr10().pull_up()
});
// 5. idr/odr/bsrr
// by condition to read or set
pf.odr.reset();
}
loop{
pf.odr.write(|w| {
w.odr9().set_bit()
.odr10().clear_bit()
});
// 這個延時不準,只用來演示使用
cortex_m::asm::delay(168*10000*3);
pf.odr.write(|w| {
w.odr9().clear_bit()
.odr10().set_bit()
});
cortex_m::asm::delay(168*10000*3);
}
}
使用準備工作中的命令行編譯後得到一個stm32app的arm程序,
通過arm-none-eabi-objdump -f stm32app
就可以得到這個文件的詳細頭部信息了。
HAL庫版本
[dependencies]
embedded-hal = "*"
nb = "*"
cortex-m = "*"
cortex-m-rt = "*"
# Panic behaviour, see https://crates.io/keywords/panic-impl for alternatives
panic-halt = "*"
[dependencies.stm32f4xx-hal]
version = "*"
features = ["rt", "stm32f407"] # replace the model of your microcontroller here
main.rs
#![deny(unsafe_code)]
#![no_main]
#![no_std]
// Halt on panic
// #[allow(unused_extern_crates)] // NOTE(allow) bug rust-lang/rust#53964
extern crate panic_halt; // panic handler
use cortex_m;
use cortex_m_rt::entry;
use stm32f4xx_hal as hal;
use crate::hal::{prelude::*, stm32};
#[entry]
fn main() -> ! {
if let (Some(dp), Some(cp)) = (
stm32::Peripherals::take(),
cortex_m::peripheral::Peripherals::take(),
) {
// Set up the LED. On the Nucleo-446RE it's connected to pin PA5.
let gf = dp.GPIOF.split();
let mut led = gf.pf10.into_push_pull_output();
// Set up the system clock. We want to run at 48MHz for this one.
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.sysclk(168.mhz()).freeze();
// Create a delay abstraction based on SysTick
let mut delay = hal::delay::Delay::new(cp.SYST, clocks);
loop {
// On for 1s, off for 1s.
led.set_high().unwrap();
delay.delay_ms(500_u32);
led.set_low().unwrap();
delay.delay_ms(500_u32);
}
}
loop {}
}
使用準備工作中的編譯指令就可以得到同樣的stm32app程序。
生成HEX/BIN文件
首先,上一步中生成的是一個arm程序,不是單片機可識別的HEX或者BIN程序,我們需要使用工具進行提取/轉換:
- bin文件
arm-none-eabi-objcopy -O binary stm32app stm32app.bin
- hex文件
arm-none-eabi-objcopy -O ihex stm32app stm32app.hex
其中,相比BIN文件,HEX文件多了機器碼的存儲地址等信息,建議轉換爲HEX文件進行下載。
下載
下載前,請確保開發板USB已經接好,並且CH340驅動成功,能在設備管理器裏面找到對應的COM口。
下載STM32 HEX文件有很多方式,我們是藉助串口進行ISP下載,這裏需要用到一些工具:
-
FlyMcu
這個軟件比較古老,但是基本能用。缺點是下載失敗率較高,建議波特率不要超過76800. -
PZ-ISP
這個是買開發板提供的下載工具,比較推薦,成功率很高,不容易出問題。 -
STM32 ISP下載工具
這個據說是STM32專供的,沒怎麼使用。
以上工具使用方法建議自己查詢,方法都比較簡單。
注意:
如果使用J-Link進行下載需要配合OpenOCD使用,此方法比較複雜,建議有需求了再嘗試。
總結
RUST編寫STM32程序十分簡單,並且下載過程需要藉助arm工具集進行代碼轉換,其他的與普通方式一樣。
目前,ISP下載無法進行調試,如有需要,建議使用串口功能進行交互,OpenOCD+J-Link理論上可行,但是因爲涉及指令較多,這裏不做討論和嘗試。
當然,使用RUST開發處理需要RUST基礎知識之外,還需要對STM32本身有深入的理解,此方面的知識建議學習正點原子的教材:
手把手教你學STM32 系列視頻之 STM32F4-基於探索者F407
只有對C編寫STM32程序有了基礎的認知,纔有可能編寫出高質量的RUST代碼。
有人說,既然這樣,我爲什麼要用RUST寫嵌入式?
- RUST本身語言特性比C更安全,規範,可以寫出高質量的代碼。
- RUST工具集本身可實現更高層次的抽象,用高階語言寫嵌入式代碼可實現更復雜的功能和複用。
- 因爲我喜歡RUST呀(_)
其實,由於我們使用的是arm芯片,可以上一些RTOS或者Linux系統,未來我們也可以在這些系統的基礎上進行更深入的開發實踐,這時候,RUST就是一個比較好的選擇。
RUST官方對於嵌入式領域也很感興趣,有專門的團隊進行此方向的維護,再加上這個語言本身的特性,我相信未來它的工具鏈和生態會更加完善,最終與C一教高下。
說到底,這裏只是進行RUST嵌入式開發的一個學習和實踐,不要問太多問什麼,喜歡就好。