Skip to main content

nucleo/platforms/stm_32/
stm32h7x3_common.rs

1// ******************************************************************************
2// * File     : platforms/stm_32/stm32h7x3_common.rs
3// * Date     : June 28, 2026
4// ******************************************************************************
5// *
6// * Copyright (c) 2023, AeroVironment, Inc.
7// * All rights reserved.
8// *
9// * Redistribution and use in source and binary forms, with or without
10// * modification, are permitted provided that the following conditions are met:
11// *
12// * 1.Redistributions of source code must retain the above copyright notice, this
13// * list of conditions and the following disclaimer.
14// *
15// * 2.Redistributions in binary form must reproduce the above copyright notice,
16// * this list of conditions and the following disclaimer in the documentation
17// * and/or other materials provided with the distribution.
18// *
19// * 3.Neither the name of the copyright holder nor the names of its
20// * contributors may be used to endorse or promote products derived from
21// * this software without specific prior written permission.
22// *
23// * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24// * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25// * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26// * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
27// * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28// * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29// * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30// * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31// * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32// * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33// *
34// ******************************************************************************
35
36use core::default::Default;
37use core::option::Option::Some;
38use embassy_stm32::{Config, rcc};
39
40use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice;
41use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
42use embassy_executor::InterruptExecutor;
43use embassy_stm32::Peripherals as EMBASSY_Peripherals;
44use embassy_stm32::bind_interrupts;
45use embassy_stm32::dma;
46use embassy_stm32::exti;
47use embassy_stm32::exti::ExtiInput;
48#[allow(unused_imports)]
49use embassy_stm32::gpio::OutputType;
50use embassy_stm32::gpio::Pull;
51use embassy_stm32::gpio::{Level, Output, Speed};
52use embassy_stm32::i2c;
53use embassy_stm32::interrupt;
54use embassy_stm32::interrupt::InterruptExt;
55use embassy_stm32::interrupt::Priority;
56use embassy_stm32::mode::Async;
57use embassy_stm32::peripherals as EMBASSY_peripherals;
58use embassy_stm32::sdmmc;
59use embassy_stm32::spi;
60use embassy_stm32::spi::mode::Master as SpiMaster;
61use embassy_stm32::time::Hertz;
62use embassy_stm32::time::mhz;
63#[allow(unused_imports)]
64use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
65use embassy_stm32::usart;
66use embassy_stm32::usart::Uart;
67use embassy_stm32::usb;
68use embassy_stm32::usb::Driver;
69use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
70use embassy_sync::mutex::Mutex;
71use static_cell::StaticCell;
72pub static SPI1_BUS: StaticCell<Mutex<CriticalSectionRawMutex, spi::Spi<'static, Async, SpiMaster>>> =
73    StaticCell::new();
74pub static SPI2_BUS: StaticCell<Mutex<CriticalSectionRawMutex, spi::Spi<'static, Async, SpiMaster>>> =
75    StaticCell::new();
76pub static SPI3_BUS: StaticCell<Mutex<CriticalSectionRawMutex, spi::Spi<'static, Async, SpiMaster>>> =
77    StaticCell::new();
78pub static SPI4_BUS: StaticCell<Mutex<CriticalSectionRawMutex, spi::Spi<'static, Async, SpiMaster>>> =
79    StaticCell::new();
80pub static SPI5_BUS: StaticCell<Mutex<CriticalSectionRawMutex, spi::Spi<'static, Async, SpiMaster>>> =
81    StaticCell::new();
82pub static SPI6_BUS: StaticCell<Mutex<CriticalSectionRawMutex, spi::Spi<'static, Async, SpiMaster>>> =
83    StaticCell::new();
84
85pub static I2C1_BUS: StaticCell<Mutex<CriticalSectionRawMutex, i2c::I2c<'static, Async, i2c::mode::Master>>> =
86    StaticCell::new();
87pub static I2C2_BUS: StaticCell<Mutex<CriticalSectionRawMutex, i2c::I2c<'static, Async, i2c::mode::Master>>> =
88    StaticCell::new();
89pub static I2C3_BUS: StaticCell<Mutex<CriticalSectionRawMutex, i2c::I2c<'static, Async, i2c::mode::Master>>> =
90    StaticCell::new();
91pub static I2C4_BUS: StaticCell<Mutex<CriticalSectionRawMutex, i2c::I2c<'static, Async, i2c::mode::Master>>> =
92    StaticCell::new();
93
94bind_interrupts!(struct BoardIrqs {
95    I2C1_EV => i2c::EventInterruptHandler<EMBASSY_peripherals::I2C1>;
96    I2C1_ER => i2c::ErrorInterruptHandler<EMBASSY_peripherals::I2C1>;
97    I2C2_EV => i2c::EventInterruptHandler<EMBASSY_peripherals::I2C2>;
98    I2C2_ER => i2c::ErrorInterruptHandler<EMBASSY_peripherals::I2C2>;
99    I2C3_EV => i2c::EventInterruptHandler<EMBASSY_peripherals::I2C3>;
100    I2C3_ER => i2c::ErrorInterruptHandler<EMBASSY_peripherals::I2C3>;
101    I2C4_EV => i2c::EventInterruptHandler<EMBASSY_peripherals::I2C4>;
102    I2C4_ER => i2c::ErrorInterruptHandler<EMBASSY_peripherals::I2C4>;
103    USART1 => usart::InterruptHandler<EMBASSY_peripherals::USART1>;
104    USART2 => usart::InterruptHandler<EMBASSY_peripherals::USART2>;
105    USART3 => usart::InterruptHandler<EMBASSY_peripherals::USART3>;
106    USART6 => usart::InterruptHandler<EMBASSY_peripherals::USART6>;
107    UART4 => usart::InterruptHandler<EMBASSY_peripherals::UART4>;
108    UART5 => usart::InterruptHandler<EMBASSY_peripherals::UART5>;
109    UART7 => usart::InterruptHandler<EMBASSY_peripherals::UART7>;
110    UART8 => usart::InterruptHandler<EMBASSY_peripherals::UART8>;
111    SDMMC1 => sdmmc::InterruptHandler<EMBASSY_peripherals::SDMMC1>;
112    DMA1_STREAM0 => dma::InterruptHandler<EMBASSY_peripherals::DMA1_CH0>;
113    DMA1_STREAM1 => dma::InterruptHandler<EMBASSY_peripherals::DMA1_CH1>;
114    DMA1_STREAM2 => dma::InterruptHandler<EMBASSY_peripherals::DMA1_CH2>;
115    DMA1_STREAM3 => dma::InterruptHandler<EMBASSY_peripherals::DMA1_CH3>;
116    DMA1_STREAM4 => dma::InterruptHandler<EMBASSY_peripherals::DMA1_CH4>;
117    DMA1_STREAM5 => dma::InterruptHandler<EMBASSY_peripherals::DMA1_CH5>;
118    DMA1_STREAM6 => dma::InterruptHandler<EMBASSY_peripherals::DMA1_CH6>;
119    DMA1_STREAM7 => dma::InterruptHandler<EMBASSY_peripherals::DMA1_CH7>;
120    DMA2_STREAM0 => dma::InterruptHandler<EMBASSY_peripherals::DMA2_CH0>;
121    DMA2_STREAM1 => dma::InterruptHandler<EMBASSY_peripherals::DMA2_CH1>;
122    DMA2_STREAM2 => dma::InterruptHandler<EMBASSY_peripherals::DMA2_CH2>;
123    DMA2_STREAM3 => dma::InterruptHandler<EMBASSY_peripherals::DMA2_CH3>;
124    DMA2_STREAM4 => dma::InterruptHandler<EMBASSY_peripherals::DMA2_CH4>;
125    DMA2_STREAM5 => dma::InterruptHandler<EMBASSY_peripherals::DMA2_CH5>;
126    DMA2_STREAM6 => dma::InterruptHandler<EMBASSY_peripherals::DMA2_CH6>;
127    DMA2_STREAM7 => dma::InterruptHandler<EMBASSY_peripherals::DMA2_CH7>;
128    EXTI0 => exti::InterruptHandler<interrupt::typelevel::EXTI0>;
129    EXTI1 => exti::InterruptHandler<interrupt::typelevel::EXTI1>;
130    EXTI2 => exti::InterruptHandler<interrupt::typelevel::EXTI2>;
131    EXTI3 => exti::InterruptHandler<interrupt::typelevel::EXTI3>;
132    EXTI4 => exti::InterruptHandler<interrupt::typelevel::EXTI4>;
133    EXTI9_5 => exti::InterruptHandler<interrupt::typelevel::EXTI9_5>;
134    EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;
135});
136
137// USB Interrupt
138bind_interrupts!(struct Irqs {
139    OTG_FS => usb::InterruptHandler<EMBASSY_peripherals::USB_OTG_FS>;
140});
141
142// Executor Interrupts
143// Use SAI1,2,3,4 as interrupt vectors since we are not using audio
144// 1-4 are only conciedntally the same as I picked for the interrupt levels
145
146static P0_EXECUTOR: InterruptExecutor = InterruptExecutor::new();
147#[interrupt]
148unsafe fn SDMMC2() {
149    unsafe { P0_EXECUTOR.on_interrupt() };
150}
151
152static P1_EXECUTOR: InterruptExecutor = InterruptExecutor::new();
153#[interrupt]
154unsafe fn SAI1() {
155    unsafe { P1_EXECUTOR.on_interrupt() };
156}
157
158static P2_EXECUTOR: InterruptExecutor = InterruptExecutor::new();
159#[interrupt]
160unsafe fn SAI2() {
161    unsafe { P2_EXECUTOR.on_interrupt() };
162}
163
164static P3_EXECUTOR: InterruptExecutor = InterruptExecutor::new();
165#[interrupt]
166unsafe fn SAI3() {
167    unsafe { P3_EXECUTOR.on_interrupt() };
168}
169
170static P4_EXECUTOR: InterruptExecutor = InterruptExecutor::new();
171#[interrupt]
172unsafe fn SAI4() {
173    unsafe { P4_EXECUTOR.on_interrupt() };
174}
175
176pub fn clock_config(mhz: u32) -> Config {
177    let mut config = Config::default();
178    {
179        use embassy_stm32::rcc::*;
180        config.rcc.hsi = Some(HSIPrescaler::DIV1); // (64 mHz, not used)
181
182        // config.rcc.csi = true;
183        // Select External 8MHz clock for Nucleo-H753ZI board
184        // Select External 50MHz clock For Varmint
185        config.rcc.hse = Some(rcc::Hse {
186            freq: embassy_stm32::time::Hertz(mhz * 1_000_000), // HSE OSC
187            mode: rcc::HseMode::Oscillator,
188        });
189
190        // This needs to be generalized for odd mhz
191        let mut hsi_prediv = PllPreDiv::DIV4; // Default 8MHz case
192
193        if mhz == 4 {
194            hsi_prediv = PllPreDiv::DIV2;
195        } else if mhz == 8  {
196            hsi_prediv = PllPreDiv::DIV4;
197        } else if mhz == 24  {
198            hsi_prediv = PllPreDiv::DIV12;
199        } else if mhz == 50  {
200            hsi_prediv = PllPreDiv::DIV25;
201        } else if mhz == 64  {
202            hsi_prediv = PllPreDiv::DIV32;
203        } else {
204            //self::panic!("HSI OSC MHz value");
205        }
206
207        config.rcc.pll1 = Some(Pll {
208            source: PllSource::HSE,   // 50MHz
209            prediv: hsi_prediv,       // 50MHz OSC / 25 = 2 MHz
210            mul: PllMul::MUL400,      // 800 MHz
211            divp: Some(PllDiv::DIV2), // 400 MHz for System Clock
212            divq: Some(PllDiv::DIV8), // 100 MHz for SDMMC
213            divr: Some(PllDiv::DIV2), // 400 MHz (not used)
214        });
215
216        config.rcc.pll2 = Some(Pll {
217            source: PllSource::HSE,    // 50MHz
218            prediv: hsi_prediv,        // 50MHz OSC / 25 = 2 MHz
219            mul: PllMul::MUL240,       // 480 MHz
220            divp: Some(PllDiv::DIV30), // 16 MHz for SPI 1,2,3
221            divq: Some(PllDiv::DIV30), // 16 MHz for SPI 4,5, and FDCAN
222            divr: Some(PllDiv::DIV5),  // 96 MHz (not used)
223        });
224
225        config.rcc.pll3 = Some(Pll {
226            source: PllSource::HSE,    // 50MHz
227            prediv: hsi_prediv,        // 50MHz OSC / 25 = 2 MHz
228            mul: PllMul::MUL480,       // 960 MHz
229            divp: Some(PllDiv::DIV48), // 20 MHz (not used)
230            divq: Some(PllDiv::DIV20), // 48 MHz for USB
231            divr: Some(PllDiv::DIV15), // 64 MHz for ADC
232        });
233        // System clock MUX
234        config.rcc.sys = Sysclk::PLL1_P; // Select PLL1_P for the System clock (400 MHz, see above)
235        // D1CPRE Prescaler
236        config.rcc.d1c_pre = AHBPrescaler::DIV1; // 400 MHz
237        // HPRE Prescaler
238        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 MHz
239        // D1PPRE, D2PPRE1, D2PPRE2, D3PPRE
240        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 MHz APB1 Peripheral Clocks for USART 2,3,4,5,7,8, I2C 1,2,3
241        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 MHz APB2 Peripheral Clocks for USART 1,6
242        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 MHz APB3 Peripheral Clocks
243        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 MHz APB4 Peripheral Clocks
244        // SYSTICK Clock Prescaler
245        config.rcc.timer_prescaler = TimerPrescaler::DefaultX2; // 400 MHz
246        // 48MHz Clock used by USB? maybe (and RNG maybe)
247        // config.rcc.hsi48 = Some(Default::default()); // Used for RNG
248        config.rcc.hsi48 = Some(Hsi48Config {
249            sync_from_usb: true,
250        }); // For USB
251        // Analog Voltage Detector level ??? (for startup?)
252        config.rcc.voltage_scale = VoltageScale::Scale1; // ???// 2.8V. Scale1 (2.1V) is what is in all the examples. PWR_CR1 ALS bits.??
253        // ADC clock
254        config.rcc.mux.adcsel = mux::Adcsel::PLL3_R; // 64 MHz
255        // USB clock
256        //config.rcc.mux.usbsel = mux::Usbsel::PLL3_Q; // 48 MHz
257        config.rcc.mux.usbsel = mux::Usbsel::HSI48;
258        // SDMMC clock
259        config.rcc.mux.sdmmcsel = mux::Sdmmcsel::PLL1_Q; // 100MHz
260        // I2C 1-3,5
261        config.rcc.mux.i2c1235sel = mux::I2c1235sel::PCLK1; // 100MHz
262        // RNG
263        config.rcc.mux.rngsel = mux::Rngsel::HSI48; // 48 MHz
264        // SPI 1-3
265        config.rcc.mux.spi123sel = mux::Saisel::PLL2_P; // 16 MHz
266        // SPI4,5
267        config.rcc.mux.spi45sel = mux::Spi45sel::PLL2_Q; // 16 MHz
268        // USART 1,6
269        config.rcc.mux.usart16910sel = mux::Usart16910sel::PCLK2; // 100 MHz
270        // USART 2-5,7,8
271        config.rcc.mux.usart234578sel = mux::Usart234578sel::PCLK1; // 100 MHz
272    }
273    return config;
274}