From 965a5f2c3fba365519bed1c2a955145783d6a05b Mon Sep 17 00:00:00 2001
From: huntc <huntchr@gmail.com>
Date: Fri, 4 Feb 2022 19:11:15 +1100
Subject: [PATCH] Introduced the SingleSequencer and a more complex Sequencer

---
 embassy-nrf/src/pwm.rs                       | 113 ++++++++++++-------
 examples/nrf/src/bin/pwm_double_sequence.rs  |  46 ++++++++
 examples/nrf/src/bin/pwm_sequence.rs         |  22 +---
 examples/nrf/src/bin/pwm_sequence_ppi.rs     |  14 +--
 examples/nrf/src/bin/pwm_sequence_ws2812b.rs |   7 +-
 5 files changed, 134 insertions(+), 68 deletions(-)
 create mode 100644 examples/nrf/src/bin/pwm_double_sequence.rs

diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs
index 55863ea56..c0d73bdc5 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<Sequence<'s>>,
 }
 
-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<Sequence<'s>>,
     ) -> 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 000000000..269015f4a
--- /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 b31c12a23..761ac0f03 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 593e7590d..7e58c37e6 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 c0c10373a..71ddd5283 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;