diff options
| author | Caleb Jamison <[email protected]> | 2024-08-07 23:20:26 -0400 |
|---|---|---|
| committer | Caleb Jamison <[email protected]> | 2024-08-08 21:35:21 -0400 |
| commit | b185e02a42ad751ec6c31ffa6a1b87503f15489d (patch) | |
| tree | 0f0c66747267d24d95b5957b22db7e5c525cb00e /examples/rp23/src/bin/interrupt.rs | |
| parent | 891c5ee10584cd990dad529e3506fe1328e4e69d (diff) | |
Initial rp235x support
Examples have been run, but there is not yet a test suite.
Diffstat (limited to 'examples/rp23/src/bin/interrupt.rs')
| -rw-r--r-- | examples/rp23/src/bin/interrupt.rs | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/examples/rp23/src/bin/interrupt.rs b/examples/rp23/src/bin/interrupt.rs new file mode 100644 index 000000000..f46117f95 --- /dev/null +++ b/examples/rp23/src/bin/interrupt.rs | |||
| @@ -0,0 +1,110 @@ | |||
| 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 | use embassy_rp::block::ImageDef; | ||
| 27 | |||
| 28 | #[link_section = ".start_block"] | ||
| 29 | #[used] | ||
| 30 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 31 | |||
| 32 | // Program metadata for `picotool info` | ||
| 33 | #[link_section = ".bi_entries"] | ||
| 34 | #[used] | ||
| 35 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 36 | embassy_rp::binary_info_rp_cargo_bin_name!(), | ||
| 37 | embassy_rp::binary_info_rp_cargo_version!(), | ||
| 38 | embassy_rp::binary_info_rp_program_description!(c"Blinky"), | ||
| 39 | embassy_rp::binary_info_rp_program_build_attribute!(), | ||
| 40 | ]; | ||
| 41 | |||
| 42 | |||
| 43 | static COUNTER: AtomicU32 = AtomicU32::new(0); | ||
| 44 | static PWM: Mutex<CriticalSectionRawMutex, RefCell<Option<Pwm>>> = Mutex::new(RefCell::new(None)); | ||
| 45 | static ADC: Mutex<CriticalSectionRawMutex, RefCell<Option<(Adc<Blocking>, adc::Channel)>>> = | ||
| 46 | Mutex::new(RefCell::new(None)); | ||
| 47 | static ADC_VALUES: Channel<CriticalSectionRawMutex, u16, 2048> = Channel::new(); | ||
| 48 | |||
| 49 | #[embassy_executor::main] | ||
| 50 | async fn main(spawner: Spawner) { | ||
| 51 | embassy_rp::pac::SIO.spinlock(31).write_value(1); | ||
| 52 | let p = embassy_rp::init(Default::default()); | ||
| 53 | |||
| 54 | let adc = Adc::new_blocking(p.ADC, Default::default()); | ||
| 55 | let p26 = adc::Channel::new_pin(p.PIN_26, Pull::None); | ||
| 56 | ADC.lock(|a| a.borrow_mut().replace((adc, p26))); | ||
| 57 | |||
| 58 | let pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, Default::default()); | ||
| 59 | PWM.lock(|p| p.borrow_mut().replace(pwm)); | ||
| 60 | |||
| 61 | // Enable the interrupt for pwm slice 4 | ||
| 62 | embassy_rp::pac::PWM.irq0_inte().modify(|w| w.set_ch4(true)); | ||
| 63 | unsafe { | ||
| 64 | cortex_m::peripheral::NVIC::unmask(interrupt::PWM_IRQ_WRAP_0); | ||
| 65 | } | ||
| 66 | |||
| 67 | // Tasks require their resources to have 'static lifetime | ||
| 68 | // No Mutex needed when sharing within the same executor/prio level | ||
| 69 | static AVG: StaticCell<Cell<u32>> = StaticCell::new(); | ||
| 70 | let avg = AVG.init(Default::default()); | ||
| 71 | spawner.must_spawn(processing(avg)); | ||
| 72 | |||
| 73 | let mut ticker = Ticker::every(Duration::from_secs(1)); | ||
| 74 | loop { | ||
| 75 | ticker.next().await; | ||
| 76 | let freq = COUNTER.swap(0, Ordering::Relaxed); | ||
| 77 | info!("pwm freq: {:?} Hz", freq); | ||
| 78 | info!("adc average: {:?}", avg.get()); | ||
| 79 | |||
| 80 | // Update the pwm duty cycle, based on the averaged adc reading | ||
| 81 | let mut config = Config::default(); | ||
| 82 | config.compare_b = ((avg.get() as f32 / 4095.0) * config.top as f32) as _; | ||
| 83 | PWM.lock(|p| p.borrow_mut().as_mut().unwrap().set_config(&config)); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | #[embassy_executor::task] | ||
| 88 | async fn processing(avg: &'static Cell<u32>) { | ||
| 89 | let mut buffer: heapless::HistoryBuffer<u16, 100> = Default::default(); | ||
| 90 | loop { | ||
| 91 | let val = ADC_VALUES.receive().await; | ||
| 92 | buffer.write(val); | ||
| 93 | let sum: u32 = buffer.iter().map(|x| *x as u32).sum(); | ||
| 94 | avg.set(sum / buffer.len() as u32); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | #[interrupt] | ||
| 99 | fn PWM_IRQ_WRAP_0() { | ||
| 100 | critical_section::with(|cs| { | ||
| 101 | let mut adc = ADC.borrow(cs).borrow_mut(); | ||
| 102 | let (adc, p26) = adc.as_mut().unwrap(); | ||
| 103 | let val = adc.blocking_read(p26).unwrap(); | ||
| 104 | ADC_VALUES.try_send(val).ok(); | ||
| 105 | |||
| 106 | // Clear the interrupt, so we don't immediately re-enter this irq handler | ||
| 107 | PWM.borrow(cs).borrow_mut().as_mut().unwrap().clear_wrapped(); | ||
| 108 | }); | ||
| 109 | COUNTER.fetch_add(1, Ordering::Relaxed); | ||
| 110 | } | ||
