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