diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 55863ea5..c0d73bdc 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -314,26 +314,58 @@ impl<'s> Sequence<'s> { } } +/// A single sequence that can be started and stopped. +/// Takes at one sequence along with its configuration. +#[non_exhaustive] +pub struct SingleSequencer<'d, 's, T: Instance> { + pub sequencer: Sequencer<'d, 's, T>, +} + +impl<'d, 's, T: Instance> SingleSequencer<'d, 's, T> { + /// Create a new sequencer + pub fn new(pwm: &'s mut SequencePwm<'d, T>, sequence: Sequence<'s>) -> Self { + Self { + sequencer: Sequencer::new(pwm, sequence, None), + } + } + + /// Start or restart playback. + #[inline(always)] + pub fn start(&self, times: SingleSequenceMode) -> Result<(), Error> { + let (start_seq, times) = match times { + SingleSequenceMode::Times(n) if n == 1 => (StartSequence::One, SequenceMode::Loop(1)), + SingleSequenceMode::Times(n) if n & 1 == 1 => { + (StartSequence::One, SequenceMode::Loop((n / 2) + 1)) + } + SingleSequenceMode::Times(n) => (StartSequence::Zero, SequenceMode::Loop(n / 2)), + SingleSequenceMode::Infinite => (StartSequence::Zero, SequenceMode::Infinite), + }; + self.sequencer.start(start_seq, times) + } +} + /// A composition of sequences that can be started and stopped. /// Takes at least one sequence along with its configuration. /// Optionally takes a second sequence and its configuration. /// In the case where no second sequence is provided then the first sequence /// is used. #[non_exhaustive] -pub struct Sequences<'d, 's, T: Instance> { - pub pwm: &'s mut SequencePwm<'d, T>, +pub struct Sequencer<'d, 's, T: Instance> { + _pwm: &'s mut SequencePwm<'d, T>, sequence0: Sequence<'s>, sequence1: Option>, } -impl<'d, 's, T: Instance> Sequences<'d, 's, T> { +impl<'d, 's, T: Instance> Sequencer<'d, 's, T> { + /// Create a new double sequence. In the absence of sequence 1, sequence 0 + /// will be used twice in the one loop. pub fn new( pwm: &'s mut SequencePwm<'d, T>, sequence0: Sequence<'s>, sequence1: Option>, ) -> Self { - Sequences { - pwm, + Sequencer { + _pwm: pwm, sequence0, sequence1, } @@ -341,7 +373,7 @@ impl<'d, 's, T: Instance> Sequences<'d, 's, T> { /// Start or restart playback. The sequence mode applies to both sequences combined as one. #[inline(always)] - pub fn start(&self, times: SequenceMode) -> Result<(), Error> { + pub fn start(&self, start_seq: StartSequence, times: SequenceMode) -> Result<(), Error> { let sequence0 = &self.sequence0; let alt_sequence = self.sequence1.as_ref().unwrap_or(&self.sequence0); @@ -352,7 +384,7 @@ impl<'d, 's, T: Instance> Sequences<'d, 's, T> { return Err(Error::SequenceTooLong); } - if let SequenceMode::Times(0) = times { + if let SequenceMode::Loop(0) = times { return Err(Error::SequenceTimesAtLeastOne); } @@ -391,41 +423,27 @@ impl<'d, 's, T: Instance> Sequences<'d, 's, T> { // defensive before seqstart compiler_fence(Ordering::SeqCst); + let seqstart_index = if start_seq == StartSequence::One { + 1 + } else { + 0 + }; + match times { // just the one time, no loop count - SequenceMode::Times(1) => { - r.loop_.write(|w| w.cnt().disabled()); - // tasks_seqstart() doesn't exist in all svds so write its bit instead - r.tasks_seqstart[0].write(|w| unsafe { w.bits(0x01) }); - } - // loop count is how many times to play BOTH sequences - // 2 total (1 x 2) - // 3 total, (2 x 2) - 1 - SequenceMode::Times(n) => { - let odd = n & 1 == 1; - let times = if odd { (n / 2) + 1 } else { n / 2 }; - - r.loop_.write(|w| unsafe { w.cnt().bits(times) }); - - // we can subtract 1 by starting at seq1 instead of seq0 - if odd { - // tasks_seqstart() doesn't exist in all svds so write its bit instead - r.tasks_seqstart[1].write(|w| unsafe { w.bits(0x01) }); - } else { - // tasks_seqstart() doesn't exist in all svds so write its bit instead - r.tasks_seqstart[0].write(|w| unsafe { w.bits(0x01) }); - } + SequenceMode::Loop(n) => { + r.loop_.write(|w| unsafe { w.cnt().bits(n) }); } // to play infinitely, repeat the sequence one time, then have loops done self trigger seq0 again SequenceMode::Infinite => { r.loop_.write(|w| unsafe { w.cnt().bits(0x1) }); r.shorts.write(|w| w.loopsdone_seqstart0().enabled()); - - // tasks_seqstart() doesn't exist in all svds so write its bit instead - r.tasks_seqstart[0].write(|w| unsafe { w.bits(0x01) }); } } + // tasks_seqstart() doesn't exist in all svds so write its bit instead + r.tasks_seqstart[seqstart_index].write(|w| unsafe { w.bits(0x01) }); + Ok(()) } @@ -447,22 +465,35 @@ impl<'d, 's, T: Instance> Sequences<'d, 's, T> { } } -impl<'d, 's, T: Instance> Drop for Sequences<'d, 's, T> { +impl<'d, 's, T: Instance> Drop for Sequencer<'d, 's, T> { fn drop(&mut self) { let _ = self.stop(); } } -/// How many times to run the sequence +/// How many times to run a single sequence +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum SingleSequenceMode { + /// Run a single sequence n Times total. + Times(u16), + /// Repeat until `stop` is called. + Infinite, +} + +/// Which sequence to start a loop with +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum StartSequence { + /// Start with Sequence 0 + Zero, + /// Start with Sequence 1 + One, +} + +/// How many loops to run two sequences #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum SequenceMode { - /// Run sequence n Times total. - /// 1 = Run sequence 0 once - /// 2 = Run sequence 0 and then sequence 1 - /// 3 = Run sequence 1, sequence 0, sequence 1 and then sequence 0 - /// 4 = Run sequence 0, sequence 1, sequence 0 and then sequence 1 - /// ...and so on. - Times(u16), + /// Run two sequences n loops i.e. (n * (seq0 + seq1.unwrap_or(seq0))) + Loop(u16), /// Repeat until `stop` is called. Infinite, } diff --git a/examples/nrf/src/bin/pwm_double_sequence.rs b/examples/nrf/src/bin/pwm_double_sequence.rs new file mode 100644 index 00000000..269015f4 --- /dev/null +++ b/examples/nrf/src/bin/pwm_double_sequence.rs @@ -0,0 +1,46 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +#[path = "../example_common.rs"] +mod example_common; +use defmt::*; +use embassy::executor::Spawner; +use embassy::time::{Duration, Timer}; +use embassy_nrf::gpio::NoPin; +use embassy_nrf::pwm::{ + Config, Prescaler, Sequence, SequenceConfig, SequenceMode, SequencePwm, Sequencer, + StartSequence, +}; +use embassy_nrf::Peripherals; + +#[embassy::main] +async fn main(_spawner: Spawner, p: Peripherals) { + let seq_words_0: [u16; 5] = [1000, 250, 100, 50, 0]; + let seq_words_1: [u16; 4] = [50, 100, 250, 1000]; + + let mut config = Config::default(); + config.prescaler = Prescaler::Div128; + // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8us + // but say we want to hold the value for 5000ms + // so we want to repeat our value as many times as necessary until 5000ms passes + // want 5000/8 = 625 periods total to occur, so 624 (we get the one period for free remember) + let mut seq_config = SequenceConfig::default(); + seq_config.refresh = 624; + // thus our sequence takes 5 * 5000ms or 25 seconds + + let mut pwm = unwrap!(SequencePwm::new( + p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config, + )); + + let sequence_0 = Sequence::new(&seq_words_0, seq_config.clone()); + let sequence_1 = Sequence::new(&seq_words_1, seq_config); + let sequencer = Sequencer::new(&mut pwm, sequence_0, Some(sequence_1)); + unwrap!(sequencer.start(StartSequence::Zero, SequenceMode::Loop(1))); + + // we can abort a sequence if we need to before its complete with pwm.stop() + // or stop is also implicitly called when the pwm peripheral is dropped + // when it goes out of scope + Timer::after(Duration::from_millis(40000)).await; + info!("pwm stopped early!"); +} diff --git a/examples/nrf/src/bin/pwm_sequence.rs b/examples/nrf/src/bin/pwm_sequence.rs index b31c12a2..761ac0f0 100644 --- a/examples/nrf/src/bin/pwm_sequence.rs +++ b/examples/nrf/src/bin/pwm_sequence.rs @@ -9,14 +9,13 @@ use embassy::executor::Spawner; use embassy::time::{Duration, Timer}; use embassy_nrf::gpio::NoPin; use embassy_nrf::pwm::{ - Config, Prescaler, Sequence, SequenceConfig, SequenceMode, SequencePwm, Sequences, + Config, Prescaler, Sequence, SequenceConfig, SequencePwm, SingleSequenceMode, SingleSequencer, }; use embassy_nrf::Peripherals; #[embassy::main] async fn main(_spawner: Spawner, p: Peripherals) { - let seq_words_1: [u16; 5] = [1000, 250, 100, 50, 0]; - let seq_words_2: [u16; 5] = [0, 50, 100, 250, 1000]; + let seq_words: [u16; 5] = [1000, 250, 100, 50, 0]; let mut config = Config::default(); config.prescaler = Prescaler::Div128; @@ -32,20 +31,9 @@ async fn main(_spawner: Spawner, p: Peripherals) { p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config, )); - let sequence0 = Sequence::new(&seq_words_1, seq_config.clone()); - let sequences = Sequences::new(&mut pwm, sequence0, None); - unwrap!(sequences.start(SequenceMode::Times(1))); - - info!("pwm started!"); - - Timer::after(Duration::from_millis(20000)).await; - info!("pwm starting with another sequence!"); - - drop(sequences); // This stops the previous sequence and returns pwm ownership back - - let sequence0 = Sequence::new(&seq_words_2, seq_config); - let sequences = Sequences::new(&mut pwm, sequence0, None); - unwrap!(sequences.start(SequenceMode::Times(1))); + let sequence = Sequence::new(&seq_words, seq_config.clone()); + let sequencer = SingleSequencer::new(&mut pwm, sequence); + unwrap!(sequencer.start(SingleSequenceMode::Times(1))); // we can abort a sequence if we need to before its complete with pwm.stop() // or stop is also implicitly called when the pwm peripheral is dropped diff --git a/examples/nrf/src/bin/pwm_sequence_ppi.rs b/examples/nrf/src/bin/pwm_sequence_ppi.rs index 593e7590..7e58c37e 100644 --- a/examples/nrf/src/bin/pwm_sequence_ppi.rs +++ b/examples/nrf/src/bin/pwm_sequence_ppi.rs @@ -12,7 +12,7 @@ use embassy_nrf::gpio::{Input, NoPin, Pull}; use embassy_nrf::gpiote::{InputChannel, InputChannelPolarity}; use embassy_nrf::ppi::Ppi; use embassy_nrf::pwm::{ - Config, Prescaler, Sequence, SequenceConfig, SequenceMode, SequencePwm, Sequences, + Config, Prescaler, Sequence, SequenceConfig, SequencePwm, SingleSequenceMode, SingleSequencer, }; use embassy_nrf::Peripherals; @@ -33,10 +33,6 @@ async fn main(_spawner: Spawner, p: Peripherals) { p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config, )); - let sequence0 = Sequence::new(&seq_words, seq_config); - let sequences = Sequences::new(&mut pwm, sequence0, None); - unwrap!(sequences.start(SequenceMode::Infinite)); - // 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 @@ -54,8 +50,12 @@ async fn main(_spawner: Spawner, p: Peripherals) { // messing with the pwm tasks is ill advised // Times::Ininite and Times even are seq0, Times odd is seq1 - let start = unsafe { sequences.pwm.task_start_seq0() }; - let stop = unsafe { sequences.pwm.task_stop() }; + let start = unsafe { pwm.task_start_seq0() }; + let stop = unsafe { pwm.task_stop() }; + + let sequence = Sequence::new(&seq_words, seq_config); + let sequencer = SingleSequencer::new(&mut pwm, sequence); + unwrap!(sequencer.start(SingleSequenceMode::Infinite)); let mut ppi = Ppi::new_one_to_one(p.PPI_CH1, button1.event_in(), start); ppi.enable(); diff --git a/examples/nrf/src/bin/pwm_sequence_ws2812b.rs b/examples/nrf/src/bin/pwm_sequence_ws2812b.rs index c0c10373..71ddd528 100644 --- a/examples/nrf/src/bin/pwm_sequence_ws2812b.rs +++ b/examples/nrf/src/bin/pwm_sequence_ws2812b.rs @@ -9,7 +9,8 @@ use embassy::executor::Spawner; use embassy::time::{Duration, Timer}; use embassy_nrf::gpio::NoPin; use embassy_nrf::pwm::{ - Config, Prescaler, Sequence, SequenceConfig, SequenceLoad, SequenceMode, SequencePwm, Sequences, + Config, Prescaler, Sequence, SequenceConfig, SequenceLoad, SequencePwm, SingleSequenceMode, + SingleSequencer, }; use embassy_nrf::Peripherals; @@ -54,8 +55,8 @@ async fn main(_spawner: Spawner, p: Peripherals) { loop { let sequence0 = Sequence::new(&seq_words, seq_config.clone()); - let sequences = Sequences::new(&mut pwm, sequence0, None); - unwrap!(sequences.start(SequenceMode::Times(1))); + let sequences = SingleSequencer::new(&mut pwm, sequence0); + unwrap!(sequences.start(SingleSequenceMode::Times(1))); Timer::after(Duration::from_millis(50)).await;