Rust嵌入式开发库stm32f4xx-hal的使用:针对STM32F4系列MCU的硬件抽象层(HAL)驱动开发
Rust嵌入式开发库stm32f4xx-hal的使用:针对STM32F4系列MCU的硬件抽象层(HAL)驱动开发
stm32f4xx-hal是一个针对STMicro STM32F4系列微控制器的硬件抽象层库,建立在外设访问API之上。它通过特性门(gate)选择不同的MCU型号,通常由板级支持包(BSP)指定。
支持的MCU型号
型号列表 |
---|
stm32f401 |
stm32f405 |
stm32f407 |
stm32f410 |
stm32f411 |
stm32f412 |
stm32f413 |
stm32f415 |
stm32f417 |
stm32f423 |
stm32f427 |
stm32f429 |
stm32f437 |
stm32f439 |
stm32f446 |
stm32f469 |
stm32f479 |
可选特性
rtic1
- 支持RTICv1框架rtic2
- 支持RTICv2框架(与rtic1不兼容,需要nightly编译器)defmt
- 为公共枚举和结构体实现defmt::Formatcan
- bxCAN外设支持i2s
- I2S外设支持usb_fs
或usb_hs
- USB OTG FS/HS外设支持fsmc_lcd
- 通过FMC/FSMC外设的LCD支持sdio-host
- SDIO外设支持dsihost
- DSI主机支持
项目设置
手动设置
- 创建新Rust项目:
cargo init
- 添加以下依赖到Cargo.toml:
[dependencies]
embedded-hal = "0.2"
nb = "1"
cortex-m = "0.7"
cortex-m-rt = "0.7"
panic-halt = "0.2"
[dependencies.stm32f4xx-hal]
version = "0.22.1"
features = ["stm32f407"] # 替换为你的微控制器型号
- 从stm32f4xx-hal仓库复制
.cargo/config.toml
和memory.x
到你的项目
快速开始
可以使用cargo generate
快速创建空项目:
$ cargo generate --git https://github.com/burrbull/stm32-template/
示例代码
以下是基于delay-syst-blinky.rs的完整示例,展示了如何使用stm32f4xx-hal进行基本的LED闪烁:
#![no_std]
#![no_main]
// 导入必要的库
use panic_halt as _;
use cortex_m_rt::entry;
use stm32f4xx_hal::{
prelude::*,
stm32,
delay::Delay,
};
#[entry]
fn main() -> ! {
// 获取外设访问权限
let dp = stm32::Peripherals::take().unwrap();
let cp = cortex_m::Peripherals::take().unwrap();
// 初始化时钟
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.sysclk(48.mhz()).freeze();
// 初始化GPIO
let gpioa = dp.GPIOA.split();
// 配置PA5(通常连接板上LED)为推挽输出
let mut led = gpioa.pa5.into_push_pull_output();
// 初始化延时器
let mut delay = Delay::new(cp.SYST, clocks);
// 主循环
loop {
// 切换LED状态
led.toggle().unwrap();
// 延时500ms
delay.delay_ms(500_u32);
}
}
完整示例扩展
以下是一个更完整的示例,展示了如何使用stm32f4xx-hal进行串口通信和按键输入检测:
#![no_std]
#![no_main]
use panic_halt as _;
use cortex_m_rt::entry;
use stm32f4xx_hal::{
prelude::*,
stm32,
delay::Delay,
serial::{Serial, Config},
gpio::{Input, PullUp},
};
#[entry]
fn main() -> ! {
// 获取外设访问权限
let dp = stm32::Peripherals::take().unwrap();
let cp = cortex_m::Peripherals::take().unwrap();
// 初始化时钟
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.sysclk(84.mhz()).freeze();
// 初始化GPIO
let gpioa = dp.GPIOA.split();
let gpioc = dp.GPIOC.split();
// 配置PA5(板上LED)为推挽输出
let mut led = gpioa.pa5.into_push_pull_output();
// 配置PC13(通常连接板上按键)为上拉输入
let button = gpioc.pc13.into_pull_up_input();
// 配置USART2 (PA2-TX, PA3-RX)
let tx_pin = gpioa.pa2.into_alternate_af7();
let rx_pin = gpioa.pa3.into_alternate_af7();
let serial = Serial::usart2(
dp.USART2,
(tx_pin, rx_pin),
Config::default().baudrate(115200.bps()),
clocks,
).unwrap();
// 将串口拆分为发送和接收部分
let (mut tx, mut rx) = serial.split();
// 初始化延时器
let mut delay = Delay::new(cp.SYST, clocks);
// 发送欢迎消息
let message = b"Hello from STM32F4!\r\n";
for &byte in message {
block!(tx.write(byte)).ok();
}
// 主循环
loop {
// 检查按键状态
if button.is_low().unwrap() {
// 按键按下时发送消息
let pressed_msg = b"Button pressed!\r\n";
for &byte in pressed_msg {
block!(tx.write(byte)).ok();
}
// 切换LED状态
led.toggle().unwrap();
// 延时消抖
delay.delay_ms(50_u32);
}
// 检查串口接收数据
if let Ok(byte) = rx.read() {
// 回显接收到的字符
block!(tx.write(byte)).ok();
// 如果收到回车符,添加换行符
if byte == b'\r' {
block!(tx.write(b'\n')).ok();
}
}
// 短延时减少CPU负载
delay.delay_ms(10_u32);
}
}
许可证
0-clause BSD license
1 回复
stm32f4xx-hal 使用指南:STM32F4系列MCU的硬件抽象层开发
简介
stm32f4xx-hal
是一个针对STM32F4系列微控制器的硬件抽象层(HAL)库,为Rust嵌入式开发提供了类型安全的API接口。它基于embedded-hal
标准,支持STM32F401、F405、F407、F411、F412、F413、F415、F417、F423、F427、F429、F437、F439、F446、F469和F479等型号。
主要特性
- 类型安全的GPIO配置
- 外设初始化与配置
- 中断处理支持
- DMA支持
- 定时器、PWM、ADC等功能
- 支持RTIC框架
使用方法
1. 添加依赖
在Cargo.toml
中添加:
[dependencies]
stm32f4xx-hal = { version = "0.15.0", features = ["stm32f407", "rt"] }
cortex-m = "0.7.6"
cortex-m-rt = "0.7.3"
注意:features
中的stm32f407
需要替换为你实际使用的MCU型号。
2. 基本GPIO控制示例
#![no_std]
#![no_main]
use panic_halt as _;
use cortex_m_rt::entry;
use stm32f4xx_hal::{
prelude::*,
gpio::GpioExt,
pac,
};
#[entry]
fn main() -> ! {
// 获取外设访问权限
let dp = pac::Peripherals::take().unwrap();
// 配置GPIO
let gpioc = dp.GPIOC.split();
let mut led = gpioc.pc13.into_push_pull_output();
loop {
// 翻转LED状态
led.toggle();
// 简单延时
cortex_m::asm::delay(8_000_000);
}
}
3. 串口通信示例
use stm32f4xx_hal::{
prelude::*,
serial::{Serial, config::Config},
pac,
};
#[entry]
fn main() -> ! {
let dp = pac::Peripherals::take().unwrap();
// 配置时钟
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.sysclk(84.mhz()).freeze();
// 配置GPIO
let gpioa = dp.GPIOA.split();
// 配置USART2
let tx_pin = gpioa.pa2.into_alternate();
let rx_pin = gpioa.pa3.into_alternate();
let serial = Serial::usart2(
dp.USART2,
(tx_pin, rx_pin),
Config::default().baudrate极速快3(115200.bps()),
&clocks,
).unwrap();
let (mut tx, mut _rx) = serial.split();
// 发送字符串
writeln!(tx, "Hello from STM32F4!\r").unwrap();
loop {}
}
4. 定时器与PWM示例
use stm32f极速快34xx-hal::{
prelude::*,
timer::Timer,
gpio::GpioExt,
pac,
};
#[entry]
fn main() -> ! {
let dp = pac::Peripherals::take().unwrap();
// 配置时钟
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.sysclk(84.mhz()).freeze();
// 配置GPIO
let gpioa = dp.GPIOA.split();
let pwm_pin = gpioa.pa8.into_alternate();
// 配置定时器1为PWM模式
let mut pwm = Timer::tim1(dp.TIM1, 1.khz(), &clocks)
.pwm(pwm_pin, 100.microseconds())
.unwrap();
let max_duty = pwm.get_max_duty();
pwm.enable();
// 呼吸灯效果
let mut duty = 0;
let mut step = 1;
loop {
pwm.set_duty(duty);
cortex_m::asm::delay(10_000);
duty += step;
if duty == 0 || duty == max_duty {
step = -step;
}
}
}
高级功能
1. 中断处理
use stm32f4xx-hal::{
interrupt,
gpio::{Input, PullUp, ExtiPin},
pac,
};
#[interrupt]
fn EXTI0() {
// 处理外部中断0
unsafe {
(*pac::EXTI::ptr()).pr.modify(|_, w| w.pr0().set_bit());
}
cortex_m::asm::bkpt(); // 调试断点
}
fn main() -> ! {
let dp = pac::Peripherals::take().unwrap();
// 配置GPIO和外部中断
let gpioa = dp.GPIOA.split();
let mut button = gpioa.pa0.into_pull_up_input();
button.make_interrupt_source(&mut dp.SYSCFG);
button.trigger_on_edge(&mut dp.EXTI, Edge::FALLING);
button.enable_interrupt(&mut dp.EXTI);
// 启用中断
unsafe {
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::EXTI0);
}
loop {}
}
2. DMA传输
use stm32f4xx-hal::{
dma::{self, StreamsTuple, StreamX, Transfer},
pac,
};
fn main() -> ! {
let dp = pac::Peripherals::take().unwrap();
// 配置DMA
let streams = StreamsTuple::new(dp.DMA2);
let mut dma = StreamX极速快3::new(streams.0);
// 创建源和目标缓冲区
let src: &'static mut [u8; 128] = cortex_m::singleton!(: [u8; 128] = [0xAA; 128]).unwrap();
let dst: &'static mut [u8; 128] = cortex_m::singleton!(: [u8; 128] = [0; 128]).unwrap();
// 创建DMA传输
let transfer = Transfer::init(
dma,
src,
dst,
None,
dma::config::DmaConfig::default()
.memory_increment(true)
.peripheral_increment(false),
);
// 启动传输
let transfer = transfer.start();
// 等待传输完成
while !transfer.get_transfer_complete_flag() {}
// 释放资源
let (_dma, _src, dst) = transfer.wait();
loop {
// 使用传输后的数据
if dst[0] == 0xAA {
cortex_m::asm::bkpt();
}
}
}
完整ADC示例
#![no_std]
#![no_main]
use panic_halt as _;
use cortex_m_rt::entry;
use stm32f4xx_hal::{
prelude::*,
pac,
adc::{
Adc,
config::AdcConfig,
},
delay::Delay,
};
#[entry]
fn main() -> ! {
// 获取外设访问权限
let dp = pac::Peripherals::take().unwrap();
let cp = cortex_m::Peripheral::take().unwrap();
// 配置时钟
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.sysclk(84.mhz()).freeze();
// 配置GPIO
let gpioa = dp.GPIOA.split();
// 配置ADC引脚
let adc_pin = gpioa.pa0.into_analog();
// 创建延时器
let mut delay = Delay::new(cp.SYST, clocks);
// 配置ADC
let mut adc = Adc::adc1(dp.ADC1, true, AdcConfig::default());
loop {
// 读取ADC值
let sample: u16 = adc.read(&adc_pin).unwrap();
// 简单延时
delay.delay_ms(1000u32);
}
}
完整I2C示例
#![no_std]
#![no_main]
use panic_halt as _;
use cortex_m_rt::entry;
use stm32f4xx_hal::{
prelude::*,
pac,
i2c::I2c,
};
#[entry]
fn main() -> ! {
// 获取外设访问权限
let dp = pac::Peripherals::take().unwrap();
// 配置时钟
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.sysclk(84.mhz()).freeze();
// 配置GPIO
let gpiob = dp.GPIOB.split();
// 配置I2C引脚
let scl = gpiob.pb6.into_alternate().set_open_drain();
let sda = gpiob.pb7.into_alternate().set_open_drain();
// 配置I2C
let mut i2c = I2c::i2c1(
dp.I2C1,
(scl, sda),
100.khz(),
clocks,
);
// I2C设备地址
const DEVICE_ADDR: u8 = 0x68;
// 寄存器地址
const REGISTER_ADDR: u8 = 0x00;
// 数据缓冲区
let mut data = [0u8; 1];
loop {
// 读取寄存器
i2c.write_read(DEVICE_ADDR, &[REGISTER_ADDR], &mut data).unwrap();
// 写入寄存器
i2c.write(DEVICE_ADDR, &[REGISTER_ADDR, data[0]]).unwrap();
}
}
开发建议
- 选择合适的MCU型号:确保在Cargo.toml中正确指定你的STM32F4型号
- 利用Rust的类型系统:HAL库的设计充分利用了Rust的类型安全特性
- 参考官方示例:stm32f4xx-hal仓库中有丰富的示例代码
- 结合RTIC框架:对于实时性要求高的应用,考虑使用RTIC框架
- 调试工具:建议使用probe-rs和cargo-embed进行调试
常见问题
- 时钟配置错误:确保正确配置系统时钟和外设时钟
- GPIO模式不正确:根据用途选择正确的GPIO模式(输入、输出、复用功能等)
- 中断未触发:检查中断优先级、使能状态和触发条件
- DMA传输失败:确认源和目标地址、传输方向和缓冲区大小设置正确
stm32f4xx-hal库为STM32F4系列MCU提供了强大的Rust支持,结合Rust的安全特性和嵌入式开发的灵活性,是开发可靠嵌入式系统的优秀选择。