aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-nrf/src/pwm.rs89
-rw-r--r--examples/nrf/src/bin/pwm_sequence.rs23
-rw-r--r--examples/nrf/src/bin/pwm_sequence_ppi.rs9
-rw-r--r--examples/nrf/src/bin/pwm_sequence_ws2812b.rs60
4 files changed, 145 insertions, 36 deletions
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 {
45 DMABufferNotInDataMemory, 45 DMABufferNotInDataMemory,
46} 46}
47 47
48const MAX_SEQUENCE_LEN: usize = 32767;
49
48impl<'d, T: Instance> SequencePwm<'d, T> { 50impl<'d, T: Instance> SequencePwm<'d, T> {
49 /// Creates the interface to a `SequencePwm`. 51 /// Creates the interface to a `SequencePwm`.
50 /// 52 ///
@@ -62,7 +64,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
62 ch1: impl Unborrow<Target = impl GpioOptionalPin> + 'd, 64 ch1: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
63 ch2: impl Unborrow<Target = impl GpioOptionalPin> + 'd, 65 ch2: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
64 ch3: impl Unborrow<Target = impl GpioOptionalPin> + 'd, 66 ch3: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
65 config: SequenceConfig, 67 config: Config,
66 ) -> Result<Self, Error> { 68 ) -> Result<Self, Error> {
67 unborrow!(ch0, ch1, ch2, ch3); 69 unborrow!(ch0, ch1, ch2, ch3);
68 70
@@ -117,16 +119,6 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
117 r.countertop 119 r.countertop
118 .write(|w| unsafe { w.countertop().bits(config.max_duty) }); 120 .write(|w| unsafe { w.countertop().bits(config.max_duty) });
119 121
120 r.seq0.refresh.write(|w| unsafe { w.bits(config.refresh) });
121 r.seq0
122 .enddelay
123 .write(|w| unsafe { w.bits(config.end_delay) });
124
125 r.seq1.refresh.write(|w| unsafe { w.bits(config.refresh) });
126 r.seq1
127 .enddelay
128 .write(|w| unsafe { w.bits(config.end_delay) });
129
130 Ok(Self { 122 Ok(Self {
131 phantom: PhantomData, 123 phantom: PhantomData,
132 ch0: ch0.degrade_optional(), 124 ch0: ch0.degrade_optional(),
@@ -136,12 +128,28 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
136 }) 128 })
137 } 129 }
138 130
139 /// Start or restart playback 131 /// Start or restart playback. Takes at least one sequence along with its
132 /// configuration. Optionally takes a second sequence and/or its configuration.
133 /// In the case where no second sequence is provided then the first sequence
134 /// is used. In the case where no second sequence configuration is supplied,
135 /// the first sequence configuration is used. The sequence mode applies to both
136 /// sequences combined as one.
140 #[inline(always)] 137 #[inline(always)]
141 pub fn start(&mut self, sequence: &'d [u16], times: SequenceMode) -> Result<(), Error> { 138 pub fn start(
142 slice_in_ram_or(sequence, Error::DMABufferNotInDataMemory)?; 139 &mut self,
143 140 sequence0: &'d [u16],
144 if sequence.len() > 32767 { 141 sequence_config0: SequenceConfig,
142 sequence1: Option<&'d [u16]>,
143 sequence_config1: Option<SequenceConfig>,
144 times: SequenceMode,
145 ) -> Result<(), Error> {
146 let alt_sequence = sequence1.unwrap_or(sequence0);
147 let alt_sequence_config = (&sequence_config1).as_ref().unwrap_or(&sequence_config0);
148
149 slice_in_ram_or(sequence0, Error::DMABufferNotInDataMemory)?;
150 slice_in_ram_or(alt_sequence, Error::DMABufferNotInDataMemory)?;
151
152 if sequence0.len() > MAX_SEQUENCE_LEN || alt_sequence.len() > MAX_SEQUENCE_LEN {
145 return Err(Error::SequenceTooLong); 153 return Err(Error::SequenceTooLong);
146 } 154 }
147 155
@@ -154,18 +162,30 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
154 let r = T::regs(); 162 let r = T::regs();
155 163
156 r.seq0 164 r.seq0
165 .refresh
166 .write(|w| unsafe { w.bits(sequence_config0.refresh) });
167 r.seq0
168 .enddelay
169 .write(|w| unsafe { w.bits(sequence_config0.end_delay) });
170 r.seq0
157 .ptr 171 .ptr
158 .write(|w| unsafe { w.bits(sequence.as_ptr() as u32) }); 172 .write(|w| unsafe { w.bits(sequence0.as_ptr() as u32) });
159 r.seq0 173 r.seq0
160 .cnt 174 .cnt
161 .write(|w| unsafe { w.bits(sequence.len() as u32) }); 175 .write(|w| unsafe { w.bits(sequence0.len() as u32) });
162 176
163 r.seq1 177 r.seq1
178 .refresh
179 .write(|w| unsafe { w.bits(alt_sequence_config.refresh) });
180 r.seq1
181 .enddelay
182 .write(|w| unsafe { w.bits(alt_sequence_config.end_delay) });
183 r.seq1
164 .ptr 184 .ptr
165 .write(|w| unsafe { w.bits(sequence.as_ptr() as u32) }); 185 .write(|w| unsafe { w.bits(alt_sequence.as_ptr() as u32) });
166 r.seq1 186 r.seq1
167 .cnt 187 .cnt
168 .write(|w| unsafe { w.bits(sequence.len() as u32) }); 188 .write(|w| unsafe { w.bits(alt_sequence.len() as u32) });
169 189
170 r.enable.write(|w| w.enable().enabled()); 190 r.enable.write(|w| w.enable().enabled());
171 191
@@ -356,9 +376,8 @@ impl<'a, T: Instance> Drop for SequencePwm<'a, T> {
356 } 376 }
357} 377}
358 378
359/// Configure an infinite looping sequence for `SequencePwm`
360#[non_exhaustive] 379#[non_exhaustive]
361pub struct SequenceConfig { 380pub struct Config {
362 /// Selects up mode or up-and-down mode for the counter 381 /// Selects up mode or up-and-down mode for the counter
363 pub counter_mode: CounterMode, 382 pub counter_mode: CounterMode,
364 /// Top value to be compared against buffer values 383 /// Top value to be compared against buffer values
@@ -367,6 +386,21 @@ pub struct SequenceConfig {
367 pub prescaler: Prescaler, 386 pub prescaler: Prescaler,
368 /// How a sequence is read from RAM and is spread to the compare register 387 /// How a sequence is read from RAM and is spread to the compare register
369 pub sequence_load: SequenceLoad, 388 pub sequence_load: SequenceLoad,
389}
390
391impl Default for Config {
392 fn default() -> Config {
393 Config {
394 counter_mode: CounterMode::Up,
395 max_duty: 1000,
396 prescaler: Prescaler::Div16,
397 sequence_load: SequenceLoad::Common,
398 }
399 }
400}
401
402#[non_exhaustive]
403pub struct SequenceConfig {
370 /// Number of PWM periods to delay between each sequence sample 404 /// Number of PWM periods to delay between each sequence sample
371 pub refresh: u32, 405 pub refresh: u32,
372 /// Number of PWM periods after the sequence ends before starting the next sequence 406 /// Number of PWM periods after the sequence ends before starting the next sequence
@@ -376,10 +410,6 @@ pub struct SequenceConfig {
376impl Default for SequenceConfig { 410impl Default for SequenceConfig {
377 fn default() -> SequenceConfig { 411 fn default() -> SequenceConfig {
378 SequenceConfig { 412 SequenceConfig {
379 counter_mode: CounterMode::Up,
380 max_duty: 1000,
381 prescaler: Prescaler::Div16,
382 sequence_load: SequenceLoad::Common,
383 refresh: 0, 413 refresh: 0,
384 end_delay: 0, 414 end_delay: 0,
385 } 415 }
@@ -389,7 +419,12 @@ impl Default for SequenceConfig {
389/// How many times to run the sequence 419/// How many times to run the sequence
390#[derive(Debug, Eq, PartialEq, Clone, Copy)] 420#[derive(Debug, Eq, PartialEq, Clone, Copy)]
391pub enum SequenceMode { 421pub enum SequenceMode {
392 /// Run sequence n Times total 422 /// Run sequence n Times total.
423 /// 1 = Run sequence 0 once
424 /// 2 = Run sequence 0 and then sequence 1
425 /// 3 to 4 = Run sequence 0, sequence 1, sequence 0 and then sequence 1
426 /// 5 to 6 = Run sequence 0, sequence 1, sequence 0, sequence 1, sequence 0 and then sequence 1
427 /// i.e the when >= 2 the loop count is determined by dividing by 2 and rounding up
393 Times(u16), 428 Times(u16),
394 /// Repeat until `stop` is called. 429 /// Repeat until `stop` is called.
395 Infinite, 430 Infinite,
diff --git a/examples/nrf/src/bin/pwm_sequence.rs b/examples/nrf/src/bin/pwm_sequence.rs
index 56c865d1c..2dcbc7475 100644
--- a/examples/nrf/src/bin/pwm_sequence.rs
+++ b/examples/nrf/src/bin/pwm_sequence.rs
@@ -8,7 +8,7 @@ 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::{Prescaler, SequenceConfig, SequenceMode, SequencePwm}; 11use embassy_nrf::pwm::{Config, Prescaler, SequenceMode, SequencePwm};
12use embassy_nrf::Peripherals; 12use embassy_nrf::Peripherals;
13 13
14#[embassy::main] 14#[embassy::main]
@@ -16,26 +16,39 @@ async fn main(_spawner: Spawner, p: Peripherals) {
16 let seq_values_1: [u16; 5] = [1000, 250, 100, 50, 0]; 16 let seq_values_1: [u16; 5] = [1000, 250, 100, 50, 0];
17 let seq_values_2: [u16; 5] = [0, 50, 100, 250, 1000]; 17 let seq_values_2: [u16; 5] = [0, 50, 100, 250, 1000];
18 18
19 let mut config = SequenceConfig::default(); 19 let mut config = Config::default();
20 config.prescaler = Prescaler::Div128; 20 config.prescaler = Prescaler::Div128;
21 // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8us 21 // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8us
22 // but say we want to hold the value for 5000ms 22 // but say we want to hold the value for 5000ms
23 // so we want to repeat our value as many times as necessary until 5000ms passes 23 // so we want to repeat our value as many times as necessary until 5000ms passes
24 // want 5000/8 = 625 periods total to occur, so 624 (we get the one period for free remember) 24 // want 5000/8 = 625 periods total to occur, so 624 (we get the one period for free remember)
25 config.refresh = 624; 25 let mut seq_config = Config::default();
26 seq_config.refresh = 624;
26 // thus our sequence takes 5 * 5000ms or 25 seconds 27 // thus our sequence takes 5 * 5000ms or 25 seconds
27 28
28 let mut pwm = unwrap!(SequencePwm::new( 29 let mut pwm = unwrap!(SequencePwm::new(
29 p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config, 30 p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config,
30 )); 31 ));
31 let _ = pwm.start(&seq_values_1, SequenceMode::Infinite); 32 let _ = pwm.start(
33 &seq_values_1,
34 seq_config,
35 None,
36 None,
37 SeqSequenceMode::Infinite,
38 );
32 39
33 info!("pwm started!"); 40 info!("pwm started!");
34 41
35 Timer::after(Duration::from_millis(20000)).await; 42 Timer::after(Duration::from_millis(20000)).await;
36 info!("pwm starting with another sequence!"); 43 info!("pwm starting with another sequence!");
37 44
38 let _ = pwm.start(&seq_values_2, SequenceMode::Infinite); 45 let _ = pwm.start(
46 &seq_values_2,
47 seq_config,
48 None,
49 None,
50 SequenceMode::Infinite,
51 );
39 52
40 // we can abort a sequence if we need to before its complete with pwm.stop() 53 // we can abort a sequence if we need to before its complete with pwm.stop()
41 // or stop is also implicitly called when the pwm peripheral is dropped 54 // 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 f03c5716a..9523671a4 100644
--- a/examples/nrf/src/bin/pwm_sequence_ppi.rs
+++ b/examples/nrf/src/bin/pwm_sequence_ppi.rs
@@ -11,26 +11,27 @@ use embassy::executor::Spawner;
11use embassy_nrf::gpio::{Input, NoPin, Pull}; 11use embassy_nrf::gpio::{Input, NoPin, Pull};
12use embassy_nrf::gpiote::{InputChannel, InputChannelPolarity}; 12use embassy_nrf::gpiote::{InputChannel, InputChannelPolarity};
13use embassy_nrf::ppi::Ppi; 13use embassy_nrf::ppi::Ppi;
14use embassy_nrf::pwm::{Prescaler, SequenceConfig, SequenceMode, SequencePwm}; 14use embassy_nrf::pwm::{Config, Prescaler, SequenceConfig, SequenceMode, SequencePwm};
15use embassy_nrf::Peripherals; 15use 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 seq_values: [u16; 5] = [1000, 250, 100, 50, 0]; 19 let seq_values: [u16; 5] = [1000, 250, 100, 50, 0];
20 20
21 let mut config = SequenceConfig::default(); 21 let mut config = Config::default();
22 config.prescaler = Prescaler::Div128; 22 config.prescaler = Prescaler::Div128;
23 // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8us 23 // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8us
24 // but say we want to hold the value for 250ms 250ms/8 = 31.25 periods 24 // but say we want to hold the value for 250ms 250ms/8 = 31.25 periods
25 // so round to 31 - 1 (we get the one period for free remember) 25 // so round to 31 - 1 (we get the one period for free remember)
26 // thus our sequence takes 5 * 250ms or 1.25 seconds 26 // thus our sequence takes 5 * 250ms or 1.25 seconds
27 config.refresh = 30; 27 let mut seq_config = SequenceConfig::default();
28 seq_config.refresh = 30;
28 29
29 let mut pwm = unwrap!(SequencePwm::new( 30 let mut pwm = unwrap!(SequencePwm::new(
30 p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config, 31 p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config,
31 )); 32 ));
32 33
33 let _ = pwm.start(&seq_values, SequenceMode::Times(1)); 34 let _ = pwm.start(&seq_values, seq_config, None, None, SequenceMode::Infinite);
34 // pwm.stop() deconfigures pins, and then the task_start_seq0 task cant work 35 // pwm.stop() deconfigures pins, and then the task_start_seq0 task cant work
35 // so its going to have to start running in order load the configuration 36 // so its going to have to start running in order load the configuration
36 37
diff --git a/examples/nrf/src/bin/pwm_sequence_ws2812b.rs b/examples/nrf/src/bin/pwm_sequence_ws2812b.rs
new file mode 100644
index 000000000..01afd043b
--- /dev/null
+++ b/examples/nrf/src/bin/pwm_sequence_ws2812b.rs
@@ -0,0 +1,60 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5#[path = "../example_common.rs"]
6mod example_common;
7use defmt::*;
8use embassy::executor::Spawner;
9use embassy::time::{Duration, Timer};
10use embassy_nrf::gpio::NoPin;
11use embassy_nrf::pwm::{
12 Config, Prescaler, SequenceConfig, SequenceLoad, SequenceMode, SequencePwm,
13};
14use embassy_nrf::Peripherals;
15
16// WS2812B LED light demonstration. Drives just one light.
17// The following reference on WS2812B may be of use:
18// https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf
19
20// In the following declarations, setting the high bit tells the PWM
21// to reverse polarity, which is what the WS2812B expects.
22
23const T1H: u16 = 0x8000 | 13; // Duty = 13/20 ticks (0.8us/1.25us) for a 1
24const T0H: u16 = 0x8000 | 7; // Duty 7/20 ticks (0.4us/1.25us) for a 0
25const RES: u16 = 0x8000;
26
27// Provides data to a WS2812b (Neopixel) LED and makes it go blue. The data
28// line is assumed to be P1_05.
29#[embassy::main]
30async fn main(_spawner: Spawner, p: Peripherals) {
31 // Declare the bits of 24 bits
32 let mut blue_seq: [u16; 8 * 3] = [
33 T0H, T0H, T0H, T0H, T0H, T0H, T0H, T0H, // G
34 T0H, T0H, T0H, T0H, T0H, T0H, T0H, T0H, // R
35 T1H, T1H, T1H, T1H, T1H, T1H, T1H, T1H, // B
36 ];
37 let reset_seq = [RES; 1];
38
39 let mut config = Config::default();
40 config.sequence_load = SequenceLoad::Common;
41 config.prescaler = Prescaler::Div1;
42 config.max_duty = 20; // 1.25us (1s / 16Mhz * 20)
43 let mut pwm = unwrap!(SequencePwm::new(
44 p.PWM0, p.P1_05, NoPin, NoPin, NoPin, config,
45 ));
46
47 let blue_seq_config = SequenceConfig::default();
48 let mut reset_seq_config = SequenceConfig::default();
49 reset_seq_config.end_delay = 799; // 50us (20 ticks * 40) - 1 tick because we've already got one RES
50 unwrap!(pwm.start(
51 &blue_seq,
52 blue_seq_config,
53 Some(&reset_seq),
54 Some(reset_seq_config),
55 SequenceMode::Times(2)
56 ));
57
58 Timer::after(Duration::from_millis(20000)).await;
59 info!("Program stopped");
60}