aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-nrf/src/pwm.rs248
-rw-r--r--examples/nrf/src/bin/pwm_sequence.rs9
-rw-r--r--examples/nrf/src/bin/pwm_simple_sin.rs9
3 files changed, 180 insertions, 86 deletions
diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs
index 947cd873b..a59fddf74 100644
--- a/embassy-nrf/src/pwm.rs
+++ b/embassy-nrf/src/pwm.rs
@@ -54,47 +54,11 @@ pub struct Pwm<'d, T: Instance> {
54 phantom: PhantomData<&'d mut T>, 54 phantom: PhantomData<&'d mut T>,
55} 55}
56 56
57#[derive(Debug, Eq, PartialEq, Clone, Copy)] 57pub struct PwmSeq<'d, T: Instance> {
58pub enum SequenceMode { 58 phantom: PhantomData<&'d mut T>,
59 /// Run sequence n Times total
60 Times(u16),
61 /// Repeat until `stop` is called.
62 Infinite,
63}
64
65/// Configure an infinite looping sequence for `simple_playback`
66pub struct SequenceConfig<'a> {
67 /// Selects up mode or up-and-down mode for the counter
68 pub counter_mode: CounterMode,
69 /// Top value to be compared against buffer values
70 pub top: u16,
71 /// Configuration for PWM_CLK
72 pub prescaler: Prescaler,
73 /// In ram buffer to be played back
74 pub sequence: &'a [u16],
75 /// How a sequence is read from RAM and is spread to the compare register
76 pub sequence_load: SequenceLoad,
77 /// Number of Times PWM periods to delay between each sequence sample
78 pub refresh: u32,
79 /// Number of Times PWM periods after the sequence ends before starting the next sequence
80 pub end_delay: u32,
81 /// How many times to play the sequence
82 pub times: SequenceMode,
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86#[cfg_attr(feature = "defmt", derive(defmt::Format))]
87#[non_exhaustive]
88pub enum Error {
89 /// Max Sequence size is 32767
90 SequenceTooLong,
91 /// Min Sequence size is 1
92 SequenceTooShort,
93 /// EasyDMA can only read from data memory, read only buffers in flash will fail.
94 DMABufferNotInDataMemory,
95} 59}
96 60
97impl<'d, T: Instance> Pwm<'d, T> { 61impl<'d, T: Instance> PwmSeq<'d, T> {
98 /// Creates the interface to a PWM instance. 62 /// Creates the interface to a PWM instance.
99 /// 63 ///
100 /// Defaults the freq to 1Mhz, max_duty 32767, duty 0, and channels low. 64 /// Defaults the freq to 1Mhz, max_duty 32767, duty 0, and channels low.
@@ -111,11 +75,20 @@ impl<'d, T: Instance> Pwm<'d, T> {
111 ch1: impl Unborrow<Target = impl GpioOptionalPin> + 'd, 75 ch1: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
112 ch2: impl Unborrow<Target = impl GpioOptionalPin> + 'd, 76 ch2: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
113 ch3: impl Unborrow<Target = impl GpioOptionalPin> + 'd, 77 ch3: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
114 ) -> Self { 78 config: SequenceConfig,
79 ) -> Result<Self, Error> {
80 slice_in_ram_or(config.sequence, Error::DMABufferNotInDataMemory)?;
81
82 if config.sequence.len() > 32767 {
83 return Err(Error::SequenceTooLong);
84 }
85 if let SequenceMode::Times(0) = config.times {
86 return Err(Error::SequenceTooShort);
87 }
88
115 unborrow!(ch0, ch1, ch2, ch3); 89 unborrow!(ch0, ch1, ch2, ch3);
116 90
117 let r = T::regs(); 91 let r = T::regs();
118 let s = T::state();
119 92
120 if let Some(pin) = ch0.pin_mut() { 93 if let Some(pin) = ch0.pin_mut() {
121 pin.set_low(); 94 pin.set_low();
@@ -148,41 +121,6 @@ impl<'d, T: Instance> Pwm<'d, T> {
148 121
149 // Enable 122 // Enable
150 r.enable.write(|w| w.enable().enabled()); 123 r.enable.write(|w| w.enable().enabled());
151
152 r.seq0
153 .ptr
154 .write(|w| unsafe { w.bits(&s.duty as *const _ as u32) });
155 r.seq0.cnt.write(|w| unsafe { w.bits(4) });
156 r.seq0.refresh.write(|w| unsafe { w.bits(0) });
157 r.seq0.enddelay.write(|w| unsafe { w.bits(0) });
158
159 r.decoder.write(|w| {
160 w.load().individual();
161 w.mode().refresh_count()
162 });
163 r.mode.write(|w| w.updown().up());
164 r.prescaler.write(|w| w.prescaler().div_16());
165 r.countertop.write(|w| unsafe { w.countertop().bits(1000) });
166 r.loop_.write(|w| w.cnt().disabled());
167
168 Self {
169 phantom: PhantomData,
170 }
171 }
172
173 /// Play a `SequenceConfig` sequence instead of a stable `set_duty` output
174 pub fn play_sequence(&self, config: SequenceConfig) -> Result<(), Error> {
175 slice_in_ram_or(config.sequence, Error::DMABufferNotInDataMemory)?;
176
177 if config.sequence.len() > 32767 {
178 return Err(Error::SequenceTooLong);
179 }
180 if let SequenceMode::Times(0) = config.times {
181 return Err(Error::SequenceTooShort);
182 }
183
184 let r = T::regs();
185
186 r.mode 124 r.mode
187 .write(|w| unsafe { w.bits(config.counter_mode as u32) }); 125 .write(|w| unsafe { w.bits(config.counter_mode as u32) });
188 r.prescaler 126 r.prescaler
@@ -250,12 +188,162 @@ impl<'d, T: Instance> Pwm<'d, T> {
250 r.tasks_seqstart[0].write(|w| unsafe { w.bits(0x01) }); 188 r.tasks_seqstart[0].write(|w| unsafe { w.bits(0x01) });
251 } 189 }
252 } 190 }
253 Ok(()) 191
192 Ok(Self {
193 phantom: PhantomData,
194 })
195 }
196
197 /// Stop playback
198 #[inline(always)]
199 pub fn stop(&self) {
200 let r = T::regs();
201
202 r.shorts.reset();
203
204 // tasks_stop doesnt exist in all svds so write its bit instead
205 r.tasks_stop.write(|w| unsafe { w.bits(0x01) });
206 }
207
208 /// Disables the PWM generator.
209 #[inline(always)]
210 pub fn disable(&self) {
211 let r = T::regs();
212 r.enable.write(|w| w.enable().disabled());
213 }
214}
215
216impl<'a, T: Instance> Drop for PwmSeq<'a, T> {
217 fn drop(&mut self) {
218 self.stop();
219 self.disable();
220
221 info!("pwm drop: done");
222
223 // TODO: disable pins
224 }
225}
226
227#[derive(Debug, Eq, PartialEq, Clone, Copy)]
228pub enum SequenceMode {
229 /// Run sequence n Times total
230 Times(u16),
231 /// Repeat until `stop` is called.
232 Infinite,
233}
234
235/// Configure an infinite looping sequence for `simple_playback`
236pub struct SequenceConfig<'a> {
237 /// Selects up mode or up-and-down mode for the counter
238 pub counter_mode: CounterMode,
239 /// Top value to be compared against buffer values
240 pub top: u16,
241 /// Configuration for PWM_CLK
242 pub prescaler: Prescaler,
243 /// In ram buffer to be played back
244 pub sequence: &'a [u16],
245 /// How a sequence is read from RAM and is spread to the compare register
246 pub sequence_load: SequenceLoad,
247 /// Number of Times PWM periods to delay between each sequence sample
248 pub refresh: u32,
249 /// Number of Times PWM periods after the sequence ends before starting the next sequence
250 pub end_delay: u32,
251 /// How many times to play the sequence
252 pub times: SequenceMode,
253}
254
255#[derive(Debug, Clone, Copy, PartialEq, Eq)]
256#[cfg_attr(feature = "defmt", derive(defmt::Format))]
257#[non_exhaustive]
258pub enum Error {
259 /// Max Sequence size is 32767
260 SequenceTooLong,
261 /// Min Sequence size is 1
262 SequenceTooShort,
263 /// EasyDMA can only read from data memory, read only buffers in flash will fail.
264 DMABufferNotInDataMemory,
265}
266
267impl<'d, T: Instance> Pwm<'d, T> {
268 /// Creates the interface to a PWM instance.
269 ///
270 /// Defaults the freq to 1Mhz, max_duty 32767, duty 0, and channels low.
271 /// Must be started by calling `set_duty`
272 ///
273 /// # Safety
274 ///
275 /// The returned API is safe unless you use `mem::forget` (or similar safe
276 /// mechanisms) on stack allocated buffers which which have been passed to
277 /// [`send()`](Pwm::send) or [`receive`](Pwm::receive).
278 #[allow(unused_unsafe)]
279 pub fn new(
280 _pwm: impl Unborrow<Target = T> + 'd,
281 ch0: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
282 ch1: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
283 ch2: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
284 ch3: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
285 ) -> Self {
286 unborrow!(ch0, ch1, ch2, ch3);
287
288 let r = T::regs();
289 let s = T::state();
290
291 if let Some(pin) = ch0.pin_mut() {
292 pin.set_low();
293 pin.conf().write(|w| w.dir().output());
294 }
295 if let Some(pin) = ch1.pin_mut() {
296 pin.set_low();
297 pin.conf().write(|w| w.dir().output());
298 }
299 if let Some(pin) = ch2.pin_mut() {
300 pin.set_low();
301 pin.conf().write(|w| w.dir().output());
302 }
303 if let Some(pin) = ch3.pin_mut() {
304 pin.set_low();
305 pin.conf().write(|w| w.dir().output());
306 }
307
308 // if NoPin provided writes disconnected (top bit 1) 0x80000000 else
309 // writes pin number ex 13 (0x0D) which is connected (top bit 0)
310 r.psel.out[0].write(|w| unsafe { w.bits(ch0.psel_bits()) });
311 r.psel.out[1].write(|w| unsafe { w.bits(ch1.psel_bits()) });
312 r.psel.out[2].write(|w| unsafe { w.bits(ch2.psel_bits()) });
313 r.psel.out[3].write(|w| unsafe { w.bits(ch3.psel_bits()) });
314
315 // Disable all interrupts
316 r.intenset.reset();
317 r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
318 r.shorts.reset();
319
320 // Enable
321 r.enable.write(|w| w.enable().enabled());
322
323 r.seq0
324 .ptr
325 .write(|w| unsafe { w.bits(&s.duty as *const _ as u32) });
326 r.seq0.cnt.write(|w| unsafe { w.bits(4) });
327 r.seq0.refresh.write(|w| unsafe { w.bits(0) });
328 r.seq0.enddelay.write(|w| unsafe { w.bits(0) });
329
330 r.decoder.write(|w| {
331 w.load().individual();
332 w.mode().refresh_count()
333 });
334 r.mode.write(|w| w.updown().up());
335 r.prescaler.write(|w| w.prescaler().div_16());
336 r.countertop.write(|w| unsafe { w.countertop().bits(1000) });
337 r.loop_.write(|w| w.cnt().disabled());
338
339 Self {
340 phantom: PhantomData,
341 }
254 } 342 }
255 343
256 /// Stop playback 344 /// Stop playback
257 #[inline(always)] 345 #[inline(always)]
258 pub fn sequence_stop(&self) { 346 pub fn stop(&self) {
259 let r = T::regs(); 347 let r = T::regs();
260 348
261 r.shorts.reset(); 349 r.shorts.reset();
@@ -264,6 +352,7 @@ impl<'d, T: Instance> Pwm<'d, T> {
264 r.tasks_stop.write(|w| unsafe { w.bits(0x01) }); 352 r.tasks_stop.write(|w| unsafe { w.bits(0x01) });
265 } 353 }
266 354
355 // todo should this do.. something useful
267 /// Enables the PWM generator. 356 /// Enables the PWM generator.
268 #[inline(always)] 357 #[inline(always)]
269 pub fn enable(&self) { 358 pub fn enable(&self) {
@@ -271,6 +360,7 @@ impl<'d, T: Instance> Pwm<'d, T> {
271 r.enable.write(|w| w.enable().enabled()); 360 r.enable.write(|w| w.enable().enabled());
272 } 361 }
273 362
363 // todo should this stop the task? or should you just use set_duty to 0?
274 /// Disables the PWM generator. 364 /// Disables the PWM generator.
275 #[inline(always)] 365 #[inline(always)]
276 pub fn disable(&self) { 366 pub fn disable(&self) {
@@ -349,7 +439,7 @@ impl<'d, T: Instance> Pwm<'d, T> {
349 439
350impl<'a, T: Instance> Drop for Pwm<'a, T> { 440impl<'a, T: Instance> Drop for Pwm<'a, T> {
351 fn drop(&mut self) { 441 fn drop(&mut self) {
352 self.sequence_stop(); 442 self.stop();
353 self.disable(); 443 self.disable();
354 444
355 info!("pwm drop: done"); 445 info!("pwm drop: done");
diff --git a/examples/nrf/src/bin/pwm_sequence.rs b/examples/nrf/src/bin/pwm_sequence.rs
index 066ab3c03..0a7bea1c4 100644
--- a/examples/nrf/src/bin/pwm_sequence.rs
+++ b/examples/nrf/src/bin/pwm_sequence.rs
@@ -7,7 +7,9 @@ mod example_common;
7use defmt::*; 7use defmt::*;
8use embassy::executor::Spawner; 8use embassy::executor::Spawner;
9use embassy::time::{Duration, Timer}; 9use embassy::time::{Duration, Timer};
10use embassy_nrf::pwm::{CounterMode, Prescaler, Pwm, SequenceConfig, SequenceLoad, SequenceMode}; 10use embassy_nrf::pwm::{
11 CounterMode, Prescaler, PwmSeq, SequenceConfig, SequenceLoad, SequenceMode,
12};
11use embassy_nrf::Peripherals; 13use embassy_nrf::Peripherals;
12 14
13#[embassy::main] 15#[embassy::main]
@@ -27,8 +29,9 @@ async fn main(_spawner: Spawner, p: Peripherals) {
27 times: SequenceMode::Times(5), 29 times: SequenceMode::Times(5),
28 }; 30 };
29 31
30 let pwm = Pwm::new(p.PWM0, p.P0_13, p.P0_15, p.P0_16, p.P0_14); 32 let _pwm = unwrap!(PwmSeq::new(
31 unwrap!(pwm.play_sequence(config)); 33 p.PWM0, p.P0_13, p.P0_15, p.P0_16, p.P0_14, config
34 ));
32 info!("pwm started!"); 35 info!("pwm started!");
33 36
34 loop { 37 loop {
diff --git a/examples/nrf/src/bin/pwm_simple_sin.rs b/examples/nrf/src/bin/pwm_simple_sin.rs
index 3c1bddbc7..6fd59c6a4 100644
--- a/examples/nrf/src/bin/pwm_simple_sin.rs
+++ b/examples/nrf/src/bin/pwm_simple_sin.rs
@@ -9,7 +9,9 @@ use defmt::*;
9use embassy::executor::Spawner; 9use embassy::executor::Spawner;
10use embassy::time::{Duration, Timer}; 10use embassy::time::{Duration, Timer};
11use embassy_nrf::gpio::NoPin; 11use embassy_nrf::gpio::NoPin;
12use embassy_nrf::pwm::{CounterMode, Prescaler, Pwm, SequenceConfig, SequenceLoad, SequenceMode}; 12use embassy_nrf::pwm::{
13 CounterMode, Prescaler, PwmSeq, SequenceConfig, SequenceLoad, SequenceMode,
14};
13use embassy_nrf::Peripherals; 15use embassy_nrf::Peripherals;
14use micromath::F32Ext; 16use micromath::F32Ext;
15 17
@@ -31,13 +33,12 @@ async fn main(_spawner: Spawner, p: Peripherals) {
31 times: SequenceMode::Infinite, 33 times: SequenceMode::Infinite,
32 }; 34 };
33 35
34 let pwm = Pwm::new(p.PWM0, p.P0_13, NoPin, NoPin, NoPin); 36 let pwm = unwrap!(PwmSeq::new(p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config));
35 unwrap!(pwm.play_sequence(config));
36 info!("pwm started!"); 37 info!("pwm started!");
37 38
38 Timer::after(Duration::from_millis(20000)).await; 39 Timer::after(Duration::from_millis(20000)).await;
39 40
40 pwm.sequence_stop(); 41 pwm.stop();
41 info!("pwm stopped!"); 42 info!("pwm stopped!");
42 43
43 loop { 44 loop {