This library provides high-level access to STM32 peripherals. It's based on the STM32 Peripheral Access Crates generated by svd2rust. It provides a consistent API across multiple STM32 families, with minimal code repetition. This makes it easy to switch MCUs within, or across families, for a given project.
Family support: F3, F4, L4, L5, G0, G4, H7, and WB. U5 is planned once its SVD files and PAC become available. WL eventually.
Motivation: Use STM32s in real-world hardware projects. Be able to switch MCUs with minimal code change.
Design priority: Get hardware working with a robust feature set, and a clean, consistent API. Design around practical uses. The flexibility to choose the right MCU for each project, without changing code bases.
Both DMA (Direct Memory Access) and non-DMA APIs and examples are provided. DMA APIs provide faster and more efficient
operations, while non-DMA APIs are simpler, and are compatible with embedded-hal
.
Review the syntax overview example
for example uses of many of this library's features. Copy and paste its whole folder (It's set up
using Knurling's app template), or copy parts of Cargo.toml
and main.rs
as required.
```rust use cortexm; use cortexmrt::entry; use stm32hal2::{ clocks::Clocks, gpio::{GpioB, PinNum, PinMode, OutputType, AltFn}, i2c::{I2c, I2cDevice}, low_power, pac, timer::{Event::TimeOut, Timer}, };
fn main() -> ! { let mut cp = cortex_m::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
let clock_cfg = Clocks::default();
clock_cfg.setup(&mut dp.RCC, &mut dp.FLASH).unwrap();
let mut gpiob = GpioB::new(dp.GPIOB, &mut dp.RCC);
let mut pb15 = gpiob.new_pin(PinNum::P15, PinMode::Output);
pb15.set_high();
let mut timer = Timer::new_tim3(dp.TIM3, 0.2, &clock_cfg, &mut dp.RCC);
timer.enable_interrupt(TimerInterrupt::Update);
let mut scl = gpiob.new_pin(PinNum::P6, PinMode::Alt(AltFn::Af4));
scl.output_type(OutputType::OpenDrain, &mut gpiob.regs);
let mut sda = gpiob.new_pin(PinNum::P7, PinMode::Alt(AltFn::Af4));
sda.output_type(OutputType::OpenDrain, &mut gpiob.regs);
let i2c = I2c::new(dp.I2C1, I2cDevice::One, 100_000, &clock_cfg, &mut dp.RCC);
loop {
low_power::sleep_now(&mut cp.SCB);
}
} ```
stm32yxx-hal
librariesThere are some areas where design philosophy is different. For example: GPIO
type-checking, level-of-abstraction from registers/PAC, role of DMA, role of embedded-hal
traits in the API,
feature parity among STM32 families, and clock config.
stm32yxx-hal
s focus is building an API that doesn't let users configure something wrong.
These guards complicate both HAL and user code, and limit functionality.
The Rust docs page is built for STM32L4x3, and some aspects are not accurate for other variants. We currently don't have a good solution to this problem, and may self-host docs in the future.
PRs are encouraged. Documenting each step using reference manuals is encouraged where applicable.
Most peripheral modules use the following format:
#[repr(u8)]
for their associated register valuesregs
field that is the appropriate reg block. Where possible, this is defined generically
in the implementation, eg:
U: Deref<Target = pac::usart1::RegisterBlock>
. Reference the stm32-rs-nightlies Github
to identify when we can take advantage of this.PeriphConfig
struct owned by the peripheral struct.
This struct impls Default
.&mut dp.RCC
to methods when able.new
that performs setup code, including RCC peripheral enable and resetenable_interrupt
and clear_interrupt
functions, which accept an enum of interrupt typeembedded-hal
implementations as required, that call native methods. Note that
we design APIs based on STM32 capabilities, and apply EH traits as applicable.en.enabled()
- use en.set_bit()
.)```rust
/// Select pulse repetition frequency. Modifies FCRDR_CR
register, PRF
field.
enum Prf {
Medium = 0,
High = 1,
}
/// Available interrupts. Enabled in FCRDR_CR
, ...IE
fields. Cleared in FCRDR_ICR
.
enum FcRadarInterrupt {
TgtAcq,
LostTrack,
}
/// Represents a Fire Control Radar (FCR) peripheral.
pub struct FcRadar
impl
regs.cr.modify(|_, w| w.prf().bit(prf as u8 != 0));
Self { regs, prf }
}
/// Track a target. See H8 RM, section 3.3.5.
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) {
match interrupt {
FcRadarInterrupt::TgtAcq => self.regs.cr.modify(|_, w| w.taie().set_bit()),
FcRadarInterrupt::LostTrack => self.regs.cr.modify(|_, w| 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) {
match interrupt {
FcRadarInterrupt::TgtAcq => self.regs.icr.write(|w| w.tacf().set_bit()),
FcRadarInterrupt::LostTrack => self.regs.icr.write(|w| w.ltcf().set_bit()),
}
}
}
/// Wrap our native methods with embedded-hal
traits.
impl
fn track(&mut self, track: u8) -> Result<(), Error> {
FcRadar::track(self, track);
Ok(())
}
} ```