From 1af6b23f970d80d881bbc83fe69846e14c512e1c Mon Sep 17 00:00:00 2001
From: huntc <huntchr@gmail.com>
Date: Fri, 4 Feb 2022 13:04:55 +1100
Subject: [PATCH] Introduces a Sequences struct

---
 embassy-nrf/src/pwm.rs                       | 274 ++++++++++---------
 examples/nrf/src/bin/pwm_sequence_ws2812b.rs |  39 ++-
 2 files changed, 161 insertions(+), 152 deletions(-)

diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs
index e561f0383..b696cbe63 100644
--- a/embassy-nrf/src/pwm.rs
+++ b/embassy-nrf/src/pwm.rs
@@ -31,8 +31,6 @@ pub struct SequencePwm<'d, T: Instance> {
     ch1: Option<AnyPin>,
     ch2: Option<AnyPin>,
     ch3: Option<AnyPin>,
-    sequence0: Option<Sequence<'d>>,
-    sequence1: Option<Sequence<'d>>,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -127,111 +125,9 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
             ch1: ch1.degrade_optional(),
             ch2: ch2.degrade_optional(),
             ch3: ch3.degrade_optional(),
-            sequence0: None,
-            sequence1: None,
         })
     }
 
-    /// Start or restart playback. 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. The sequence mode applies to both sequences combined as one.
-    #[inline(always)]
-    pub fn start(
-        &mut self,
-        sequence0: Sequence<'d>,
-        sequence1: Option<Sequence<'d>>,
-        times: SequenceMode,
-    ) -> Result<(), Error> {
-        let alt_sequence = sequence1.as_ref().unwrap_or(&sequence0);
-
-        slice_in_ram_or(sequence0.words, Error::DMABufferNotInDataMemory)?;
-        slice_in_ram_or(alt_sequence.words, Error::DMABufferNotInDataMemory)?;
-
-        if sequence0.words.len() > MAX_SEQUENCE_LEN || alt_sequence.words.len() > MAX_SEQUENCE_LEN {
-            return Err(Error::SequenceTooLong);
-        }
-
-        if let SequenceMode::Times(0) = times {
-            return Err(Error::SequenceTimesAtLeastOne);
-        }
-
-        let _ = self.stop();
-
-        let r = T::regs();
-
-        r.seq0
-            .refresh
-            .write(|w| unsafe { w.bits(sequence0.config.refresh) });
-        r.seq0
-            .enddelay
-            .write(|w| unsafe { w.bits(sequence0.config.end_delay) });
-        r.seq0
-            .ptr
-            .write(|w| unsafe { w.bits(sequence0.words.as_ptr() as u32) });
-        r.seq0
-            .cnt
-            .write(|w| unsafe { w.bits(sequence0.words.len() as u32) });
-
-        r.seq1
-            .refresh
-            .write(|w| unsafe { w.bits(alt_sequence.config.refresh) });
-        r.seq1
-            .enddelay
-            .write(|w| unsafe { w.bits(alt_sequence.config.end_delay) });
-        r.seq1
-            .ptr
-            .write(|w| unsafe { w.bits(alt_sequence.words.as_ptr() as u32) });
-        r.seq1
-            .cnt
-            .write(|w| unsafe { w.bits(alt_sequence.words.len() as u32) });
-
-        r.enable.write(|w| w.enable().enabled());
-
-        // defensive before seqstart
-        compiler_fence(Ordering::SeqCst);
-
-        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) });
-                }
-            }
-            // 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) });
-            }
-        }
-
-        self.sequence0 = Some(sequence0);
-        self.sequence1 = sequence1;
-
-        Ok(())
-    }
-
     /// Returns reference to `Stopped` event endpoint for PPI.
     #[inline(always)]
     pub fn event_stopped(&self) -> Event {
@@ -331,33 +227,12 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
 
         Task::from_reg(&r.tasks_stop)
     }
-
-    /// Stop playback. Disables the peripheral. Does NOT clear the last duty
-    /// cycle from the pin. Returns any sequences previously provided to
-    /// `start` so that they may be further mutated.
-    #[inline(always)]
-    pub fn stop(&mut self) -> (Option<Sequence<'d>>, Option<Sequence<'d>>) {
-        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) });
-
-        r.enable.write(|w| w.enable().disabled());
-
-        (self.sequence0.take(), self.sequence1.take())
-    }
 }
 
 impl<'a, T: Instance> Drop for SequencePwm<'a, T> {
     fn drop(&mut self) {
         let r = T::regs();
 
-        let _ = self.stop();
-
         if let Some(pin) = &self.ch0 {
             pin.set_low();
             pin.conf().reset();
@@ -426,21 +301,158 @@ impl Default for SequenceConfig {
 
 /// A composition of a sequence buffer and its configuration.
 #[non_exhaustive]
-pub struct Sequence<'d> {
+pub struct Sequence<'s> {
     /// The words comprising the sequence. Must not exceed 32767 words.
-    /// The reason for this buffer to be mutable is so that stopping
-    /// the PWM can relinquish the sequence for subsequent modification.
-    pub words: &'d mut [u16],
+    pub words: &'s [u16],
     /// Configuration associated with the sequence.
     pub config: SequenceConfig,
 }
 
-impl<'d> Sequence<'d> {
-    pub fn new(words: &'d mut [u16], config: SequenceConfig) -> Self {
+impl<'s> Sequence<'s> {
+    pub fn new(words: &'s [u16], config: SequenceConfig) -> Self {
         Self { words, config }
     }
 }
 
+/// 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> {
+    _pwm: &'s mut SequencePwm<'d, T>,
+    sequence0: Sequence<'s>,
+    sequence1: Option<Sequence<'s>>,
+}
+
+impl<'d, 's, T: Instance> Sequences<'d, 's, T> {
+    pub fn new(
+        pwm: &'s mut SequencePwm<'d, T>,
+        sequence0: Sequence<'s>,
+        sequence1: Option<Sequence<'s>>,
+    ) -> Self {
+        Sequences {
+            _pwm: pwm,
+            sequence0,
+            sequence1,
+        }
+    }
+
+    /// Start or restart playback. The sequence mode applies to both sequences combined as one.
+    #[inline(always)]
+    pub fn start(&self, times: SequenceMode) -> Result<(), Error> {
+        let sequence0 = &self.sequence0;
+        let alt_sequence = self.sequence1.as_ref().unwrap_or(&self.sequence0);
+
+        slice_in_ram_or(sequence0.words, Error::DMABufferNotInDataMemory)?;
+        slice_in_ram_or(alt_sequence.words, Error::DMABufferNotInDataMemory)?;
+
+        if sequence0.words.len() > MAX_SEQUENCE_LEN || alt_sequence.words.len() > MAX_SEQUENCE_LEN {
+            return Err(Error::SequenceTooLong);
+        }
+
+        if let SequenceMode::Times(0) = times {
+            return Err(Error::SequenceTimesAtLeastOne);
+        }
+
+        let _ = self.stop();
+
+        let r = T::regs();
+
+        r.seq0
+            .refresh
+            .write(|w| unsafe { w.bits(sequence0.config.refresh) });
+        r.seq0
+            .enddelay
+            .write(|w| unsafe { w.bits(sequence0.config.end_delay) });
+        r.seq0
+            .ptr
+            .write(|w| unsafe { w.bits(sequence0.words.as_ptr() as u32) });
+        r.seq0
+            .cnt
+            .write(|w| unsafe { w.bits(sequence0.words.len() as u32) });
+
+        r.seq1
+            .refresh
+            .write(|w| unsafe { w.bits(alt_sequence.config.refresh) });
+        r.seq1
+            .enddelay
+            .write(|w| unsafe { w.bits(alt_sequence.config.end_delay) });
+        r.seq1
+            .ptr
+            .write(|w| unsafe { w.bits(alt_sequence.words.as_ptr() as u32) });
+        r.seq1
+            .cnt
+            .write(|w| unsafe { w.bits(alt_sequence.words.len() as u32) });
+
+        r.enable.write(|w| w.enable().enabled());
+
+        // defensive before seqstart
+        compiler_fence(Ordering::SeqCst);
+
+        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) });
+                }
+            }
+            // 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) });
+            }
+        }
+
+        Ok(())
+    }
+
+    /// Stop playback. Disables the peripheral. Does NOT clear the last duty
+    /// cycle from the pin. Returns any sequences previously provided to
+    /// `start` so that they may be further mutated.
+    #[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) });
+
+        r.enable.write(|w| w.enable().disabled());
+    }
+}
+
+impl<'d, 's, T: Instance> Drop for Sequences<'d, 's, T> {
+    fn drop(&mut self) {
+        let _ = self.stop();
+    }
+}
+
 /// How many times to run the sequence
 #[derive(Debug, Eq, PartialEq, Clone, Copy)]
 pub enum SequenceMode {
diff --git a/examples/nrf/src/bin/pwm_sequence_ws2812b.rs b/examples/nrf/src/bin/pwm_sequence_ws2812b.rs
index 0ce79cbe0..310842d8d 100644
--- a/examples/nrf/src/bin/pwm_sequence_ws2812b.rs
+++ b/examples/nrf/src/bin/pwm_sequence_ws2812b.rs
@@ -9,7 +9,7 @@ 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,
+    Config, Prescaler, Sequence, SequenceConfig, SequenceLoad, SequenceMode, SequencePwm, Sequences,
 };
 use embassy_nrf::Peripherals;
 
@@ -30,19 +30,6 @@ const RES: u16 = 0x8000;
 // line is assumed to be P1_05.
 #[embassy::main]
 async fn main(_spawner: Spawner, p: Peripherals) {
-    // Declare the bits of 24 bits
-    let mut color_seq_words = [
-        T0H, T0H, T0H, T0H, T0H, T0H, T0H, T0H, // G
-        T0H, T0H, T0H, T0H, T0H, T0H, T0H, T0H, // R
-        T1H, T1H, T1H, T1H, T1H, T1H, T1H, T1H, // B
-    ];
-    let color_seq = Sequence::new(&mut color_seq_words, SequenceConfig::default());
-
-    let mut reset_seq_words = [RES; 1];
-    let mut reset_seq_config = SequenceConfig::default();
-    reset_seq_config.end_delay = 799; // 50us (20 ticks * 40) - 1 tick because we've already got one RES;
-    let reset_seq = Sequence::new(&mut reset_seq_words, reset_seq_config);
-
     let mut config = Config::default();
     config.sequence_load = SequenceLoad::Common;
     config.prescaler = Prescaler::Div1;
@@ -51,18 +38,24 @@ async fn main(_spawner: Spawner, p: Peripherals) {
         p.PWM0, p.P1_05, NoPin, NoPin, NoPin, config,
     ));
 
-    unwrap!(pwm.start(color_seq, Some(reset_seq), SequenceMode::Times(2)));
-
-    Timer::after(Duration::from_millis(1000)).await;
+    // Declare the bits of 24 bits in a buffer we'll be
+    // mutating later.
+    let mut seq_words = [
+        T0H, T0H, T0H, T0H, T0H, T0H, T0H, T0H, // G
+        T0H, T0H, T0H, T0H, T0H, T0H, T0H, T0H, // R
+        T1H, T1H, T1H, T1H, T1H, T1H, T1H, T1H, // B
+        RES,
+    ];
+    let mut seq_config = SequenceConfig::default();
+    seq_config.end_delay = 799; // 50us (20 ticks * 40) - 1 tick because we've already got one RES;
 
     let mut color_bit = 16;
     let mut bit_value = T0H;
 
     loop {
-        if let (Some(color_seq), Some(reset_seq)) = pwm.stop() {
-            color_seq.words[color_bit] = bit_value;
-            unwrap!(pwm.start(color_seq, Some(reset_seq), SequenceMode::Times(2)));
-        }
+        let sequence0 = Sequence::new(&seq_words, seq_config.clone());
+        let sequences = Sequences::new(&mut pwm, sequence0, None);
+        unwrap!(sequences.start(SequenceMode::Times(2)));
 
         Timer::after(Duration::from_millis(50)).await;
 
@@ -79,5 +72,9 @@ async fn main(_spawner: Spawner, p: Peripherals) {
                 color_bit -= 1;
             }
         }
+
+        drop(sequences);
+
+        seq_words[color_bit] = bit_value;
     }
 }