ariel_os_stm32/
lib.rs

1//! Items specific to the STMicroelectronics STM32 MCUs.
2
3#![no_std]
4#![cfg_attr(nightly, feature(doc_cfg))]
5#![cfg_attr(feature = "rcc-config-override", expect(unsafe_code))]
6#![deny(missing_docs)]
7
8pub mod gpio;
9
10#[doc(hidden)]
11pub mod peripheral {
12    pub use embassy_stm32::Peri;
13}
14
15#[cfg(feature = "external-interrupts")]
16#[doc(hidden)]
17pub mod extint_registry;
18
19#[cfg(feature = "i2c")]
20pub mod i2c;
21
22#[doc(hidden)]
23pub mod identity;
24
25#[cfg(feature = "spi")]
26pub mod spi;
27
28#[cfg(feature = "uart")]
29pub mod uart;
30
31#[cfg(feature = "storage")]
32#[doc(hidden)]
33pub mod storage;
34
35#[cfg(feature = "usb")]
36#[doc(hidden)]
37pub mod usb;
38
39#[cfg(feature = "eth")]
40#[doc(hidden)]
41pub mod eth;
42
43use embassy_stm32::Config;
44
45#[doc(hidden)]
46pub use embassy_stm32::{OptionalPeripherals, Peri, PeripheralType, Peripherals, interrupt};
47
48pub use embassy_stm32::peripherals;
49
50#[cfg(feature = "executor-interrupt")]
51pub(crate) use embassy_executor::InterruptExecutor as Executor;
52
53#[cfg(feature = "hwrng")]
54#[doc(hidden)]
55pub mod hwrng;
56
57#[cfg(feature = "executor-interrupt")]
58include!(concat!(env!("OUT_DIR"), "/swi.rs"));
59
60#[cfg(capability = "hw/stm32-dual-core")]
61use {core::mem::MaybeUninit, embassy_stm32::SharedData};
62
63// Ariel OS doesn't support the second core yet, but upstream needs this.
64#[cfg(capability = "hw/stm32-dual-core")]
65static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();
66
67#[cfg(feature = "executor-interrupt")]
68#[doc(hidden)]
69pub static EXECUTOR: Executor = Executor::new();
70
71#[doc(hidden)]
72pub trait IntoPeripheral<'a, T: PeripheralType> {
73    fn into_hal_peripheral(self) -> Peri<'a, T>;
74}
75
76#[doc(hidden)]
77impl<'a, T: PeripheralType> IntoPeripheral<'a, T> for Peri<'a, T> {
78    fn into_hal_peripheral(self) -> Peri<'a, T> {
79        self
80    }
81}
82
83#[doc(hidden)]
84#[must_use]
85pub fn init() -> OptionalPeripherals {
86    let mut config = Config::default();
87    board_config(&mut config);
88
89    #[cfg(not(capability = "hw/stm32-dual-core"))]
90    let peripherals = embassy_stm32::init(config);
91
92    #[cfg(capability = "hw/stm32-dual-core")]
93    let peripherals = embassy_stm32::init_primary(config, &SHARED_DATA);
94
95    enable_flash_cache();
96
97    OptionalPeripherals::from(peripherals)
98}
99
100fn board_config(config: &mut Config) {
101    cfg_if::cfg_if! {
102        if #[cfg(feature = "rcc-config-override")] {
103            unsafe extern "Rust" {
104                fn __ariel_os_rcc_config() -> embassy_stm32::rcc::Config;
105            }
106            config.rcc = unsafe { __ariel_os_rcc_config() };
107        } else {
108            config.rcc = rcc_config();
109        }
110    }
111}
112
113// TODO: find better place for this
114#[cfg_attr(feature = "rcc-config-override", expect(dead_code))]
115fn rcc_config() -> embassy_stm32::rcc::Config {
116    #[allow(unused_mut, reason = "conditional compilation")]
117    let mut rcc = embassy_stm32::rcc::Config::default();
118
119    #[cfg(context = "st-b-l475e-iot01a")]
120    {
121        use embassy_stm32::rcc::*;
122
123        // This board has an LSE clock, we can use it to calibrate the MSI clock
124        rcc.ls = LsConfig {
125            rtc: RtcClockSource::LSE,
126            lsi: false,
127            lse: Some(LseConfig {
128                frequency: embassy_stm32::time::Hertz(32768),
129                mode: LseMode::Oscillator(LseDrive::MediumHigh),
130            }),
131        };
132        rcc.hsi = false;
133        // Setting the MSI range to 48 MHz crashes the system. If the source of the issue is found,
134        // we can use MSI as the clock source for the usb peripheral directly and avoid using more PLLs.
135        rcc.msi = Some(MSIRange::RANGE8M);
136        rcc.pll = Some(Pll {
137            source: PllSource::MSI,
138            prediv: PllPreDiv::DIV1, // 8 Mhz
139            mul: PllMul::MUL20,      // 160 MHz
140            divp: None,
141            divq: None,
142            divr: Some(PllRDiv::DIV2), // sysclk 80Mhz (8 / 1  * 20 / 2)
143        });
144        rcc.sys = Sysclk::PLL1_R;
145        rcc.pllsai1 = Some(Pll {
146            source: PllSource::MSI,
147            prediv: PllPreDiv::DIV1,
148            mul: PllMul::MUL12, // 8 MHz MSI * 12 = 96 MHz
149            divp: None,
150            divq: Some(PllQDiv::DIV2), // USB 48 MHz (8 / 1 * 12 / 2)
151            divr: None,
152        });
153        // With a 32.768 kHz LSE, the MSI clock will be calibrated and considered accurate enough.
154        // Embassy automatically enables MSIPLLEN if the LSE is configured.
155        rcc.mux.clk48sel = mux::Clk48sel::PLLSAI1_Q;
156    }
157
158    #[cfg(context = "st-nucleo-wb55")]
159    {
160        use embassy_stm32::rcc::*;
161
162        rcc.hsi48 = Some(Hsi48Config {
163            sync_from_usb: true,
164        }); // needed for USB
165        rcc.sys = Sysclk::PLL1_R;
166        rcc.hse = Some(Hse {
167            freq: embassy_stm32::time::Hertz(32000000),
168            mode: HseMode::Oscillator,
169            prescaler: HsePrescaler::DIV1,
170        });
171        rcc.pll = Some(Pll {
172            source: PllSource::HSE,
173            prediv: PllPreDiv::DIV2,
174            mul: PllMul::MUL10,
175            divp: None,
176            divq: None,
177            divr: Some(PllRDiv::DIV2), // sysclk 80Mhz (32 / 2 * 10 / 2)
178        });
179        rcc.mux.clk48sel = mux::Clk48sel::HSI48;
180    }
181
182    #[cfg(any(context = "stm32f303cb", context = "stm32f303re"))]
183    {
184        use embassy_stm32::rcc::*;
185
186        rcc.hse = Some(Hse {
187            freq: embassy_stm32::time::Hertz(8000000),
188            mode: HseMode::Oscillator,
189        });
190        rcc.pll = Some(Pll {
191            src: PllSource::HSE,
192            prediv: PllPreDiv::DIV1,
193            mul: PllMul::MUL9,
194        });
195        rcc.ahb_pre = AHBPrescaler::DIV1;
196        rcc.apb1_pre = APBPrescaler::DIV4;
197        rcc.apb2_pre = APBPrescaler::DIV2;
198        rcc.sys = Sysclk::PLL1_P; // 72 MHz (8 / 1 * 9)
199    }
200
201    #[cfg(context = "st-nucleo-f401re")]
202    {
203        use embassy_stm32::rcc::*;
204        rcc.hse = Some(Hse {
205            freq: embassy_stm32::time::Hertz(8000000),
206            mode: HseMode::Bypass,
207        });
208        rcc.pll_src = PllSource::HSE;
209        rcc.pll = Some(Pll {
210            prediv: PllPreDiv::DIV4,
211            mul: PllMul::MUL168,
212            divp: Some(PllPDiv::DIV4),
213            divq: Some(PllQDiv::DIV7),
214            divr: None,
215        });
216        rcc.ahb_pre = AHBPrescaler::DIV1;
217        rcc.apb1_pre = APBPrescaler::DIV4;
218        rcc.apb2_pre = APBPrescaler::DIV2;
219        rcc.sys = Sysclk::PLL1_P;
220    }
221
222    #[cfg(context = "st-nucleo-f767zi")]
223    {
224        use embassy_stm32::rcc::*;
225        rcc.hse = Some(Hse {
226            freq: embassy_stm32::time::Hertz(8000000),
227            mode: HseMode::Bypass,
228        });
229        rcc.pll_src = PllSource::HSE;
230        rcc.pll = Some(Pll {
231            prediv: PllPreDiv::DIV4,
232            mul: PllMul::MUL216,
233            divp: Some(PllPDiv::DIV2),
234            divq: None,
235            divr: None,
236        });
237        rcc.ahb_pre = AHBPrescaler::DIV1;
238        rcc.apb1_pre = APBPrescaler::DIV4;
239        rcc.apb2_pre = APBPrescaler::DIV2;
240        rcc.sys = Sysclk::PLL1_P;
241    }
242
243    #[cfg(context = "stm32h755zi")]
244    {
245        use embassy_stm32::rcc::*;
246
247        rcc.hsi = Some(HSIPrescaler::DIV1);
248        rcc.csi = true;
249        rcc.hsi48 = Some(Hsi48Config {
250            sync_from_usb: true,
251        }); // needed for USB
252        rcc.pll1 = Some(Pll {
253            source: PllSource::HSI,
254            prediv: PllPreDiv::DIV4,
255            mul: PllMul::MUL50,
256            divp: Some(PllDiv::DIV2),
257            // Required for SPI (configured by `spi123sel`)
258            divq: Some(PllDiv::DIV16), // FIXME: adjust this divider
259            divr: None,
260        });
261        rcc.sys = Sysclk::PLL1_P; // 400 Mhz
262        rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
263        rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
264        rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
265        rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
266        rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
267        rcc.voltage_scale = VoltageScale::Scale1;
268        // Set SMPS power config otherwise MCU will not powered after next power-off
269        rcc.supply_config = SupplyConfig::DirectSMPS;
270        rcc.mux.usbsel = mux::Usbsel::HSI48;
271        // Select the clock signal used for SPI1, SPI2, and SPI3.
272        // FIXME: what to do about SPI4, SPI5, and SPI6?
273        rcc.mux.spi123sel = mux::Saisel::PLL1_Q; // Reset value
274    }
275
276    #[cfg(context = "stm32h753zi")]
277    {
278        use embassy_stm32::rcc::*;
279
280        rcc.hsi = Some(HSIPrescaler::DIV1);
281        rcc.csi = true;
282        rcc.hsi48 = Some(Hsi48Config {
283            sync_from_usb: true,
284        }); // needed for USB
285        rcc.pll1 = Some(Pll {
286            source: PllSource::HSI,
287            prediv: PllPreDiv::DIV4,
288            mul: PllMul::MUL50,
289            divp: Some(PllDiv::DIV2),
290            divq: Some(PllDiv::DIV16), // 50 MHz
291            divr: None,
292        });
293        rcc.sys = Sysclk::PLL1_P; // 400 Mhz
294        rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
295        rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
296        rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
297        rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
298        rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
299        rcc.voltage_scale = VoltageScale::Scale1;
300        rcc.mux.usbsel = mux::Usbsel::HSI48;
301        // Select the clock signal used for SPI1, SPI2, and SPI3.
302        // FIXME: what to do about SPI4, SPI5, and SPI6?
303        rcc.mux.spi123sel = mux::Saisel::PLL1_Q; // Reset value
304    }
305
306    #[cfg(any(context = "stm32u073kc", context = "stm32u083mc"))]
307    {
308        use embassy_stm32::rcc::*;
309
310        rcc.hsi48 = Some(Hsi48Config {
311            sync_from_usb: true,
312        }); // needed for USB
313        // No HSE fitted on the stm32u083c-dk board
314        rcc.hsi = true;
315        rcc.sys = Sysclk::PLL1_R;
316        rcc.pll = Some(Pll {
317            source: PllSource::HSI,
318            prediv: PllPreDiv::DIV1,
319            mul: PllMul::MUL7,
320            divp: None,
321            divq: None,
322            divr: Some(PllRDiv::DIV2), // sysclk 56Mhz
323        });
324        rcc.mux.clk48sel = mux::Clk48sel::HSI48;
325    }
326
327    #[cfg(context = "st-steval-mkboxpro")]
328    {
329        use embassy_stm32::rcc::*;
330
331        rcc.ls = LsConfig {
332            rtc: RtcClockSource::LSE,
333            lsi: true,
334            lse: Some(LseConfig {
335                peripherals_clocked: true,
336                frequency: embassy_stm32::time::Hertz(32768),
337                mode: LseMode::Oscillator(LseDrive::MediumHigh),
338            }),
339        };
340        rcc.hsi = true;
341        rcc.hsi48 = Some(Hsi48Config {
342            sync_from_usb: true,
343        }); // needed for USB
344        rcc.sys = Sysclk::PLL1_R;
345        rcc.hse = Some(Hse {
346            freq: embassy_stm32::time::Hertz(16_000_000),
347            mode: HseMode::Oscillator,
348        });
349        rcc.pll1 = Some(Pll {
350            source: PllSource::HSE,
351            prediv: PllPreDiv::DIV1,
352            mul: PllMul::MUL10,
353            divp: None,
354            divq: None,
355            divr: Some(PllDiv::DIV1), // sysclk 160Mhz (16 / 1 * 10 / 1)
356        });
357        rcc.sys = Sysclk::PLL1_R;
358        rcc.mux.iclksel = mux::Iclksel::HSI48;
359        rcc.voltage_range = VoltageScale::RANGE1;
360    }
361
362    #[cfg(context = "stm32f042k6")]
363    {
364        use embassy_stm32::rcc::*;
365
366        rcc.hsi48 = Some(Hsi48Config {
367            sync_from_usb: true,
368        }); // needed for USB
369        rcc.sys = Sysclk::HSI48;
370        rcc.pll = Some(Pll {
371            src: PllSource::HSI48,
372            prediv: PllPreDiv::DIV2,
373            mul: PllMul::MUL2,
374        });
375    }
376
377    #[cfg(context = "seeedstudio-lora-e5-mini")]
378    {
379        use embassy_stm32::rcc::*;
380
381        rcc.hse = Some(Hse {
382            freq: embassy_stm32::time::Hertz(32_000_000),
383            mode: HseMode::Bypass,
384            prescaler: HsePrescaler::DIV1,
385        });
386        rcc.ls = LsConfig::default_lse();
387        rcc.msi = None;
388        rcc.pll = Some(Pll {
389            source: PllSource::HSE,
390            prediv: PllPreDiv::DIV2,
391            mul: PllMul::MUL6,
392            divp: None,
393            divq: Some(PllQDiv::DIV2), // PLL1_Q clock (32 / 2 * 6 / 2), used for RNG
394            divr: Some(PllRDiv::DIV2), // sysclk 48Mhz clock (32 / 2 * 6 / 2)
395        });
396
397        rcc.sys = Sysclk::PLL1_R;
398    }
399
400    #[cfg(context = "st-nucleo-wba65ri")]
401    {
402        use embassy_stm32::rcc::*;
403
404        rcc.hse = Some(Hse {
405            prescaler: HsePrescaler::DIV1,
406        });
407        rcc.pll1 = Some(Pll {
408            source: PllSource::HSE,
409            prediv: PllPreDiv::DIV2,  // 32 / 2 = 16 MHz
410            mul: PllMul::MUL12,       // 16 * 12 = 192 MHz
411            divp: Some(PllDiv::DIV6), // 192 / 6 = 32 MHz (for SAI1)
412            divq: None,
413            divr: Some(PllDiv::DIV2), // 192 / 2 = 96 MHz (sysclk)
414            frac: None,
415        });
416        rcc.sys = Sysclk::PLL1_R;
417        rcc.voltage_scale = VoltageScale::RANGE1;
418        rcc.mux.otghssel = Otghssel::HSE; // USB OTG HS ref clock from HSE (32 MHz)
419    }
420
421    rcc
422}
423
424fn enable_flash_cache() {
425    // F2 and F4 support these
426    #[cfg(any(context = "stm32f401re", context = "stm32f411re",))]
427    {
428        // reset the instruction cache
429        embassy_stm32::pac::FLASH
430            .acr()
431            .modify(|w| w.set_icrst(true));
432        // enable the instruction cache and prefetch
433        embassy_stm32::pac::FLASH.acr().modify(|w| w.set_icen(true));
434        embassy_stm32::pac::FLASH
435            .acr()
436            .modify(|w| w.set_prften(true));
437        // reset the data cache
438        embassy_stm32::pac::FLASH
439            .acr()
440            .modify(|w| w.set_dcrst(true));
441        embassy_stm32::pac::FLASH
442            .acr()
443            .modify(|w| w.set_dcrst(false));
444        // enable the data cache
445        embassy_stm32::pac::FLASH.acr().modify(|w| w.set_dcen(true));
446    }
447}