aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-02-05 02:20:13 +0000
committerGitHub <[email protected]>2022-02-05 02:20:13 +0000
commita1d60774464dfb8d401706fc8a5ea682c246d97b (patch)
tree665ace9908eb1539ec8fde8bc62ade39616c6715 /examples
parentdef3283e44cfc61d6ed95a0d6c3d8b9f418b4d3f (diff)
parentdf5ba727f2c8bd3f2a67f51a3f43d7f47b011b1c (diff)
Merge #591
591: PWM WS2812B example and flexible sequence config r=Dirbaio a=huntc I've permitted the PWM sequences to be mutated on stopping the PWM by associating them with a new `SingleSequencer` structure. This is so that we can perform effects on the LEDs (and other use-cases, I'm sure!). The example has been updated to illustrate the use of this by flashing a WS2812B LED. There's also a `Sequencer` structure for more sophisticated PWM interactions, along with a `pwm_double_sequence` example to illustrate. These changes should make it possible to attain all of the nRF PWM functionality available. Co-authored-by: huntc <[email protected]>
Diffstat (limited to 'examples')
-rw-r--r--examples/nrf/src/bin/pwm_double_sequence.rs46
-rw-r--r--examples/nrf/src/bin/pwm_sequence.rs21
-rw-r--r--examples/nrf/src/bin/pwm_sequence_ppi.rs15
-rw-r--r--examples/nrf/src/bin/pwm_sequence_ws2812b.rs80
4 files changed, 145 insertions, 17 deletions
diff --git a/examples/nrf/src/bin/pwm_double_sequence.rs b/examples/nrf/src/bin/pwm_double_sequence.rs
new file mode 100644
index 000000000..269015f4a
--- /dev/null
+++ b/examples/nrf/src/bin/pwm_double_sequence.rs
@@ -0,0 +1,46 @@
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, Sequence, SequenceConfig, SequenceMode, SequencePwm, Sequencer,
13 StartSequence,
14};
15use embassy_nrf::Peripherals;
16
17#[embassy::main]
18async fn main(_spawner: Spawner, p: Peripherals) {
19 let seq_words_0: [u16; 5] = [1000, 250, 100, 50, 0];
20 let seq_words_1: [u16; 4] = [50, 100, 250, 1000];
21
22 let mut config = Config::default();
23 config.prescaler = Prescaler::Div128;
24 // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8us
25 // but say we want to hold the value for 5000ms
26 // so we want to repeat our value as many times as necessary until 5000ms passes
27 // want 5000/8 = 625 periods total to occur, so 624 (we get the one period for free remember)
28 let mut seq_config = SequenceConfig::default();
29 seq_config.refresh = 624;
30 // thus our sequence takes 5 * 5000ms or 25 seconds
31
32 let mut pwm = unwrap!(SequencePwm::new(
33 p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config,
34 ));
35
36 let sequence_0 = Sequence::new(&seq_words_0, seq_config.clone());
37 let sequence_1 = Sequence::new(&seq_words_1, seq_config);
38 let sequencer = Sequencer::new(&mut pwm, sequence_0, Some(sequence_1));
39 unwrap!(sequencer.start(StartSequence::Zero, SequenceMode::Loop(1)));
40
41 // we can abort a sequence if we need to before its complete with pwm.stop()
42 // or stop is also implicitly called when the pwm peripheral is dropped
43 // when it goes out of scope
44 Timer::after(Duration::from_millis(40000)).await;
45 info!("pwm stopped early!");
46}
diff --git a/examples/nrf/src/bin/pwm_sequence.rs b/examples/nrf/src/bin/pwm_sequence.rs
index 56c865d1c..f06ea0b19 100644
--- a/examples/nrf/src/bin/pwm_sequence.rs
+++ b/examples/nrf/src/bin/pwm_sequence.rs
@@ -8,34 +8,31 @@ 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::{
12 Config, Prescaler, SequenceConfig, SequencePwm, SingleSequenceMode, SingleSequencer,
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 seq_values_1: [u16; 5] = [1000, 250, 100, 50, 0]; 18 let seq_words: [u16; 5] = [1000, 250, 100, 50, 0];
17 let seq_values_2: [u16; 5] = [0, 50, 100, 250, 1000];
18 19
19 let mut config = SequenceConfig::default(); 20 let mut config = Config::default();
20 config.prescaler = Prescaler::Div128; 21 config.prescaler = Prescaler::Div128;
21 // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8us 22 // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8us
22 // but say we want to hold the value for 5000ms 23 // 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 24 // 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) 25 // want 5000/8 = 625 periods total to occur, so 624 (we get the one period for free remember)
25 config.refresh = 624; 26 let mut seq_config = SequenceConfig::default();
27 seq_config.refresh = 624;
26 // thus our sequence takes 5 * 5000ms or 25 seconds 28 // thus our sequence takes 5 * 5000ms or 25 seconds
27 29
28 let mut pwm = unwrap!(SequencePwm::new( 30 let mut pwm = unwrap!(SequencePwm::new(
29 p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config, 31 p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config,
30 )); 32 ));
31 let _ = pwm.start(&seq_values_1, SequenceMode::Infinite);
32 33
33 info!("pwm started!"); 34 let sequencer = SingleSequencer::new(&mut pwm, &seq_words, seq_config);
34 35 unwrap!(sequencer.start(SingleSequenceMode::Times(1)));
35 Timer::after(Duration::from_millis(20000)).await;
36 info!("pwm starting with another sequence!");
37
38 let _ = pwm.start(&seq_values_2, SequenceMode::Infinite);
39 36
40 // we can abort a sequence if we need to before its complete with pwm.stop() 37 // 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 38 // 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..c25c5e10d 100644
--- a/examples/nrf/src/bin/pwm_sequence_ppi.rs
+++ b/examples/nrf/src/bin/pwm_sequence_ppi.rs
@@ -11,26 +11,28 @@ 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::{
15 Config, Prescaler, SequenceConfig, SequencePwm, SingleSequenceMode, SingleSequencer,
16};
15use embassy_nrf::Peripherals; 17use embassy_nrf::Peripherals;
16 18
17#[embassy::main] 19#[embassy::main]
18async fn main(_spawner: Spawner, p: Peripherals) { 20async fn main(_spawner: Spawner, p: Peripherals) {
19 let seq_values: [u16; 5] = [1000, 250, 100, 50, 0]; 21 let seq_words: [u16; 5] = [1000, 250, 100, 50, 0];
20 22
21 let mut config = SequenceConfig::default(); 23 let mut config = Config::default();
22 config.prescaler = Prescaler::Div128; 24 config.prescaler = Prescaler::Div128;
23 // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8us 25 // 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 26 // 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) 27 // so round to 31 - 1 (we get the one period for free remember)
26 // thus our sequence takes 5 * 250ms or 1.25 seconds 28 // thus our sequence takes 5 * 250ms or 1.25 seconds
27 config.refresh = 30; 29 let mut seq_config = SequenceConfig::default();
30 seq_config.refresh = 30;
28 31
29 let mut pwm = unwrap!(SequencePwm::new( 32 let mut pwm = unwrap!(SequencePwm::new(
30 p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config, 33 p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config,
31 )); 34 ));
32 35
33 let _ = pwm.start(&seq_values, SequenceMode::Times(1));
34 // pwm.stop() deconfigures pins, and then the task_start_seq0 task cant work 36 // 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 37 // so its going to have to start running in order load the configuration
36 38
@@ -51,6 +53,9 @@ async fn main(_spawner: Spawner, p: Peripherals) {
51 let start = unsafe { pwm.task_start_seq0() }; 53 let start = unsafe { pwm.task_start_seq0() };
52 let stop = unsafe { pwm.task_stop() }; 54 let stop = unsafe { pwm.task_stop() };
53 55
56 let sequencer = SingleSequencer::new(&mut pwm, &seq_words, seq_config);
57 unwrap!(sequencer.start(SingleSequenceMode::Infinite));
58
54 let mut ppi = Ppi::new_one_to_one(p.PPI_CH1, button1.event_in(), start); 59 let mut ppi = Ppi::new_one_to_one(p.PPI_CH1, button1.event_in(), start);
55 ppi.enable(); 60 ppi.enable();
56 61
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..d1a027a7e
--- /dev/null
+++ b/examples/nrf/src/bin/pwm_sequence_ws2812b.rs
@@ -0,0 +1,80 @@
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, SequencePwm, SingleSequenceMode,
13 SingleSequencer,
14};
15use embassy_nrf::Peripherals;
16
17// WS2812B LED light demonstration. Drives just one light.
18// The following reference on WS2812B may be of use:
19// https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf.
20// This demo lights up a single LED in blue. It then proceeds
21// to pulsate the LED rapidly.
22
23// In the following declarations, setting the high bit tells the PWM
24// to reverse polarity, which is what the WS2812B expects.
25
26const T1H: u16 = 0x8000 | 13; // Duty = 13/20 ticks (0.8us/1.25us) for a 1
27const T0H: u16 = 0x8000 | 7; // Duty 7/20 ticks (0.4us/1.25us) for a 0
28const RES: u16 = 0x8000;
29
30// Provides data to a WS2812b (Neopixel) LED and makes it go blue. The data
31// line is assumed to be P1_05.
32#[embassy::main]
33async fn main(_spawner: Spawner, p: Peripherals) {
34 let mut config = Config::default();
35 config.sequence_load = SequenceLoad::Common;
36 config.prescaler = Prescaler::Div1;
37 config.max_duty = 20; // 1.25us (1s / 16Mhz * 20)
38 let mut pwm = unwrap!(SequencePwm::new(
39 p.PWM0, p.P1_05, NoPin, NoPin, NoPin, config,
40 ));
41
42 // Declare the bits of 24 bits in a buffer we'll be
43 // mutating later.
44 let mut seq_words = [
45 T0H, T0H, T0H, T0H, T0H, T0H, T0H, T0H, // G
46 T0H, T0H, T0H, T0H, T0H, T0H, T0H, T0H, // R
47 T1H, T1H, T1H, T1H, T1H, T1H, T1H, T1H, // B
48 RES,
49 ];
50 let mut seq_config = SequenceConfig::default();
51 seq_config.end_delay = 799; // 50us (20 ticks * 40) - 1 tick because we've already got one RES;
52
53 let mut color_bit = 16;
54 let mut bit_value = T0H;
55
56 loop {
57 let sequences = SingleSequencer::new(&mut pwm, &seq_words, seq_config.clone());
58 unwrap!(sequences.start(SingleSequenceMode::Times(1)));
59
60 Timer::after(Duration::from_millis(50)).await;
61
62 if bit_value == T0H {
63 if color_bit == 20 {
64 bit_value = T1H;
65 } else {
66 color_bit += 1;
67 }
68 } else {
69 if color_bit == 16 {
70 bit_value = T0H;
71 } else {
72 color_bit -= 1;
73 }
74 }
75
76 drop(sequences);
77
78 seq_words[color_bit] = bit_value;
79 }
80}