Skip to main content

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