aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhuntc <[email protected]>2022-01-29 15:26:31 +1100
committerhuntc <[email protected]>2022-01-29 18:01:06 +1100
commit482389a6911d8d3505872e6ad03d5b0af565eaf9 (patch)
treed23d5bb09378d223485e40073717b48ac3a398c2
parent9ac52a768bbcd4bc8b753c64805fc23906b2c91f (diff)
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.
-rw-r--r--embassy-nrf/src/pwm.rs108
-rw-r--r--examples/nrf/src/bin/pwm_sequence.rs22
-rw-r--r--examples/nrf/src/bin/pwm_sequence_ppi.rs13
-rw-r--r--examples/nrf/src/bin/pwm_sequence_ws2812b.rs33
4 files changed, 108 insertions, 68 deletions
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> {
25 25
26/// SequencePwm allows you to offload the updating of a sequence of duty cycles 26/// SequencePwm allows you to offload the updating of a sequence of duty cycles
27/// to up to four channels, as well as repeat that sequence n times. 27/// to up to four channels, as well as repeat that sequence n times.
28pub struct SequencePwm<'d, T: Instance> { 28pub struct SequencePwm<'d, T: Instance, const S0: usize, const S1: usize> {
29 phantom: PhantomData<&'d mut T>, 29 phantom: PhantomData<&'d mut T>,
30 ch0: Option<AnyPin>, 30 ch0: Option<AnyPin>,
31 ch1: Option<AnyPin>, 31 ch1: Option<AnyPin>,
32 ch2: Option<AnyPin>, 32 ch2: Option<AnyPin>,
33 ch3: Option<AnyPin>, 33 ch3: Option<AnyPin>,
34 sequence0: Option<Sequence<'d>>, 34 sequence0: Option<Sequence<S0>>,
35 sequence1: Option<Sequence<'d>>, 35 sequence1: Option<Sequence<S1>>,
36} 36}
37 37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)] 38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -43,13 +43,17 @@ pub enum Error {
43 SequenceTooLong, 43 SequenceTooLong,
44 /// Min Sequence count is 1 44 /// Min Sequence count is 1
45 SequenceTimesAtLeastOne, 45 SequenceTimesAtLeastOne,
46 /// Sequence 0 is required, Sequence 1 is NOT required
47 SequenceTimesRequireSeq0Only,
48 /// Sequence 0 is required, Sequence 1 is required
49 SequenceTimesRequireBothSeq0AndSeq1,
46 /// EasyDMA can only read from data memory, read only buffers in flash will fail. 50 /// EasyDMA can only read from data memory, read only buffers in flash will fail.
47 DMABufferNotInDataMemory, 51 DMABufferNotInDataMemory,
48} 52}
49 53
50const MAX_SEQUENCE_LEN: usize = 32767; 54const MAX_SEQUENCE_LEN: usize = 32767;
51 55
52impl<'d, T: Instance> SequencePwm<'d, T> { 56impl<'d, T: Instance, const S0: usize, const S1: usize> SequencePwm<'d, T, S0, S1> {
53 /// Creates the interface to a `SequencePwm`. 57 /// Creates the interface to a `SequencePwm`.
54 /// 58 ///
55 /// Must be started by calling `start` 59 /// Must be started by calling `start`
@@ -68,6 +72,10 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
68 ch3: impl Unborrow<Target = impl GpioOptionalPin> + 'd, 72 ch3: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
69 config: Config, 73 config: Config,
70 ) -> Result<Self, Error> { 74 ) -> Result<Self, Error> {
75 if S0 > MAX_SEQUENCE_LEN || S1 > MAX_SEQUENCE_LEN {
76 return Err(Error::SequenceTooLong);
77 }
78
71 unborrow!(ch0, ch1, ch2, ch3); 79 unborrow!(ch0, ch1, ch2, ch3);
72 80
73 let r = T::regs(); 81 let r = T::regs();
@@ -133,31 +141,49 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
133 } 141 }
134 142
135 /// Start or restart playback. Takes at least one sequence along with its 143 /// Start or restart playback. Takes at least one sequence along with its
136 /// configuration. Optionally takes a second sequence and its configuration. 144 /// configuration. A second sequence must be provided when looping i.e.
137 /// In the case where no second sequence is provided then the first sequence 145 /// when the sequence mode is anything other than Times(1).
138 /// is used. The sequence mode applies to both sequences combined as one.
139 #[inline(always)] 146 #[inline(always)]
140 pub fn start( 147 pub fn start(
141 &mut self, 148 &mut self,
142 sequence0: Sequence<'d>, 149 sequence0: Sequence<S0>,
143 sequence1: Option<Sequence<'d>>, 150 sequence1: Sequence<S1>,
144 times: SequenceMode, 151 times: SequenceMode,
145 ) -> Result<(), Error> { 152 ) -> Result<(), Error> {
146 let alt_sequence = sequence1.as_ref().unwrap_or(&sequence0); 153 slice_in_ram_or(&sequence0.words, Error::DMABufferNotInDataMemory)?;
147 154 slice_in_ram_or(&sequence1.words, Error::DMABufferNotInDataMemory)?;
148 slice_in_ram_or(sequence0.words, Error::DMABufferNotInDataMemory)?;
149 slice_in_ram_or(alt_sequence.words, Error::DMABufferNotInDataMemory)?;
150 155
151 if sequence0.words.len() > MAX_SEQUENCE_LEN || alt_sequence.words.len() > MAX_SEQUENCE_LEN { 156 let seq_0_word_count = sequence0.word_count.unwrap_or(S0);
157 let seq_1_word_count = sequence0.word_count.unwrap_or(S1);
158 if seq_0_word_count > S0 || seq_1_word_count > S1 {
152 return Err(Error::SequenceTooLong); 159 return Err(Error::SequenceTooLong);
153 } 160 }
154 161
155 if let SequenceMode::Times(0) = times { 162 match times {
156 return Err(Error::SequenceTimesAtLeastOne); 163 SequenceMode::Times(0) => return Err(Error::SequenceTimesAtLeastOne),
164 SequenceMode::Times(1) if seq_0_word_count == 0 || seq_1_word_count != 0 => {
165 return Err(Error::SequenceTimesRequireSeq0Only)
166 }
167 SequenceMode::Times(1) => (),
168 SequenceMode::Times(_) | SequenceMode::Infinite
169 if seq_0_word_count == 0 || seq_1_word_count == 0 =>
170 {
171 return Err(Error::SequenceTimesRequireBothSeq0AndSeq1)
172 }
173 SequenceMode::Times(_) | SequenceMode::Infinite => (),
157 } 174 }
158 175
159 let _ = self.stop(); 176 let _ = self.stop();
160 177
178 // We now own these sequences and they will be moved. We want
179 // the peripheral to point at the right bits of memory hence
180 // moving the sequences early.
181 self.sequence0 = Some(sequence0);
182 self.sequence1 = Some(sequence1);
183
184 let sequence0 = self.sequence0.as_ref().unwrap();
185 let sequence1 = self.sequence1.as_ref().unwrap();
186
161 let r = T::regs(); 187 let r = T::regs();
162 188
163 r.seq0 189 r.seq0
@@ -171,20 +197,20 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
171 .write(|w| unsafe { w.bits(sequence0.words.as_ptr() as u32) }); 197 .write(|w| unsafe { w.bits(sequence0.words.as_ptr() as u32) });
172 r.seq0 198 r.seq0
173 .cnt 199 .cnt
174 .write(|w| unsafe { w.bits(sequence0.words.len() as u32) }); 200 .write(|w| unsafe { w.bits(seq_0_word_count as u32) });
175 201
176 r.seq1 202 r.seq1
177 .refresh 203 .refresh
178 .write(|w| unsafe { w.bits(alt_sequence.config.refresh) }); 204 .write(|w| unsafe { w.bits(sequence1.config.refresh) });
179 r.seq1 205 r.seq1
180 .enddelay 206 .enddelay
181 .write(|w| unsafe { w.bits(alt_sequence.config.end_delay) }); 207 .write(|w| unsafe { w.bits(sequence1.config.end_delay) });
182 r.seq1 208 r.seq1
183 .ptr 209 .ptr
184 .write(|w| unsafe { w.bits(alt_sequence.words.as_ptr() as u32) }); 210 .write(|w| unsafe { w.bits(sequence1.words.as_ptr() as u32) });
185 r.seq1 211 r.seq1
186 .cnt 212 .cnt
187 .write(|w| unsafe { w.bits(alt_sequence.words.len() as u32) }); 213 .write(|w| unsafe { w.bits(seq_1_word_count as u32) });
188 214
189 r.enable.write(|w| w.enable().enabled()); 215 r.enable.write(|w| w.enable().enabled());
190 216
@@ -226,9 +252,6 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
226 } 252 }
227 } 253 }
228 254
229 self.sequence0 = Some(sequence0);
230 self.sequence1 = sequence1;
231
232 Ok(()) 255 Ok(())
233 } 256 }
234 257
@@ -336,7 +359,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
336 /// cycle from the pin. Returns any sequences previously provided to 359 /// cycle from the pin. Returns any sequences previously provided to
337 /// `start` so that they may be further mutated. 360 /// `start` so that they may be further mutated.
338 #[inline(always)] 361 #[inline(always)]
339 pub fn stop(&mut self) -> (Option<Sequence<'d>>, Option<Sequence<'d>>) { 362 pub fn stop(&mut self) -> (Option<Sequence<S0>>, Option<Sequence<S1>>) {
340 let r = T::regs(); 363 let r = T::regs();
341 364
342 r.shorts.reset(); 365 r.shorts.reset();
@@ -352,7 +375,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
352 } 375 }
353} 376}
354 377
355impl<'a, T: Instance> Drop for SequencePwm<'a, T> { 378impl<'a, T: Instance, const S0: usize, const S1: usize> Drop for SequencePwm<'a, T, S0, S1> {
356 fn drop(&mut self) { 379 fn drop(&mut self) {
357 let r = T::regs(); 380 let r = T::regs();
358 381
@@ -381,6 +404,7 @@ impl<'a, T: Instance> Drop for SequencePwm<'a, T> {
381 } 404 }
382} 405}
383 406
407/// Configuration for the PWM as a whole.
384#[non_exhaustive] 408#[non_exhaustive]
385pub struct Config { 409pub struct Config {
386 /// Selects up mode or up-and-down mode for the counter 410 /// Selects up mode or up-and-down mode for the counter
@@ -404,6 +428,7 @@ impl Default for Config {
404 } 428 }
405} 429}
406 430
431/// Configuration per sequence
407#[non_exhaustive] 432#[non_exhaustive]
408#[derive(Clone)] 433#[derive(Clone)]
409pub struct SequenceConfig { 434pub struct SequenceConfig {
@@ -422,20 +447,39 @@ impl Default for SequenceConfig {
422 } 447 }
423} 448}
424 449
450/// A composition of a sequence buffer and its configuration.
425#[non_exhaustive] 451#[non_exhaustive]
426pub struct Sequence<'d> { 452#[derive(Clone)]
453pub struct Sequence<const S: usize> {
427 /// The words comprising the sequence. Must not exceed 32767 words. 454 /// The words comprising the sequence. Must not exceed 32767 words.
428 pub words: &'d mut [u16], 455 pub words: [u16; S],
456 /// The count of words to use. If None the S will be used.
457 pub word_count: Option<usize>,
429 /// Configuration associated with the sequence. 458 /// Configuration associated with the sequence.
430 pub config: SequenceConfig, 459 pub config: SequenceConfig,
431} 460}
432 461
433impl<'d> Sequence<'d> { 462impl<const S: usize> Sequence<S> {
434 pub fn new(words: &'d mut [u16], config: SequenceConfig) -> Self { 463 pub const fn new(words: [u16; S], config: SequenceConfig) -> Self {
435 Self { words, config } 464 Self {
465 words,
466 word_count: None,
467 config,
468 }
436 } 469 }
437} 470}
438 471
472/// Declares an empty sequence which will cause it to be disabled.
473/// Note that any looping i.e. !Times(1), will require a second
474/// sequence given the way the PWM peripheral works.
475pub const EMPTY_SEQ: Sequence<0> = Sequence::new(
476 [],
477 SequenceConfig {
478 refresh: 0,
479 end_delay: 0,
480 },
481);
482
439/// How many times to run the sequence 483/// How many times to run the sequence
440#[derive(Debug, Eq, PartialEq, Clone, Copy)] 484#[derive(Debug, Eq, PartialEq, Clone, Copy)]
441pub enum SequenceMode { 485pub enum SequenceMode {
@@ -446,7 +490,7 @@ pub enum SequenceMode {
446 /// 5 to 6 = Run sequence 0, sequence 1, sequence 0, sequence 1, sequence 0 and then sequence 1 490 /// 5 to 6 = Run sequence 0, sequence 1, sequence 0, sequence 1, sequence 0 and then sequence 1
447 /// i.e the when >= 2 the loop count is determined by dividing by 2 and rounding up 491 /// i.e the when >= 2 the loop count is determined by dividing by 2 and rounding up
448 Times(u16), 492 Times(u16),
449 /// Repeat until `stop` is called. 493 /// Repeat until `stop` is called. Both sequences must be provided.
450 Infinite, 494 Infinite,
451} 495}
452 496
diff --git a/examples/nrf/src/bin/pwm_sequence.rs b/examples/nrf/src/bin/pwm_sequence.rs
index 6fb861e8f..6fe957d24 100644
--- a/examples/nrf/src/bin/pwm_sequence.rs
+++ b/examples/nrf/src/bin/pwm_sequence.rs
@@ -8,14 +8,13 @@ use defmt::*;
8use embassy::executor::Spawner; 8use embassy::executor::Spawner;
9use embassy::time::{Duration, Timer}; 9use embassy::time::{Duration, Timer};
10use embassy_nrf::gpio::NoPin; 10use embassy_nrf::gpio::NoPin;
11use embassy_nrf::pwm::{Config, Prescaler, Sequence, SequenceConfig, SequenceMode, SequencePwm}; 11use embassy_nrf::pwm::{
12 Config, Prescaler, Sequence, SequenceConfig, SequenceMode, SequencePwm, EMPTY_SEQ,
13};
12use embassy_nrf::Peripherals; 14use embassy_nrf::Peripherals;
13 15
14#[embassy::main] 16#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) { 17async fn main(_spawner: Spawner, p: Peripherals) {
16 let mut seq_words_1: [u16; 5] = [1000, 250, 100, 50, 0];
17 let mut seq_words_2: [u16; 5] = [0, 50, 100, 250, 1000];
18
19 let mut config = Config::default(); 18 let mut config = Config::default();
20 config.prescaler = Prescaler::Div128; 19 config.prescaler = Prescaler::Div128;
21 // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8us 20 // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8us
@@ -26,25 +25,20 @@ async fn main(_spawner: Spawner, p: Peripherals) {
26 seq_config.refresh = 624; 25 seq_config.refresh = 624;
27 // thus our sequence takes 5 * 5000ms or 25 seconds 26 // thus our sequence takes 5 * 5000ms or 25 seconds
28 27
28 let seq_1 = Sequence::new([1000, 250, 100, 50, 0], seq_config.clone());
29 let seq_2 = Sequence::new([0, 50, 100, 250, 1000], seq_config);
30
29 let mut pwm = unwrap!(SequencePwm::new( 31 let mut pwm = unwrap!(SequencePwm::new(
30 p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config, 32 p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config,
31 )); 33 ));
32 let _ = pwm.start( 34 unwrap!(pwm.start(seq_1, EMPTY_SEQ, SequenceMode::Times(1)));
33 Sequence::new(&mut seq_words_1, seq_config.clone()),
34 None,
35 SequenceMode::Infinite,
36 );
37 35
38 info!("pwm started!"); 36 info!("pwm started!");
39 37
40 Timer::after(Duration::from_millis(20000)).await; 38 Timer::after(Duration::from_millis(20000)).await;
41 info!("pwm starting with another sequence!"); 39 info!("pwm starting with another sequence!");
42 40
43 let _ = pwm.start( 41 unwrap!(pwm.start(seq_2, EMPTY_SEQ, SequenceMode::Times(1)));
44 Sequence::new(&mut seq_words_2, seq_config),
45 None,
46 SequenceMode::Infinite,
47 );
48 42
49 // we can abort a sequence if we need to before its complete with pwm.stop() 43 // we can abort a sequence if we need to before its complete with pwm.stop()
50 // or stop is also implicitly called when the pwm peripheral is dropped 44 // 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 f5d734bb1..4883222a8 100644
--- a/examples/nrf/src/bin/pwm_sequence_ppi.rs
+++ b/examples/nrf/src/bin/pwm_sequence_ppi.rs
@@ -16,7 +16,7 @@ use embassy_nrf::Peripherals;
16 16
17#[embassy::main] 17#[embassy::main]
18async fn main(_spawner: Spawner, p: Peripherals) { 18async fn main(_spawner: Spawner, p: Peripherals) {
19 let mut seq_words: [u16; 5] = [1000, 250, 100, 50, 0]; 19 let seq_words: [u16; 5] = [1000, 250, 100, 50, 0];
20 20
21 let mut config = Config::default(); 21 let mut config = Config::default();
22 config.prescaler = Prescaler::Div128; 22 config.prescaler = Prescaler::Div128;
@@ -31,11 +31,12 @@ async fn main(_spawner: Spawner, p: Peripherals) {
31 p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config, 31 p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config,
32 )); 32 ));
33 33
34 let _ = pwm.start( 34 // If we loop in any way i.e. not Times(1), then we must provide
35 Sequence::new(&mut seq_words, seq_config), 35 // the PWM peripheral with two sequences.
36 None, 36 let seq_0 = Sequence::new(seq_words, seq_config);
37 SequenceMode::Infinite, 37 let seq_1 = seq_0.clone();
38 ); 38
39 unwrap!(pwm.start(seq_0, seq_1, SequenceMode::Infinite));
39 // pwm.stop() deconfigures pins, and then the task_start_seq0 task cant work 40 // pwm.stop() deconfigures pins, and then the task_start_seq0 task cant work
40 // so its going to have to start running in order load the configuration 41 // so its going to have to start running in order load the configuration
41 42
diff --git a/examples/nrf/src/bin/pwm_sequence_ws2812b.rs b/examples/nrf/src/bin/pwm_sequence_ws2812b.rs
index 0ce79cbe0..8acb209cc 100644
--- a/examples/nrf/src/bin/pwm_sequence_ws2812b.rs
+++ b/examples/nrf/src/bin/pwm_sequence_ws2812b.rs
@@ -30,19 +30,6 @@ const RES: u16 = 0x8000;
30// line is assumed to be P1_05. 30// line is assumed to be P1_05.
31#[embassy::main] 31#[embassy::main]
32async fn main(_spawner: Spawner, p: Peripherals) { 32async fn main(_spawner: Spawner, p: Peripherals) {
33 // Declare the bits of 24 bits
34 let mut color_seq_words = [
35 T0H, T0H, T0H, T0H, T0H, T0H, T0H, T0H, // G
36 T0H, T0H, T0H, T0H, T0H, T0H, T0H, T0H, // R
37 T1H, T1H, T1H, T1H, T1H, T1H, T1H, T1H, // B
38 ];
39 let color_seq = Sequence::new(&mut color_seq_words, SequenceConfig::default());
40
41 let mut reset_seq_words = [RES; 1];
42 let mut reset_seq_config = SequenceConfig::default();
43 reset_seq_config.end_delay = 799; // 50us (20 ticks * 40) - 1 tick because we've already got one RES;
44 let reset_seq = Sequence::new(&mut reset_seq_words, reset_seq_config);
45
46 let mut config = Config::default(); 33 let mut config = Config::default();
47 config.sequence_load = SequenceLoad::Common; 34 config.sequence_load = SequenceLoad::Common;
48 config.prescaler = Prescaler::Div1; 35 config.prescaler = Prescaler::Div1;
@@ -51,7 +38,21 @@ async fn main(_spawner: Spawner, p: Peripherals) {
51 p.PWM0, p.P1_05, NoPin, NoPin, NoPin, config, 38 p.PWM0, p.P1_05, NoPin, NoPin, NoPin, config,
52 )); 39 ));
53 40
54 unwrap!(pwm.start(color_seq, Some(reset_seq), SequenceMode::Times(2))); 41 // Declare the bits of 24 bits
42 let color_seq = Sequence::new(
43 [
44 T0H, T0H, T0H, T0H, T0H, T0H, T0H, T0H, // G
45 T0H, T0H, T0H, T0H, T0H, T0H, T0H, T0H, // R
46 T1H, T1H, T1H, T1H, T1H, T1H, T1H, T1H, // B
47 ],
48 SequenceConfig::default(),
49 );
50
51 let mut reset_seq_config = SequenceConfig::default();
52 reset_seq_config.end_delay = 799; // 50us (20 ticks * 40) - 1 tick because we've already got one RES;
53 let reset_seq = Sequence::new([RES], reset_seq_config);
54
55 unwrap!(pwm.start(color_seq, reset_seq, SequenceMode::Times(2)));
55 56
56 Timer::after(Duration::from_millis(1000)).await; 57 Timer::after(Duration::from_millis(1000)).await;
57 58
@@ -59,9 +60,9 @@ async fn main(_spawner: Spawner, p: Peripherals) {
59 let mut bit_value = T0H; 60 let mut bit_value = T0H;
60 61
61 loop { 62 loop {
62 if let (Some(color_seq), Some(reset_seq)) = pwm.stop() { 63 if let (Some(mut color_seq), Some(reset_seq)) = pwm.stop() {
63 color_seq.words[color_bit] = bit_value; 64 color_seq.words[color_bit] = bit_value;
64 unwrap!(pwm.start(color_seq, Some(reset_seq), SequenceMode::Times(2))); 65 unwrap!(pwm.start(color_seq, reset_seq, SequenceMode::Times(2)));
65 } 66 }
66 67
67 Timer::after(Duration::from_millis(50)).await; 68 Timer::after(Duration::from_millis(50)).await;