diff options
Diffstat (limited to 'examples/rp235x/src/bin/interrupt.rs')
| -rw-r--r-- | examples/rp235x/src/bin/interrupt.rs | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/examples/rp235x/src/bin/interrupt.rs b/examples/rp235x/src/bin/interrupt.rs new file mode 100644 index 000000000..e9ac76486 --- /dev/null +++ b/examples/rp235x/src/bin/interrupt.rs | |||
| @@ -0,0 +1,93 @@ | |||
| 1 | //! This example shows how you can use raw interrupt handlers alongside embassy. | ||
| 2 | //! The example also showcases some of the options available for sharing resources/data. | ||
| 3 | //! | ||
| 4 | //! In the example, an ADC reading is triggered every time the PWM wraps around. | ||
| 5 | //! The sample data is sent down a channel, to be processed inside a low priority task. | ||
| 6 | //! The processed data is then used to adjust the PWM duty cycle, once every second. | ||
| 7 | |||
| 8 | #![no_std] | ||
| 9 | #![no_main] | ||
| 10 | |||
| 11 | use core::cell::{Cell, RefCell}; | ||
| 12 | |||
| 13 | use defmt::*; | ||
| 14 | use embassy_executor::Spawner; | ||
| 15 | use embassy_rp::adc::{self, Adc, Blocking}; | ||
| 16 | use embassy_rp::gpio::Pull; | ||
| 17 | use embassy_rp::interrupt; | ||
| 18 | use embassy_rp::pwm::{Config, Pwm}; | ||
| 19 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 20 | use embassy_sync::blocking_mutex::Mutex; | ||
| 21 | use embassy_sync::channel::Channel; | ||
| 22 | use embassy_time::{Duration, Ticker}; | ||
| 23 | use portable_atomic::{AtomicU32, Ordering}; | ||
| 24 | use static_cell::StaticCell; | ||
| 25 | use {defmt_rtt as _, panic_probe as _}; | ||
| 26 | |||
| 27 | static COUNTER: AtomicU32 = AtomicU32::new(0); | ||
| 28 | static PWM: Mutex<CriticalSectionRawMutex, RefCell<Option<Pwm>>> = Mutex::new(RefCell::new(None)); | ||
| 29 | static ADC: Mutex<CriticalSectionRawMutex, RefCell<Option<(Adc<Blocking>, adc::Channel)>>> = | ||
| 30 | Mutex::new(RefCell::new(None)); | ||
| 31 | static ADC_VALUES: Channel<CriticalSectionRawMutex, u16, 2048> = Channel::new(); | ||
| 32 | |||
| 33 | #[embassy_executor::main] | ||
| 34 | async fn main(spawner: Spawner) { | ||
| 35 | let p = embassy_rp::init(Default::default()); | ||
| 36 | |||
| 37 | let adc = Adc::new_blocking(p.ADC, Default::default()); | ||
| 38 | let p26 = adc::Channel::new_pin(p.PIN_26, Pull::None); | ||
| 39 | ADC.lock(|a| a.borrow_mut().replace((adc, p26))); | ||
| 40 | |||
| 41 | let pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, Default::default()); | ||
| 42 | PWM.lock(|p| p.borrow_mut().replace(pwm)); | ||
| 43 | |||
| 44 | // Enable the interrupt for pwm slice 4 | ||
| 45 | embassy_rp::pac::PWM.irq0_inte().modify(|w| w.set_ch4(true)); | ||
| 46 | unsafe { | ||
| 47 | cortex_m::peripheral::NVIC::unmask(interrupt::PWM_IRQ_WRAP_0); | ||
| 48 | } | ||
| 49 | |||
| 50 | // Tasks require their resources to have 'static lifetime | ||
| 51 | // No Mutex needed when sharing within the same executor/prio level | ||
| 52 | static AVG: StaticCell<Cell<u32>> = StaticCell::new(); | ||
| 53 | let avg = AVG.init(Default::default()); | ||
| 54 | spawner.must_spawn(processing(avg)); | ||
| 55 | |||
| 56 | let mut ticker = Ticker::every(Duration::from_secs(1)); | ||
| 57 | loop { | ||
| 58 | ticker.next().await; | ||
| 59 | let freq = COUNTER.swap(0, Ordering::Relaxed); | ||
| 60 | info!("pwm freq: {:?} Hz", freq); | ||
| 61 | info!("adc average: {:?}", avg.get()); | ||
| 62 | |||
| 63 | // Update the pwm duty cycle, based on the averaged adc reading | ||
| 64 | let mut config = Config::default(); | ||
| 65 | config.compare_b = ((avg.get() as f32 / 4095.0) * config.top as f32) as _; | ||
| 66 | PWM.lock(|p| p.borrow_mut().as_mut().unwrap().set_config(&config)); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | #[embassy_executor::task] | ||
| 71 | async fn processing(avg: &'static Cell<u32>) { | ||
| 72 | let mut buffer: heapless::HistoryBuffer<u16, 100> = Default::default(); | ||
| 73 | loop { | ||
| 74 | let val = ADC_VALUES.receive().await; | ||
| 75 | buffer.write(val); | ||
| 76 | let sum: u32 = buffer.iter().map(|x| *x as u32).sum(); | ||
| 77 | avg.set(sum / buffer.len() as u32); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | #[interrupt] | ||
| 82 | fn PWM_IRQ_WRAP_0() { | ||
| 83 | critical_section::with(|cs| { | ||
| 84 | let mut adc = ADC.borrow(cs).borrow_mut(); | ||
| 85 | let (adc, p26) = adc.as_mut().unwrap(); | ||
| 86 | let val = adc.blocking_read(p26).unwrap(); | ||
| 87 | ADC_VALUES.try_send(val).ok(); | ||
| 88 | |||
| 89 | // Clear the interrupt, so we don't immediately re-enter this irq handler | ||
| 90 | PWM.borrow(cs).borrow_mut().as_mut().unwrap().clear_wrapped(); | ||
| 91 | }); | ||
| 92 | COUNTER.fetch_add(1, Ordering::Relaxed); | ||
| 93 | } | ||
