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