aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-nrf/src/pwm.rs170
-rw-r--r--examples/nrf/src/bin/pwm_sequence_ppi.rs73
2 files changed, 206 insertions, 37 deletions
diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs
index 90cdf69c6..3fdc37ec0 100644
--- a/embassy-nrf/src/pwm.rs
+++ b/embassy-nrf/src/pwm.rs
@@ -9,6 +9,7 @@ use crate::gpio::sealed::Pin as _;
9use crate::gpio::{AnyPin, OptionalPin as GpioOptionalPin}; 9use crate::gpio::{AnyPin, OptionalPin as GpioOptionalPin};
10use crate::interrupt::Interrupt; 10use crate::interrupt::Interrupt;
11use crate::pac; 11use crate::pac;
12use crate::ppi::{Event, Task};
12use crate::util::slice_in_ram_or; 13use crate::util::slice_in_ram_or;
13 14
14/// SimplePwm is the traditional pwm interface you're probably used to, allowing 15/// SimplePwm is the traditional pwm interface you're probably used to, allowing
@@ -101,6 +102,13 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
101 // Disable all interrupts 102 // Disable all interrupts
102 r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); 103 r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
103 r.shorts.reset(); 104 r.shorts.reset();
105 r.events_stopped.reset();
106 r.events_loopsdone.reset();
107 r.events_seqend[0].reset();
108 r.events_seqend[1].reset();
109 r.events_pwmperiodend.reset();
110 r.events_seqstarted[0].reset();
111 r.events_seqstarted[1].reset();
104 112
105 r.seq0 113 r.seq0
106 .ptr 114 .ptr
@@ -200,12 +208,111 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
200 Ok(()) 208 Ok(())
201 } 209 }
202 210
203 /// Stop playback. 211 /// Returns reference to `Stopped` event endpoint for PPI.
204 #[inline(always)] 212 #[inline(always)]
205 pub fn stop(&self) { 213 pub fn event_stopped(&self) -> Event {
206 let r = T::regs(); 214 let r = T::regs();
207 215
208 r.enable.write(|w| w.enable().disabled()); 216 Event::from_reg(&r.events_stopped)
217 }
218
219 /// Returns reference to `LoopsDone` event endpoint for PPI.
220 #[inline(always)]
221 pub fn event_loops_done(&self) -> Event {
222 let r = T::regs();
223
224 Event::from_reg(&r.events_loopsdone)
225 }
226
227 /// Returns reference to `PwmPeriodEnd` event endpoint for PPI.
228 #[inline(always)]
229 pub fn event_pwm_period_end(&self) -> Event {
230 let r = T::regs();
231
232 Event::from_reg(&r.events_pwmperiodend)
233 }
234
235 /// Returns reference to `Seq0 End` event endpoint for PPI.
236 #[inline(always)]
237 pub fn event_seq_end(&self) -> Event {
238 let r = T::regs();
239
240 Event::from_reg(&r.events_seqend[0])
241 }
242
243 /// Returns reference to `Seq1 End` event endpoint for PPI.
244 #[inline(always)]
245 pub fn event_seq1_end(&self) -> Event {
246 let r = T::regs();
247
248 Event::from_reg(&r.events_seqend[1])
249 }
250
251 /// Returns reference to `Seq0 Started` event endpoint for PPI.
252 #[inline(always)]
253 pub fn event_seq0_started(&self) -> Event {
254 let r = T::regs();
255
256 Event::from_reg(&r.events_seqstarted[0])
257 }
258
259 /// Returns reference to `Seq1 Started` event endpoint for PPI.
260 #[inline(always)]
261 pub fn event_seq1_started(&self) -> Event {
262 let r = T::regs();
263
264 Event::from_reg(&r.events_seqstarted[1])
265 }
266
267 /// Returns reference to `Seq0 Start` task endpoint for PPI.
268 /// # Safety
269 ///
270 /// Interacting with the sequence while it runs puts it in an unknown state
271 #[inline(always)]
272 pub unsafe fn task_start_seq0(&self) -> Task {
273 let r = T::regs();
274
275 Task::from_reg(&r.tasks_seqstart[0])
276 }
277
278 /// Returns reference to `Seq1 Started` task endpoint for PPI.
279 /// # Safety
280 ///
281 /// Interacting with the sequence while it runs puts it in an unknown state
282 #[inline(always)]
283 pub unsafe fn task_start_seq1(&self) -> Task {
284 let r = T::regs();
285
286 Task::from_reg(&r.tasks_seqstart[1])
287 }
288
289 /// Returns reference to `NextStep` task endpoint for PPI.
290 /// # Safety
291 ///
292 /// Interacting with the sequence while it runs puts it in an unknown state
293 #[inline(always)]
294 pub unsafe fn task_next_step(&self) -> Task {
295 let r = T::regs();
296
297 Task::from_reg(&r.tasks_nextstep)
298 }
299
300 /// Returns reference to `Stop` task endpoint for PPI.
301 /// # Safety
302 ///
303 /// Interacting with the sequence while it runs puts it in an unknown state
304 #[inline(always)]
305 pub unsafe fn task_stop(&self) -> Task {
306 let r = T::regs();
307
308 Task::from_reg(&r.tasks_stop)
309 }
310
311 /// Stop playback. Disables the peripheral. Does NOT clear the last duty
312 /// cycle from the pin.
313 #[inline(always)]
314 pub fn stop(&self) {
315 let r = T::regs();
209 316
210 r.shorts.reset(); 317 r.shorts.reset();
211 318
@@ -213,6 +320,8 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
213 320
214 // tasks_stop() doesn't exist in all svds so write its bit instead 321 // tasks_stop() doesn't exist in all svds so write its bit instead
215 r.tasks_stop.write(|w| unsafe { w.bits(0x01) }); 322 r.tasks_stop.write(|w| unsafe { w.bits(0x01) });
323
324 r.enable.write(|w| w.enable().disabled());
216 } 325 }
217} 326}
218 327
@@ -224,23 +333,23 @@ impl<'a, T: Instance> Drop for SequencePwm<'a, T> {
224 333
225 if let Some(pin) = &self.ch0 { 334 if let Some(pin) = &self.ch0 {
226 pin.set_low(); 335 pin.set_low();
227 pin.conf().write(|w| w); 336 pin.conf().reset();
228 r.psel.out[0].write(|w| unsafe { w.bits(0x80000000) }); 337 r.psel.out[0].reset();
229 } 338 }
230 if let Some(pin) = &self.ch1 { 339 if let Some(pin) = &self.ch1 {
231 pin.set_low(); 340 pin.set_low();
232 pin.conf().write(|w| w); 341 pin.conf().reset();
233 r.psel.out[1].write(|w| unsafe { w.bits(0x80000000) }); 342 r.psel.out[1].reset();
234 } 343 }
235 if let Some(pin) = &self.ch2 { 344 if let Some(pin) = &self.ch2 {
236 pin.set_low(); 345 pin.set_low();
237 pin.conf().write(|w| w); 346 pin.conf().reset();
238 r.psel.out[2].write(|w| unsafe { w.bits(0x80000000) }); 347 r.psel.out[2].reset();
239 } 348 }
240 if let Some(pin) = &self.ch3 { 349 if let Some(pin) = &self.ch3 {
241 pin.set_low(); 350 pin.set_low();
242 pin.conf().write(|w| w); 351 pin.conf().reset();
243 r.psel.out[3].write(|w| unsafe { w.bits(0x80000000) }); 352 r.psel.out[3].reset();
244 } 353 }
245 } 354 }
246} 355}
@@ -325,8 +434,8 @@ pub enum CounterMode {
325impl<'d, T: Instance> SimplePwm<'d, T> { 434impl<'d, T: Instance> SimplePwm<'d, T> {
326 /// Creates the interface to a `SimplePwm` 435 /// Creates the interface to a `SimplePwm`
327 /// 436 ///
328 /// Defaults the freq to 1Mhz, max_duty 1000, duty 0, up mode, and pins low. 437 /// Enables the peripheral, defaults the freq to 1Mhz, max_duty 1000, duty
329 /// Must be started by calling `set_duty` 438 /// 0, up mode, and pins low. Must be started by calling `set_duty`
330 /// 439 ///
331 /// # Safety 440 /// # Safety
332 /// 441 ///
@@ -405,19 +514,6 @@ impl<'d, T: Instance> SimplePwm<'d, T> {
405 pwm 514 pwm
406 } 515 }
407 516
408 /// Stop playback
409 #[inline(always)]
410 pub fn stop(&self) {
411 let r = T::regs();
412
413 r.shorts.reset();
414
415 compiler_fence(Ordering::SeqCst);
416
417 // tasks_stop() doesn't exist in all svds so write its bit instead
418 r.tasks_stop.write(|w| unsafe { w.bits(0x01) });
419 }
420
421 /// Enables the PWM generator. 517 /// Enables the PWM generator.
422 #[inline(always)] 518 #[inline(always)]
423 pub fn enable(&self) { 519 pub fn enable(&self) {
@@ -425,7 +521,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> {
425 r.enable.write(|w| w.enable().enabled()); 521 r.enable.write(|w| w.enable().enabled());
426 } 522 }
427 523
428 /// Disables the PWM generator. 524 /// Disables the PWM generator. Does NOT clear the last duty cycle from the pin.
429 #[inline(always)] 525 #[inline(always)]
430 pub fn disable(&self) { 526 pub fn disable(&self) {
431 let r = T::regs(); 527 let r = T::regs();
@@ -446,13 +542,14 @@ impl<'d, T: Instance> SimplePwm<'d, T> {
446 // defensive before seqstart 542 // defensive before seqstart
447 compiler_fence(Ordering::SeqCst); 543 compiler_fence(Ordering::SeqCst);
448 544
545 r.events_seqend[0].reset();
546
449 // tasks_seqstart() doesn't exist in all svds so write its bit instead 547 // tasks_seqstart() doesn't exist in all svds so write its bit instead
450 r.tasks_seqstart[0].write(|w| unsafe { w.bits(1) }); 548 r.tasks_seqstart[0].write(|w| unsafe { w.bits(1) });
451 549
452 // defensive wait until waveform is loaded after seqstart so set_duty 550 // defensive wait until waveform is loaded after seqstart so set_duty
453 // can't be called again while dma is still reading 551 // can't be called again while dma is still reading
454 while r.events_seqend[0].read().bits() == 0 {} 552 while r.events_seqend[0].read().bits() == 0 {}
455 r.events_seqend[0].write(|w| w);
456 } 553 }
457 554
458 /// Sets the PWM clock prescaler. 555 /// Sets the PWM clock prescaler.
@@ -512,28 +609,27 @@ impl<'a, T: Instance> Drop for SimplePwm<'a, T> {
512 fn drop(&mut self) { 609 fn drop(&mut self) {
513 let r = T::regs(); 610 let r = T::regs();
514 611
515 self.stop();
516 self.disable(); 612 self.disable();
517 613
518 if let Some(pin) = &self.ch0 { 614 if let Some(pin) = &self.ch0 {
519 pin.set_low(); 615 pin.set_low();
520 pin.conf().write(|w| w); 616 pin.conf().reset();
521 r.psel.out[0].write(|w| unsafe { w.bits(0x80000000) }); 617 r.psel.out[0].reset();
522 } 618 }
523 if let Some(pin) = &self.ch1 { 619 if let Some(pin) = &self.ch1 {
524 pin.set_low(); 620 pin.set_low();
525 pin.conf().write(|w| w); 621 pin.conf().reset();
526 r.psel.out[1].write(|w| unsafe { w.bits(0x80000000) }); 622 r.psel.out[1].reset();
527 } 623 }
528 if let Some(pin) = &self.ch2 { 624 if let Some(pin) = &self.ch2 {
529 pin.set_low(); 625 pin.set_low();
530 pin.conf().write(|w| w); 626 pin.conf().reset();
531 r.psel.out[2].write(|w| unsafe { w.bits(0x80000000) }); 627 r.psel.out[2].reset();
532 } 628 }
533 if let Some(pin) = &self.ch3 { 629 if let Some(pin) = &self.ch3 {
534 pin.set_low(); 630 pin.set_low();
535 pin.conf().write(|w| w); 631 pin.conf().reset();
536 r.psel.out[3].write(|w| unsafe { w.bits(0x80000000) }); 632 r.psel.out[3].reset();
537 } 633 }
538 } 634 }
539} 635}
diff --git a/examples/nrf/src/bin/pwm_sequence_ppi.rs b/examples/nrf/src/bin/pwm_sequence_ppi.rs
new file mode 100644
index 000000000..aaea9ff00
--- /dev/null
+++ b/examples/nrf/src/bin/pwm_sequence_ppi.rs
@@ -0,0 +1,73 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4#![feature(array_from_fn)]
5
6#[path = "../example_common.rs"]
7mod example_common;
8use core::future::pending;
9use defmt::*;
10use embassy::executor::Spawner;
11use embassy_nrf::gpio::{Input, NoPin, Pull};
12use embassy_nrf::gpiote::{InputChannel, InputChannelPolarity};
13use embassy_nrf::ppi::Ppi;
14use embassy_nrf::pwm::{Prescaler, SequenceConfig, SequenceMode, SequencePwm};
15use embassy_nrf::Peripherals;
16
17#[embassy::main]
18async fn main(_spawner: Spawner, p: Peripherals) {
19 let seq_values: [u16; 5] = [1000, 250, 100, 50, 0];
20
21 let mut config = SequenceConfig::default();
22 config.prescaler = Prescaler::Div128;
23 // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8ms
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)
26 // thus our sequence takes 5 * 250ms or 1.25 seconds
27 config.refresh = 30;
28
29 let pwm = unwrap!(SequencePwm::new(
30 p.PWM0,
31 p.P0_13,
32 NoPin,
33 NoPin,
34 NoPin,
35 config,
36 &seq_values
37 ));
38
39 let _ = pwm.start(SequenceMode::Times(1));
40 // pwm.stop() deconfigures pins, and then the task_start_seq0 task cant work
41 // so its going to have to start running in order load the configuration
42
43 let button1 = InputChannel::new(
44 p.GPIOTE_CH0,
45 Input::new(p.P0_11, Pull::Up),
46 InputChannelPolarity::HiToLo,
47 );
48
49 let button2 = InputChannel::new(
50 p.GPIOTE_CH1,
51 Input::new(p.P0_12, Pull::Up),
52 InputChannelPolarity::HiToLo,
53 );
54
55 // messing with the pwm tasks is ill advised
56 // Times::Ininite and Times even are seq0, Times odd is seq1
57 let start = unsafe { pwm.task_start_seq0() };
58 let stop = unsafe { pwm.task_stop() };
59
60 let mut ppi = Ppi::new_one_to_one(p.PPI_CH1, button1.event_in(), start);
61 ppi.enable();
62
63 let mut ppi2 = Ppi::new_one_to_one(p.PPI_CH0, button2.event_in(), stop);
64 ppi2.enable();
65
66 info!("PPI setup!");
67 info!("Press button 1 to start LED 1");
68 info!("Press button 2 to stop LED 1");
69 info!("Note! task_stop stops the sequence, but not the pin output");
70
71 // Block forever so the above drivers don't get dropped
72 pending::<()>().await;
73}