50个版本 (稳定)
1.8.3 | 2024年5月8日 |
---|---|
1.8.2 | 2024年2月24日 |
1.8.1 | 2024年1月25日 |
1.7.0 | 2023年12月19日 |
0.2.0 | 2021年3月27日 |
26 in 嵌入式开发
每月325次下载
1MB
17K SLoC
STM32-HAL
此库提供对STM32外设的高级访问。
要求
- 提供对大多数STM32外设的高级访问
- 支持以下STM32系列:
F3
、F4
、L4
、L5
、G
、H
、U
和W
- 允许通过最小的代码更改切换MCU
- 在所有外设模块中提供一致的API
- 支持DMA和非DMA接口
- 适用于商业项目
- 提供清晰、简洁的API
- 提供可由任何交叉检查参考手册(RM)读取的源代码
规范
- 基于参考手册(RM)中描述的指令编写基本代码;将与相关摘录[4, 8]相关的文档内联
- 使用STM32外围访问Crates允许高级寄存器访问[2]
- 将PAC寄存器块包装在表示适用外设的结构体中,并使用公共方法访问这些外设的功能[1]
- 使用
#[cfg]
块和cfg_if!
宏来处理MCU之间的差异;在存在较大差异的地方使用单独的模块[2, 3] - 优先考虑功能、人机工程学和显式接口[6, 7]
- 使用描述设置的寄存器和字段以及来自RMs的描述来记录配置代码[4, 8]
- 提供示例和文档,以演示使用中断和DMA的外设使用[6]
支持的MCU
F3、F4、L4、L5、G0、G4、H5、H7、WB和WL。U5计划在SVD文件和PAC可用后进行。
在以下设备上进行测试
- STM32F303
- STM32F401、F411
- STM32L476、L433、L443、L412、L432
- STM32L552
- STM32WB5MMG
- STM32G431、G491、G473
- STM32H743(V)、H745(双核心)
入门指南
快速入门
- 安装Rust.
- 安装适用于您的MCU的编译目标。例如,运行以下命令:
rustup target add thumbv7em-none-eabihf
。如果您使用的是Cortex-M0、Cortex-M33(例如Stm32G0或L5),或者您不想使用硬件浮点数,则需要更改最后一部分。 - 安装闪存和调试工具:
cargo install flip-link
,cargo install probe-rs --features cli
。 - 克隆quickstart仓库:
git clone https://github.com/David-OConnor/stm32-hal-quickstart1
。 - 将以下行更改为与您的MCU匹配。如果您需要帮助,请发布一个问题
Cargo.toml
:hal = { package = "stm32-hal2", version = "^1.5.0", features = ["l4x3", "l4rt"]}
memory.x
:FLASH
和RAM
行.cargo/config.toml
:runner
和target
行。
- 连接您的设备。运行
cargo run --release
以编译和烧写。
详细信息
查看语法概述示例,了解该库许多功能的用法示例。复制并粘贴其整个文件夹(它使用Knurling的应用程序模板设置),或者根据需要复制Cargo.toml
和main.rs
的部分。
闪烁示例提供了一个详细的示例和说明,说明如何使用STM32F411 "blackpill"板设置闪烁灯(即hello world)。其readme提供了从头开始设置的说明,其代码包含解释每个部分的详细注释。具有定时器中断的闪烁示例展示了如何使用硬件定时器以非阻塞方式完成相同任务。它使用RTIC。
电导率模块示例是简单生产固件的完整示例。它使用DAC、I2C、定时器和UART外设,具有基于简单中断的控制流程。
PDM麦克风,DAC输出中继示例展示了如何从数字麦克风读取音频,通过DAC输出到耳机或扬声器,并使用DMA高效地执行此操作。它执行最少的处理,但可以修改为在输入和输出之间使用DSP进行处理。此示例使用RTIC。
《SPI IMU 过滤示例》演示了如何通过 SPI 配置外部 IMU(3 轴加速度计和陀螺仪),使用 DMA 在数据就绪时立即进行单个 SPI 传输读取多个寄存器,并使用 CMSIS-DSP 库进行数字滤波。本示例使用 RTIC。
在示例文件夹中的其他示例演示了如何使用各种 STM32 外设;大多数这些示例都侧重于单个外设。
当在 Cargo.toml
中将此软件包指定为依赖项时,您需要指定代表您的 MCU 的功能。如果是直接在 MCU 上运行的代码(即不是库),则还需要包含运行时功能,按照模板 l4rt
进行。例如
cortex-m = { version = "^0.7.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7.2"
hal = { package = "stm32-hal2", version = "^1.5.5", features = ["l4x3", "l4rt"]}
如果您需要 embedded-hal
特性,请包含 embedded_hal
功能。
您可以通过查看 Cargo.toml 的此部分 来了解哪些 MCU 和运行时功能可用。
示例亮点
use cortex_m;
use cortex_m_rt::entry;
use hal::{
clocks::Clocks,
gpio::{Pin, Port, PinMode, OutputType},
i2c::I2c,
low_power,
pac,
timer::{Timer, TimerInterrupt},
};
#[entry]
fn main() -> ! {
let mut dp = pac::Peripherals::take().unwrap();
let clock_cfg = Clocks::default();
clock_cfg.setup().unwrap();
let mut pb15 = Pin::new(Port::A, 15, PinMode::Output);
pb15.set_high();
let mut timer = Timer::new_tim3(dp.TIM3, 0.2, Default::default(), &clock_cfg);
timer.enable_interrupt(TimerInterrupt::Update);
let mut scl = Pin::new(Port::B, 6, PinMode::Alt(4));
scl.output_type(OutputType::OpenDrain);
let mut sda = Pin::new(Port::B, 7, PinMode::Alt(4));
sda.output_type(OutputType::OpenDrain);
let mut dma = Dma::new(dp.DMA1);
dma::mux(DmaPeriph::Dma1, DmaChannel::C1, DmaInput::I2c1Tx);
let i2c = I2c::new(dp.I2C1, Default::default(), &clock_cfg);
loop {
i2c.write(0x50, &[1, 2, 3]);
// Or:
i2c.write_dma(0x50, &BUF, DmaChannel::C1, Default::default(), DmaPeriph::Dma1);
low_power::sleep_now();
}
}
API
大多数外设的 API 具有这些方法
new()
接受一个 PAC 寄存器结构体,通常还有一个 Config 结构体。enable_interrupt()
接受一个中断类型枚举。clear_interrupt()
接受一个中断类型枚举。read_status()
返回外设状态寄存器作为整数。比较参考手册,例如在转换为二进制后。read()
,write()
等:阻塞read_dma()
,write_dma()
等:根据需要启动一个 DMA 传输,应在 ISR 中清理
特定外设具有不同的功能,根据需要参考文档。
兼容 RTIC
实时中断驱动并发 是一个轻量级框架,用于在上下文之间安全地共享状态。例如,在 ISRs 和主循环之间。一些示例使用全局 Mutex
、RefCell
和 Cell
;其他示例使用宏来简化语法;一些示例使用 RTIC。
支持 RTIC Monotonic
特性。要启用,请使用 monotonic
功能。
为什么这与 stm32yxx-hal
库不同
- 与多个 STM32 系列兼容,在可能的情况下具有相同的语法
- 更简单的语法
- 不使用类型状态,对外设抽象的拥有权依赖较少
- 不依赖于
embedded-hal
特性;将它们视为可选附加组件 - 对 DMA 的不同方法
- 显式时钟配置
- 详细的、一致的代码文档,包括参考手册摘录和引用
如果您想了解更多关于其他 HAL 的信息,请到 stm32-rs Github 上查看。如果您更看重对 GPIO 引脚和其他硬件的严格类型检查,例如,您可能更喜欢它们。
文档注意事项
该 Rust 文档页面是为 STM32H735
构建的,并且某些方面可能不适用于其他变体。我们目前还没有找到解决这个问题的好方法,未来可能会自行托管文档,并为每个 STM32 系列创建单独的页面。
贡献
鼓励提出PR。在适用的情况下,建议使用参考手册来记录每一步。
大多数外设模块使用以下格式
- 为各种配置设置使用枚举,实现与其相关寄存器值的
#[repr(u8)]
- 一个具有公共配置字段的 外设结构体。此结构体还包括一个私有的
regs
字段,它是一个适当的寄存器块。尽可能在实现中泛型定义,例如:U: Deref<Target = pac::usart1::RegisterBlock>
。参考 stm32-rs-nightlies Github 以确定何时可以利用这一点。 - 如果配置字段复杂,我们使用由外设结构体拥有的单独的
PeriphConfig
结构体。此结构体实现Default
。 - 一个名为
new
的构造函数,执行设置代码 enable_interrupt
和clear_interrupt
函数,它们接受一个中断类型的枚举。- 根据需要添加
embedded-hal
实现,调用本地方法。请注意,我们基于STM32的能力设计API,并应用适用的EH特质。我们仅在选择了embedded_hal
功能时公开这些实现。 - 当可用时,基于参考手册中提供的说明进行基本设置和使用步骤。这些步骤在执行每个步骤之前以注释形式粘贴到代码中。
- 不要使用PAC便利字段设置;它们在PAC之间实现不一致。(例如,不要使用类似
en.enabled()
的内容;使用en.set_bit()
。) - 如果使用常用的配置枚举,例如
Mode
,请在前面加上外设类型的前缀,例如使用RadarMode
。这防止了直接导入枚举时的命名空间冲突。
示例模块结构
#[derive(clone, copy)]
#[repr(u8)]
/// Select pulse repetition frequency. Sets `FCRDR_CR` register, `PRF` field.
enum Prf {
/// Medium PRF (less than 10Ghz)
Medium = 0,
/// High PRF (10Ghz or greater)
High = 1,
}
#[derive(clone, copy)]
/// Available interrupts. Enabled in `FCRDR_CR`, `...IE` fields. Cleared in `FCRDR_ICR`.
enum FcRadarInterrupt {
/// Target acquired, and the system is now in tracking mode.
TgtAcq,
/// Lost the track, for any reason.
LostTrack,
}
/// Represents a Fire Control Radar (FCR) peripheral.
pub struct FcRadar<R> {
// (`regs` is public, so users can use the PAC API directly, eg for unsupported features.)
pub regs: R,
pub prf: Prf,
}
impl<F> FcRadar<R>
where
R: Deref<Target = pac::fcrdr1::RegisterBlock>,
{
/// Initialize a FCR peripheral, including configuration register writes, and enabling and resetting
/// its RCC peripheral clock.
pub fn new(regs: R, prf: Prf) -> Self {
// (A critical section here prevents race conditions, while preventing
// the user from needing to pass RCC in explicitly.)
let mut rcc = unsafe { &(*RCC::ptr()) };
rcc_en_reset!(apb1, fcradar1, rcc);
regs.cr.modify(|_, w| w.prf().bit(prf as u8 != 0));
Self { regs, prf }
}
/// Track a target. See H8 RM, section 3.3.5: Tracking procedures.
pub fn track(&mut self, hit_num: u8) -> Self {
// RM: "To begin tracking a target, perform the following steps:"
// 1. Select the hit to track by setting the HIT bits in the FCRDR_TR register.
#[cfg(feature = "h8")]
self.regs.tr.modify(|_, w| unsafe { w.hit().bits(hit_num) });
#[cfg(feature = "g5")]
self.regs.tr.modify(|_, w| unsafe { w.hitn().bits(hit_num) });
// 2. Begin tracking by setting the TRKEN bit in the FCRDR_TR register.
self.regs.tr.modify(|_, w| w.trken().set_bit());
// In tracking mode, the TA flag can be monitored to make sure that the radar
// is still tracking the target.
}
/// Enable an interrupt.
pub fn enable_interrupt(&mut self, interrupt: FcRadarInterrupt) {
self.regs.cr.modify(|_, w| match interrupt {
FcRadarInterrupt::TgtAcq => w.taie().set_bit(),
FcRadarInterrupt::LostTrack => w.ltie().set_bit(),
});
}
/// Clear an interrupt flag - run this in the interrupt's handler to prevent
/// repeat firings.
pub fn clear_interrupt(&mut self, interrupt: FcRadarInterrupt) {
self.regs.icr.write(|w| match interrupt {
FcRadarInterrupt::TgtAcq => w.tacf().set_bit(),
FcRadarInterrupt::LostTrack => w.ltcf().set_bit(),
});
}
}
这篇文章 提供了一些有关使用此库的信息,以及有关Rust嵌入式的一般背景信息。
STM32WB 和 WL 无线电
此库不包含STM32WB的任何无线电功能。如果您想与蓝牙一起使用,请使用此HAL与 @eupn 的 stm32wb55 蓝牙库一起使用。
STM32WL无线电支持正在进行中,将通过与newAM的 stm32wl-hal 库的交互提供。
错误
- SDIO 和以太网未实现
- F4 和 L552 上的DMA未实现
- H7 BDMA 和 MDMA 未实现
- H5 GPDMA 未实现
- F4 上的USART中断未实现
- F4 上的CRC未实现
- 高分辨率定时器(HRTIM)、低功耗定时器(LPTIM)和低功耗USART(LPUSART)未实现
- F4 上的ADC未实现
- H7 之外的低功耗模式(除csleep和cstop外)未实现
- WB 和 WL 缺少与第二个核心操作和射频相关的功能
- L4+ MCU不受支持
- WL 缺少GPIO端口C和GPIO中断支持
- 如果在高级控制定时器(例如 TIM1 或 8)上使用 PWM(或一般输出比较),您必须手动设置
TIMx_BDTR
寄存器,MOE
位。 - Octospi 实现有误
- L4x6 上缺少 DFSDM 的 Filter 1。
- G0 和 H7:仅实现了 FDCAN1。
- H5 缺少很多功能,包括 DMA。
依赖项
~2–88MB
~2.5M SLoC