STM32-HAL

Crate Docs

This library provides high-level access to STM32 peripherals.

Requirements

  1. Provide high-level access to most STM32 peripherals
  2. Support these STM32 families: F3, F4, L4, L5, G, H, U, and W
  3. Allow switching MCUs with minimal code change
  4. Provide a consistent API across peripheral modules
  5. Support both DMA and non-DMA interfaces
  6. Be suitable for commercial projects
  7. Implement embedded-hal traits for all applicable peripherals
  8. Provide a clear, concise API
  9. Provide source code readable by anyone cross-checking a reference manual (RM)

Specifications

Supported MCUs

F3, F4, L4, L5, G0, G4, H7, WB, and WL. U5 is planned once its SVD files and PAC become available.

Operationally tested on the following devices: - STM32F303 - STM32F411 - STM32L476, L433, L443, L412, L432 - STM32L552 - STM32WB5MMG - STM32H743(V)

Getting started

Quickstart

Details

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.

The blinky example, written by toudi, provides a detailed example and instructions for how to set up a blinking light (ie hello world) using an STM32F411 "blackpill" board. Its readme provides instructions for how to get started from scratch, and its code contains detailed comments explaining each part.

The conductivity module example is a complete example of simple production firmware. It uses the DAC, I2C, Timer, and UART peripherals, with a simple interupt-based control flow.

Additional examples in the examples folder demonstrate how to use various STM32 peripherals; most of these examples focus on a single peripheral.

When specifying this crate as a dependency in Cargo.toml, you need to specify a feature representing your MCU. If this is for code that runs on an MCU directly (ie not a library), also include a run-time feature, following the template l4rt. For example: toml cortex-m = "0.7.3" cortex-m-rt = "0.6.13" stm32-hal2 = { version = "^1.2.0", features = ["l4x3", "l4rt"]}

If you need embedded-hal traits, include the embedded-hal feature.

You can review this section of Cargo.toml to see which MCU and runtime features are available.

Example highlights:

```rust use cortexm; use cortexmrt::entry; use stm32hal2::{ clocks::Clocks, gpio::{Pin, Port, PinMode, OutputType}, i2c::{I2c, I2cDevice}, low_power, pac, timer::{Timer, TimerInterrupt}, };

[entry]

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().unwrap();

let mut pb15 = Pin::new(Port::A, 15, PinMode::Output);
pb15.set_high();

let mut timer = Timer::new_tim3(dp.TIM3, 0.2, &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 i2c = I2c::new(dp.I2C1, I2cDevice::One, Default::default(), &clock_cfg);

loop {
    i2c.write(0x50, &[1, 2, 3]);
    low_power::sleep_now(&mut cp.SCB);
}

} ```

Compatible with RTIC

Real-Time Interrupt-driven Concurrency is a light-weight framework that manages safely sharing state between contexts. Eg between ISRs and the main loop. Our examples use global Mutexes, RefCells, and Cells, sometimes with macros to simplify syntax; you could easily substitue RTIC syntax.

Why this module is different from stm32yxx-hal libraries

There 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, code documentation, code structure, and clock config.

If you'd like to learn more about these HALs, check them out on the stm32-rs Github. You may prefer them if you prioritize strict type checks on GPIO pins, for example.

Docs caveat

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.

Contributing

PRs are encouraged. Documenting each step using reference manuals is encouraged where applicable.

Most peripheral modules use the following format:

Example module structure:

```rust

[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 { // (regs is public, so users can use the PAC API directly, eg for unsupported features.) pub regs: R, pub prf: Prf, }

impl FcRadar where R: Deref, { /// 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.) free(|cs| { let mut rcc = unsafe { &(*RCC::ptr()) }; rccenreset!(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(),
    });
}

}

[cfg(feature = "embedded-hal")]

/// Wrap our native methods with embedded-hal traits. impl embedded_hal::TargetTrack for FcRadar where F: Deref, { type Error = Error;

fn track(&mut self, track: u8) -> Result<(), Error> {
    FcRadar::track(self, track);
    Ok(())
}

} ```

STM32WB and WL radio

This library doesn't include any radio functionality for the STM32WB. If you'd like to use it with bluetooth, use this HAL in conjuction with with @eupn's stm32wb55 bluetooth library.

STM32WL radio support is WIP, and will be provided through interaction withnewAM's stm32wl-hal library.

Errata