diff options
| author | Jacob Rosenthal <[email protected]> | 2021-10-26 00:37:52 -0700 |
|---|---|---|
| committer | Jacob Rosenthal <[email protected]> | 2021-10-29 16:27:26 -0700 |
| commit | eb0bf1fd7a33330425a12420e5d948ca6e88d74f (patch) | |
| tree | d1d93ce868c98f6411b5fa6881a6f54c43a24f30 | |
| parent | dfccb84fcbb9ab55c3d0f89d99d2049376bff901 (diff) | |
simple_playback api from nrf sdk
| -rw-r--r-- | embassy-nrf/src/pwm.rs | 167 | ||||
| -rw-r--r-- | embassy-nrf/src/util.rs | 4 | ||||
| -rw-r--r-- | examples/nrf/src/bin/pwm_sequence.rs | 41 |
3 files changed, 207 insertions, 5 deletions
diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 5e996e882..ccab6f48d 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs | |||
| @@ -9,7 +9,8 @@ use embassy_hal_common::unborrow; | |||
| 9 | use crate::gpio::sealed::Pin as _; | 9 | use crate::gpio::sealed::Pin as _; |
| 10 | use crate::gpio::OptionalPin as GpioOptionalPin; | 10 | use crate::gpio::OptionalPin as GpioOptionalPin; |
| 11 | use crate::interrupt::Interrupt; | 11 | use crate::interrupt::Interrupt; |
| 12 | use crate::pac; | 12 | use crate::util::slice_in_ram_or; |
| 13 | use crate::{pac, EASY_DMA_SIZE}; | ||
| 13 | 14 | ||
| 14 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | 15 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] |
| 15 | pub enum Prescaler { | 16 | pub enum Prescaler { |
| @@ -23,11 +24,57 @@ pub enum Prescaler { | |||
| 23 | Div128, | 24 | Div128, |
| 24 | } | 25 | } |
| 25 | 26 | ||
| 26 | /// Interface to the UARTE peripheral | 27 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] |
| 28 | pub enum SequenceLoad { | ||
| 29 | Common, | ||
| 30 | Grouped, | ||
| 31 | Individual, | ||
| 32 | Waveform, | ||
| 33 | } | ||
| 34 | |||
| 35 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 36 | pub enum CounterMode { | ||
| 37 | Up, | ||
| 38 | UpAndDown, | ||
| 39 | } | ||
| 40 | |||
| 41 | /// Interface to the PWM peripheral | ||
| 27 | pub struct Pwm<'d, T: Instance> { | 42 | pub struct Pwm<'d, T: Instance> { |
| 28 | phantom: PhantomData<&'d mut T>, | 43 | phantom: PhantomData<&'d mut T>, |
| 29 | } | 44 | } |
| 30 | 45 | ||
| 46 | // Configure an infinite looping sequence for `simple_playback` | ||
| 47 | pub struct LoopingConfig<'a> { | ||
| 48 | /// Selects up mode or up-and-down mode for the counter | ||
| 49 | pub counter_mode: CounterMode, | ||
| 50 | // top value to be compared against buffer values | ||
| 51 | pub top: u16, | ||
| 52 | /// Configuration for PWM_CLK | ||
| 53 | pub prescaler: Prescaler, | ||
| 54 | /// In ram buffer to be played back | ||
| 55 | pub sequence: &'a [u16], | ||
| 56 | /// Common Mode means seq in buffer will be used across all channels | ||
| 57 | /// Individual Mode buffer holds [ch0_0, ch1_0, ch2_0, ch3_0, ch0_1, ch1_1, | ||
| 58 | /// ch2_1, ch3_1 ... ch0_n, ch1_n, ch2_n, ch3_n] | ||
| 59 | pub sequence_load: SequenceLoad, | ||
| 60 | /// will instruct a new RAM stored pulse width value on every (N+1)th PWM | ||
| 61 | /// period. Setting the register to zero will result in a new duty cycle | ||
| 62 | /// update every PWM period as long as the minimum PWM period is observed. | ||
| 63 | pub repeats: u32, | ||
| 64 | /// enddelay PWM period delays between last period on sequence 0 before repeating | ||
| 65 | pub enddelay: u32, | ||
| 66 | } | ||
| 67 | |||
| 68 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 69 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 70 | #[non_exhaustive] | ||
| 71 | pub enum Error { | ||
| 72 | Seq0BufferTooLong, | ||
| 73 | Seq1BufferTooLong, | ||
| 74 | /// EasyDMA can only read from data memory, read only buffers in flash will fail. | ||
| 75 | DMABufferNotInDataMemory, | ||
| 76 | } | ||
| 77 | |||
| 31 | impl<'d, T: Instance> Pwm<'d, T> { | 78 | impl<'d, T: Instance> Pwm<'d, T> { |
| 32 | /// Creates the interface to a UARTE instance. | 79 | /// Creates the interface to a UARTE instance. |
| 33 | /// Sets the baud rate, parity and assigns the pins to the UARTE peripheral. | 80 | /// Sets the baud rate, parity and assigns the pins to the UARTE peripheral. |
| @@ -99,6 +146,120 @@ impl<'d, T: Instance> Pwm<'d, T> { | |||
| 99 | } | 146 | } |
| 100 | } | 147 | } |
| 101 | 148 | ||
| 149 | pub fn simple_playback( | ||
| 150 | _pwm: impl Unborrow<Target = T> + 'd, | ||
| 151 | ch0: impl Unborrow<Target = impl GpioOptionalPin> + 'd, | ||
| 152 | ch1: impl Unborrow<Target = impl GpioOptionalPin> + 'd, | ||
| 153 | ch2: impl Unborrow<Target = impl GpioOptionalPin> + 'd, | ||
| 154 | ch3: impl Unborrow<Target = impl GpioOptionalPin> + 'd, | ||
| 155 | config: LoopingConfig, | ||
| 156 | count: u16, | ||
| 157 | ) -> Result<Self, Error> { | ||
| 158 | slice_in_ram_or(config.sequence, Error::DMABufferNotInDataMemory)?; | ||
| 159 | |||
| 160 | if config.sequence.len() > EASY_DMA_SIZE { | ||
| 161 | return Err(Error::Seq0BufferTooLong); | ||
| 162 | } | ||
| 163 | |||
| 164 | unborrow!(ch0, ch1, ch2, ch3); | ||
| 165 | |||
| 166 | let odd: bool = count & 1 == 1; | ||
| 167 | |||
| 168 | let r = T::regs(); | ||
| 169 | |||
| 170 | if let Some(pin) = ch0.pin_mut() { | ||
| 171 | pin.set_low(); | ||
| 172 | pin.conf().write(|w| w.dir().output()); | ||
| 173 | r.psel.out[0].write(|w| unsafe { w.bits(ch0.psel_bits()) }); | ||
| 174 | } | ||
| 175 | if let Some(pin) = ch1.pin_mut() { | ||
| 176 | pin.set_low(); | ||
| 177 | pin.conf().write(|w| w.dir().output()); | ||
| 178 | r.psel.out[1].write(|w| unsafe { w.bits(ch1.psel_bits()) }); | ||
| 179 | } | ||
| 180 | if let Some(pin) = ch2.pin_mut() { | ||
| 181 | pin.set_low(); | ||
| 182 | pin.conf().write(|w| w.dir().output()); | ||
| 183 | r.psel.out[2].write(|w| unsafe { w.bits(ch2.psel_bits()) }); | ||
| 184 | } | ||
| 185 | if let Some(pin) = ch3.pin_mut() { | ||
| 186 | pin.set_low(); | ||
| 187 | pin.conf().write(|w| w.dir().output()); | ||
| 188 | r.psel.out[3].write(|w| unsafe { w.bits(ch3.psel_bits()) }); | ||
| 189 | } | ||
| 190 | |||
| 191 | r.enable.write(|w| w.enable().enabled()); | ||
| 192 | |||
| 193 | r.mode | ||
| 194 | .write(|w| unsafe { w.bits(config.counter_mode as u32) }); | ||
| 195 | r.prescaler | ||
| 196 | .write(|w| w.prescaler().bits(config.prescaler as u8)); | ||
| 197 | r.countertop | ||
| 198 | .write(|w| unsafe { w.countertop().bits(config.top) }); | ||
| 199 | |||
| 200 | r.decoder.write(|w| { | ||
| 201 | w.load().bits(config.sequence_load as u8); | ||
| 202 | w.mode().refresh_count() | ||
| 203 | }); | ||
| 204 | |||
| 205 | r.seq0 | ||
| 206 | .ptr | ||
| 207 | .write(|w| unsafe { w.bits(config.sequence.as_ptr() as u32) }); | ||
| 208 | r.seq0 | ||
| 209 | .cnt | ||
| 210 | .write(|w| unsafe { w.bits(config.sequence.len() as u32) }); | ||
| 211 | r.seq0.refresh.write(|w| unsafe { w.bits(config.repeats) }); | ||
| 212 | r.seq0 | ||
| 213 | .enddelay | ||
| 214 | .write(|w| unsafe { w.bits(config.enddelay) }); | ||
| 215 | |||
| 216 | r.seq1 | ||
| 217 | .ptr | ||
| 218 | .write(|w| unsafe { w.bits(config.sequence.as_ptr() as u32) }); | ||
| 219 | r.seq1 | ||
| 220 | .cnt | ||
| 221 | .write(|w| unsafe { w.bits(config.sequence.len() as u32) }); | ||
| 222 | r.seq1.refresh.write(|w| unsafe { w.bits(config.repeats) }); | ||
| 223 | r.seq1 | ||
| 224 | .enddelay | ||
| 225 | .write(|w| unsafe { w.bits(config.enddelay) }); | ||
| 226 | |||
| 227 | let mut loop_: u16 = count / 2; | ||
| 228 | if odd { | ||
| 229 | loop_ += 1; | ||
| 230 | } | ||
| 231 | |||
| 232 | r.loop_.write(|w| unsafe { w.cnt().bits(loop_) }); | ||
| 233 | |||
| 234 | if odd { | ||
| 235 | r.shorts.write(|w| w.loopsdone_seqstart1().set_bit()); | ||
| 236 | } else { | ||
| 237 | r.shorts.write(|w| w.loopsdone_seqstart0().set_bit()); | ||
| 238 | } | ||
| 239 | |||
| 240 | // tasks_seqstart doesnt exist in all svds so write its bit instead | ||
| 241 | if odd { | ||
| 242 | r.tasks_seqstart[1].write(|w| unsafe { w.bits(0x01) }); | ||
| 243 | } else { | ||
| 244 | r.tasks_seqstart[0].write(|w| unsafe { w.bits(0x01) }); | ||
| 245 | } | ||
| 246 | |||
| 247 | Ok(Self { | ||
| 248 | phantom: PhantomData, | ||
| 249 | }) | ||
| 250 | } | ||
| 251 | |||
| 252 | /// Stop playback | ||
| 253 | #[inline(always)] | ||
| 254 | pub fn stop(&self) { | ||
| 255 | let r = T::regs(); | ||
| 256 | |||
| 257 | r.shorts.write(|w| unsafe { w.bits(0x0) }); | ||
| 258 | |||
| 259 | // tasks_stop doesnt exist in all svds so write its bit instead | ||
| 260 | r.tasks_stop.write(|w| unsafe { w.bits(0x01) }); | ||
| 261 | } | ||
| 262 | |||
| 102 | /// Enables the PWM generator. | 263 | /// Enables the PWM generator. |
| 103 | #[inline(always)] | 264 | #[inline(always)] |
| 104 | pub fn enable(&self) { | 265 | pub fn enable(&self) { |
| @@ -128,7 +289,7 @@ impl<'d, T: Instance> Pwm<'d, T> { | |||
| 128 | T::regs().prescaler.write(|w| w.prescaler().bits(div as u8)); | 289 | T::regs().prescaler.write(|w| w.prescaler().bits(div as u8)); |
| 129 | } | 290 | } |
| 130 | 291 | ||
| 131 | /// Sets the PWM clock prescaler. | 292 | /// Gets the PWM clock prescaler. |
| 132 | #[inline(always)] | 293 | #[inline(always)] |
| 133 | pub fn prescaler(&self) -> Prescaler { | 294 | pub fn prescaler(&self) -> Prescaler { |
| 134 | match T::regs().prescaler.read().prescaler().bits() { | 295 | match T::regs().prescaler.read().prescaler().bits() { |
diff --git a/embassy-nrf/src/util.rs b/embassy-nrf/src/util.rs index 344fb01f9..d17ec5032 100644 --- a/embassy-nrf/src/util.rs +++ b/embassy-nrf/src/util.rs | |||
| @@ -2,14 +2,14 @@ const SRAM_LOWER: usize = 0x2000_0000; | |||
| 2 | const SRAM_UPPER: usize = 0x3000_0000; | 2 | const SRAM_UPPER: usize = 0x3000_0000; |
| 3 | 3 | ||
| 4 | /// Does this slice reside entirely within RAM? | 4 | /// Does this slice reside entirely within RAM? |
| 5 | pub(crate) fn slice_in_ram(slice: &[u8]) -> bool { | 5 | pub(crate) fn slice_in_ram<T>(slice: &[T]) -> bool { |
| 6 | let ptr = slice.as_ptr() as usize; | 6 | let ptr = slice.as_ptr() as usize; |
| 7 | ptr >= SRAM_LOWER && (ptr + slice.len()) < SRAM_UPPER | 7 | ptr >= SRAM_LOWER && (ptr + slice.len()) < SRAM_UPPER |
| 8 | } | 8 | } |
| 9 | 9 | ||
| 10 | /// Return an error if slice is not in RAM. | 10 | /// Return an error if slice is not in RAM. |
| 11 | #[cfg(not(feature = "nrf51"))] | 11 | #[cfg(not(feature = "nrf51"))] |
| 12 | pub(crate) fn slice_in_ram_or<T>(slice: &[u8], err: T) -> Result<(), T> { | 12 | pub(crate) fn slice_in_ram_or<T, E>(slice: &[T], err: E) -> Result<(), E> { |
| 13 | if slice.len() == 0 || slice_in_ram(slice) { | 13 | if slice.len() == 0 || slice_in_ram(slice) { |
| 14 | Ok(()) | 14 | Ok(()) |
| 15 | } else { | 15 | } else { |
diff --git a/examples/nrf/src/bin/pwm_sequence.rs b/examples/nrf/src/bin/pwm_sequence.rs new file mode 100644 index 000000000..93ee9f5b2 --- /dev/null +++ b/examples/nrf/src/bin/pwm_sequence.rs | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | #[path = "../example_common.rs"] | ||
| 6 | mod example_common; | ||
| 7 | use defmt::*; | ||
| 8 | use embassy::executor::Spawner; | ||
| 9 | use embassy::time::{Duration, Timer}; | ||
| 10 | use embassy_nrf::gpio::NoPin; | ||
| 11 | use embassy_nrf::pwm::{CounterMode, LoopingConfig, Prescaler, Pwm, SequenceLoad}; | ||
| 12 | use embassy_nrf::Peripherals; | ||
| 13 | |||
| 14 | #[embassy::main] | ||
| 15 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 16 | let seq_values: [u16; 2] = [0, 0x8000]; | ||
| 17 | |||
| 18 | let config = LoopingConfig { | ||
| 19 | counter_mode: CounterMode::Up, | ||
| 20 | top: 31250, | ||
| 21 | prescaler: Prescaler::Div128, | ||
| 22 | sequence: &seq_values, | ||
| 23 | sequence_load: SequenceLoad::Common, | ||
| 24 | repeats: 1, | ||
| 25 | enddelay: 0, | ||
| 26 | }; | ||
| 27 | |||
| 28 | let pwm = unwrap!(Pwm::simple_playback( | ||
| 29 | p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config, 1 | ||
| 30 | )); | ||
| 31 | info!("pwm started!"); | ||
| 32 | |||
| 33 | Timer::after(Duration::from_millis(10000)).await; | ||
| 34 | |||
| 35 | pwm.stop(); | ||
| 36 | info!("pwm stopped!"); | ||
| 37 | |||
| 38 | loop { | ||
| 39 | Timer::after(Duration::from_millis(1000)).await; | ||
| 40 | } | ||
| 41 | } | ||
