stm32_hal2/
lib.rs

1#![allow(unsafe_op_in_unsafe_fn)] // Suppresses a warning.
2
3//! This library provides high-level access to STM32 peripherals.
4//!
5//! **Current family support**: F3, F4, L4, L5, G0, G4, H7, and WB. U5 is planned once its SVD files and PAC
6//! become available.
7//!
8//! Please see the [Readme](https://github.com/David-OConnor/stm32-hal/blob/main/README.md) for a detailed overview,
9//! and the [examples folder on Github](https://github.com/David-OConnor/stm32-hal/tree/main/examples)
10//! for example code and project structure.
11//!
12//! ## Getting started
13//! Review the [syntax overview example](https://github.com/David-OConnor/stm32-hal/tree/main/examples/syntax_overview)
14//! for example uses of many of this library's features. Copy and paste its whole folder (It's set up
15//! using [Knurling's app template](https://github.com/knurling-rs/app-template)), or copy parts of `Cargo.toml`
16//! and `main.rs` as required.
17//!
18//! The [blinky example](https://github.com/David-OConnor/stm32-hal/tree/main/examples/blinky) provides a detailed example and instructions for how to set up a blinking
19//! light (ie hello world) using an STM32F411 "blackpill" board. Its readme provides instructions for how to get
20//! started from scratch, and its code contains detailed comments explaining each part. The
21//! [blinky with timer interrupt example](https://github.com/David-OConnor/stm32-hal/tree/main/examples/blinky_timer_interrupt)
22//! demonstrates how to accomplish the same in a non-blocking way, using a hardware timer. It uses RTIC.
23//!
24//! The [conductivity module example](https://github.com/David-OConnor/stm32-hal/tree/main/examples/conductivity_module)
25//! is a complete example of simple production firmware. It uses the DAC, I2C, Timer, and UART peripherals,
26//! with a simple interupt-based control flow.
27//!
28//! The [PDM mic, DAC output passthrough example](https://github.com/David-OConnor/stm32-hal/tree/main/examples/pdm_mic_dac_output.rs)
29//! demonstrates how to read audio from a digital microphone, output it to headphones or speakers using the DAC, and use DMA
30//! to do this efficiently. It conducts minimal processing, but can be modified to process using DSP between input and output.
31//! This example uses RTIC.
32//!
33//! Additional examples in the [examples folder](https://github.com/David-OConnor/stm32-hal/tree/main/examples) demonstrate
34//! how to use various STM32 peripherals; most of these examples focus on a single peripheral.
35//!
36//! When specifying this crate as a dependency in `Cargo.toml`, you need to specify a feature
37//! representing your MCU. If this is for code that runs on an MCU directly (ie not a library), also
38//!  include a run-time feature, following the template `l4rt`. For example:
39//! ```toml
40//! cortex-m = { version = "^0.7.7", features = ["critical-section-single-core"] }
41//! cortex-m-rt = "0.7.2"
42//! hal = { package = "stm32-hal2", version = "^1.5.5", features = ["l4x3", "l4rt"]}
43//! ```
44//!
45//! If you need `embedded-hal` traits, include the `embedded-hal` feature.
46//!
47//! You can review [this section of Cargo.toml](https://github.com/David-OConnor/stm32-hal/blob/main/Cargo.toml#L61)
48//! to see which MCU and runtime features are available.
49//!
50//! ### Example highlights:
51//! ```rust
52//! use cortex_m;
53//! use cortex_m_rt::entry;
54//! use hal::{
55//!     clocks::Clocks,
56//!     gpio::{Pin, Port, PinMode, OutputType},
57//!     i2c::I2c,
58//!     low_power,
59//!     pac,
60//!     timer::{Timer, TimerInterrupt},
61//!     setup_nvic,
62//! };
63//!
64//! #[entry]
65//! fn main() -> ! {
66//!    let mut cp = cortex_m::Peripherals::take().unwrap();
67//!    let mut dp = pac::Peripherals::take().unwrap();
68//!
69//!    let clock_cfg = Clocks::default();
70//!    clock_cfg.setup().unwrap();
71//!
72//!    let mut pb15 = Pin::new(Port::A, 15, PinMode::Output);
73//!    pb15.set_high();
74//!
75//!    let mut timer = Timer::new_tim3(dp.TIM3, 0.2, Default::default(), &clock_cfg);
76//!    timer.enable_interrupt(TimerInterrupt::Update);
77//!
78//!    let mut scl = Pin::new(Port::B, 6, PinMode::Alt(4));
79//!    scl.output_type(OutputType::OpenDrain);
80//!
81//!    let mut sda = Pin::new(Port::B, 7, PinMode::Alt(4));
82//!    sda.output_type(OutputType::OpenDrain);
83//!
84//!    let mut dma = Dma::new(dp.DMA1);
85//!    dma::mux(DmaPeriph::Dma1, DmaChannel::C1, DmaInput::I2c1Tx);
86//!
87//!    let i2c = I2c::new(dp.I2C1, Default::default(), &clock_cfg);
88//!
89//!    setup_nvic!([
90//!        (TIM3, 1),   
91//!    ], cp);
92//!
93//!    loop {
94//!        i2c.write(0x50, &[1, 2, 3]);
95//!        // Or:
96//!        i2c.write_dma(0x50, &BUF, DmaChannel::C1, Default::default(), DmaPeriph::Dma1);
97//!
98//!        low_power::sleep_now();
99//!    }
100//!}
101//!
102//! #[interrupt]
103//! /// Timer interrupt
104//! fn TIM3() {
105//!    timer::clear_update_interrupt(3);
106//!    // Perform an action here.
107//!}
108//! ```
109//!
110//! Supports the RTIC `Monotonic` trait. To enable, use the `monotonic` feature.
111//!
112//! [This article](https://www.anyleaf.org/blog/writing-embedded-firmware-using-rust) provides some information
113//! on using this library, as well as background information on Rust embedded in general.
114//!
115//! ## Docs caveat
116//! This Rust docs page is built for `STM32H735`, and some aspects are not accurate for other
117//! variants. Clock (RCC) config in particular varies significantly between variants. We currently
118//! don't have a good solution to this problem, and may self-host docs in the future.
119
120// Some overall notes:
121// We generally don't use the named field methods provided by PACs, as these are inconsistently
122// implemented among PACs. Ie f3's may have a `'`.enabled()` method, but `l4` does not;
123// in these cases, writing `set_bit()` works for both.
124
125// We use a combination of macros and feature-gating to handle differences in families, as appropriate.
126// We leverage the `paste` and `cfg-if` crates to improve syntax.
127
128// The main way we divide MCUs is by PAC modules. Note that there are sub-variants that may have differences
129// that this doesn't take into account. (eg different USB memory sizes among f303 variants)
130
131// We use `unsafe` blocks for most multi-fit field writes. This is required by some PACs, but not others.
132// The rust embedded team removes requirement for `unsafe` on fields that are deemed sufficiently
133// constrained as to not need these blocks.
134// Using `unsafe` for all is cleaner than feature-gating, due to how many fields this affects. We've allowed
135// these warnings; ie hidden during build.
136
137#![no_std]
138// Some reg modifications are marked `unsafe` in some PAC crates, but not others.
139// Disable these warnings.
140#![allow(unused_unsafe)]
141// The `doc_cfg` feature allows us to show functionality that is feature-gated on `docs.rs`.
142// todo: Re-implement the doc_cfg feature and the relevant tags (From all modules that impl EH traits)
143// todo oncoe this is in stable.
144// #![feature(doc_cfg)]
145
146// todo: H7B3 has too many changes in v14 PAC; not supporting at this time. (2021-10-07)
147
148// TODO: Unify the different error types into a more sensible structure.
149
150#[cfg(not(any(
151    feature = "f301",
152    feature = "f302",
153    feature = "f303",
154    feature = "f373",
155    feature = "f3x4",
156    feature = "f401",
157    feature = "f405",
158    feature = "f407",
159    feature = "f410",
160    feature = "f411",
161    feature = "f412",
162    feature = "f413",
163    feature = "f427",
164    feature = "f429",
165    feature = "f446",
166    feature = "f469",
167    feature = "l4x1",
168    feature = "l4x2",
169    feature = "l412",
170    feature = "l4x3",
171    feature = "l4x5",
172    feature = "l4x6",
173    feature = "l552",
174    feature = "l562",
175    feature = "g030",
176    feature = "g031",
177    feature = "g041",
178    feature = "g050",
179    feature = "g051",
180    feature = "g061",
181    feature = "g070",
182    feature = "g071",
183    feature = "g081",
184    feature = "g0b0",
185    feature = "g0b1",
186    feature = "g0c1",
187    feature = "g431",
188    feature = "g441",
189    feature = "g471",
190    feature = "g473",
191    feature = "g474",
192    feature = "g483",
193    feature = "g484",
194    feature = "g491",
195    feature = "g4a1",
196    feature = "h503",
197    feature = "h562",
198    feature = "h563",
199    feature = "h573",
200    feature = "h735",
201    feature = "h743",
202    feature = "h743v",
203    feature = "h747cm4",
204    feature = "h747cm7",
205    feature = "h753",
206    feature = "h753v",
207    feature = "h7b3",
208    feature = "wb55",
209    feature = "wle5",
210    feature = "c011",
211    feature = "c031",
212    feature = "c071",
213)))]
214compile_error!("This crate requires an MCU-specifying feature to be enabled. eg `l552`.");
215
216// Re-export of the [svd2rust](https://crates.io/crates/svd2rust) auto-generated API for
217// stm32 peripherals.
218
219// todo: U5 once SVD is out.
220use cfg_if::cfg_if;
221use cortex_m::{self, delay::Delay};
222// C0 PAC
223#[cfg(feature = "c011")]
224pub use stm32c0::stm32c011 as pac;
225#[cfg(feature = "c031")]
226pub use stm32c0::stm32c031 as pac;
227#[cfg(feature = "c071")]
228pub use stm32c0::stm32c071 as pac;
229#[cfg(feature = "f3x4")]
230pub use stm32f3::stm32f3x4 as pac;
231#[cfg(feature = "f301")]
232pub use stm32f3::stm32f301 as pac;
233#[cfg(feature = "f302")]
234pub use stm32f3::stm32f302 as pac;
235#[cfg(feature = "f303")]
236pub use stm32f3::stm32f303 as pac;
237#[cfg(feature = "f373")]
238pub use stm32f3::stm32f373 as pac;
239// F4 PAC
240#[cfg(feature = "f401")]
241pub use stm32f4::stm32f401 as pac;
242#[cfg(feature = "f405")]
243pub use stm32f4::stm32f405 as pac;
244#[cfg(feature = "f407")]
245pub use stm32f4::stm32f407 as pac;
246#[cfg(feature = "f410")]
247pub use stm32f4::stm32f410 as pac;
248#[cfg(feature = "f411")]
249pub use stm32f4::stm32f411 as pac;
250#[cfg(feature = "f412")]
251pub use stm32f4::stm32f412 as pac;
252#[cfg(feature = "f413")]
253pub use stm32f4::stm32f413 as pac;
254#[cfg(feature = "f427")]
255pub use stm32f4::stm32f427 as pac;
256#[cfg(feature = "f429")]
257pub use stm32f4::stm32f429 as pac;
258#[cfg(feature = "f446")]
259pub use stm32f4::stm32f446 as pac;
260#[cfg(feature = "f469")]
261pub use stm32f4::stm32f469 as pac;
262#[cfg(feature = "g0b0")]
263pub use stm32g0::stm32g0b0 as pac;
264#[cfg(feature = "g0b1")]
265pub use stm32g0::stm32g0b1 as pac;
266#[cfg(feature = "g0c1")]
267pub use stm32g0::stm32g0c1 as pac;
268// todo: Test and make accomodations for recently added G0 variants 50, 51, 61, B0, B1 and C1, in
269// todo the individual modules.
270
271// G0 PAC
272#[cfg(feature = "g030")]
273pub use stm32g0::stm32g030 as pac;
274#[cfg(feature = "g031")]
275pub use stm32g0::stm32g031 as pac;
276#[cfg(feature = "g041")]
277pub use stm32g0::stm32g041 as pac;
278#[cfg(feature = "g050")]
279pub use stm32g0::stm32g050 as pac;
280#[cfg(feature = "g051")]
281pub use stm32g0::stm32g051 as pac;
282#[cfg(feature = "g061")]
283pub use stm32g0::stm32g061 as pac;
284#[cfg(feature = "g070")]
285pub use stm32g0::stm32g070 as pac;
286#[cfg(feature = "g071")]
287pub use stm32g0::stm32g071 as pac;
288#[cfg(feature = "g081")]
289pub use stm32g0::stm32g081 as pac;
290#[cfg(feature = "g4a1")]
291pub use stm32g4::stm32g4a1 as pac;
292// G4 PAC
293#[cfg(feature = "g431")]
294pub use stm32g4::stm32g431 as pac;
295#[cfg(feature = "g441")]
296pub use stm32g4::stm32g441 as pac;
297#[cfg(feature = "g471")]
298pub use stm32g4::stm32g471 as pac;
299#[cfg(feature = "g473")]
300pub use stm32g4::stm32g473 as pac;
301#[cfg(feature = "g474")]
302pub use stm32g4::stm32g474 as pac;
303#[cfg(feature = "g483")]
304pub use stm32g4::stm32g483 as pac;
305#[cfg(feature = "g484")]
306pub use stm32g4::stm32g484 as pac;
307#[cfg(feature = "g491")]
308pub use stm32g4::stm32g491 as pac;
309// H5 PAC
310#[cfg(feature = "h503")]
311pub use stm32h5::stm32h503 as pac;
312#[cfg(feature = "h562")]
313pub use stm32h5::stm32h562 as pac;
314#[cfg(feature = "h563")]
315pub use stm32h5::stm32h563 as pac;
316#[cfg(feature = "h573")]
317pub use stm32h5::stm32h573 as pac;
318#[cfg(feature = "h7b3")]
319pub use stm32h7::stm32h7b3 as pac;
320// H7 PAC
321#[cfg(feature = "h735")]
322pub use stm32h7::stm32h735 as pac;
323#[cfg(feature = "h743")]
324pub use stm32h7::stm32h743 as pac;
325#[cfg(feature = "h743v")]
326pub use stm32h7::stm32h743v as pac;
327#[cfg(feature = "h747cm4")]
328pub use stm32h7::stm32h747cm4 as pac;
329#[cfg(feature = "h747cm7")]
330pub use stm32h7::stm32h747cm7 as pac;
331#[cfg(feature = "h753")]
332pub use stm32h7::stm32h753 as pac;
333#[cfg(feature = "h753v")]
334pub use stm32h7::stm32h753v as pac;
335// L4 PAC
336#[cfg(feature = "l4x1")]
337pub use stm32l4::stm32l4x1 as pac;
338#[cfg(feature = "l4x2")]
339pub use stm32l4::stm32l4x2 as pac;
340#[cfg(feature = "l4x3")]
341pub use stm32l4::stm32l4x3 as pac;
342#[cfg(feature = "l4x5")]
343pub use stm32l4::stm32l4x5 as pac;
344#[cfg(feature = "l4x6")]
345pub use stm32l4::stm32l4x6 as pac;
346#[cfg(feature = "l412")]
347pub use stm32l4::stm32l412 as pac;
348// L5 PAC
349#[cfg(feature = "l552")]
350pub use stm32l5::stm32l552 as pac;
351#[cfg(feature = "l562")]
352pub use stm32l5::stm32l562 as pac;
353#[cfg(feature = "wb55")]
354pub use stm32wb::stm32wb55 as pac;
355#[cfg(feature = "wle5")]
356pub use stm32wl::stm32wle5 as pac;
357
358pub mod macros;
359
360#[cfg(not(any(feature = "f301", feature = "f302", feature = "c0")))]
361pub mod adc;
362
363// bxCAN families: F3, F4, L4,
364// fdCAN families: L5, U5, G4, H7, C0
365// H7 suppords fd and can_ccu. (What's that?)
366// WB and WL?
367// todo: Support c0, if/when `fdcan supports it.`
368#[cfg(all(any(feature = "can_bx", feature = "can_fd_g", feature = "can_fd_h"),))]
369pub mod can;
370
371pub mod clocks;
372
373#[cfg(not(any(feature = "f", feature = "wb", feature = "wl",)))]
374pub mod crc;
375
376#[cfg(not(any(
377    feature = "f401",
378    feature = "f411",
379    feature = "f412",
380    feature = "l412",
381    feature = "wb",
382    feature = "g0",
383    feature = "c0",
384)))]
385// WB doesn't have a DAC. Some G0 variants do - add it! Most F4 variants have it, some don't
386pub mod dac;
387
388#[cfg(not(any(
389    feature = "f",
390    feature = "l4x1",
391    feature = "l4x2",
392    feature = "l412",
393    feature = "l4x3",
394    feature = "l4x5",
395    feature = "g0",
396    feature = "g4",
397    feature = "wb",
398    feature = "wl",
399    feature = "h5", // todo: Check PAC and impl A/R.
400    feature = "c0",
401// todo: DFSDM support for other platforms that don't support clustering
402)))]
403pub mod dfsdm;
404
405#[cfg(not(feature = "f4"))]
406pub mod dma;
407
408pub mod error;
409pub use error::Error;
410
411#[cfg(all(feature = "h7", feature = "net"))]
412pub mod ethernet;
413
414// todo: July 2025. Fix h735 flash, and put back. Mess of feature gate changes from 0.16 PAC.
415#[cfg(not(feature = "h735"))]
416pub mod flash;
417
418// todo: PAC doesn't yet support these newer H7 MCUs that use FMAC.
419// #[cfg(any(feature = "h723", feature = "h725", feature = "h733", feature = "h735"))]
420// todo: Also G4.
421// pub mod fmac;
422
423pub mod gpio;
424
425#[cfg(feature = "wb")]
426pub mod hsem;
427
428#[cfg(not(any(feature = "f4")))]
429pub mod i2c;
430#[cfg(feature = "f4")]
431pub mod i2c_f4;
432#[cfg(feature = "f4")]
433pub use i2c_f4 as i2c;
434
435#[cfg(feature = "wb")]
436pub mod ipcc;
437
438pub mod iwdg;
439
440pub mod low_power;
441
442#[cfg(any(feature = "h747cm4", feature = "h747cm7"))]
443pub mod power;
444
445// F3, F4, G0, and WL don't have Quad SPI. L5 and newer H variants (eg H735) use OctoSPI,
446// also supported by this module.
447#[cfg(not(any(
448    feature = "f",
449    feature = "l4x3", // todo: PAC bug?
450    feature = "g0",
451    feature = "g431",
452    feature = "g441",
453    feature = "g471",
454    feature = "g491",
455    feature = "g4a1",
456    feature = "wl",
457    feature = "l5", // todo: PAC errors on some regs.
458    feature = "h5",
459    feature = "c0",
460)))]
461pub mod qspi;
462
463// Note: Some F4 variants support RNG, but we haven't figured out the details yet. Send a PR if interested.
464#[cfg(not(any(feature = "f", feature = "g0", feature = "c0",)))]
465pub mod rng;
466
467pub mod rtc;
468
469#[cfg(not(any(
470    feature = "f",
471    feature = "g0",
472    feature = "h7b3",
473    feature = "wl",
474    feature = "h5", // todo
475    feature = "c0", // todo
476    feature = "l412",
477)))]
478pub mod sai;
479
480// as of Pac 0.16, unable to find spi1 reg for f301. (Renamed to something else?)
481#[cfg(not(feature = "f301"))]
482pub mod spi;
483
484pub mod timer;
485
486pub mod usart;
487
488// todo: More MCUs A/R. They will need modifications in the module.
489#[cfg(not(any(
490    feature = "f",
491    feature = "l",
492    feature = "g0",
493    feature = "wl",
494    feature = "c0"
495)))]
496pub mod lpuart;
497
498// todo: Fix and put this back when ready. Broke after PAC 0.16 update. (July 2025)
499// #[cfg(any(
500//     feature = "l4",
501//     // feature = "g4",
502//     feature = "g473", // todo: Not compiling on G431
503//     feature = "h7"
504// ))]
505// pub mod comp;
506
507// See note at top of `usb` module for info on G0; not avail on modules the PAC has avail.
508cfg_if! {
509    if #[cfg(all(
510        feature = "usb",
511        all(
512            any(
513                feature = "f303",
514                feature = "l4x2",
515                feature = "l412",
516                feature = "l4x3",
517                feature = "l4x5",
518                feature = "l5",
519                feature = "g4",
520                feature = "wb",
521                feature = "c071", // Only C071 from C0 has USB.
522            ),
523        not(feature = "g4a1"))
524    ))] {
525        pub mod usb;
526    } else if #[cfg(all(
527        // H7 has HS (high-speed), while F4 and L4 have FS. The names get confusing, starting
528        // on ST's side.
529        any(feature = "usbotg_fs", feature = "usbotg_hs"),
530        any(feature = "f4", feature = "l4x6", feature = "h7"),
531        not(feature = "f410")
532    ))] {
533        pub mod usb_otg;
534        pub use usb_otg as usb;
535    }
536}
537
538// For use with timers; converting ticks to real time.
539pub mod instant;
540mod util;
541pub use util::{BaudPeriph, RccPeriph};
542
543// todo: Remove this debug_workaroudn function on MCUs that don't require it. Ie, is this required on G4? G0?
544#[cfg(not(any(feature = "g0", feature = "c0", feature = "h747cm4")))]
545/// Workaround due to debugger disconnecting in WFI (and low-power) modes.
546/// This affects most (all?) STM32 devices. In production on battery-powered
547/// devices that don't use DMA, consider removing this, to prevent power
548/// use by the DMA clock.
549/// For why we enable the DMA clock, see STM32F446 errata, section 2.1.1.
550pub fn debug_workaround() {
551    let dbgmcu = unsafe { &(*pac::DBGMCU::ptr()) };
552
553    cfg_if! {
554        if #[cfg(all(feature = "h7", not(any(feature = "h747cm4", feature = "h747cm7"))))] {
555            dbgmcu.cr().modify(|_, w| w.dbgsleep_d1().bit(true));
556            dbgmcu.cr().modify(|_, w| w.dbgstop_d1().bit(true));
557            dbgmcu.cr().modify(|_, w| w.dbgstby_d1().bit(true));
558        } else if #[cfg(feature = "h7")] {
559            dbgmcu.cr().modify(|_, w| w.dbgslpd1().bit(true));
560            dbgmcu.cr().modify(|_, w| w.dbgstpd1().bit(true));
561            dbgmcu.cr().modify(|_, w| w.dbgstbd1().bit(true));
562        } else {
563            #[cfg(not(any(feature = "l5", feature = "h5")))]
564            dbgmcu.cr().modify(|_, w| w.dbg_sleep().bit(true));
565            dbgmcu.cr().modify(|_, w| w.dbg_stop().bit(true));
566            dbgmcu.cr().modify(|_, w| w.dbg_standby().bit(true));
567        }
568    }
569
570    let rcc = unsafe { &(*pac::RCC::ptr()) };
571
572    // todo Some MCUs may need the dbgmcu lines, but not DMA enabled.
573    // todo: Remove this part on MCUs not affected. F4 and L4 are confirmed affected.
574
575    cfg_if! {
576        if #[cfg(feature = "f3")] {
577            rcc.ahbenr().modify(|_, w| w.dma1en().bit(true));
578        } else if #[cfg(feature = "h5")] {
579            rcc.ahb1enr().modify(|_, w| w.gpdma1en().bit(true));
580        } else {
581            rcc.ahb1enr().modify(|_, w| w.dma1en().bit(true));
582        }
583    }
584}
585
586/// A blocking delay, for a specified time in ms.
587pub fn delay_ms(num_ms: u32, ahb_freq: u32) {
588    let cp = unsafe { cortex_m::Peripherals::steal() };
589    let mut delay = Delay::new(cp.SYST, ahb_freq);
590    delay.delay_ms(num_ms);
591}
592
593/// A blocking delay, for a specified time in μs.
594pub fn delay_us(num_us: u32, ahb_freq: u32) {
595    let cp = unsafe { cortex_m::Peripherals::steal() };
596    let mut delay = Delay::new(cp.SYST, ahb_freq);
597    delay.delay_us(num_us);
598}
599
600/// In the prelude, we export helper macros.
601pub mod prelude {
602    pub use crate::{
603        access_global, access_globals, copy_be, copy_le, init_globals, make_globals,
604        make_simple_globals, parse_be, parse_le, setup_nvic,
605    };
606}