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// Used for while loops, to allow returning an error instead of hanging.
149pub(crate) const MAX_ITERS: u32 = 300_000; // todo: What should this be?
150
151#[cfg(not(any(
152    feature = "f301",
153    feature = "f302",
154    feature = "f303",
155    feature = "f373",
156    feature = "f3x4",
157    feature = "f401",
158    feature = "f405",
159    feature = "f407",
160    feature = "f410",
161    feature = "f411",
162    feature = "f412",
163    feature = "f413",
164    feature = "f427",
165    feature = "f429",
166    feature = "f446",
167    feature = "f469",
168    feature = "l4x1",
169    feature = "l4x2",
170    feature = "l412",
171    feature = "l4x3",
172    feature = "l4x5",
173    feature = "l4x6",
174    feature = "l552",
175    feature = "l562",
176    feature = "g030",
177    feature = "g031",
178    feature = "g041",
179    feature = "g050",
180    feature = "g051",
181    feature = "g061",
182    feature = "g070",
183    feature = "g071",
184    feature = "g081",
185    feature = "g0b0",
186    feature = "g0b1",
187    feature = "g0c1",
188    feature = "g431",
189    feature = "g441",
190    feature = "g471",
191    feature = "g473",
192    feature = "g474",
193    feature = "g483",
194    feature = "g484",
195    feature = "g491",
196    feature = "g4a1",
197    feature = "h503",
198    feature = "h562",
199    feature = "h563",
200    feature = "h573",
201    feature = "h735",
202    feature = "h743",
203    feature = "h743v",
204    feature = "h747cm4",
205    feature = "h747cm7",
206    feature = "h753",
207    feature = "h753v",
208    feature = "h7b3",
209    feature = "wb55",
210    feature = "wle5",
211    feature = "c011",
212    feature = "c031",
213    feature = "c071",
214)))]
215compile_error!("This crate requires an MCU-specifying feature to be enabled. eg `l552`.");
216
217// Re-export of the [svd2rust](https://crates.io/crates/svd2rust) auto-generated API for
218// stm32 peripherals.
219
220// todo: U5 once SVD is out.
221use cfg_if::cfg_if;
222use cortex_m::{self, delay::Delay};
223// C0 PAC
224#[cfg(feature = "c011")]
225pub use stm32c0::stm32c011 as pac;
226#[cfg(feature = "c031")]
227pub use stm32c0::stm32c031 as pac;
228#[cfg(feature = "c071")]
229pub use stm32c0::stm32c071 as pac;
230#[cfg(feature = "f3x4")]
231pub use stm32f3::stm32f3x4 as pac;
232#[cfg(feature = "f301")]
233pub use stm32f3::stm32f301 as pac;
234#[cfg(feature = "f302")]
235pub use stm32f3::stm32f302 as pac;
236#[cfg(feature = "f303")]
237pub use stm32f3::stm32f303 as pac;
238#[cfg(feature = "f373")]
239pub use stm32f3::stm32f373 as pac;
240// F4 PAC
241#[cfg(feature = "f401")]
242pub use stm32f4::stm32f401 as pac;
243#[cfg(feature = "f405")]
244pub use stm32f4::stm32f405 as pac;
245#[cfg(feature = "f407")]
246pub use stm32f4::stm32f407 as pac;
247#[cfg(feature = "f410")]
248pub use stm32f4::stm32f410 as pac;
249#[cfg(feature = "f411")]
250pub use stm32f4::stm32f411 as pac;
251#[cfg(feature = "f412")]
252pub use stm32f4::stm32f412 as pac;
253#[cfg(feature = "f413")]
254pub use stm32f4::stm32f413 as pac;
255#[cfg(feature = "f427")]
256pub use stm32f4::stm32f427 as pac;
257#[cfg(feature = "f429")]
258pub use stm32f4::stm32f429 as pac;
259#[cfg(feature = "f446")]
260pub use stm32f4::stm32f446 as pac;
261#[cfg(feature = "f469")]
262pub use stm32f4::stm32f469 as pac;
263#[cfg(feature = "g0b0")]
264pub use stm32g0::stm32g0b0 as pac;
265#[cfg(feature = "g0b1")]
266pub use stm32g0::stm32g0b1 as pac;
267#[cfg(feature = "g0c1")]
268pub use stm32g0::stm32g0c1 as pac;
269// todo: Test and make accomodations for recently added G0 variants 50, 51, 61, B0, B1 and C1, in
270// todo the individual modules.
271
272// G0 PAC
273#[cfg(feature = "g030")]
274pub use stm32g0::stm32g030 as pac;
275#[cfg(feature = "g031")]
276pub use stm32g0::stm32g031 as pac;
277#[cfg(feature = "g041")]
278pub use stm32g0::stm32g041 as pac;
279#[cfg(feature = "g050")]
280pub use stm32g0::stm32g050 as pac;
281#[cfg(feature = "g051")]
282pub use stm32g0::stm32g051 as pac;
283#[cfg(feature = "g061")]
284pub use stm32g0::stm32g061 as pac;
285#[cfg(feature = "g070")]
286pub use stm32g0::stm32g070 as pac;
287#[cfg(feature = "g071")]
288pub use stm32g0::stm32g071 as pac;
289#[cfg(feature = "g081")]
290pub use stm32g0::stm32g081 as pac;
291#[cfg(feature = "g4a1")]
292pub use stm32g4::stm32g4a1 as pac;
293// G4 PAC
294#[cfg(feature = "g431")]
295pub use stm32g4::stm32g431 as pac;
296#[cfg(feature = "g441")]
297pub use stm32g4::stm32g441 as pac;
298#[cfg(feature = "g471")]
299pub use stm32g4::stm32g471 as pac;
300#[cfg(feature = "g473")]
301pub use stm32g4::stm32g473 as pac;
302#[cfg(feature = "g474")]
303pub use stm32g4::stm32g474 as pac;
304#[cfg(feature = "g483")]
305pub use stm32g4::stm32g483 as pac;
306#[cfg(feature = "g484")]
307pub use stm32g4::stm32g484 as pac;
308#[cfg(feature = "g491")]
309pub use stm32g4::stm32g491 as pac;
310// H5 PAC
311#[cfg(feature = "h503")]
312pub use stm32h5::stm32h503 as pac;
313#[cfg(feature = "h562")]
314pub use stm32h5::stm32h562 as pac;
315#[cfg(feature = "h563")]
316pub use stm32h5::stm32h563 as pac;
317#[cfg(feature = "h573")]
318pub use stm32h5::stm32h573 as pac;
319#[cfg(feature = "h7b3")]
320pub use stm32h7::stm32h7b3 as pac;
321// H7 PAC
322#[cfg(feature = "h735")]
323pub use stm32h7::stm32h735 as pac;
324#[cfg(feature = "h743")]
325pub use stm32h7::stm32h743 as pac;
326#[cfg(feature = "h743v")]
327pub use stm32h7::stm32h743v as pac;
328#[cfg(feature = "h747cm4")]
329pub use stm32h7::stm32h747cm4 as pac;
330#[cfg(feature = "h747cm7")]
331pub use stm32h7::stm32h747cm7 as pac;
332#[cfg(feature = "h753")]
333pub use stm32h7::stm32h753 as pac;
334#[cfg(feature = "h753v")]
335pub use stm32h7::stm32h753v as pac;
336// L4 PAC
337#[cfg(feature = "l4x1")]
338pub use stm32l4::stm32l4x1 as pac;
339#[cfg(feature = "l4x2")]
340pub use stm32l4::stm32l4x2 as pac;
341#[cfg(feature = "l4x3")]
342pub use stm32l4::stm32l4x3 as pac;
343#[cfg(feature = "l4x5")]
344pub use stm32l4::stm32l4x5 as pac;
345#[cfg(feature = "l4x6")]
346pub use stm32l4::stm32l4x6 as pac;
347#[cfg(feature = "l412")]
348pub use stm32l4::stm32l412 as pac;
349// L5 PAC
350#[cfg(feature = "l552")]
351pub use stm32l5::stm32l552 as pac;
352#[cfg(feature = "l562")]
353pub use stm32l5::stm32l562 as pac;
354#[cfg(feature = "wb55")]
355pub use stm32wb::stm32wb55 as pac;
356#[cfg(feature = "wle5")]
357pub use stm32wl::stm32wle5 as pac;
358
359pub mod macros;
360
361#[cfg(not(any(feature = "f301", feature = "f302", feature = "c0")))]
362pub mod adc;
363
364// bxCAN families: F3, F4, L4,
365// fdCAN families: L5, U5, G4, H7, C0
366// H7 suppords fd and can_ccu. (What's that?)
367// WB and WL?
368// todo: Support c0, if/when `fdcan supports it.`
369#[cfg(all(any(feature = "can_bx", feature = "can_fd_g", feature = "can_fd_h"),))]
370pub mod can;
371
372pub mod clocks;
373
374#[cfg(not(any(feature = "f", feature = "wb", feature = "wl",)))]
375pub mod crc;
376
377#[cfg(not(any(
378    feature = "f401",
379    feature = "f411",
380    feature = "f412",
381    feature = "l412",
382    feature = "wb",
383    feature = "g0",
384    feature = "c0",
385)))]
386// WB doesn't have a DAC. Some G0 variants do - add it! Most F4 variants have it, some don't
387pub mod dac;
388
389#[cfg(not(any(
390    feature = "f",
391    feature = "l4x1",
392    feature = "l4x2",
393    feature = "l412",
394    feature = "l4x3",
395    feature = "l4x5",
396    feature = "g0",
397    feature = "g4",
398    feature = "wb",
399    feature = "wl",
400    feature = "h5", // todo: Check PAC and impl A/R.
401    feature = "c0",
402// todo: DFSDM support for other platforms that don't support clustering
403)))]
404pub mod dfsdm;
405
406#[cfg(not(feature = "f4"))]
407pub mod dma;
408
409#[cfg(all(feature = "h7", feature = "net"))]
410pub mod ethernet;
411
412// todo: July 2025. Fix h735 flash, and put back. Mess of feature gate changes from 0.16 PAC.
413#[cfg(not(feature = "h735"))]
414pub mod flash;
415
416// todo: PAC doesn't yet support these newer H7 MCUs that use FMAC.
417// #[cfg(any(feature = "h723", feature = "h725", feature = "h733", feature = "h735"))]
418// todo: Also G4.
419// pub mod fmac;
420
421pub mod gpio;
422
423#[cfg(feature = "wb")]
424pub mod hsem;
425
426#[cfg(not(any(feature = "f4")))]
427pub mod i2c;
428#[cfg(feature = "f4")]
429pub mod i2c_f4;
430#[cfg(feature = "f4")]
431pub use i2c_f4 as i2c;
432
433#[cfg(feature = "wb")]
434pub mod ipcc;
435
436pub mod iwdg;
437
438pub mod low_power;
439
440#[cfg(any(feature = "h747cm4", feature = "h747cm7"))]
441pub mod power;
442
443// F3, F4, G0, and WL don't have Quad SPI. L5 and newer H variants (eg H735) use OctoSPI,
444// also supported by this module.
445#[cfg(not(any(
446feature = "f",
447feature = "l4x3", // todo: PAC bug?
448feature = "g0",
449feature = "g431",
450feature = "g441",
451feature = "g471",
452feature = "g491",
453feature = "g4a1",
454feature = "wl",
455feature = "l5", // todo: PAC errors on some regs.
456feature = "h5",
457feature = "c0",
458)))]
459pub mod qspi;
460
461// Note: Some F4 variants support RNG, but we haven't figured out the details yet. Send a PR if interested.
462#[cfg(not(any(feature = "f", feature = "g0", feature = "c0",)))]
463pub mod rng;
464
465#[cfg(not(feature = "c0"))] // todo
466pub mod rtc;
467
468#[cfg(not(any(
469    feature = "f",
470    feature = "g0",
471    feature = "h7b3",
472    feature = "wl",
473    feature = "h5", // todo
474    feature = "c0", // todo
475    feature = "l412",
476)))]
477pub mod sai;
478
479// as of Pac 0.16, unable to find spi1 reg for f301. (Renamed to something else?)
480#[cfg(not(feature = "f301"))]
481pub mod spi;
482
483pub mod timer;
484
485pub mod usart;
486
487// todo: More MCUs A/R. They will need modifications in the module.
488#[cfg(not(any(
489    feature = "f",
490    feature = "l",
491    feature = "g0",
492    feature = "wl",
493    feature = "c0"
494)))]
495pub mod lpuart;
496
497// todo: Fix and put this back when ready. Broke after PAC 0.16 update. (July 2025)
498// #[cfg(any(
499//     feature = "l4",
500//     // feature = "g4",
501//     feature = "g473", // todo: Not compiling on G431
502//     feature = "h7"
503// ))]
504// pub mod comp;
505
506// See note at top of `usb` module for info on G0; not avail on modules the PAC has avail.
507cfg_if! {
508    if #[cfg(all(
509        feature = "usb",
510        all(
511            any(
512                feature = "f303",
513                feature = "l4x2",
514                feature = "l412",
515                feature = "l4x3",
516                feature = "l4x5",
517                feature = "l5",
518                feature = "g4",
519                feature = "wb",
520                feature = "c071", // Only C071 from C0 has USB.
521            ),
522        not(feature = "g4a1"))
523    ))] {
524        pub mod usb;
525    } else if #[cfg(all(
526        // H7 has HS (high-speed), while F4 and L4 have FS. The names get confusing, starting
527        // on ST's side.
528        any(feature = "usbotg_fs", feature = "usbotg_hs"),
529        any(feature = "f4", feature = "l4x6", feature = "h7"),
530        not(feature = "f410")
531    ))] {
532        pub mod usb_otg;
533        pub use usb_otg as usb;
534    }
535}
536
537// For use with timers; converting ticks to real time.
538pub mod instant;
539mod util;
540pub use util::{BaudPeriph, RccPeriph};
541
542// todo: Remove this debug_workaroudn function on MCUs that don't require it. Ie, is this required on G4? G0?
543#[cfg(not(any(feature = "g0", feature = "c0", feature = "h747cm4")))]
544/// Workaround due to debugger disconnecting in WFI (and low-power) modes.
545/// This affects most (all?) STM32 devices. In production on battery-powered
546/// devices that don't use DMA, consider removing this, to prevent power
547/// use by the DMA clock.
548/// For why we enable the DMA clock, see STM32F446 errata, section 2.1.1.
549pub fn debug_workaround() {
550    let dbgmcu = unsafe { &(*pac::DBGMCU::ptr()) };
551
552    cfg_if! {
553        if #[cfg(all(feature = "h7", not(any(feature = "h747cm4", feature = "h747cm7"))))] {
554            dbgmcu.cr().modify(|_, w| w.dbgsleep_d1().bit(true));
555            dbgmcu.cr().modify(|_, w| w.dbgstop_d1().bit(true));
556            dbgmcu.cr().modify(|_, w| w.dbgstby_d1().bit(true));
557        } else if #[cfg(feature = "h7")] {
558            dbgmcu.cr().modify(|_, w| w.dbgslpd1().bit(true));
559            dbgmcu.cr().modify(|_, w| w.dbgstpd1().bit(true));
560            dbgmcu.cr().modify(|_, w| w.dbgstbd1().bit(true));
561        } else {
562            #[cfg(not(any(feature = "l5", feature = "h5")))]
563            dbgmcu.cr().modify(|_, w| w.dbg_sleep().bit(true));
564            dbgmcu.cr().modify(|_, w| w.dbg_stop().bit(true));
565            dbgmcu.cr().modify(|_, w| w.dbg_standby().bit(true));
566        }
567    }
568
569    let rcc = unsafe { &(*pac::RCC::ptr()) };
570
571    // todo Some MCUs may need the dbgmcu lines, but not DMA enabled.
572    // todo: Remove this part on MCUs not affected. F4 and L4 are confirmed affected.
573
574    cfg_if! {
575        if #[cfg(feature = "f3")] {
576            rcc.ahbenr().modify(|_, w| w.dma1en().bit(true));
577        } else if #[cfg(feature = "h5")] {
578            rcc.ahb1enr().modify(|_, w| w.gpdma1en().bit(true));
579        } else {
580            rcc.ahb1enr().modify(|_, w| w.dma1en().bit(true));
581        }
582    }
583}
584
585/// A blocking delay, for a specified time in ms.
586pub fn delay_ms(num_ms: u32, ahb_freq: u32) {
587    let cp = unsafe { cortex_m::Peripherals::steal() };
588    let mut delay = Delay::new(cp.SYST, ahb_freq);
589    delay.delay_ms(num_ms);
590}
591
592/// A blocking delay, for a specified time in μs.
593pub fn delay_us(num_us: u32, ahb_freq: u32) {
594    let cp = unsafe { cortex_m::Peripherals::steal() };
595    let mut delay = Delay::new(cp.SYST, ahb_freq);
596    delay.delay_us(num_us);
597}
598
599/// In the prelude, we export helper macros.
600pub mod prelude {
601    pub use crate::{
602        access_global, access_globals, copy_be, copy_le, init_globals, make_globals,
603        make_simple_globals, parse_be, parse_le, setup_nvic,
604    };
605}