diff options
| author | Jacob Rosenthal <[email protected]> | 2021-11-03 18:37:54 -0700 |
|---|---|---|
| committer | Jacob Rosenthal <[email protected]> | 2021-11-03 18:37:54 -0700 |
| commit | b726ef1886c65ab76b01f2c54ad559573e19083d (patch) | |
| tree | 1be0d14f92e3b60c4088fe63853abfce6defb83b | |
| parent | d961fd1015f7911dc1085035c37b0554b0184982 (diff) | |
make SequenceConfig struct is consistent with other Config structs, that are always non_exhaustive and have a Default
| -rw-r--r-- | embassy-nrf/src/pwm.rs | 143 | ||||
| -rw-r--r-- | examples/nrf/src/bin/pwm_sequence.rs | 25 | ||||
| -rw-r--r-- | examples/nrf/src/bin/pwm_simple_sin.rs | 24 |
3 files changed, 101 insertions, 91 deletions
diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 0d06f845c..9f88633c4 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs | |||
| @@ -12,43 +12,6 @@ use crate::interrupt::Interrupt; | |||
| 12 | use crate::pac; | 12 | use crate::pac; |
| 13 | use crate::util::slice_in_ram_or; | 13 | use crate::util::slice_in_ram_or; |
| 14 | 14 | ||
| 15 | /// PWM Base clock is system clock (16MHz) divided by prescaler | ||
| 16 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 17 | pub enum Prescaler { | ||
| 18 | Div1, | ||
| 19 | Div2, | ||
| 20 | Div4, | ||
| 21 | Div8, | ||
| 22 | Div16, | ||
| 23 | Div32, | ||
| 24 | Div64, | ||
| 25 | Div128, | ||
| 26 | } | ||
| 27 | |||
| 28 | /// How the sequence values are distributed across the channels | ||
| 29 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 30 | pub enum SequenceLoad { | ||
| 31 | /// Provided sequence will be used across all channels | ||
| 32 | Common, | ||
| 33 | /// Provided sequence contains grouped values for each channel ex: | ||
| 34 | /// [ch0_0_and_ch1_0, ch2_0_and_ch3_0, ... ch0_n_and_ch1_n, ch2_n_and_ch3_n] | ||
| 35 | Grouped, | ||
| 36 | /// Provided sequence contains individual values for each channel ex: | ||
| 37 | /// [ch0_0, ch1_0, ch2_0, ch3_0... ch0_n, ch1_n, ch2_n, ch3_n] | ||
| 38 | Individual, | ||
| 39 | /// Similar to Individual mode, but only three channels are used. The fourth | ||
| 40 | /// value is loaded into the pulse generator counter as its top value. | ||
| 41 | Waveform, | ||
| 42 | } | ||
| 43 | |||
| 44 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 45 | pub enum CounterMode { | ||
| 46 | /// Up counter (edge-aligned PWM duty cycle) | ||
| 47 | Up, | ||
| 48 | /// Up and down counter (center-aligned PWM duty cycle) | ||
| 49 | UpAndDown, | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Interface to the PWM peripheral | 15 | /// Interface to the PWM peripheral |
| 53 | pub struct SimplePwm<'d, T: Instance> { | 16 | pub struct SimplePwm<'d, T: Instance> { |
| 54 | phantom: PhantomData<&'d mut T>, | 17 | phantom: PhantomData<&'d mut T>, |
| @@ -58,6 +21,18 @@ pub struct SequencePwm<'d, T: Instance> { | |||
| 58 | phantom: PhantomData<&'d mut T>, | 21 | phantom: PhantomData<&'d mut T>, |
| 59 | } | 22 | } |
| 60 | 23 | ||
| 24 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 25 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 26 | #[non_exhaustive] | ||
| 27 | pub enum Error { | ||
| 28 | /// Max Sequence size is 32767 | ||
| 29 | SequenceTooLong, | ||
| 30 | /// Min Sequence count is 1 | ||
| 31 | SequenceTimesAtLeastOne, | ||
| 32 | /// EasyDMA can only read from data memory, read only buffers in flash will fail. | ||
| 33 | DMABufferNotInDataMemory, | ||
| 34 | } | ||
| 35 | |||
| 61 | impl<'d, T: Instance> SequencePwm<'d, T> { | 36 | impl<'d, T: Instance> SequencePwm<'d, T> { |
| 62 | /// Creates the interface to a PWM Sequence interface. | 37 | /// Creates the interface to a PWM Sequence interface. |
| 63 | /// | 38 | /// |
| @@ -69,17 +44,18 @@ impl<'d, T: Instance> SequencePwm<'d, T> { | |||
| 69 | /// mechanisms) on stack allocated buffers which which have been passed to | 44 | /// mechanisms) on stack allocated buffers which which have been passed to |
| 70 | /// [`new()`](SequencePwm::new). | 45 | /// [`new()`](SequencePwm::new). |
| 71 | #[allow(unused_unsafe)] | 46 | #[allow(unused_unsafe)] |
| 72 | pub fn new( | 47 | pub fn new<'a>( |
| 73 | _pwm: impl Unborrow<Target = T> + 'd, | 48 | _pwm: impl Unborrow<Target = T> + 'd, |
| 74 | ch0: impl Unborrow<Target = impl GpioOptionalPin> + 'd, | 49 | ch0: impl Unborrow<Target = impl GpioOptionalPin> + 'd, |
| 75 | ch1: impl Unborrow<Target = impl GpioOptionalPin> + 'd, | 50 | ch1: impl Unborrow<Target = impl GpioOptionalPin> + 'd, |
| 76 | ch2: impl Unborrow<Target = impl GpioOptionalPin> + 'd, | 51 | ch2: impl Unborrow<Target = impl GpioOptionalPin> + 'd, |
| 77 | ch3: impl Unborrow<Target = impl GpioOptionalPin> + 'd, | 52 | ch3: impl Unborrow<Target = impl GpioOptionalPin> + 'd, |
| 78 | config: SequenceConfig, | 53 | config: SequenceConfig, |
| 54 | sequence: &'a [u16], | ||
| 79 | ) -> Result<Self, Error> { | 55 | ) -> Result<Self, Error> { |
| 80 | slice_in_ram_or(config.sequence, Error::DMABufferNotInDataMemory)?; | 56 | slice_in_ram_or(sequence, Error::DMABufferNotInDataMemory)?; |
| 81 | 57 | ||
| 82 | if config.sequence.len() > 32767 { | 58 | if sequence.len() > 32767 { |
| 83 | return Err(Error::SequenceTooLong); | 59 | return Err(Error::SequenceTooLong); |
| 84 | } | 60 | } |
| 85 | 61 | ||
| @@ -120,10 +96,10 @@ impl<'d, T: Instance> SequencePwm<'d, T> { | |||
| 120 | 96 | ||
| 121 | r.seq0 | 97 | r.seq0 |
| 122 | .ptr | 98 | .ptr |
| 123 | .write(|w| unsafe { w.bits(config.sequence.as_ptr() as u32) }); | 99 | .write(|w| unsafe { w.bits(sequence.as_ptr() as u32) }); |
| 124 | r.seq0 | 100 | r.seq0 |
| 125 | .cnt | 101 | .cnt |
| 126 | .write(|w| unsafe { w.bits(config.sequence.len() as u32) }); | 102 | .write(|w| unsafe { w.bits(sequence.len() as u32) }); |
| 127 | r.seq0.refresh.write(|w| unsafe { w.bits(config.refresh) }); | 103 | r.seq0.refresh.write(|w| unsafe { w.bits(config.refresh) }); |
| 128 | r.seq0 | 104 | r.seq0 |
| 129 | .enddelay | 105 | .enddelay |
| @@ -131,10 +107,10 @@ impl<'d, T: Instance> SequencePwm<'d, T> { | |||
| 131 | 107 | ||
| 132 | r.seq1 | 108 | r.seq1 |
| 133 | .ptr | 109 | .ptr |
| 134 | .write(|w| unsafe { w.bits(config.sequence.as_ptr() as u32) }); | 110 | .write(|w| unsafe { w.bits(sequence.as_ptr() as u32) }); |
| 135 | r.seq1 | 111 | r.seq1 |
| 136 | .cnt | 112 | .cnt |
| 137 | .write(|w| unsafe { w.bits(config.sequence.len() as u32) }); | 113 | .write(|w| unsafe { w.bits(sequence.len() as u32) }); |
| 138 | r.seq1.refresh.write(|w| unsafe { w.bits(config.refresh) }); | 114 | r.seq1.refresh.write(|w| unsafe { w.bits(config.refresh) }); |
| 139 | r.seq1 | 115 | r.seq1 |
| 140 | .enddelay | 116 | .enddelay |
| @@ -237,24 +213,15 @@ impl<'a, T: Instance> Drop for SequencePwm<'a, T> { | |||
| 237 | } | 213 | } |
| 238 | } | 214 | } |
| 239 | 215 | ||
| 240 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 241 | pub enum SequenceMode { | ||
| 242 | /// Run sequence n Times total | ||
| 243 | Times(u16), | ||
| 244 | /// Repeat until `stop` is called. | ||
| 245 | Infinite, | ||
| 246 | } | ||
| 247 | |||
| 248 | /// Configure an infinite looping sequence for `simple_playback` | 216 | /// Configure an infinite looping sequence for `simple_playback` |
| 249 | pub struct SequenceConfig<'a> { | 217 | #[non_exhaustive] |
| 218 | pub struct SequenceConfig { | ||
| 250 | /// Selects up mode or up-and-down mode for the counter | 219 | /// Selects up mode or up-and-down mode for the counter |
| 251 | pub counter_mode: CounterMode, | 220 | pub counter_mode: CounterMode, |
| 252 | /// Top value to be compared against buffer values | 221 | /// Top value to be compared against buffer values |
| 253 | pub top: u16, | 222 | pub top: u16, |
| 254 | /// Configuration for PWM_CLK | 223 | /// Configuration for PWM_CLK |
| 255 | pub prescaler: Prescaler, | 224 | pub prescaler: Prescaler, |
| 256 | /// In ram buffer to be played back | ||
| 257 | pub sequence: &'a [u16], | ||
| 258 | /// How a sequence is read from RAM and is spread to the compare register | 225 | /// How a sequence is read from RAM and is spread to the compare register |
| 259 | pub sequence_load: SequenceLoad, | 226 | pub sequence_load: SequenceLoad, |
| 260 | /// Number of Times PWM periods to delay between each sequence sample | 227 | /// Number of Times PWM periods to delay between each sequence sample |
| @@ -263,16 +230,62 @@ pub struct SequenceConfig<'a> { | |||
| 263 | pub end_delay: u32, | 230 | pub end_delay: u32, |
| 264 | } | 231 | } |
| 265 | 232 | ||
| 266 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 233 | impl Default for SequenceConfig { |
| 267 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 234 | fn default() -> SequenceConfig { |
| 268 | #[non_exhaustive] | 235 | SequenceConfig { |
| 269 | pub enum Error { | 236 | counter_mode: CounterMode::Up, |
| 270 | /// Max Sequence size is 32767 | 237 | top: 1000, |
| 271 | SequenceTooLong, | 238 | prescaler: Prescaler::Div16, |
| 272 | /// Min Sequence size is 1 | 239 | sequence_load: SequenceLoad::Common, |
| 273 | SequenceTimesAtLeastOne, | 240 | refresh: 0, |
| 274 | /// EasyDMA can only read from data memory, read only buffers in flash will fail. | 241 | end_delay: 0, |
| 275 | DMABufferNotInDataMemory, | 242 | } |
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 247 | pub enum SequenceMode { | ||
| 248 | /// Run sequence n Times total | ||
| 249 | Times(u16), | ||
| 250 | /// Repeat until `stop` is called. | ||
| 251 | Infinite, | ||
| 252 | } | ||
| 253 | |||
| 254 | /// PWM Base clock is system clock (16MHz) divided by prescaler | ||
| 255 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 256 | pub enum Prescaler { | ||
| 257 | Div1, | ||
| 258 | Div2, | ||
| 259 | Div4, | ||
| 260 | Div8, | ||
| 261 | Div16, | ||
| 262 | Div32, | ||
| 263 | Div64, | ||
| 264 | Div128, | ||
| 265 | } | ||
| 266 | |||
| 267 | /// How the sequence values are distributed across the channels | ||
| 268 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 269 | pub enum SequenceLoad { | ||
| 270 | /// Provided sequence will be used across all channels | ||
| 271 | Common, | ||
| 272 | /// Provided sequence contains grouped values for each channel ex: | ||
| 273 | /// [ch0_0_and_ch1_0, ch2_0_and_ch3_0, ... ch0_n_and_ch1_n, ch2_n_and_ch3_n] | ||
| 274 | Grouped, | ||
| 275 | /// Provided sequence contains individual values for each channel ex: | ||
| 276 | /// [ch0_0, ch1_0, ch2_0, ch3_0... ch0_n, ch1_n, ch2_n, ch3_n] | ||
| 277 | Individual, | ||
| 278 | /// Similar to Individual mode, but only three channels are used. The fourth | ||
| 279 | /// value is loaded into the pulse generator counter as its top value. | ||
| 280 | Waveform, | ||
| 281 | } | ||
| 282 | |||
| 283 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 284 | pub enum CounterMode { | ||
| 285 | /// Up counter (edge-aligned PWM duty cycle) | ||
| 286 | Up, | ||
| 287 | /// Up and down counter (center-aligned PWM duty cycle) | ||
| 288 | UpAndDown, | ||
| 276 | } | 289 | } |
| 277 | 290 | ||
| 278 | impl<'d, T: Instance> SimplePwm<'d, T> { | 291 | impl<'d, T: Instance> SimplePwm<'d, T> { |
diff --git a/examples/nrf/src/bin/pwm_sequence.rs b/examples/nrf/src/bin/pwm_sequence.rs index 82575d703..3f8b051dd 100644 --- a/examples/nrf/src/bin/pwm_sequence.rs +++ b/examples/nrf/src/bin/pwm_sequence.rs | |||
| @@ -7,9 +7,7 @@ mod example_common; | |||
| 7 | use defmt::*; | 7 | use defmt::*; |
| 8 | use embassy::executor::Spawner; | 8 | use embassy::executor::Spawner; |
| 9 | use embassy::time::{Duration, Timer}; | 9 | use embassy::time::{Duration, Timer}; |
| 10 | use embassy_nrf::pwm::{ | 10 | use embassy_nrf::pwm::{Prescaler, SequenceConfig, SequenceLoad, SequenceMode, SequencePwm}; |
| 11 | CounterMode, Prescaler, SequenceConfig, SequenceLoad, SequenceMode, SequencePwm, | ||
| 12 | }; | ||
| 13 | use embassy_nrf::Peripherals; | 11 | use embassy_nrf::Peripherals; |
| 14 | 12 | ||
| 15 | #[embassy::main] | 13 | #[embassy::main] |
| @@ -18,18 +16,19 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 18 | 0x8000, 0, 0, 0, 0, 0x8000, 0, 0, 0, 0, 0x8000, 0, 0, 0, 0, 0x8000, | 16 | 0x8000, 0, 0, 0, 0, 0x8000, 0, 0, 0, 0, 0x8000, 0, 0, 0, 0, 0x8000, |
| 19 | ]; | 17 | ]; |
| 20 | 18 | ||
| 21 | let config = SequenceConfig { | 19 | let mut config = SequenceConfig::default(); |
| 22 | counter_mode: CounterMode::Up, | 20 | config.top = 15625; |
| 23 | top: 15625, | 21 | config.prescaler = Prescaler::Div128; |
| 24 | prescaler: Prescaler::Div128, | 22 | config.sequence_load = SequenceLoad::Individual; |
| 25 | sequence: &seq_values, | ||
| 26 | sequence_load: SequenceLoad::Individual, | ||
| 27 | refresh: 0, | ||
| 28 | end_delay: 0, | ||
| 29 | }; | ||
| 30 | 23 | ||
| 31 | let pwm = unwrap!(SequencePwm::new( | 24 | let pwm = unwrap!(SequencePwm::new( |
| 32 | p.PWM0, p.P0_13, p.P0_15, p.P0_16, p.P0_14, config | 25 | p.PWM0, |
| 26 | p.P0_13, | ||
| 27 | p.P0_15, | ||
| 28 | p.P0_16, | ||
| 29 | p.P0_14, | ||
| 30 | config, | ||
| 31 | &seq_values, | ||
| 33 | )); | 32 | )); |
| 34 | let _ = pwm.start(SequenceMode::Times(5)); | 33 | let _ = pwm.start(SequenceMode::Times(5)); |
| 35 | info!("pwm started!"); | 34 | info!("pwm started!"); |
diff --git a/examples/nrf/src/bin/pwm_simple_sin.rs b/examples/nrf/src/bin/pwm_simple_sin.rs index afafb5d96..33fa6dcfe 100644 --- a/examples/nrf/src/bin/pwm_simple_sin.rs +++ b/examples/nrf/src/bin/pwm_simple_sin.rs | |||
| @@ -9,9 +9,7 @@ use defmt::*; | |||
| 9 | use embassy::executor::Spawner; | 9 | use embassy::executor::Spawner; |
| 10 | use embassy::time::{Duration, Timer}; | 10 | use embassy::time::{Duration, Timer}; |
| 11 | use embassy_nrf::gpio::NoPin; | 11 | use embassy_nrf::gpio::NoPin; |
| 12 | use embassy_nrf::pwm::{ | 12 | use embassy_nrf::pwm::{CounterMode, SequenceConfig, SequenceMode, SequencePwm}; |
| 13 | CounterMode, Prescaler, SequenceConfig, SequenceLoad, SequenceMode, SequencePwm, | ||
| 14 | }; | ||
| 15 | use embassy_nrf::Peripherals; | 13 | use embassy_nrf::Peripherals; |
| 16 | use micromath::F32Ext; | 14 | use micromath::F32Ext; |
| 17 | 15 | ||
| @@ -22,18 +20,18 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 22 | // probably not best use of resources to create the table at runtime, but makes testing fast | 20 | // probably not best use of resources to create the table at runtime, but makes testing fast |
| 23 | let seq_values: [u16; 220] = core::array::from_fn(|n| ((W1 * n as f32).sin() * 10000.0) as u16); | 21 | let seq_values: [u16; 220] = core::array::from_fn(|n| ((W1 * n as f32).sin() * 10000.0) as u16); |
| 24 | 22 | ||
| 25 | let config = SequenceConfig { | 23 | let mut config = SequenceConfig::default(); |
| 26 | counter_mode: CounterMode::UpAndDown, | 24 | config.counter_mode = CounterMode::UpAndDown; |
| 27 | top: 12000, | 25 | config.top = 12000; |
| 28 | prescaler: Prescaler::Div16, | ||
| 29 | sequence: &seq_values, | ||
| 30 | sequence_load: SequenceLoad::Common, | ||
| 31 | refresh: 0, | ||
| 32 | end_delay: 0, | ||
| 33 | }; | ||
| 34 | 26 | ||
| 35 | let pwm = unwrap!(SequencePwm::new( | 27 | let pwm = unwrap!(SequencePwm::new( |
| 36 | p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config | 28 | p.PWM0, |
| 29 | p.P0_13, | ||
| 30 | NoPin, | ||
| 31 | NoPin, | ||
| 32 | NoPin, | ||
| 33 | config, | ||
| 34 | &seq_values | ||
| 37 | )); | 35 | )); |
| 38 | let _ = pwm.start(SequenceMode::Infinite); | 36 | let _ = pwm.start(SequenceMode::Infinite); |
| 39 | info!("pwm started!"); | 37 | info!("pwm started!"); |
