From 2bcacd4f16276fccb88c7dc3b5e541581e60039b Mon Sep 17 00:00:00 2001 From: Jacob Rosenthal Date: Sat, 13 Nov 2021 15:03:10 -0700 Subject: [PATCH 1/5] nrf: sequencepwm add events --- embassy-nrf/src/pwm.rs | 108 +++++++++++++++++++++++ examples/nrf/src/bin/pwm_sequence_ppi.rs | 73 +++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 examples/nrf/src/bin/pwm_sequence_ppi.rs diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 90cdf69c6..87a2d5b65 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 000000000..aaea9ff00 --- /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; +} From 7dfc0acb2fd4ecbebc8ae4a63fc94f1f484e66a9 Mon Sep 17 00:00:00 2001 From: Jacob Rosenthal Date: Sat, 13 Nov 2021 16:38:35 -0700 Subject: [PATCH 2/5] nrf: pwm clarify stop and disable pin state --- embassy-nrf/src/pwm.rs | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 87a2d5b65..206be5af4 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -308,19 +308,23 @@ impl<'d, T: Instance> SequencePwm<'d, T> { Task::from_reg(&r.tasks_stop) } - /// Stop playback. + /// Stop playback. Does NOT clear the last duty cycle from the pin. #[inline(always)] pub fn stop(&self) { let r = T::regs(); - r.enable.write(|w| w.enable().disabled()); - r.shorts.reset(); compiler_fence(Ordering::SeqCst); + r.events_stopped.reset(); + // tasks_stop() doesn't exist in all svds so write its bit instead r.tasks_stop.write(|w| unsafe { w.bits(0x01) }); + + while r.events_stopped.read().bits() == 0 {} + + r.enable.write(|w| w.enable().disabled()); } } @@ -513,19 +517,6 @@ impl<'d, T: Instance> SimplePwm<'d, T> { pwm } - /// Stop playback - #[inline(always)] - pub fn stop(&self) { - let r = T::regs(); - - r.shorts.reset(); - - compiler_fence(Ordering::SeqCst); - - // tasks_stop() doesn't exist in all svds so write its bit instead - r.tasks_stop.write(|w| unsafe { w.bits(0x01) }); - } - /// Enables the PWM generator. #[inline(always)] pub fn enable(&self) { @@ -533,7 +524,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> { r.enable.write(|w| w.enable().enabled()); } - /// Disables the PWM generator. + /// Disables the PWM generator. Does NOT clear the last duty cycle from the pin. #[inline(always)] pub fn disable(&self) { let r = T::regs(); @@ -554,13 +545,14 @@ impl<'d, T: Instance> SimplePwm<'d, T> { // defensive before seqstart compiler_fence(Ordering::SeqCst); + r.events_seqend[0].reset(); + // tasks_seqstart() doesn't exist in all svds so write its bit instead r.tasks_seqstart[0].write(|w| unsafe { w.bits(1) }); // defensive wait until waveform is loaded after seqstart so set_duty // can't be called again while dma is still reading while r.events_seqend[0].read().bits() == 0 {} - r.events_seqend[0].write(|w| w); } /// Sets the PWM clock prescaler. @@ -620,7 +612,6 @@ impl<'a, T: Instance> Drop for SimplePwm<'a, T> { fn drop(&mut self) { let r = T::regs(); - self.stop(); self.disable(); if let Some(pin) = &self.ch0 { From 3a166db2800c2b4a141d955d035bb3120a2ac757 Mon Sep 17 00:00:00 2001 From: Jacob Rosenthal Date: Sat, 13 Nov 2021 16:41:44 -0700 Subject: [PATCH 3/5] nrf: pwm use register reset --- embassy-nrf/src/pwm.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 206be5af4..48a826e9e 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -336,23 +336,23 @@ impl<'a, T: Instance> Drop for SequencePwm<'a, T> { if let Some(pin) = &self.ch0 { pin.set_low(); - pin.conf().write(|w| w); - r.psel.out[0].write(|w| unsafe { w.bits(0x80000000) }); + pin.conf().reset(); + r.psel.out[0].reset(); } if let Some(pin) = &self.ch1 { pin.set_low(); - pin.conf().write(|w| w); - r.psel.out[1].write(|w| unsafe { w.bits(0x80000000) }); + pin.conf().reset(); + r.psel.out[1].reset(); } if let Some(pin) = &self.ch2 { pin.set_low(); - pin.conf().write(|w| w); - r.psel.out[2].write(|w| unsafe { w.bits(0x80000000) }); + pin.conf().reset(); + r.psel.out[2].reset(); } if let Some(pin) = &self.ch3 { pin.set_low(); - pin.conf().write(|w| w); - r.psel.out[3].write(|w| unsafe { w.bits(0x80000000) }); + pin.conf().reset(); + r.psel.out[3].reset(); } } } @@ -616,23 +616,23 @@ impl<'a, T: Instance> Drop for SimplePwm<'a, T> { if let Some(pin) = &self.ch0 { pin.set_low(); - pin.conf().write(|w| w); - r.psel.out[0].write(|w| unsafe { w.bits(0x80000000) }); + pin.conf().reset(); + r.psel.out[0].reset(); } if let Some(pin) = &self.ch1 { pin.set_low(); - pin.conf().write(|w| w); - r.psel.out[1].write(|w| unsafe { w.bits(0x80000000) }); + pin.conf().reset(); + r.psel.out[1].reset(); } if let Some(pin) = &self.ch2 { pin.set_low(); - pin.conf().write(|w| w); - r.psel.out[2].write(|w| unsafe { w.bits(0x80000000) }); + pin.conf().reset(); + r.psel.out[2].reset(); } if let Some(pin) = &self.ch3 { pin.set_low(); - pin.conf().write(|w| w); - r.psel.out[3].write(|w| unsafe { w.bits(0x80000000) }); + pin.conf().reset(); + r.psel.out[3].reset(); } } } From 4d61f4aa0dcdaf330e7b9c3df407be03dd8181d4 Mon Sep 17 00:00:00 2001 From: Jacob Rosenthal Date: Sat, 13 Nov 2021 17:31:02 -0700 Subject: [PATCH 4/5] nrf: sequencepwm dont wait for tasks_stop afterall --- embassy-nrf/src/pwm.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 48a826e9e..7f08866ee 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -317,13 +317,9 @@ impl<'d, T: Instance> SequencePwm<'d, T> { compiler_fence(Ordering::SeqCst); - r.events_stopped.reset(); - // tasks_stop() doesn't exist in all svds so write its bit instead r.tasks_stop.write(|w| unsafe { w.bits(0x01) }); - while r.events_stopped.read().bits() == 0 {} - r.enable.write(|w| w.enable().disabled()); } } From 4010a829aef42dd7aec5bdf967675119f2fa0857 Mon Sep 17 00:00:00 2001 From: Jacob Rosenthal Date: Sun, 14 Nov 2021 11:26:30 -0700 Subject: [PATCH 5/5] nrf: pwm clarify stop and disable pin state --- embassy-nrf/src/pwm.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 7f08866ee..3fdc37ec0 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -308,7 +308,8 @@ impl<'d, T: Instance> SequencePwm<'d, T> { Task::from_reg(&r.tasks_stop) } - /// Stop playback. Does NOT clear the last duty cycle from the pin. + /// Stop playback. Disables the peripheral. Does NOT clear the last duty + /// cycle from the pin. #[inline(always)] pub fn stop(&self) { let r = T::regs(); @@ -433,8 +434,8 @@ pub enum CounterMode { impl<'d, T: Instance> SimplePwm<'d, T> { /// Creates the interface to a `SimplePwm` /// - /// Defaults the freq to 1Mhz, max_duty 1000, duty 0, up mode, and pins low. - /// Must be started by calling `set_duty` + /// Enables the peripheral, defaults the freq to 1Mhz, max_duty 1000, duty + /// 0, up mode, and pins low. Must be started by calling `set_duty` /// /// # Safety ///