Skip to main content

cu_micoairh743/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3
4use cortex_m::peripheral::DWT;
5use cu_bdshot::{Stm32H7Board, Stm32H7BoardResources, register_stm32h7_board};
6use cu_sdlogger::{EMMCLogger, EMMCSectionStorage, ForceSyncSend, find_copper_partition};
7use cu29::resource::{ResourceBundle, ResourceManager};
8use cu29::{CuError, CuResult, bundle_resources};
9use embedded_hal::adc::OneShot;
10use embedded_hal::blocking::delay::DelayMs;
11use embedded_hal::blocking::spi::Transfer;
12use embedded_hal::digital::v2::OutputPin;
13use spin::Mutex;
14use stm32h7xx_hal::{
15    adc,
16    delay::Delay,
17    gpio::{Analog, Output, PushPull, Speed},
18    nb, pac,
19    prelude::*,
20    rcc::{self, rec::AdcClkSel},
21    sdmmc::{self, SdCard, Sdmmc, SdmmcBlockDevice},
22    serial::{Error as UartError, Serial, config::Config},
23    spi::{Config as SpiConfig, Mode, Phase, Polarity, SpiExt},
24};
25
26// Throttle UART overrun warnings to once per second.
27const UART_OVERRUN_LOG_PERIOD_SECS: u32 = 1;
28const UART6_BAUD: u32 = 420_000;
29const UART2_BAUD: u32 = 115_200;
30const UART4_BAUD: u32 = 115_200;
31
32pub type SerialPortError = embedded_io::ErrorKind;
33
34/// UART wrapper implementing embedded-io traits with overrun throttling.
35pub struct SerialWrapper<U> {
36    inner: Serial<U>,
37    label: &'static str,
38    last_overrun_cycle: Option<u32>,
39    overrun_period_cycles: u32,
40}
41
42// SAFETY: The firmware uses a single-threaded runtime; no concurrent access.
43unsafe impl<U> Send for SerialWrapper<U> {}
44// SAFETY: The firmware uses a single-threaded runtime; no concurrent access.
45unsafe impl<U> Sync for SerialWrapper<U> {}
46
47impl<U> SerialWrapper<U> {
48    fn new(inner: Serial<U>, label: &'static str, overrun_period_cycles: u32) -> Self {
49        Self {
50            inner,
51            label,
52            last_overrun_cycle: None,
53            overrun_period_cycles,
54        }
55    }
56
57    fn should_warn_overrun(&mut self) -> bool {
58        if self.overrun_period_cycles == 0 {
59            return true;
60        }
61        let now = DWT::cycle_count();
62        match self.last_overrun_cycle {
63            Some(prev) if now.wrapping_sub(prev) < self.overrun_period_cycles => false,
64            _ => {
65                self.last_overrun_cycle = Some(now);
66                true
67            }
68        }
69    }
70}
71
72macro_rules! impl_serial_wrapper_io {
73    ($usart:ty) => {
74        impl embedded_io::ErrorType for SerialWrapper<$usart> {
75            type Error = SerialPortError;
76        }
77
78        impl embedded_io::Read for SerialWrapper<$usart> {
79            fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
80                let mut read = 0;
81                for b in buf.iter_mut() {
82                    match self.inner.read() {
83                        Ok(byte) => {
84                            *b = byte;
85                            read += 1;
86                        }
87                        Err(nb::Error::WouldBlock) => break, // no more data right now
88                        Err(nb::Error::Other(e)) => match e {
89                            UartError::Overrun => {
90                                if self.should_warn_overrun() {
91                                    defmt::warn!("{} overrun, data lost", self.label);
92                                }
93                                // Clear and keep going; treat as no more data.
94                                break;
95                            }
96                            _ => {
97                                defmt::error!("{} read err: {:?}", self.label, e);
98                                return Err(SerialPortError::Other);
99                            }
100                        },
101                    }
102                }
103                Ok(read)
104            }
105        }
106
107        impl embedded_io::Write for SerialWrapper<$usart> {
108            fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
109                let mut written = 0;
110                for &b in buf {
111                    match nb::block!(self.inner.write(b)) {
112                        Ok(()) => written += 1,
113                        Err(e) => {
114                            defmt::error!("{} write err: {:?}", self.label, e);
115                            return Err(SerialPortError::Other);
116                        }
117                    }
118                }
119                Ok(written)
120            }
121
122            fn flush(&mut self) -> Result<(), Self::Error> {
123                Ok(())
124            }
125        }
126    };
127}
128
129impl_serial_wrapper_io!(pac::USART6);
130impl_serial_wrapper_io!(pac::USART2);
131impl_serial_wrapper_io!(pac::UART4);
132
133// Resource type aliases exposed by the bundle.
134pub type Uart6Port = SerialWrapper<pac::USART6>;
135pub type Uart2Port = SerialWrapper<pac::USART2>;
136pub type Uart4Port = SerialWrapper<pac::UART4>;
137
138/// Wraps HAL types that are only used from the single-threaded runtime.
139pub struct SingleThreaded<T>(T);
140
141impl<T> SingleThreaded<T> {
142    pub fn new(inner: T) -> Self {
143        Self(inner)
144    }
145}
146
147// SAFETY: The firmware uses a single-threaded runtime; no concurrent access.
148unsafe impl<T> Send for SingleThreaded<T> {}
149// SAFETY: The firmware uses a single-threaded runtime; no concurrent access.
150unsafe impl<T> Sync for SingleThreaded<T> {}
151
152impl<T> Transfer<u8> for SingleThreaded<T>
153where
154    T: Transfer<u8>,
155{
156    type Error = T::Error;
157
158    fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
159        self.0.transfer(words)
160    }
161}
162
163impl<T> OutputPin for SingleThreaded<T>
164where
165    T: OutputPin,
166{
167    type Error = T::Error;
168
169    fn set_low(&mut self) -> Result<(), Self::Error> {
170        self.0.set_low()
171    }
172
173    fn set_high(&mut self) -> Result<(), Self::Error> {
174        self.0.set_high()
175    }
176}
177
178impl<T> DelayMs<u32> for SingleThreaded<T>
179where
180    T: DelayMs<u32>,
181{
182    fn delay_ms(&mut self, ms: u32) {
183        self.0.delay_ms(ms);
184    }
185}
186
187pub type GreenLed =
188    stm32h7xx_hal::gpio::gpioe::PE6<stm32h7xx_hal::gpio::Output<stm32h7xx_hal::gpio::PushPull>>;
189
190pub type Bmi088Spi =
191    SingleThreaded<stm32h7xx_hal::spi::Spi<pac::SPI2, stm32h7xx_hal::spi::Enabled, u8>>;
192pub type Bmi088AccCs = SingleThreaded<stm32h7xx_hal::gpio::gpiod::PD4<Output<PushPull>>>;
193pub type Bmi088GyrCs = SingleThreaded<stm32h7xx_hal::gpio::gpiod::PD5<Output<PushPull>>>;
194pub type Bmi088Delay = SingleThreaded<Delay>;
195pub type BatterySensePin = stm32h7xx_hal::gpio::gpioc::PC0<Analog>;
196
197pub struct BatteryAdc {
198    adc: adc::Adc<pac::ADC1, adc::Enabled>,
199    pin: BatterySensePin,
200}
201
202// SAFETY: The firmware uses a single-threaded runtime; no concurrent access.
203unsafe impl Send for BatteryAdc {}
204// SAFETY: The firmware uses a single-threaded runtime; no concurrent access.
205unsafe impl Sync for BatteryAdc {}
206
207impl BatteryAdc {
208    fn new(adc: adc::Adc<pac::ADC1, adc::Enabled>, pin: BatterySensePin) -> Self {
209        Self { adc, pin }
210    }
211
212    pub fn read_raw_blocking(&mut self) -> u32 {
213        nb::block!(self.adc.read(&mut self.pin)).unwrap_or(0)
214    }
215
216    pub fn slope(&self) -> u32 {
217        self.adc.slope()
218    }
219}
220
221type SdBlockDev = ForceSyncSend<SdmmcBlockDevice<Sdmmc<pac::SDMMC1, SdCard>>>;
222pub type LogStorage = EMMCSectionStorage<SdBlockDev>;
223pub type Logger = EMMCLogger<SdBlockDev>;
224
225fn init_sd_logger(
226    sdmmc1: pac::SDMMC1,
227    sdmmc1_rec: rcc::rec::Sdmmc1,
228    clocks: &rcc::CoreClocks,
229    pins: (
230        impl sdmmc::PinClk<pac::SDMMC1>,
231        impl sdmmc::PinCmd<pac::SDMMC1>,
232        impl sdmmc::PinD0<pac::SDMMC1>,
233        impl sdmmc::PinD1<pac::SDMMC1>,
234        impl sdmmc::PinD2<pac::SDMMC1>,
235        impl sdmmc::PinD3<pac::SDMMC1>,
236    ),
237) -> CuResult<Logger> {
238    defmt::info!("Basic Init done, creating sdmmc...");
239    let mut sdmmc: Sdmmc<_, SdCard> = sdmmc1.sdmmc(pins, sdmmc1_rec, clocks);
240
241    defmt::info!("Init()... ");
242    sdmmc
243        .init(25.MHz())
244        .map_err(|_| CuError::from("SDMMC init failed"))?;
245    defmt::info!("Init passed ... ");
246    sdmmc.card().map_err(|_| CuError::from("no sd card"))?;
247    defmt::info!("Card detected!!");
248
249    let bd = ForceSyncSend::new(sdmmc.sdmmc_block_device());
250
251    let (start, count) = match find_copper_partition(&bd) {
252        Ok(Some((s, c))) => (s, c),
253        Ok(None) => {
254            defmt::warn!("Copper partition not found on SD");
255            return Err(CuError::from("copper partition missing"));
256        }
257        Err(_) => {
258            defmt::warn!("SD read error during partition scan");
259            return Err(CuError::from("sd read error during partition scan"));
260        }
261    };
262
263    Logger::new(bd, start, count)
264}
265
266/// Resource bundle for the MicoAir H743 board.
267pub struct MicoAirH743;
268
269bundle_resources!(
270    MicoAirH743: Uart6, Uart2, Uart4, GreenLed, Logger, Bmi088Spi, Bmi088AccCs, Bmi088GyrCs, Bmi088Delay, BatteryAdc
271);
272
273impl ResourceBundle for MicoAirH743 {
274    fn build(
275        bundle: cu29::resource::BundleContext<Self>,
276        _config: Option<&cu29::config::ComponentConfig>,
277        manager: &mut ResourceManager,
278    ) -> CuResult<()> {
279        // Here we bring up the entire board and register the resources.
280
281        let mut cp = cortex_m::Peripherals::take()
282            .ok_or_else(|| CuError::from("cortex-m peripherals already taken"))?;
283        #[cfg(all(target_arch = "arm", target_abi = "eabihf"))]
284        cp.SCB.enable_fpu();
285        cp.DCB.enable_trace();
286        DWT::unlock();
287        let mut dwt = cp.DWT;
288        dwt.enable_cycle_counter();
289        let syst = cp.SYST;
290
291        let dp = pac::Peripherals::take()
292            .ok_or_else(|| CuError::from("stm32 peripherals already taken"))?;
293
294        // Clocks: mirror the known-good sdcard-test setup.
295        let pwr = dp.PWR.constrain();
296        let vos = pwr.freeze();
297        let rcc = dp.RCC.constrain();
298        let mut ccdr = rcc
299            .sys_ck(400.MHz())
300            .pll1_q_ck(100.MHz())
301            .freeze(vos, &dp.SYSCFG);
302        ccdr.peripheral.kernel_adc_clk_mux(AdcClkSel::Per);
303        let sysclk_hz = ccdr.clocks.sys_ck().raw();
304        let sdmmc1_rec = ccdr.peripheral.SDMMC1;
305        let mut delay = Delay::new(syst, ccdr.clocks);
306
307        let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA);
308        let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC);
309        let gpiod = dp.GPIOD.split(ccdr.peripheral.GPIOD);
310        let gpioe = dp.GPIOE.split(ccdr.peripheral.GPIOE);
311        let battery_pin = gpioc.pc0.into_analog();
312
313        let green_led = gpioe.pe6.into_push_pull_output();
314        let mut adc1 = adc::Adc::adc1(
315            dp.ADC1,
316            4.MHz(),
317            &mut delay,
318            ccdr.peripheral.ADC12,
319            &ccdr.clocks,
320        )
321        .enable();
322        adc1.set_resolution(adc::Resolution::TwelveBit);
323        adc1.set_sample_time(adc::AdcSampleTime::T_64);
324        let battery_adc = BatteryAdc::new(adc1, battery_pin);
325
326        let overrun_period_cycles = sysclk_hz.saturating_mul(UART_OVERRUN_LOG_PERIOD_SECS);
327        let uart6_config = Config::default().baudrate(UART6_BAUD.bps());
328        let uart6 = SerialWrapper::new(
329            dp.USART6
330                .serial(
331                    (gpioc.pc6.into_alternate(), gpioc.pc7.into_alternate()),
332                    uart6_config,
333                    ccdr.peripheral.USART6,
334                    &ccdr.clocks,
335                )
336                .map_err(|_| CuError::from("uart6 init failed"))?,
337            "UART6",
338            overrun_period_cycles,
339        );
340
341        let uart2_baud = match _config {
342            Some(cfg) => cfg.get::<u32>("uart2_baud")?.unwrap_or(UART2_BAUD),
343            None => UART2_BAUD,
344        };
345        let uart2_config = Config::default().baudrate(uart2_baud.bps());
346        let uart2 = SerialWrapper::new(
347            dp.USART2
348                .serial(
349                    (gpioa.pa2.into_alternate(), gpioa.pa3.into_alternate()),
350                    uart2_config,
351                    ccdr.peripheral.USART2,
352                    &ccdr.clocks,
353                )
354                .map_err(|_| CuError::from("uart2 init failed"))?,
355            "UART2",
356            overrun_period_cycles,
357        );
358
359        let uart4_baud = match _config {
360            Some(cfg) => cfg.get::<u32>("uart4_baud")?.unwrap_or(UART4_BAUD),
361            None => UART4_BAUD,
362        };
363        let uart4_config = Config::default().baudrate(uart4_baud.bps());
364        let uart4 = SerialWrapper::new(
365            dp.UART4
366                .serial(
367                    (gpioa.pa0.into_alternate(), gpioa.pa1.into_alternate()),
368                    uart4_config,
369                    ccdr.peripheral.UART4,
370                    &ccdr.clocks,
371                )
372                .map_err(|_| CuError::from("uart4 init failed"))?,
373            "UART4",
374            overrun_period_cycles,
375        );
376
377        let logger = init_sd_logger(
378            dp.SDMMC1,
379            sdmmc1_rec,
380            &ccdr.clocks,
381            (
382                gpioc.pc12.into_alternate::<12>().speed(Speed::VeryHigh),
383                gpiod.pd2.into_alternate::<12>().speed(Speed::VeryHigh),
384                gpioc.pc8.into_alternate::<12>().speed(Speed::VeryHigh),
385                gpioc.pc9.into_alternate::<12>().speed(Speed::VeryHigh),
386                gpioc.pc10.into_alternate::<12>().speed(Speed::VeryHigh),
387                gpioc.pc11.into_alternate::<12>().speed(Speed::VeryHigh),
388            ),
389        )?;
390
391        let sck = gpiod.pd3.into_alternate::<5>().speed(Speed::VeryHigh);
392        let miso = gpioc.pc2.into_alternate::<5>().speed(Speed::VeryHigh);
393        let mosi = gpioc.pc3.into_alternate::<5>().speed(Speed::VeryHigh);
394        let mut bmi088_acc_cs = gpiod.pd4.into_push_pull_output();
395        let mut bmi088_gyr_cs = gpiod.pd5.into_push_pull_output();
396        bmi088_acc_cs.set_high();
397        bmi088_gyr_cs.set_high();
398
399        let spi_cfg = SpiConfig::new(Mode {
400            polarity: Polarity::IdleHigh,
401            phase: Phase::CaptureOnSecondTransition,
402        });
403        let bmi088_spi: stm32h7xx_hal::spi::Spi<pac::SPI2, stm32h7xx_hal::spi::Enabled, u8> =
404            dp.SPI2.spi(
405                (sck, miso, mosi),
406                spi_cfg,
407                10.MHz(),
408                ccdr.peripheral.SPI2,
409                &ccdr.clocks,
410            );
411
412        let bmi088_spi = SingleThreaded::new(bmi088_spi);
413        let bmi088_acc_cs = SingleThreaded::new(bmi088_acc_cs);
414        let bmi088_gyr_cs = SingleThreaded::new(bmi088_gyr_cs);
415        let bmi088_delay = SingleThreaded::new(delay);
416
417        let bdshot_resources = Stm32H7BoardResources {
418            m1: gpioe.pe14,
419            m2: gpioe.pe13,
420            m3: gpioe.pe11,
421            m4: gpioe.pe9,
422            dwt,
423            sysclk_hz,
424        };
425        let bdshot_board = Stm32H7Board::new(bdshot_resources)?;
426        register_stm32h7_board(bdshot_board)?;
427
428        let uart6_key = bundle.key(MicoAirH743Id::Uart6);
429        let uart2_key = bundle.key(MicoAirH743Id::Uart2);
430        let uart4_key = bundle.key(MicoAirH743Id::Uart4);
431        let led_key = bundle.key(MicoAirH743Id::GreenLed);
432        let logger_key = bundle.key(MicoAirH743Id::Logger);
433        let bmi088_spi_key = bundle.key(MicoAirH743Id::Bmi088Spi);
434        let bmi088_acc_cs_key = bundle.key(MicoAirH743Id::Bmi088AccCs);
435        let bmi088_gyr_cs_key = bundle.key(MicoAirH743Id::Bmi088GyrCs);
436        let bmi088_delay_key = bundle.key(MicoAirH743Id::Bmi088Delay);
437        let battery_adc_key = bundle.key(MicoAirH743Id::BatteryAdc);
438
439        manager.add_owned(uart6_key, uart6)?;
440        manager.add_owned(uart2_key, uart2)?;
441        manager.add_owned(uart4_key, uart4)?;
442        manager.add_owned(led_key, Mutex::new(green_led))?;
443        manager.add_owned(logger_key, logger)?;
444        manager.add_owned(bmi088_spi_key, bmi088_spi)?;
445        manager.add_owned(bmi088_acc_cs_key, bmi088_acc_cs)?;
446        manager.add_owned(bmi088_gyr_cs_key, bmi088_gyr_cs)?;
447        manager.add_owned(bmi088_delay_key, bmi088_delay)?;
448        manager.add_owned(battery_adc_key, battery_adc)?;
449        Ok(())
450    }
451}