From ccb2680954551bb81aa5ed7c377d21f4b5fd899d Mon Sep 17 00:00:00 2001 From: Victoria Fischer Date: Wed, 11 Mar 2026 13:59:56 +0100 Subject: [PATCH] main: first implementation of a way to handle interrupts from sensors. untested!! --- src/bin/main.rs | 28 ++++++++++++++++--- src/gpio_interrupt.rs | 62 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/tasks/mpu.rs | 3 ++- 4 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 src/gpio_interrupt.rs diff --git a/src/bin/main.rs b/src/bin/main.rs index 46be582..8afb9a0 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -12,17 +12,17 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Instant, Timer, WithTimeout}; use enum_map::EnumMap; -use esp_hal::{gpio::{Output, OutputConfig, Pin}, time::Rate}; +use esp_hal::{gpio::{Event, Input, InputConfig, Output, OutputConfig, Pin}, handler, time::Rate}; use esp_hal::{ clock::CpuClock, timer::{systimer::SystemTimer, timg::{TimerGroup, Wdt}} }; use embassy_sync::{ - blocking_mutex::raw::NoopRawMutex, channel::DynamicReceiver, pubsub::PubSubChannel + blocking_mutex::raw::NoopRawMutex, channel::DynamicReceiver, once_lock::OnceLock, pubsub::PubSubChannel, signal::Signal }; use esp_storage::FlashStorage; use log::*; -use renderbug_bike::{events::{Prediction, SensorSource, SensorState}, graphics::display::DisplayControls, logging::RenderbugLogger, simdata::IMUReading, storage::{SharedFlash, SimDataRecorder}, tasks::{oled::{OledUI, OledUiSurfacePool, oled_ui}, safetyui::{SafetyUi, safety_ui_main}, ui::UiSurfacePool}, tracing::Tracer}; +use renderbug_bike::{events::{Prediction, SensorSource, SensorState}, gpio_interrupt::{InterruptDispatch, PinInterrupt}, graphics::display::DisplayControls, logging::RenderbugLogger, simdata::IMUReading, storage::{SharedFlash, SimDataRecorder}, tasks::{oled::{OledUI, OledUiSurfacePool, oled_ui}, safetyui::{SafetyUi, safety_ui_main}, ui::UiSurfacePool}, tracing::Tracer}; use renderbug_bike::events::Measurement; use static_cell::StaticCell; use esp_backtrace as _; @@ -45,6 +45,12 @@ static WIFI_INIT: StaticCell> = StaticCell::new() rtos_trace::global_trace!(Tracer); +static INTERRUPTS: OnceLock> = OnceLock::new(); +#[handler] +fn gpio_interrupt_handler() { + INTERRUPTS.try_get().unwrap().process_interrupts(); +} + #[esp_rtos::main] async fn main(spawner: Spawner) { // If we aren't using the second CPU, we can use the bootloader space for the heap instead @@ -94,6 +100,20 @@ async fn main(spawner: Spawner) { // Spawn the rendering task as soon as possible so it can start pushing pixels spawner.must_spawn(renderbug_bike::tasks::render::render(peripherals.SPI2.degrade(), peripherals.DMA_CH2.degrade(), peripherals.GPIO5.degrade(), surfaces, safety_surfaces, display_controls, wdt)); + let imu_interrupt = PinInterrupt::new(Input::new(peripherals.GPIO36.degrade(), InputConfig::default()), Event::RisingEdge); + let pd_interrupt = PinInterrupt::new(Input::new(peripherals.GPIO16.degrade(), InputConfig::default()), Event::RisingEdge); + let sd_detect_interrupt = PinInterrupt::new(Input::new(peripherals.GPIO42.degrade(), InputConfig::default()), Event::RisingEdge); + let gps_pulse_interrupt = PinInterrupt::new(Input::new(peripherals.GPIO45.degrade(), InputConfig::default()), Event::RisingEdge); + + INTERRUPTS.init(InterruptDispatch::new([ + imu_interrupt.clone(), + pd_interrupt.clone(), + sd_detect_interrupt.clone(), + gps_pulse_interrupt.clone() + ])).ok(); + let mut io = esp_hal::gpio::Io::new(peripherals.IO_MUX); + io.set_interrupt_handler(gpio_interrupt_handler); + #[cfg(feature="motion")] { use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice; @@ -108,7 +128,7 @@ async fn main(spawner: Spawner) { let i2c = I2c::new(peripherals.I2C1, Config::default()).unwrap().with_scl(scl).with_sda(sda).into_async(); let i2c_bus = I2C_BUS.init(Mutex::new(i2c)); #[cfg(feature="mpu")] - spawner.must_spawn(renderbug_bike::tasks::mpu::mpu_task(motion_bus.dyn_sender(), I2cDevice::new(i2c_bus))); + spawner.must_spawn(renderbug_bike::tasks::mpu::mpu_task(motion_bus.dyn_sender(), I2cDevice::new(i2c_bus), imu_interrupt)); #[cfg(feature="gps")] spawner.must_spawn(renderbug_bike::tasks::gps::gps_task(motion_bus.dyn_sender(), I2cDevice::new(i2c_bus))); } diff --git a/src/gpio_interrupt.rs b/src/gpio_interrupt.rs new file mode 100644 index 0000000..364f28b --- /dev/null +++ b/src/gpio_interrupt.rs @@ -0,0 +1,62 @@ +use core::cell::RefCell; + +use alloc::sync::Arc; +use critical_section::Mutex; +use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal}; +use esp_hal::gpio::Input; + +pub struct InterruptDispatch<'a, const COUNT: usize> { + interrupts: [PinInterrupt<'a>; COUNT] +} + +impl<'a, const COUNT: usize> InterruptDispatch<'a, COUNT> { + pub fn new(interrupts: [PinInterrupt<'a>; COUNT]) -> Self { + Self { interrupts } + } + + pub fn process_interrupts(&self) { + for interrupt in &self.interrupts { + interrupt.handle_interrupt(); + } + } +} + +#[derive(Clone)] +pub struct PinInterrupt<'a> { + pin: Arc>>>, + signal: Arc>, + event: esp_hal::gpio::Event +} + +impl<'a> PinInterrupt<'a> { + pub fn new(pin: Input<'a>, event: esp_hal::gpio::Event) -> Self { + Self { + pin: Arc::new(Mutex::new(RefCell::new(pin))), + signal: Arc::new(Signal::new()), + event + } + } + + pub fn handle_interrupt(&self) { + critical_section::with(|cs| { + let locked = self.pin.borrow(cs); + let mut pin = locked.borrow_mut(); + if pin.is_interrupt_set() { + pin.clear_interrupt(); + self.signal.signal(pin.level()); + } + }); + } + + pub fn listen(&self) { + critical_section::with(|cs| { + let locked = self.pin.borrow(cs); + let mut pin = locked.borrow_mut(); + pin.listen(self.event); + }); + } + + pub async fn wait_for_interrupt(&self) -> esp_hal::gpio::Level { + self.signal.wait().await + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 41aaa9d..941629b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ pub mod graphics; pub mod tracing; pub mod storage; pub mod simdata; +pub mod gpio_interrupt; extern crate alloc; diff --git a/src/tasks/mpu.rs b/src/tasks/mpu.rs index cfe8f12..870bde1 100644 --- a/src/tasks/mpu.rs +++ b/src/tasks/mpu.rs @@ -12,6 +12,7 @@ use mpu6050_dmp::{address::Address, error_async::Error, sensor_async::Mpu6050}; use nalgebra::Vector3; use crate::events::SensorSource; +use crate::gpio_interrupt::PinInterrupt; use crate::{backoff::Backoff, events::Measurement}; const G: f32 = 9.80665; @@ -19,7 +20,7 @@ const GYRO_SCALE: GyroFullScale = GyroFullScale::Deg2000; const ACCEL_SCALE: AccelFullScale = AccelFullScale::G2; #[embassy_executor::task] -pub async fn mpu_task(events: DynamicSender<'static, Measurement>, bus: I2cDevice<'static, NoopRawMutex, I2c<'static, Async>>) { +pub async fn mpu_task(events: DynamicSender<'static, Measurement>, bus: I2cDevice<'static, NoopRawMutex, I2c<'static, Async>>, _interrupt: PinInterrupt<'static>) { let backoff = Backoff::from_millis(5); let busref = RefCell::new(Some(bus));