diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 90cdf69c..87a2d5b6 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -9,6 +9,7 @@ use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, OptionalPin as GpioOptionalPin}; use crate::interrupt::Interrupt; use crate::pac; +use crate::ppi::{Event, Task}; use crate::util::slice_in_ram_or; /// SimplePwm is the traditional pwm interface you're probably used to, allowing @@ -101,6 +102,13 @@ impl<'d, T: Instance> SequencePwm<'d, T> { // Disable all interrupts r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); r.shorts.reset(); + r.events_stopped.reset(); + r.events_loopsdone.reset(); + r.events_seqend[0].reset(); + r.events_seqend[1].reset(); + r.events_pwmperiodend.reset(); + r.events_seqstarted[0].reset(); + r.events_seqstarted[1].reset(); r.seq0 .ptr @@ -200,6 +208,106 @@ impl<'d, T: Instance> SequencePwm<'d, T> { Ok(()) } + /// Returns reference to `Stopped` event endpoint for PPI. + #[inline(always)] + pub fn event_stopped(&self) -> Event { + let r = T::regs(); + + Event::from_reg(&r.events_stopped) + } + + /// Returns reference to `LoopsDone` event endpoint for PPI. + #[inline(always)] + pub fn event_loops_done(&self) -> Event { + let r = T::regs(); + + Event::from_reg(&r.events_loopsdone) + } + + /// Returns reference to `PwmPeriodEnd` event endpoint for PPI. + #[inline(always)] + pub fn event_pwm_period_end(&self) -> Event { + let r = T::regs(); + + Event::from_reg(&r.events_pwmperiodend) + } + + /// Returns reference to `Seq0 End` event endpoint for PPI. + #[inline(always)] + pub fn event_seq_end(&self) -> Event { + let r = T::regs(); + + Event::from_reg(&r.events_seqend[0]) + } + + /// Returns reference to `Seq1 End` event endpoint for PPI. + #[inline(always)] + pub fn event_seq1_end(&self) -> Event { + let r = T::regs(); + + Event::from_reg(&r.events_seqend[1]) + } + + /// Returns reference to `Seq0 Started` event endpoint for PPI. + #[inline(always)] + pub fn event_seq0_started(&self) -> Event { + let r = T::regs(); + + Event::from_reg(&r.events_seqstarted[0]) + } + + /// Returns reference to `Seq1 Started` event endpoint for PPI. + #[inline(always)] + pub fn event_seq1_started(&self) -> Event { + let r = T::regs(); + + Event::from_reg(&r.events_seqstarted[1]) + } + + /// Returns reference to `Seq0 Start` task endpoint for PPI. + /// # Safety + /// + /// Interacting with the sequence while it runs puts it in an unknown state + #[inline(always)] + pub unsafe fn task_start_seq0(&self) -> Task { + let r = T::regs(); + + Task::from_reg(&r.tasks_seqstart[0]) + } + + /// Returns reference to `Seq1 Started` task endpoint for PPI. + /// # Safety + /// + /// Interacting with the sequence while it runs puts it in an unknown state + #[inline(always)] + pub unsafe fn task_start_seq1(&self) -> Task { + let r = T::regs(); + + Task::from_reg(&r.tasks_seqstart[1]) + } + + /// Returns reference to `NextStep` task endpoint for PPI. + /// # Safety + /// + /// Interacting with the sequence while it runs puts it in an unknown state + #[inline(always)] + pub unsafe fn task_next_step(&self) -> Task { + let r = T::regs(); + + Task::from_reg(&r.tasks_nextstep) + } + + /// Returns reference to `Stop` task endpoint for PPI. + /// # Safety + /// + /// Interacting with the sequence while it runs puts it in an unknown state + #[inline(always)] + pub unsafe fn task_stop(&self) -> Task { + let r = T::regs(); + + Task::from_reg(&r.tasks_stop) + } + /// Stop playback. #[inline(always)] pub fn stop(&self) { diff --git a/examples/nrf/src/bin/pwm_sequence_ppi.rs b/examples/nrf/src/bin/pwm_sequence_ppi.rs new file mode 100644 index 00000000..aaea9ff0 --- /dev/null +++ b/examples/nrf/src/bin/pwm_sequence_ppi.rs @@ -0,0 +1,73 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#![feature(array_from_fn)] + +#[path = "../example_common.rs"] +mod example_common; +use core::future::pending; +use defmt::*; +use embassy::executor::Spawner; +use embassy_nrf::gpio::{Input, NoPin, Pull}; +use embassy_nrf::gpiote::{InputChannel, InputChannelPolarity}; +use embassy_nrf::ppi::Ppi; +use embassy_nrf::pwm::{Prescaler, SequenceConfig, SequenceMode, SequencePwm}; +use embassy_nrf::Peripherals; + +#[embassy::main] +async fn main(_spawner: Spawner, p: Peripherals) { + let seq_values: [u16; 5] = [1000, 250, 100, 50, 0]; + + let mut config = SequenceConfig::default(); + config.prescaler = Prescaler::Div128; + // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8ms + // but say we want to hold the value for 250ms 250ms/8 = 31.25 periods + // so round to 31 - 1 (we get the one period for free remember) + // thus our sequence takes 5 * 250ms or 1.25 seconds + config.refresh = 30; + + let pwm = unwrap!(SequencePwm::new( + p.PWM0, + p.P0_13, + NoPin, + NoPin, + NoPin, + config, + &seq_values + )); + + let _ = pwm.start(SequenceMode::Times(1)); + // pwm.stop() deconfigures pins, and then the task_start_seq0 task cant work + // so its going to have to start running in order load the configuration + + let button1 = InputChannel::new( + p.GPIOTE_CH0, + Input::new(p.P0_11, Pull::Up), + InputChannelPolarity::HiToLo, + ); + + let button2 = InputChannel::new( + p.GPIOTE_CH1, + Input::new(p.P0_12, Pull::Up), + InputChannelPolarity::HiToLo, + ); + + // messing with the pwm tasks is ill advised + // Times::Ininite and Times even are seq0, Times odd is seq1 + let start = unsafe { pwm.task_start_seq0() }; + let stop = unsafe { pwm.task_stop() }; + + let mut ppi = Ppi::new_one_to_one(p.PPI_CH1, button1.event_in(), start); + ppi.enable(); + + let mut ppi2 = Ppi::new_one_to_one(p.PPI_CH0, button2.event_in(), stop); + ppi2.enable(); + + info!("PPI setup!"); + info!("Press button 1 to start LED 1"); + info!("Press button 2 to stop LED 1"); + info!("Note! task_stop stops the sequence, but not the pin output"); + + // Block forever so the above drivers don't get dropped + pending::<()>().await; +}