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