From 47aeab152fff59e64d8244475dfbec338e6f98e5 Mon Sep 17 00:00:00 2001 From: huntc Date: Tue, 25 Jan 2022 18:06:42 +1100 Subject: PWM WS2812B example and per sequence config Demonstrates how to set the colour of a WS2812B to blue using PWM, and the use of multiple sequences along with their own config. This required an API change. --- embassy-nrf/src/pwm.rs | 89 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 27 deletions(-) (limited to 'embassy-nrf/src') diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index a77cd6332..21b450b19 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -45,6 +45,8 @@ pub enum Error { DMABufferNotInDataMemory, } +const MAX_SEQUENCE_LEN: usize = 32767; + impl<'d, T: Instance> SequencePwm<'d, T> { /// Creates the interface to a `SequencePwm`. /// @@ -62,7 +64,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { ch1: impl Unborrow + 'd, ch2: impl Unborrow + 'd, ch3: impl Unborrow + 'd, - config: SequenceConfig, + config: Config, ) -> Result { unborrow!(ch0, ch1, ch2, ch3); @@ -117,16 +119,6 @@ impl<'d, T: Instance> SequencePwm<'d, T> { r.countertop .write(|w| unsafe { w.countertop().bits(config.max_duty) }); - r.seq0.refresh.write(|w| unsafe { w.bits(config.refresh) }); - r.seq0 - .enddelay - .write(|w| unsafe { w.bits(config.end_delay) }); - - r.seq1.refresh.write(|w| unsafe { w.bits(config.refresh) }); - r.seq1 - .enddelay - .write(|w| unsafe { w.bits(config.end_delay) }); - Ok(Self { phantom: PhantomData, ch0: ch0.degrade_optional(), @@ -136,12 +128,28 @@ impl<'d, T: Instance> SequencePwm<'d, T> { }) } - /// Start or restart playback + /// Start or restart playback. Takes at least one sequence along with its + /// configuration. Optionally takes a second sequence and/or its configuration. + /// In the case where no second sequence is provided then the first sequence + /// is used. In the case where no second sequence configuration is supplied, + /// the first sequence configuration is used. The sequence mode applies to both + /// sequences combined as one. #[inline(always)] - pub fn start(&mut self, sequence: &'d [u16], times: SequenceMode) -> Result<(), Error> { - slice_in_ram_or(sequence, Error::DMABufferNotInDataMemory)?; - - if sequence.len() > 32767 { + pub fn start( + &mut self, + sequence0: &'d [u16], + sequence_config0: SequenceConfig, + sequence1: Option<&'d [u16]>, + sequence_config1: Option, + times: SequenceMode, + ) -> Result<(), Error> { + let alt_sequence = sequence1.unwrap_or(sequence0); + let alt_sequence_config = (&sequence_config1).as_ref().unwrap_or(&sequence_config0); + + slice_in_ram_or(sequence0, Error::DMABufferNotInDataMemory)?; + slice_in_ram_or(alt_sequence, Error::DMABufferNotInDataMemory)?; + + if sequence0.len() > MAX_SEQUENCE_LEN || alt_sequence.len() > MAX_SEQUENCE_LEN { return Err(Error::SequenceTooLong); } @@ -153,19 +161,31 @@ impl<'d, T: Instance> SequencePwm<'d, T> { let r = T::regs(); + r.seq0 + .refresh + .write(|w| unsafe { w.bits(sequence_config0.refresh) }); + r.seq0 + .enddelay + .write(|w| unsafe { w.bits(sequence_config0.end_delay) }); r.seq0 .ptr - .write(|w| unsafe { w.bits(sequence.as_ptr() as u32) }); + .write(|w| unsafe { w.bits(sequence0.as_ptr() as u32) }); r.seq0 .cnt - .write(|w| unsafe { w.bits(sequence.len() as u32) }); + .write(|w| unsafe { w.bits(sequence0.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(sequence.as_ptr() as u32) }); + .write(|w| unsafe { w.bits(alt_sequence.as_ptr() as u32) }); r.seq1 .cnt - .write(|w| unsafe { w.bits(sequence.len() as u32) }); + .write(|w| unsafe { w.bits(alt_sequence.len() as u32) }); r.enable.write(|w| w.enable().enabled()); @@ -356,9 +376,8 @@ impl<'a, T: Instance> Drop for SequencePwm<'a, T> { } } -/// Configure an infinite looping sequence for `SequencePwm` #[non_exhaustive] -pub struct SequenceConfig { +pub struct Config { /// Selects up mode or up-and-down mode for the counter pub counter_mode: CounterMode, /// Top value to be compared against buffer values @@ -367,6 +386,21 @@ pub struct SequenceConfig { pub prescaler: Prescaler, /// How a sequence is read from RAM and is spread to the compare register pub sequence_load: SequenceLoad, +} + +impl Default for Config { + fn default() -> Config { + Config { + counter_mode: CounterMode::Up, + max_duty: 1000, + prescaler: Prescaler::Div16, + sequence_load: SequenceLoad::Common, + } + } +} + +#[non_exhaustive] +pub struct SequenceConfig { /// Number of PWM periods to delay between each sequence sample pub refresh: u32, /// Number of PWM periods after the sequence ends before starting the next sequence @@ -376,10 +410,6 @@ pub struct SequenceConfig { impl Default for SequenceConfig { fn default() -> SequenceConfig { SequenceConfig { - counter_mode: CounterMode::Up, - max_duty: 1000, - prescaler: Prescaler::Div16, - sequence_load: SequenceLoad::Common, refresh: 0, end_delay: 0, } @@ -389,7 +419,12 @@ impl Default for SequenceConfig { /// How many times to run the sequence #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum SequenceMode { - /// Run sequence n Times total + /// Run sequence n Times total. + /// 1 = Run sequence 0 once + /// 2 = Run sequence 0 and then sequence 1 + /// 3 to 4 = Run sequence 0, sequence 1, sequence 0 and then sequence 1 + /// 5 to 6 = Run sequence 0, sequence 1, sequence 0, sequence 1, sequence 0 and then sequence 1 + /// i.e the when >= 2 the loop count is determined by dividing by 2 and rounding up Times(u16), /// Repeat until `stop` is called. Infinite, -- cgit From 12ce02457438c1dd1566f3f3c43537ae4f54f699 Mon Sep 17 00:00:00 2001 From: huntc Date: Fri, 28 Jan 2022 13:38:20 +1100 Subject: Make the sequence a little nicer to pass around --- embassy-nrf/src/pwm.rs | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) (limited to 'embassy-nrf/src') diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 21b450b19..97c02eddc 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -137,19 +137,16 @@ impl<'d, T: Instance> SequencePwm<'d, T> { #[inline(always)] pub fn start( &mut self, - sequence0: &'d [u16], - sequence_config0: SequenceConfig, - sequence1: Option<&'d [u16]>, - sequence_config1: Option, + sequence0: Sequence<'d>, + sequence1: Option>, times: SequenceMode, ) -> Result<(), Error> { - let alt_sequence = sequence1.unwrap_or(sequence0); - let alt_sequence_config = (&sequence_config1).as_ref().unwrap_or(&sequence_config0); + let alt_sequence = sequence1.as_ref().unwrap_or(&sequence0); - slice_in_ram_or(sequence0, Error::DMABufferNotInDataMemory)?; - slice_in_ram_or(alt_sequence, Error::DMABufferNotInDataMemory)?; + slice_in_ram_or(sequence0.words, Error::DMABufferNotInDataMemory)?; + slice_in_ram_or(alt_sequence.words, Error::DMABufferNotInDataMemory)?; - if sequence0.len() > MAX_SEQUENCE_LEN || alt_sequence.len() > MAX_SEQUENCE_LEN { + if sequence0.words.len() > MAX_SEQUENCE_LEN || alt_sequence.words.len() > MAX_SEQUENCE_LEN { return Err(Error::SequenceTooLong); } @@ -163,29 +160,29 @@ impl<'d, T: Instance> SequencePwm<'d, T> { r.seq0 .refresh - .write(|w| unsafe { w.bits(sequence_config0.refresh) }); + .write(|w| unsafe { w.bits(sequence0.config.refresh) }); r.seq0 .enddelay - .write(|w| unsafe { w.bits(sequence_config0.end_delay) }); + .write(|w| unsafe { w.bits(sequence0.config.end_delay) }); r.seq0 .ptr - .write(|w| unsafe { w.bits(sequence0.as_ptr() as u32) }); + .write(|w| unsafe { w.bits(sequence0.words.as_ptr() as u32) }); r.seq0 .cnt - .write(|w| unsafe { w.bits(sequence0.len() as u32) }); + .write(|w| unsafe { w.bits(sequence0.words.len() as u32) }); r.seq1 .refresh - .write(|w| unsafe { w.bits(alt_sequence_config.refresh) }); + .write(|w| unsafe { w.bits(alt_sequence.config.refresh) }); r.seq1 .enddelay - .write(|w| unsafe { w.bits(alt_sequence_config.end_delay) }); + .write(|w| unsafe { w.bits(alt_sequence.config.end_delay) }); r.seq1 .ptr - .write(|w| unsafe { w.bits(alt_sequence.as_ptr() as u32) }); + .write(|w| unsafe { w.bits(alt_sequence.words.as_ptr() as u32) }); r.seq1 .cnt - .write(|w| unsafe { w.bits(alt_sequence.len() as u32) }); + .write(|w| unsafe { w.bits(alt_sequence.words.len() as u32) }); r.enable.write(|w| w.enable().enabled()); @@ -400,6 +397,7 @@ impl Default for Config { } #[non_exhaustive] +#[derive(Clone)] pub struct SequenceConfig { /// Number of PWM periods to delay between each sequence sample pub refresh: u32, @@ -416,6 +414,20 @@ impl Default for SequenceConfig { } } +#[non_exhaustive] +pub struct Sequence<'d> { + /// The words comprising the sequence. Must not exceed 32767 words. + pub words: &'d [u16], + /// Configuration associated with the sequence. + pub config: SequenceConfig, +} + +impl<'d> Sequence<'d> { + pub fn new(words: &'d [u16], config: SequenceConfig) -> Self { + Self { words, config } + } +} + /// How many times to run the sequence #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum SequenceMode { -- cgit From 8e9f4488662699877cf10335991554eb5407f940 Mon Sep 17 00:00:00 2001 From: huntc Date: Fri, 28 Jan 2022 13:43:36 +1100 Subject: Doc tidying --- embassy-nrf/src/pwm.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'embassy-nrf/src') diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 97c02eddc..41dcce049 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -131,9 +131,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Start or restart playback. Takes at least one sequence along with its /// configuration. Optionally takes a second sequence and/or its configuration. /// In the case where no second sequence is provided then the first sequence - /// is used. In the case where no second sequence configuration is supplied, - /// the first sequence configuration is used. The sequence mode applies to both - /// sequences combined as one. + /// is used. The sequence mode applies to both sequences combined as one. #[inline(always)] pub fn start( &mut self, -- cgit From 9ac52a768bbcd4bc8b753c64805fc23906b2c91f Mon Sep 17 00:00:00 2001 From: huntc Date: Fri, 28 Jan 2022 16:21:53 +1100 Subject: Now permits sequences to be mutated subsequently --- embassy-nrf/src/pwm.rs | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'embassy-nrf/src') diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 41dcce049..94dfdeda6 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -31,6 +31,8 @@ pub struct SequencePwm<'d, T: Instance> { ch1: Option, ch2: Option, ch3: Option, + sequence0: Option>, + sequence1: Option>, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -125,11 +127,13 @@ 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/or its configuration. + /// 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)] @@ -152,7 +156,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { return Err(Error::SequenceTimesAtLeastOne); } - self.stop(); + let _ = self.stop(); let r = T::regs(); @@ -222,6 +226,9 @@ impl<'d, T: Instance> SequencePwm<'d, T> { } } + self.sequence0 = Some(sequence0); + self.sequence1 = sequence1; + Ok(()) } @@ -326,9 +333,10 @@ impl<'d, T: Instance> SequencePwm<'d, T> { } /// Stop playback. Disables the peripheral. Does NOT clear the last duty - /// cycle from the pin. + /// cycle from the pin. Returns any sequences previously provided to + /// `start` so that they may be further mutated. #[inline(always)] - pub fn stop(&self) { + pub fn stop(&mut self) -> (Option>, Option>) { let r = T::regs(); r.shorts.reset(); @@ -339,6 +347,8 @@ impl<'d, T: Instance> SequencePwm<'d, T> { r.tasks_stop.write(|w| unsafe { w.bits(0x01) }); r.enable.write(|w| w.enable().disabled()); + + (self.sequence0.take(), self.sequence1.take()) } } @@ -346,7 +356,7 @@ impl<'a, T: Instance> Drop for SequencePwm<'a, T> { fn drop(&mut self) { let r = T::regs(); - self.stop(); + let _ = self.stop(); if let Some(pin) = &self.ch0 { pin.set_low(); @@ -415,13 +425,13 @@ impl Default for SequenceConfig { #[non_exhaustive] pub struct Sequence<'d> { /// The words comprising the sequence. Must not exceed 32767 words. - pub words: &'d [u16], + pub words: &'d mut [u16], /// Configuration associated with the sequence. pub config: SequenceConfig, } impl<'d> Sequence<'d> { - pub fn new(words: &'d [u16], config: SequenceConfig) -> Self { + pub fn new(words: &'d mut [u16], config: SequenceConfig) -> Self { Self { words, config } } } -- cgit From 482389a6911d8d3505872e6ad03d5b0af565eaf9 Mon Sep 17 00:00:00 2001 From: huntc Date: Sat, 29 Jan 2022 15:26:31 +1100 Subject: Own the sequence buffer This approach owns the sequence buffers which, while introducing an extra move, it eliminates the need to guard the lifetime of the sequence buffer. Given ownership, the buffer will be retained until the PWM sequence task is stopped. --- embassy-nrf/src/pwm.rs | 108 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 76 insertions(+), 32 deletions(-) (limited to 'embassy-nrf/src') diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 94dfdeda6..9146160cd 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -25,14 +25,14 @@ pub struct SimplePwm<'d, T: Instance> { /// SequencePwm allows you to offload the updating of a sequence of duty cycles /// to up to four channels, as well as repeat that sequence n times. -pub struct SequencePwm<'d, T: Instance> { +pub struct SequencePwm<'d, T: Instance, const S0: usize, const S1: usize> { phantom: PhantomData<&'d mut T>, ch0: Option, ch1: Option, ch2: Option, ch3: Option, - sequence0: Option>, - sequence1: Option>, + sequence0: Option>, + sequence1: Option>, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -43,13 +43,17 @@ pub enum Error { SequenceTooLong, /// Min Sequence count is 1 SequenceTimesAtLeastOne, + /// Sequence 0 is required, Sequence 1 is NOT required + SequenceTimesRequireSeq0Only, + /// Sequence 0 is required, Sequence 1 is required + SequenceTimesRequireBothSeq0AndSeq1, /// EasyDMA can only read from data memory, read only buffers in flash will fail. DMABufferNotInDataMemory, } const MAX_SEQUENCE_LEN: usize = 32767; -impl<'d, T: Instance> SequencePwm<'d, T> { +impl<'d, T: Instance, const S0: usize, const S1: usize> SequencePwm<'d, T, S0, S1> { /// Creates the interface to a `SequencePwm`. /// /// Must be started by calling `start` @@ -68,6 +72,10 @@ impl<'d, T: Instance> SequencePwm<'d, T> { ch3: impl Unborrow + 'd, config: Config, ) -> Result { + if S0 > MAX_SEQUENCE_LEN || S1 > MAX_SEQUENCE_LEN { + return Err(Error::SequenceTooLong); + } + unborrow!(ch0, ch1, ch2, ch3); let r = T::regs(); @@ -133,31 +141,49 @@ impl<'d, T: Instance> SequencePwm<'d, T> { } /// 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. + /// configuration. A second sequence must be provided when looping i.e. + /// when the sequence mode is anything other than Times(1). #[inline(always)] pub fn start( &mut self, - sequence0: Sequence<'d>, - sequence1: Option>, + sequence0: Sequence, + sequence1: Sequence, 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)?; + slice_in_ram_or(&sequence0.words, Error::DMABufferNotInDataMemory)?; + slice_in_ram_or(&sequence1.words, Error::DMABufferNotInDataMemory)?; - if sequence0.words.len() > MAX_SEQUENCE_LEN || alt_sequence.words.len() > MAX_SEQUENCE_LEN { + let seq_0_word_count = sequence0.word_count.unwrap_or(S0); + let seq_1_word_count = sequence0.word_count.unwrap_or(S1); + if seq_0_word_count > S0 || seq_1_word_count > S1 { return Err(Error::SequenceTooLong); } - if let SequenceMode::Times(0) = times { - return Err(Error::SequenceTimesAtLeastOne); + match times { + SequenceMode::Times(0) => return Err(Error::SequenceTimesAtLeastOne), + SequenceMode::Times(1) if seq_0_word_count == 0 || seq_1_word_count != 0 => { + return Err(Error::SequenceTimesRequireSeq0Only) + } + SequenceMode::Times(1) => (), + SequenceMode::Times(_) | SequenceMode::Infinite + if seq_0_word_count == 0 || seq_1_word_count == 0 => + { + return Err(Error::SequenceTimesRequireBothSeq0AndSeq1) + } + SequenceMode::Times(_) | SequenceMode::Infinite => (), } let _ = self.stop(); + // We now own these sequences and they will be moved. We want + // the peripheral to point at the right bits of memory hence + // moving the sequences early. + self.sequence0 = Some(sequence0); + self.sequence1 = Some(sequence1); + + let sequence0 = self.sequence0.as_ref().unwrap(); + let sequence1 = self.sequence1.as_ref().unwrap(); + let r = T::regs(); r.seq0 @@ -171,20 +197,20 @@ impl<'d, T: Instance> SequencePwm<'d, T> { .write(|w| unsafe { w.bits(sequence0.words.as_ptr() as u32) }); r.seq0 .cnt - .write(|w| unsafe { w.bits(sequence0.words.len() as u32) }); + .write(|w| unsafe { w.bits(seq_0_word_count as u32) }); r.seq1 .refresh - .write(|w| unsafe { w.bits(alt_sequence.config.refresh) }); + .write(|w| unsafe { w.bits(sequence1.config.refresh) }); r.seq1 .enddelay - .write(|w| unsafe { w.bits(alt_sequence.config.end_delay) }); + .write(|w| unsafe { w.bits(sequence1.config.end_delay) }); r.seq1 .ptr - .write(|w| unsafe { w.bits(alt_sequence.words.as_ptr() as u32) }); + .write(|w| unsafe { w.bits(sequence1.words.as_ptr() as u32) }); r.seq1 .cnt - .write(|w| unsafe { w.bits(alt_sequence.words.len() as u32) }); + .write(|w| unsafe { w.bits(seq_1_word_count as u32) }); r.enable.write(|w| w.enable().enabled()); @@ -226,9 +252,6 @@ impl<'d, T: Instance> SequencePwm<'d, T> { } } - self.sequence0 = Some(sequence0); - self.sequence1 = sequence1; - Ok(()) } @@ -336,7 +359,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// 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>, Option>) { + pub fn stop(&mut self) -> (Option>, Option>) { let r = T::regs(); r.shorts.reset(); @@ -352,7 +375,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { } } -impl<'a, T: Instance> Drop for SequencePwm<'a, T> { +impl<'a, T: Instance, const S0: usize, const S1: usize> Drop for SequencePwm<'a, T, S0, S1> { fn drop(&mut self) { let r = T::regs(); @@ -381,6 +404,7 @@ impl<'a, T: Instance> Drop for SequencePwm<'a, T> { } } +/// Configuration for the PWM as a whole. #[non_exhaustive] pub struct Config { /// Selects up mode or up-and-down mode for the counter @@ -404,6 +428,7 @@ impl Default for Config { } } +/// Configuration per sequence #[non_exhaustive] #[derive(Clone)] pub struct SequenceConfig { @@ -422,20 +447,39 @@ impl Default for SequenceConfig { } } +/// A composition of a sequence buffer and its configuration. #[non_exhaustive] -pub struct Sequence<'d> { +#[derive(Clone)] +pub struct Sequence { /// The words comprising the sequence. Must not exceed 32767 words. - pub words: &'d mut [u16], + pub words: [u16; S], + /// The count of words to use. If None the S will be used. + pub word_count: Option, /// Configuration associated with the sequence. pub config: SequenceConfig, } -impl<'d> Sequence<'d> { - pub fn new(words: &'d mut [u16], config: SequenceConfig) -> Self { - Self { words, config } +impl Sequence { + pub const fn new(words: [u16; S], config: SequenceConfig) -> Self { + Self { + words, + word_count: None, + config, + } } } +/// Declares an empty sequence which will cause it to be disabled. +/// Note that any looping i.e. !Times(1), will require a second +/// sequence given the way the PWM peripheral works. +pub const EMPTY_SEQ: Sequence<0> = Sequence::new( + [], + SequenceConfig { + refresh: 0, + end_delay: 0, + }, +); + /// How many times to run the sequence #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum SequenceMode { @@ -446,7 +490,7 @@ pub enum SequenceMode { /// 5 to 6 = Run sequence 0, sequence 1, sequence 0, sequence 1, sequence 0 and then sequence 1 /// i.e the when >= 2 the loop count is determined by dividing by 2 and rounding up Times(u16), - /// Repeat until `stop` is called. + /// Repeat until `stop` is called. Both sequences must be provided. Infinite, } -- cgit From 1c67bd46433734d9280e976da33975cf5beb773e Mon Sep 17 00:00:00 2001 From: huntc Date: Sun, 30 Jan 2022 16:21:23 +1100 Subject: Revert "Own the sequence buffer" This reverts commit 482389a6911d8d3505872e6ad03d5b0af565eaf9. --- embassy-nrf/src/pwm.rs | 108 +++++++++++++++---------------------------------- 1 file changed, 32 insertions(+), 76 deletions(-) (limited to 'embassy-nrf/src') diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 9146160cd..94dfdeda6 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -25,14 +25,14 @@ pub struct SimplePwm<'d, T: Instance> { /// SequencePwm allows you to offload the updating of a sequence of duty cycles /// to up to four channels, as well as repeat that sequence n times. -pub struct SequencePwm<'d, T: Instance, const S0: usize, const S1: usize> { +pub struct SequencePwm<'d, T: Instance> { phantom: PhantomData<&'d mut T>, ch0: Option, ch1: Option, ch2: Option, ch3: Option, - sequence0: Option>, - sequence1: Option>, + sequence0: Option>, + sequence1: Option>, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -43,17 +43,13 @@ pub enum Error { SequenceTooLong, /// Min Sequence count is 1 SequenceTimesAtLeastOne, - /// Sequence 0 is required, Sequence 1 is NOT required - SequenceTimesRequireSeq0Only, - /// Sequence 0 is required, Sequence 1 is required - SequenceTimesRequireBothSeq0AndSeq1, /// EasyDMA can only read from data memory, read only buffers in flash will fail. DMABufferNotInDataMemory, } const MAX_SEQUENCE_LEN: usize = 32767; -impl<'d, T: Instance, const S0: usize, const S1: usize> SequencePwm<'d, T, S0, S1> { +impl<'d, T: Instance> SequencePwm<'d, T> { /// Creates the interface to a `SequencePwm`. /// /// Must be started by calling `start` @@ -72,10 +68,6 @@ impl<'d, T: Instance, const S0: usize, const S1: usize> SequencePwm<'d, T, S0, S ch3: impl Unborrow + 'd, config: Config, ) -> Result { - if S0 > MAX_SEQUENCE_LEN || S1 > MAX_SEQUENCE_LEN { - return Err(Error::SequenceTooLong); - } - unborrow!(ch0, ch1, ch2, ch3); let r = T::regs(); @@ -141,49 +133,31 @@ impl<'d, T: Instance, const S0: usize, const S1: usize> SequencePwm<'d, T, S0, S } /// Start or restart playback. Takes at least one sequence along with its - /// configuration. A second sequence must be provided when looping i.e. - /// when the sequence mode is anything other than Times(1). + /// 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, - sequence1: Sequence, + sequence0: Sequence<'d>, + sequence1: Option>, times: SequenceMode, ) -> Result<(), Error> { - slice_in_ram_or(&sequence0.words, Error::DMABufferNotInDataMemory)?; - slice_in_ram_or(&sequence1.words, Error::DMABufferNotInDataMemory)?; + 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)?; - let seq_0_word_count = sequence0.word_count.unwrap_or(S0); - let seq_1_word_count = sequence0.word_count.unwrap_or(S1); - if seq_0_word_count > S0 || seq_1_word_count > S1 { + if sequence0.words.len() > MAX_SEQUENCE_LEN || alt_sequence.words.len() > MAX_SEQUENCE_LEN { return Err(Error::SequenceTooLong); } - match times { - SequenceMode::Times(0) => return Err(Error::SequenceTimesAtLeastOne), - SequenceMode::Times(1) if seq_0_word_count == 0 || seq_1_word_count != 0 => { - return Err(Error::SequenceTimesRequireSeq0Only) - } - SequenceMode::Times(1) => (), - SequenceMode::Times(_) | SequenceMode::Infinite - if seq_0_word_count == 0 || seq_1_word_count == 0 => - { - return Err(Error::SequenceTimesRequireBothSeq0AndSeq1) - } - SequenceMode::Times(_) | SequenceMode::Infinite => (), + if let SequenceMode::Times(0) = times { + return Err(Error::SequenceTimesAtLeastOne); } let _ = self.stop(); - // We now own these sequences and they will be moved. We want - // the peripheral to point at the right bits of memory hence - // moving the sequences early. - self.sequence0 = Some(sequence0); - self.sequence1 = Some(sequence1); - - let sequence0 = self.sequence0.as_ref().unwrap(); - let sequence1 = self.sequence1.as_ref().unwrap(); - let r = T::regs(); r.seq0 @@ -197,20 +171,20 @@ impl<'d, T: Instance, const S0: usize, const S1: usize> SequencePwm<'d, T, S0, S .write(|w| unsafe { w.bits(sequence0.words.as_ptr() as u32) }); r.seq0 .cnt - .write(|w| unsafe { w.bits(seq_0_word_count as u32) }); + .write(|w| unsafe { w.bits(sequence0.words.len() as u32) }); r.seq1 .refresh - .write(|w| unsafe { w.bits(sequence1.config.refresh) }); + .write(|w| unsafe { w.bits(alt_sequence.config.refresh) }); r.seq1 .enddelay - .write(|w| unsafe { w.bits(sequence1.config.end_delay) }); + .write(|w| unsafe { w.bits(alt_sequence.config.end_delay) }); r.seq1 .ptr - .write(|w| unsafe { w.bits(sequence1.words.as_ptr() as u32) }); + .write(|w| unsafe { w.bits(alt_sequence.words.as_ptr() as u32) }); r.seq1 .cnt - .write(|w| unsafe { w.bits(seq_1_word_count as u32) }); + .write(|w| unsafe { w.bits(alt_sequence.words.len() as u32) }); r.enable.write(|w| w.enable().enabled()); @@ -252,6 +226,9 @@ impl<'d, T: Instance, const S0: usize, const S1: usize> SequencePwm<'d, T, S0, S } } + self.sequence0 = Some(sequence0); + self.sequence1 = sequence1; + Ok(()) } @@ -359,7 +336,7 @@ impl<'d, T: Instance, const S0: usize, const S1: usize> SequencePwm<'d, T, S0, S /// 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>, Option>) { + pub fn stop(&mut self) -> (Option>, Option>) { let r = T::regs(); r.shorts.reset(); @@ -375,7 +352,7 @@ impl<'d, T: Instance, const S0: usize, const S1: usize> SequencePwm<'d, T, S0, S } } -impl<'a, T: Instance, const S0: usize, const S1: usize> Drop for SequencePwm<'a, T, S0, S1> { +impl<'a, T: Instance> Drop for SequencePwm<'a, T> { fn drop(&mut self) { let r = T::regs(); @@ -404,7 +381,6 @@ impl<'a, T: Instance, const S0: usize, const S1: usize> Drop for SequencePwm<'a, } } -/// Configuration for the PWM as a whole. #[non_exhaustive] pub struct Config { /// Selects up mode or up-and-down mode for the counter @@ -428,7 +404,6 @@ impl Default for Config { } } -/// Configuration per sequence #[non_exhaustive] #[derive(Clone)] pub struct SequenceConfig { @@ -447,39 +422,20 @@ impl Default for SequenceConfig { } } -/// A composition of a sequence buffer and its configuration. #[non_exhaustive] -#[derive(Clone)] -pub struct Sequence { +pub struct Sequence<'d> { /// The words comprising the sequence. Must not exceed 32767 words. - pub words: [u16; S], - /// The count of words to use. If None the S will be used. - pub word_count: Option, + pub words: &'d mut [u16], /// Configuration associated with the sequence. pub config: SequenceConfig, } -impl Sequence { - pub const fn new(words: [u16; S], config: SequenceConfig) -> Self { - Self { - words, - word_count: None, - config, - } +impl<'d> Sequence<'d> { + pub fn new(words: &'d mut [u16], config: SequenceConfig) -> Self { + Self { words, config } } } -/// Declares an empty sequence which will cause it to be disabled. -/// Note that any looping i.e. !Times(1), will require a second -/// sequence given the way the PWM peripheral works. -pub const EMPTY_SEQ: Sequence<0> = Sequence::new( - [], - SequenceConfig { - refresh: 0, - end_delay: 0, - }, -); - /// How many times to run the sequence #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum SequenceMode { @@ -490,7 +446,7 @@ pub enum SequenceMode { /// 5 to 6 = Run sequence 0, sequence 1, sequence 0, sequence 1, sequence 0 and then sequence 1 /// i.e the when >= 2 the loop count is determined by dividing by 2 and rounding up Times(u16), - /// Repeat until `stop` is called. Both sequences must be provided. + /// Repeat until `stop` is called. Infinite, } -- cgit From 986295998a3fa8c665364d7b4a5fc009d186dee9 Mon Sep 17 00:00:00 2001 From: huntc Date: Sun, 30 Jan 2022 16:26:09 +1100 Subject: Some more doco --- embassy-nrf/src/pwm.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'embassy-nrf/src') diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 94dfdeda6..a4bc94765 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -381,6 +381,7 @@ impl<'a, T: Instance> Drop for SequencePwm<'a, T> { } } +/// Configuration for the PWM as a whole. #[non_exhaustive] pub struct Config { /// Selects up mode or up-and-down mode for the counter @@ -404,6 +405,7 @@ impl Default for Config { } } +/// Configuration per sequence #[non_exhaustive] #[derive(Clone)] pub struct SequenceConfig { @@ -422,6 +424,7 @@ impl Default for SequenceConfig { } } +/// A composition of a sequence buffer and its configuration. #[non_exhaustive] pub struct Sequence<'d> { /// The words comprising the sequence. Must not exceed 32767 words. -- cgit From bc7266394ddba3d6128cc9de131109c2454f3d05 Mon Sep 17 00:00:00 2001 From: huntc Date: Fri, 4 Feb 2022 11:48:08 +1100 Subject: Clarify why we need the mut buffer --- embassy-nrf/src/pwm.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'embassy-nrf/src') diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index a4bc94765..e561f0383 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -428,6 +428,8 @@ impl Default for SequenceConfig { #[non_exhaustive] pub struct Sequence<'d> { /// 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], /// Configuration associated with the sequence. pub config: SequenceConfig, -- cgit From 1af6b23f970d80d881bbc83fe69846e14c512e1c Mon Sep 17 00:00:00 2001 From: huntc Date: Fri, 4 Feb 2022 13:04:55 +1100 Subject: Introduces a Sequences struct --- embassy-nrf/src/pwm.rs | 274 ++++++++++++++++++++++++++----------------------- 1 file changed, 143 insertions(+), 131 deletions(-) (limited to 'embassy-nrf/src') 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, ch2: Option, ch3: Option, - sequence0: Option>, - sequence1: Option>, } #[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>, - 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>, Option>) { - 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>, +} + +impl<'d, 's, T: Instance> Sequences<'d, 's, T> { + pub fn new( + pwm: &'s mut SequencePwm<'d, T>, + sequence0: Sequence<'s>, + sequence1: Option>, + ) -> 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 { -- cgit From 25be00878c44550a7ecbb6f6501490dba770c20e Mon Sep 17 00:00:00 2001 From: huntc Date: Fri, 4 Feb 2022 15:55:04 +1100 Subject: Doco correction --- embassy-nrf/src/pwm.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'embassy-nrf/src') diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index b696cbe63..d7433502c 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -459,9 +459,9 @@ pub enum SequenceMode { /// Run sequence n Times total. /// 1 = Run sequence 0 once /// 2 = Run sequence 0 and then sequence 1 - /// 3 to 4 = Run sequence 0, sequence 1, sequence 0 and then sequence 1 - /// 5 to 6 = Run sequence 0, sequence 1, sequence 0, sequence 1, sequence 0 and then sequence 1 - /// i.e the when >= 2 the loop count is determined by dividing by 2 and rounding up + /// 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), /// Repeat until `stop` is called. Infinite, -- cgit From fe5501293f39307fbfa419d6f882f4a2cd10c115 Mon Sep 17 00:00:00 2001 From: huntc Date: Fri, 4 Feb 2022 16:26:23 +1100 Subject: Expose PWM --- embassy-nrf/src/pwm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'embassy-nrf/src') diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index d7433502c..55863ea56 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -321,7 +321,7 @@ impl<'s> Sequence<'s> { /// is used. #[non_exhaustive] pub struct Sequences<'d, 's, T: Instance> { - _pwm: &'s mut SequencePwm<'d, T>, + pub pwm: &'s mut SequencePwm<'d, T>, sequence0: Sequence<'s>, sequence1: Option>, } @@ -333,7 +333,7 @@ impl<'d, 's, T: Instance> Sequences<'d, 's, T> { sequence1: Option>, ) -> Self { Sequences { - _pwm: pwm, + pwm, sequence0, sequence1, } -- cgit From 965a5f2c3fba365519bed1c2a955145783d6a05b Mon Sep 17 00:00:00 2001 From: huntc Date: Fri, 4 Feb 2022 19:11:15 +1100 Subject: Introduced the SingleSequencer and a more complex Sequencer --- embassy-nrf/src/pwm.rs | 113 +++++++++++++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 41 deletions(-) (limited to 'embassy-nrf/src') 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>, } -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,26 +465,39 @@ 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 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. +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 two sequences n loops i.e. (n * (seq0 + seq1.unwrap_or(seq0))) + Loop(u16), + /// Repeat until `stop` is called. + Infinite, +} + /// PWM Base clock is system clock (16MHz) divided by prescaler #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Prescaler { -- cgit From 3b2beddc7a5397864d12c66f4b562a2391d3c57c Mon Sep 17 00:00:00 2001 From: huntc Date: Fri, 4 Feb 2022 19:14:24 +1100 Subject: Forgot to expose the stop method --- embassy-nrf/src/pwm.rs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'embassy-nrf/src') diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index c0d73bdc5..895292bc8 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -342,6 +342,14 @@ impl<'d, 's, T: Instance> SingleSequencer<'d, 's, T> { }; self.sequencer.start(start_seq, times) } + + /// 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) { + self.sequencer.stop(); + } } /// A composition of sequences that can be started and stopped. -- cgit From df5ba727f2c8bd3f2a67f51a3f43d7f47b011b1c Mon Sep 17 00:00:00 2001 From: huntc Date: Sat, 5 Feb 2022 08:05:23 +1100 Subject: Further API simplification for the single seq scenario --- embassy-nrf/src/pwm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'embassy-nrf/src') diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 895292bc8..01b1f48d9 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -323,9 +323,9 @@ pub struct SingleSequencer<'d, 's, T: Instance> { 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 { + pub fn new(pwm: &'s mut SequencePwm<'d, T>, words: &'s [u16], config: SequenceConfig) -> Self { Self { - sequencer: Sequencer::new(pwm, sequence, None), + sequencer: Sequencer::new(pwm, Sequence::new(words, config), None), } } -- cgit