diff options
| -rw-r--r-- | examples/rp/Cargo.toml | 1 | ||||
| -rw-r--r-- | examples/rp/src/bin/interrupt.rs | 95 |
2 files changed, 96 insertions, 0 deletions
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" | |||
| 27 | #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 27 | #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 28 | cortex-m = { version = "0.7.6", features = ["inline-asm"] } | 28 | cortex-m = { version = "0.7.6", features = ["inline-asm"] } |
| 29 | cortex-m-rt = "0.7.0" | 29 | cortex-m-rt = "0.7.0" |
| 30 | critical-section = "1.1" | ||
| 30 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 31 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 31 | display-interface-spi = "0.4.1" | 32 | display-interface-spi = "0.4.1" |
| 32 | embedded-graphics = "0.7.1" | 33 | 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..d334d35d7 --- /dev/null +++ b/examples/rp/src/bin/interrupt.rs | |||
| @@ -0,0 +1,95 @@ | |||
| 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::peripherals::PWM_SLICE4; | ||
| 19 | use embassy_rp::pwm::{Config, Pwm}; | ||
| 20 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 21 | use embassy_sync::blocking_mutex::Mutex; | ||
| 22 | use embassy_sync::channel::Channel; | ||
| 23 | use embassy_time::{Duration, Ticker}; | ||
| 24 | use portable_atomic::{AtomicU32, Ordering}; | ||
| 25 | use static_cell::StaticCell; | ||
| 26 | use {defmt_rtt as _, panic_probe as _}; | ||
| 27 | |||
| 28 | static COUNTER: AtomicU32 = AtomicU32::new(0); | ||
| 29 | static PWM: Mutex<CriticalSectionRawMutex, RefCell<Option<Pwm<PWM_SLICE4>>>> = Mutex::new(RefCell::new(None)); | ||
| 30 | static ADC: Mutex<CriticalSectionRawMutex, RefCell<Option<(Adc<Blocking>, adc::Channel)>>> = | ||
| 31 | Mutex::new(RefCell::new(None)); | ||
| 32 | static ADC_VALUES: Channel<CriticalSectionRawMutex, u16, 2048> = Channel::new(); | ||
| 33 | |||
| 34 | #[embassy_executor::main] | ||
| 35 | async fn main(spawner: Spawner) { | ||
| 36 | embassy_rp::pac::SIO.spinlock(31).write_value(1); | ||
| 37 | let p = embassy_rp::init(Default::default()); | ||
| 38 | |||
| 39 | let adc = Adc::new_blocking(p.ADC, Default::default()); | ||
| 40 | let p26 = adc::Channel::new_pin(p.PIN_26, Pull::None); | ||
| 41 | ADC.lock(|a| a.borrow_mut().replace((adc, p26))); | ||
| 42 | |||
| 43 | let pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, Default::default()); | ||
| 44 | PWM.lock(|p| p.borrow_mut().replace(pwm)); | ||
| 45 | |||
| 46 | // Enable the interrupt for pwm slice 4 | ||
| 47 | embassy_rp::pac::PWM.inte().modify(|w| w.set_ch4(true)); | ||
| 48 | unsafe { | ||
| 49 | cortex_m::peripheral::NVIC::unmask(interrupt::PWM_IRQ_WRAP); | ||
| 50 | } | ||
| 51 | |||
| 52 | // Tasks require their resources to have 'static lifetime | ||
| 53 | // No Mutex needed when sharing within the same executor/prio level | ||
| 54 | static AVG: StaticCell<Cell<u32>> = StaticCell::new(); | ||
| 55 | let avg = AVG.init(Default::default()); | ||
| 56 | spawner.must_spawn(processing(avg)); | ||
| 57 | |||
| 58 | let mut ticker = Ticker::every(Duration::from_secs(1)); | ||
| 59 | loop { | ||
| 60 | ticker.next().await; | ||
| 61 | let freq = COUNTER.swap(0, Ordering::Relaxed); | ||
| 62 | info!("pwm freq: {:?} Hz", freq); | ||
| 63 | info!("adc average: {:?}", avg.get()); | ||
| 64 | |||
| 65 | // Update the pwm duty cycle, based on the averaged adc reading | ||
| 66 | let mut config = Config::default(); | ||
| 67 | config.compare_b = ((avg.get() as f32 / 4095.0) * config.top as f32) as _; | ||
| 68 | PWM.lock(|p| p.borrow_mut().as_mut().unwrap().set_config(&config)); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | #[embassy_executor::task] | ||
| 73 | async fn processing(avg: &'static Cell<u32>) { | ||
| 74 | let mut buffer: heapless::HistoryBuffer<u16, 100> = Default::default(); | ||
| 75 | loop { | ||
| 76 | let val = ADC_VALUES.receive().await; | ||
| 77 | buffer.write(val); | ||
| 78 | let sum: u32 = buffer.iter().map(|x| *x as u32).sum(); | ||
| 79 | avg.set(sum / buffer.len() as u32); | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | #[interrupt] | ||
| 84 | fn PWM_IRQ_WRAP() { | ||
| 85 | critical_section::with(|cs| { | ||
| 86 | let mut adc = ADC.borrow(cs).borrow_mut(); | ||
| 87 | let (adc, p26) = adc.as_mut().unwrap(); | ||
| 88 | let val = adc.blocking_read(p26).unwrap(); | ||
| 89 | ADC_VALUES.try_send(val).ok(); | ||
| 90 | |||
| 91 | // Clear the interrupt, so we don't immediately re-enter this irq handler | ||
| 92 | PWM.borrow(cs).borrow_mut().as_mut().unwrap().clear_wrapped(); | ||
| 93 | }); | ||
| 94 | COUNTER.fetch_add(1, Ordering::Relaxed); | ||
| 95 | } | ||
