From dc108ca31d2509f1297b4613f452e86830e19b12 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Thu, 9 May 2024 11:04:31 +0200 Subject: [PATCH] rp: Add raw interrupt handler example --- examples/rp/Cargo.toml | 1 + examples/rp/src/bin/interrupt.rs | 95 ++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 examples/rp/src/bin/interrupt.rs diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index ae6746dce..8162e4dcb 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -27,6 +27,7 @@ fixed-macro = "1.2" #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m = { version = "0.7.6", features = ["inline-asm"] } cortex-m-rt = "0.7.0" +critical-section = "1.1" panic-probe = { version = "0.3", features = ["print-defmt"] } display-interface-spi = "0.4.1" embedded-graphics = "0.7.1" diff --git a/examples/rp/src/bin/interrupt.rs b/examples/rp/src/bin/interrupt.rs new file mode 100644 index 000000000..51dacca10 --- /dev/null +++ b/examples/rp/src/bin/interrupt.rs @@ -0,0 +1,95 @@ +//! This example shows how you can use raw interrupt handlers alongside embassy. +//! The example also showcases some of the options available for sharing resources/data. +//! +//! In the example, an ADC reading is triggered every time the PWM wraps around. +//! The sample data is sent down a channel, to be processed inside a low priority task. +//! The processed data is then used to adjust the PWM duty cycle, once every second. + +#![no_std] +#![no_main] + +use core::cell::{Cell, RefCell}; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::adc::{self, Adc, Blocking}; +use embassy_rp::interrupt; +use embassy_rp::pwm::{Config, Pwm}; +use embassy_rp::{gpio::Pull, peripherals::PWM_SLICE4}; +use embassy_sync::{ + blocking_mutex::{raw::CriticalSectionRawMutex, Mutex}, + channel::Channel, +}; +use embassy_time::{Duration, Ticker}; +use portable_atomic::{AtomicU32, Ordering}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +static COUNTER: AtomicU32 = AtomicU32::new(0); +static PWM: Mutex>>> = Mutex::new(RefCell::new(None)); +static ADC: Mutex, adc::Channel)>>> = + Mutex::new(RefCell::new(None)); +static ADC_VALUES: Channel = Channel::new(); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + embassy_rp::pac::SIO.spinlock(31).write_value(1); + let p = embassy_rp::init(Default::default()); + + let adc = Adc::new_blocking(p.ADC, Default::default()); + let p26 = adc::Channel::new_pin(p.PIN_26, Pull::None); + ADC.lock(|a| a.borrow_mut().replace((adc, p26))); + + let pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, Default::default()); + PWM.lock(|p| p.borrow_mut().replace(pwm)); + + // Enable the interrupt for pwm slice 4 + embassy_rp::pac::PWM.inte().modify(|w| w.set_ch4(true)); + unsafe { + cortex_m::peripheral::NVIC::unmask(interrupt::PWM_IRQ_WRAP); + } + + // Tasks require their resources to have 'static lifetime + // No Mutex needed when sharing within the same executor/prio level + static AVG: StaticCell> = StaticCell::new(); + let avg = AVG.init(Default::default()); + spawner.must_spawn(processing(avg)); + + let mut ticker = Ticker::every(Duration::from_secs(1)); + loop { + ticker.next().await; + let freq = COUNTER.swap(0, Ordering::Relaxed); + info!("pwm freq: {:?} Hz", freq); + info!("adc average: {:?}", avg.get()); + + // Update the pwm duty cycle, based on the averaged adc reading + let mut config = Config::default(); + config.compare_b = ((avg.get() as f32 / 4095.0) * config.top as f32) as _; + PWM.lock(|p| p.borrow_mut().as_mut().unwrap().set_config(&config)); + } +} + +#[embassy_executor::task] +async fn processing(avg: &'static Cell) { + let mut buffer: heapless::HistoryBuffer = Default::default(); + loop { + let val = ADC_VALUES.receive().await; + buffer.write(val); + let sum: u32 = buffer.iter().map(|x| *x as u32).sum(); + avg.set(sum / buffer.len() as u32); + } +} + +#[interrupt] +fn PWM_IRQ_WRAP() { + critical_section::with(|cs| { + let mut adc = ADC.borrow(cs).borrow_mut(); + let (adc, p26) = adc.as_mut().unwrap(); + let val = adc.blocking_read(p26).unwrap(); + ADC_VALUES.try_send(val).ok(); + + // Clear the interrupt, so we don't immediately re-enter this irq handler + PWM.borrow(cs).borrow_mut().as_mut().unwrap().clear_wrapped(); + }); + COUNTER.fetch_add(1, Ordering::Relaxed); +}