aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-nrf/CHANGELOG.md4
-rw-r--r--embassy-nrf/src/pwm.rs324
-rw-r--r--examples/nrf52840/src/bin/i2s_monitor.rs9
-rw-r--r--examples/nrf52840/src/bin/pwm.rs14
-rw-r--r--examples/nrf52840/src/bin/pwm_servo.rs14
5 files changed, 261 insertions, 104 deletions
diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md
index 0280e2730..89adaf2da 100644
--- a/embassy-nrf/CHANGELOG.md
+++ b/embassy-nrf/CHANGELOG.md
@@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13- changed: do not panic on BufferedUarte overrun 13- changed: do not panic on BufferedUarte overrun
14- added: allow direct access to the input pin of `gpiote::InputChannel` 14- added: allow direct access to the input pin of `gpiote::InputChannel`
15- bugfix: use DETECTMODE_SEC in GPIOTE in secure mode 15- bugfix: use DETECTMODE_SEC in GPIOTE in secure mode
16- added: allow configuring the idle state of GPIO pins connected to PWM channels
17- changed: allow configuring the PWM peripheral in the constructor of `SimplePwm`
18- changed: support setting duty cycles with inverted polarity in `SimplePwm`
19- added: support setting the duty cycles of all channels at once in `SimplePwm`
16 20
17## 0.8.0 - 2025-09-30 21## 0.8.0 - 2025-09-30
18 22
diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs
index e038f44b8..00b3278c7 100644
--- a/embassy-nrf/src/pwm.rs
+++ b/embassy-nrf/src/pwm.rs
@@ -6,7 +6,7 @@ use core::sync::atomic::{Ordering, compiler_fence};
6 6
7use embassy_hal_internal::{Peri, PeripheralType}; 7use embassy_hal_internal::{Peri, PeripheralType};
8 8
9use crate::gpio::{AnyPin, DISCONNECTED, OutputDrive, Pin as GpioPin, PselBits, SealedPin as _, convert_drive}; 9use crate::gpio::{AnyPin, DISCONNECTED, Level, OutputDrive, Pin as GpioPin, PselBits, SealedPin as _, convert_drive};
10use crate::pac::gpio::vals as gpiovals; 10use crate::pac::gpio::vals as gpiovals;
11use crate::pac::pwm::vals; 11use crate::pac::pwm::vals;
12use crate::ppi::{Event, Task}; 12use crate::ppi::{Event, Task};
@@ -17,7 +17,7 @@ use crate::{interrupt, pac};
17/// to simply set a duty cycle across up to four channels. 17/// to simply set a duty cycle across up to four channels.
18pub struct SimplePwm<'d> { 18pub struct SimplePwm<'d> {
19 r: pac::pwm::Pwm, 19 r: pac::pwm::Pwm,
20 duty: [u16; 4], 20 duty: [DutyCycle; 4],
21 ch0: Option<Peri<'d, AnyPin>>, 21 ch0: Option<Peri<'d, AnyPin>>,
22 ch1: Option<Peri<'d, AnyPin>>, 22 ch1: Option<Peri<'d, AnyPin>>,
23 ch2: Option<Peri<'d, AnyPin>>, 23 ch2: Option<Peri<'d, AnyPin>>,
@@ -53,13 +53,11 @@ pub const PWM_CLK_HZ: u32 = 16_000_000;
53 53
54impl<'d> SequencePwm<'d> { 54impl<'d> SequencePwm<'d> {
55 /// Create a new 1-channel PWM 55 /// Create a new 1-channel PWM
56 #[allow(unused_unsafe)]
57 pub fn new_1ch<T: Instance>(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, config: Config) -> Result<Self, Error> { 56 pub fn new_1ch<T: Instance>(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, config: Config) -> Result<Self, Error> {
58 Self::new_inner(pwm, Some(ch0.into()), None, None, None, config) 57 Self::new_inner(pwm, Some(ch0.into()), None, None, None, config)
59 } 58 }
60 59
61 /// Create a new 2-channel PWM 60 /// Create a new 2-channel PWM
62 #[allow(unused_unsafe)]
63 pub fn new_2ch<T: Instance>( 61 pub fn new_2ch<T: Instance>(
64 pwm: Peri<'d, T>, 62 pwm: Peri<'d, T>,
65 ch0: Peri<'d, impl GpioPin>, 63 ch0: Peri<'d, impl GpioPin>,
@@ -70,7 +68,6 @@ impl<'d> SequencePwm<'d> {
70 } 68 }
71 69
72 /// Create a new 3-channel PWM 70 /// Create a new 3-channel PWM
73 #[allow(unused_unsafe)]
74 pub fn new_3ch<T: Instance>( 71 pub fn new_3ch<T: Instance>(
75 pwm: Peri<'d, T>, 72 pwm: Peri<'d, T>,
76 ch0: Peri<'d, impl GpioPin>, 73 ch0: Peri<'d, impl GpioPin>,
@@ -82,7 +79,6 @@ impl<'d> SequencePwm<'d> {
82 } 79 }
83 80
84 /// Create a new 4-channel PWM 81 /// Create a new 4-channel PWM
85 #[allow(unused_unsafe)]
86 pub fn new_4ch<T: Instance>( 82 pub fn new_4ch<T: Instance>(
87 pwm: Peri<'d, T>, 83 pwm: Peri<'d, T>,
88 ch0: Peri<'d, impl GpioPin>, 84 ch0: Peri<'d, impl GpioPin>,
@@ -111,44 +107,27 @@ impl<'d> SequencePwm<'d> {
111 ) -> Result<Self, Error> { 107 ) -> Result<Self, Error> {
112 let r = T::regs(); 108 let r = T::regs();
113 109
114 if let Some(pin) = &ch0 { 110 let channels = [
115 pin.set_low(); 111 (&ch0, config.ch0_drive, config.ch0_idle_level),
116 pin.conf().write(|w| { 112 (&ch1, config.ch1_drive, config.ch1_idle_level),
117 w.set_dir(gpiovals::Dir::OUTPUT); 113 (&ch2, config.ch2_drive, config.ch2_idle_level),
118 w.set_input(gpiovals::Input::DISCONNECT); 114 (&ch3, config.ch3_drive, config.ch3_idle_level),
119 convert_drive(w, config.ch0_drive); 115 ];
120 }); 116 for (i, (pin, drive, idle_level)) in channels.into_iter().enumerate() {
121 } 117 if let Some(pin) = pin {
122 if let Some(pin) = &ch1 { 118 match idle_level {
123 pin.set_low(); 119 Level::Low => pin.set_low(),
124 pin.conf().write(|w| { 120 Level::High => pin.set_high(),
125 w.set_dir(gpiovals::Dir::OUTPUT); 121 }
126 w.set_input(gpiovals::Input::DISCONNECT); 122 pin.conf().write(|w| {
127 convert_drive(w, config.ch1_drive); 123 w.set_dir(gpiovals::Dir::OUTPUT);
128 }); 124 w.set_input(gpiovals::Input::DISCONNECT);
129 } 125 convert_drive(w, drive);
130 if let Some(pin) = &ch2 { 126 });
131 pin.set_low(); 127 }
132 pin.conf().write(|w| { 128 r.psel().out(i).write_value(pin.psel_bits());
133 w.set_dir(gpiovals::Dir::OUTPUT);
134 w.set_input(gpiovals::Input::DISCONNECT);
135 convert_drive(w, config.ch2_drive);
136 });
137 }
138 if let Some(pin) = &ch3 {
139 pin.set_low();
140 pin.conf().write(|w| {
141 w.set_dir(gpiovals::Dir::OUTPUT);
142 w.set_input(gpiovals::Input::DISCONNECT);
143 convert_drive(w, config.ch3_drive);
144 });
145 } 129 }
146 130
147 r.psel().out(0).write_value(ch0.psel_bits());
148 r.psel().out(1).write_value(ch1.psel_bits());
149 r.psel().out(2).write_value(ch2.psel_bits());
150 r.psel().out(3).write_value(ch3.psel_bits());
151
152 // Disable all interrupts 131 // Disable all interrupts
153 r.intenclr().write(|w| w.0 = 0xFFFF_FFFF); 132 r.intenclr().write(|w| w.0 = 0xFFFF_FFFF);
154 r.shorts().write(|_| ()); 133 r.shorts().write(|_| ());
@@ -173,13 +152,7 @@ impl<'d> SequencePwm<'d> {
173 .write(|w| w.set_prescaler(vals::Prescaler::from_bits(config.prescaler as u8))); 152 .write(|w| w.set_prescaler(vals::Prescaler::from_bits(config.prescaler as u8)));
174 r.countertop().write(|w| w.set_countertop(config.max_duty)); 153 r.countertop().write(|w| w.set_countertop(config.max_duty));
175 154
176 Ok(Self { 155 Ok(Self { r, ch0, ch1, ch2, ch3 })
177 r: T::regs(),
178 ch0,
179 ch1,
180 ch2,
181 ch3,
182 })
183 } 156 }
184 157
185 /// Returns reference to `Stopped` event endpoint for PPI. 158 /// Returns reference to `Stopped` event endpoint for PPI.
@@ -289,6 +262,8 @@ impl<'a> Drop for SequencePwm<'a> {
289} 262}
290 263
291/// Configuration for the PWM as a whole. 264/// Configuration for the PWM as a whole.
265#[derive(Debug, Clone)]
266#[cfg_attr(feature = "defmt", derive(defmt::Format))]
292#[non_exhaustive] 267#[non_exhaustive]
293pub struct Config { 268pub struct Config {
294 /// Selects up mode or up-and-down mode for the counter 269 /// Selects up mode or up-and-down mode for the counter
@@ -307,11 +282,19 @@ pub struct Config {
307 pub ch2_drive: OutputDrive, 282 pub ch2_drive: OutputDrive,
308 /// Drive strength for the channel 3 line. 283 /// Drive strength for the channel 3 line.
309 pub ch3_drive: OutputDrive, 284 pub ch3_drive: OutputDrive,
285 /// Output level for the channel 0 line when PWM if disabled.
286 pub ch0_idle_level: Level,
287 /// Output level for the channel 1 line when PWM if disabled.
288 pub ch1_idle_level: Level,
289 /// Output level for the channel 2 line when PWM if disabled.
290 pub ch2_idle_level: Level,
291 /// Output level for the channel 3 line when PWM if disabled.
292 pub ch3_idle_level: Level,
310} 293}
311 294
312impl Default for Config { 295impl Default for Config {
313 fn default() -> Config { 296 fn default() -> Self {
314 Config { 297 Self {
315 counter_mode: CounterMode::Up, 298 counter_mode: CounterMode::Up,
316 max_duty: 1000, 299 max_duty: 1000,
317 prescaler: Prescaler::Div16, 300 prescaler: Prescaler::Div16,
@@ -320,13 +303,65 @@ impl Default for Config {
320 ch1_drive: OutputDrive::Standard, 303 ch1_drive: OutputDrive::Standard,
321 ch2_drive: OutputDrive::Standard, 304 ch2_drive: OutputDrive::Standard,
322 ch3_drive: OutputDrive::Standard, 305 ch3_drive: OutputDrive::Standard,
306 ch0_idle_level: Level::Low,
307 ch1_idle_level: Level::Low,
308 ch2_idle_level: Level::Low,
309 ch3_idle_level: Level::Low,
310 }
311 }
312}
313
314/// Configuration for the simple PWM driver.
315#[derive(Debug, Clone)]
316#[cfg_attr(feature = "defmt", derive(defmt::Format))]
317#[non_exhaustive]
318pub struct SimpleConfig {
319 /// Selects up mode or up-and-down mode for the counter
320 pub counter_mode: CounterMode,
321 /// Top value to be compared against buffer values
322 pub max_duty: u16,
323 /// Configuration for PWM_CLK
324 pub prescaler: Prescaler,
325 /// Drive strength for the channel 0 line.
326 pub ch0_drive: OutputDrive,
327 /// Drive strength for the channel 1 line.
328 pub ch1_drive: OutputDrive,
329 /// Drive strength for the channel 2 line.
330 pub ch2_drive: OutputDrive,
331 /// Drive strength for the channel 3 line.
332 pub ch3_drive: OutputDrive,
333 /// Output level for the channel 0 line when PWM if disabled.
334 pub ch0_idle_level: Level,
335 /// Output level for the channel 1 line when PWM if disabled.
336 pub ch1_idle_level: Level,
337 /// Output level for the channel 2 line when PWM if disabled.
338 pub ch2_idle_level: Level,
339 /// Output level for the channel 3 line when PWM if disabled.
340 pub ch3_idle_level: Level,
341}
342
343impl Default for SimpleConfig {
344 fn default() -> Self {
345 Self {
346 counter_mode: CounterMode::Up,
347 max_duty: 1000,
348 prescaler: Prescaler::Div16,
349 ch0_drive: OutputDrive::Standard,
350 ch1_drive: OutputDrive::Standard,
351 ch2_drive: OutputDrive::Standard,
352 ch3_drive: OutputDrive::Standard,
353 ch0_idle_level: Level::Low,
354 ch1_idle_level: Level::Low,
355 ch2_idle_level: Level::Low,
356 ch3_idle_level: Level::Low,
323 } 357 }
324 } 358 }
325} 359}
326 360
327/// Configuration per sequence 361/// Configuration per sequence
328#[non_exhaustive] 362#[non_exhaustive]
329#[derive(Clone)] 363#[derive(Debug, Clone)]
364#[cfg_attr(feature = "defmt", derive(defmt::Format))]
330pub struct SequenceConfig { 365pub struct SequenceConfig {
331 /// Number of PWM periods to delay between each sequence sample 366 /// Number of PWM periods to delay between each sequence sample
332 pub refresh: u32, 367 pub refresh: u32,
@@ -345,6 +380,8 @@ impl Default for SequenceConfig {
345 380
346/// A composition of a sequence buffer and its configuration. 381/// A composition of a sequence buffer and its configuration.
347#[non_exhaustive] 382#[non_exhaustive]
383#[derive(Debug, Clone)]
384#[cfg_attr(feature = "defmt", derive(defmt::Format))]
348pub struct Sequence<'s> { 385pub struct Sequence<'s> {
349 /// The words comprising the sequence. Must not exceed 32767 words. 386 /// The words comprising the sequence. Must not exceed 32767 words.
350 pub words: &'s [u16], 387 pub words: &'s [u16],
@@ -496,6 +533,7 @@ impl<'d, 's> Drop for Sequencer<'d, 's> {
496 533
497/// How many times to run a single sequence 534/// How many times to run a single sequence
498#[derive(Debug, Eq, PartialEq, Clone, Copy)] 535#[derive(Debug, Eq, PartialEq, Clone, Copy)]
536#[cfg_attr(feature = "defmt", derive(defmt::Format))]
499pub enum SingleSequenceMode { 537pub enum SingleSequenceMode {
500 /// Run a single sequence n Times total. 538 /// Run a single sequence n Times total.
501 Times(u16), 539 Times(u16),
@@ -505,6 +543,7 @@ pub enum SingleSequenceMode {
505 543
506/// Which sequence to start a loop with 544/// Which sequence to start a loop with
507#[derive(Debug, Eq, PartialEq, Clone, Copy)] 545#[derive(Debug, Eq, PartialEq, Clone, Copy)]
546#[cfg_attr(feature = "defmt", derive(defmt::Format))]
508pub enum StartSequence { 547pub enum StartSequence {
509 /// Start with Sequence 0 548 /// Start with Sequence 0
510 Zero, 549 Zero,
@@ -514,6 +553,7 @@ pub enum StartSequence {
514 553
515/// How many loops to run two sequences 554/// How many loops to run two sequences
516#[derive(Debug, Eq, PartialEq, Clone, Copy)] 555#[derive(Debug, Eq, PartialEq, Clone, Copy)]
556#[cfg_attr(feature = "defmt", derive(defmt::Format))]
517pub enum SequenceMode { 557pub enum SequenceMode {
518 /// Run two sequences n loops i.e. (n * (seq0 + seq1.unwrap_or(seq0))) 558 /// Run two sequences n loops i.e. (n * (seq0 + seq1.unwrap_or(seq0)))
519 Loop(u16), 559 Loop(u16),
@@ -523,6 +563,7 @@ pub enum SequenceMode {
523 563
524/// PWM Base clock is system clock (16MHz) divided by prescaler 564/// PWM Base clock is system clock (16MHz) divided by prescaler
525#[derive(Debug, Eq, PartialEq, Clone, Copy)] 565#[derive(Debug, Eq, PartialEq, Clone, Copy)]
566#[cfg_attr(feature = "defmt", derive(defmt::Format))]
526pub enum Prescaler { 567pub enum Prescaler {
527 /// Divide by 1 568 /// Divide by 1
528 Div1, 569 Div1,
@@ -544,6 +585,7 @@ pub enum Prescaler {
544 585
545/// How the sequence values are distributed across the channels 586/// How the sequence values are distributed across the channels
546#[derive(Debug, Eq, PartialEq, Clone, Copy)] 587#[derive(Debug, Eq, PartialEq, Clone, Copy)]
588#[cfg_attr(feature = "defmt", derive(defmt::Format))]
547pub enum SequenceLoad { 589pub enum SequenceLoad {
548 /// Provided sequence will be used across all channels 590 /// Provided sequence will be used across all channels
549 Common, 591 Common,
@@ -560,6 +602,7 @@ pub enum SequenceLoad {
560 602
561/// Selects up mode or up-and-down mode for the counter 603/// Selects up mode or up-and-down mode for the counter
562#[derive(Debug, Eq, PartialEq, Clone, Copy)] 604#[derive(Debug, Eq, PartialEq, Clone, Copy)]
605#[cfg_attr(feature = "defmt", derive(defmt::Format))]
563pub enum CounterMode { 606pub enum CounterMode {
564 /// Up counter (edge-aligned PWM duty cycle) 607 /// Up counter (edge-aligned PWM duty cycle)
565 Up, 608 Up,
@@ -567,48 +610,128 @@ pub enum CounterMode {
567 UpAndDown, 610 UpAndDown,
568} 611}
569 612
613/// Duty value and polarity for a single channel.
614///
615/// If the channel has inverted polarity, the output is set high as long as the counter is below the duty value.
616#[repr(transparent)]
617#[derive(Eq, PartialEq, Clone, Copy)]
618pub struct DutyCycle {
619 /// The raw duty cycle valuea.
620 ///
621 /// This has the duty cycle in the lower 15 bits.
622 /// The highest bit indicates that the duty cycle has inverted polarity.
623 raw: u16,
624}
625
626impl DutyCycle {
627 /// Make a new duty value with normal polarity.
628 ///
629 /// The value is truncated to 15 bits.
630 ///
631 /// The output is set high if the counter is at or above the duty value.
632 pub const fn normal(value: u16) -> Self {
633 let raw = value & 0x7FFF;
634 Self { raw }
635 }
636
637 /// Make a new duty cycle with inverted polarity.
638 ///
639 /// The value is truncated to 15 bits.
640 ///
641 /// The output is set high if the counter is below the duty value.
642 pub const fn inverted(value: u16) -> Self {
643 let raw = value | 0x8000;
644 Self { raw }
645 }
646
647 /// Adjust the polarity of the duty cycle (returns a new object).
648 #[must_use = "this function return a new object, it does not modify self"]
649 pub const fn with_inverted(self, inverted_polarity: bool) -> Self {
650 if inverted_polarity {
651 Self::inverted(self.value())
652 } else {
653 Self::normal(self.value())
654 }
655 }
656
657 /// Gets the 15-bit value of the duty cycle.
658 pub const fn value(&self) -> u16 {
659 self.raw & 0x7FFF
660 }
661
662 /// Checks if the duty period has inverted polarity.
663 ///
664 /// If the channel has inverted polarity, the output is set high as long as the counter is below the duty value.
665 pub const fn is_inverted(&self) -> bool {
666 self.raw & 0x8000 != 0
667 }
668}
669
670impl core::fmt::Debug for DutyCycle {
671 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
672 f.debug_struct("DutyCycle")
673 .field("value", &self.value())
674 .field("inverted", &self.is_inverted())
675 .finish()
676 }
677}
678
679#[cfg(feature = "defmt")]
680impl defmt::Format for DutyCycle {
681 fn format(&self, f: defmt::Formatter) {
682 defmt::write!(
683 f,
684 "DutyCycle {{ value: {=u16}, inverted: {=bool} }}",
685 self.value(),
686 self.is_inverted(),
687 );
688 }
689}
690
570impl<'d> SimplePwm<'d> { 691impl<'d> SimplePwm<'d> {
571 /// Create a new 1-channel PWM 692 /// Create a new 1-channel PWM
572 #[allow(unused_unsafe)] 693 pub fn new_1ch<T: Instance>(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, config: &SimpleConfig) -> Self {
573 pub fn new_1ch<T: Instance>(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>) -> Self { 694 Self::new_inner(pwm, Some(ch0.into()), None, None, None, config)
574 unsafe { Self::new_inner(pwm, Some(ch0.into()), None, None, None) }
575 } 695 }
576 696
577 /// Create a new 2-channel PWM 697 /// Create a new 2-channel PWM
578 #[allow(unused_unsafe)] 698 pub fn new_2ch<T: Instance>(
579 pub fn new_2ch<T: Instance>(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, ch1: Peri<'d, impl GpioPin>) -> Self { 699 pwm: Peri<'d, T>,
580 Self::new_inner(pwm, Some(ch0.into()), Some(ch1.into()), None, None) 700 ch0: Peri<'d, impl GpioPin>,
701 ch1: Peri<'d, impl GpioPin>,
702 config: &SimpleConfig,
703 ) -> Self {
704 Self::new_inner(pwm, Some(ch0.into()), Some(ch1.into()), None, None, config)
581 } 705 }
582 706
583 /// Create a new 3-channel PWM 707 /// Create a new 3-channel PWM
584 #[allow(unused_unsafe)]
585 pub fn new_3ch<T: Instance>( 708 pub fn new_3ch<T: Instance>(
586 pwm: Peri<'d, T>, 709 pwm: Peri<'d, T>,
587 ch0: Peri<'d, impl GpioPin>, 710 ch0: Peri<'d, impl GpioPin>,
588 ch1: Peri<'d, impl GpioPin>, 711 ch1: Peri<'d, impl GpioPin>,
589 ch2: Peri<'d, impl GpioPin>, 712 ch2: Peri<'d, impl GpioPin>,
713 config: &SimpleConfig,
590 ) -> Self { 714 ) -> Self {
591 unsafe { Self::new_inner(pwm, Some(ch0.into()), Some(ch1.into()), Some(ch2.into()), None) } 715 Self::new_inner(pwm, Some(ch0.into()), Some(ch1.into()), Some(ch2.into()), None, config)
592 } 716 }
593 717
594 /// Create a new 4-channel PWM 718 /// Create a new 4-channel PWM
595 #[allow(unused_unsafe)]
596 pub fn new_4ch<T: Instance>( 719 pub fn new_4ch<T: Instance>(
597 pwm: Peri<'d, T>, 720 pwm: Peri<'d, T>,
598 ch0: Peri<'d, impl GpioPin>, 721 ch0: Peri<'d, impl GpioPin>,
599 ch1: Peri<'d, impl GpioPin>, 722 ch1: Peri<'d, impl GpioPin>,
600 ch2: Peri<'d, impl GpioPin>, 723 ch2: Peri<'d, impl GpioPin>,
601 ch3: Peri<'d, impl GpioPin>, 724 ch3: Peri<'d, impl GpioPin>,
725 config: &SimpleConfig,
602 ) -> Self { 726 ) -> Self {
603 unsafe { 727 Self::new_inner(
604 Self::new_inner( 728 pwm,
605 pwm, 729 Some(ch0.into()),
606 Some(ch0.into()), 730 Some(ch1.into()),
607 Some(ch1.into()), 731 Some(ch2.into()),
608 Some(ch2.into()), 732 Some(ch3.into()),
609 Some(ch3.into()), 733 config,
610 ) 734 )
611 }
612 } 735 }
613 736
614 fn new_inner<T: Instance>( 737 fn new_inner<T: Instance>(
@@ -617,29 +740,38 @@ impl<'d> SimplePwm<'d> {
617 ch1: Option<Peri<'d, AnyPin>>, 740 ch1: Option<Peri<'d, AnyPin>>,
618 ch2: Option<Peri<'d, AnyPin>>, 741 ch2: Option<Peri<'d, AnyPin>>,
619 ch3: Option<Peri<'d, AnyPin>>, 742 ch3: Option<Peri<'d, AnyPin>>,
743 config: &SimpleConfig,
620 ) -> Self { 744 ) -> Self {
621 let r = T::regs(); 745 let r = T::regs();
622 746
623 for (i, ch) in [&ch0, &ch1, &ch2, &ch3].into_iter().enumerate() { 747 let channels = [
624 if let Some(pin) = ch { 748 (&ch0, config.ch0_drive, config.ch0_idle_level),
625 pin.set_low(); 749 (&ch1, config.ch1_drive, config.ch1_idle_level),
626 750 (&ch2, config.ch2_drive, config.ch2_idle_level),
751 (&ch3, config.ch3_drive, config.ch3_idle_level),
752 ];
753 for (i, (pin, drive, idle_level)) in channels.into_iter().enumerate() {
754 if let Some(pin) = pin {
755 match idle_level {
756 Level::Low => pin.set_low(),
757 Level::High => pin.set_high(),
758 }
627 pin.conf().write(|w| { 759 pin.conf().write(|w| {
628 w.set_dir(gpiovals::Dir::OUTPUT); 760 w.set_dir(gpiovals::Dir::OUTPUT);
629 w.set_input(gpiovals::Input::DISCONNECT); 761 w.set_input(gpiovals::Input::DISCONNECT);
630 w.set_drive(gpiovals::Drive::S0S1); 762 convert_drive(w, drive);
631 }); 763 });
632 } 764 }
633 r.psel().out(i).write_value(ch.psel_bits()); 765 r.psel().out(i).write_value(pin.psel_bits());
634 } 766 }
635 767
636 let pwm = Self { 768 let pwm = Self {
637 r: T::regs(), 769 r,
638 ch0, 770 ch0,
639 ch1, 771 ch1,
640 ch2, 772 ch2,
641 ch3, 773 ch3,
642 duty: [0; 4], 774 duty: [const { DutyCycle::normal(0) }; 4],
643 }; 775 };
644 776
645 // Disable all interrupts 777 // Disable all interrupts
@@ -658,9 +790,13 @@ impl<'d> SimplePwm<'d> {
658 w.set_load(vals::Load::INDIVIDUAL); 790 w.set_load(vals::Load::INDIVIDUAL);
659 w.set_mode(vals::Mode::REFRESH_COUNT); 791 w.set_mode(vals::Mode::REFRESH_COUNT);
660 }); 792 });
661 r.mode().write(|w| w.set_updown(vals::Updown::UP)); 793 r.mode().write(|w| match config.counter_mode {
662 r.prescaler().write(|w| w.set_prescaler(vals::Prescaler::DIV_16)); 794 CounterMode::UpAndDown => w.set_updown(vals::Updown::UP_AND_DOWN),
663 r.countertop().write(|w| w.set_countertop(1000)); 795 CounterMode::Up => w.set_updown(vals::Updown::UP),
796 });
797 r.prescaler()
798 .write(|w| w.set_prescaler(vals::Prescaler::from_bits(config.prescaler as u8)));
799 r.countertop().write(|w| w.set_countertop(config.max_duty));
664 r.loop_().write(|w| w.set_cnt(vals::LoopCnt::DISABLED)); 800 r.loop_().write(|w| w.set_cnt(vals::LoopCnt::DISABLED));
665 801
666 pwm 802 pwm
@@ -684,15 +820,31 @@ impl<'d> SimplePwm<'d> {
684 self.r.enable().write(|w| w.set_enable(false)); 820 self.r.enable().write(|w| w.set_enable(false));
685 } 821 }
686 822
687 /// Returns the current duty of the channel 823 /// Returns the current duty of the channel.
688 pub fn duty(&self, channel: usize) -> u16 { 824 pub fn duty(&self, channel: usize) -> DutyCycle {
689 self.duty[channel] 825 self.duty[channel]
690 } 826 }
691 827
692 /// Sets duty cycle (15 bit) for a PWM channel. 828 /// Sets duty cycle (15 bit) and polarity for a PWM channel.
693 pub fn set_duty(&mut self, channel: usize, duty: u16) { 829 pub fn set_duty(&mut self, channel: usize, duty: DutyCycle) {
694 self.duty[channel] = duty & 0x7FFF; 830 self.duty[channel] = duty;
831 self.sync_duty_cyles_to_peripheral();
832 }
833
834 /// Sets the duty cycle (15 bit) and polarity for all PWM channels.
835 ///
836 /// You can safely set the duty cycle of disabled PWM channels.
837 ///
838 /// When using this function, a single DMA transfer sets all the duty cycles.
839 /// If you call [`Self::set_duty()`] multiple times,
840 /// each duty cycle will be set by a separate DMA transfer.
841 pub fn set_all_duties(&mut self, duty: [DutyCycle; 4]) {
842 self.duty = duty;
843 self.sync_duty_cyles_to_peripheral();
844 }
695 845
846 /// Transfer the duty cycles from `self` to the peripheral.
847 fn sync_duty_cyles_to_peripheral(&self) {
696 // reload ptr in case self was moved 848 // reload ptr in case self was moved
697 self.r.seq(0).ptr().write_value((self.duty).as_ptr() as u32); 849 self.r.seq(0).ptr().write_value((self.duty).as_ptr() as u32);
698 850
diff --git a/examples/nrf52840/src/bin/i2s_monitor.rs b/examples/nrf52840/src/bin/i2s_monitor.rs
index 66b429b09..a54659101 100644
--- a/examples/nrf52840/src/bin/i2s_monitor.rs
+++ b/examples/nrf52840/src/bin/i2s_monitor.rs
@@ -4,7 +4,7 @@
4use defmt::{debug, error, info}; 4use defmt::{debug, error, info};
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, I2S, MasterClock, Sample as _, SampleWidth}; 6use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, I2S, MasterClock, Sample as _, SampleWidth};
7use embassy_nrf::pwm::{Prescaler, SimplePwm}; 7use embassy_nrf::pwm::{DutyCycle, Prescaler, SimplePwm};
8use embassy_nrf::{bind_interrupts, peripherals}; 8use embassy_nrf::{bind_interrupts, peripherals};
9use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
10 10
@@ -34,7 +34,7 @@ async fn main(_spawner: Spawner) {
34 I2S::new_master(p.I2S, Irqs, p.P0_25, p.P0_26, p.P0_27, master_clock, config).input(p.P0_29, buffers); 34 I2S::new_master(p.I2S, Irqs, p.P0_25, p.P0_26, p.P0_27, master_clock, config).input(p.P0_29, buffers);
35 35
36 // Configure the PWM to use the pins corresponding to the RGB leds 36 // Configure the PWM to use the pins corresponding to the RGB leds
37 let mut pwm = SimplePwm::new_3ch(p.PWM0, p.P0_23, p.P0_22, p.P0_24); 37 let mut pwm = SimplePwm::new_3ch(p.PWM0, p.P0_23, p.P0_22, p.P0_24, &Default::default());
38 pwm.set_prescaler(Prescaler::Div1); 38 pwm.set_prescaler(Prescaler::Div1);
39 pwm.set_max_duty(255); 39 pwm.set_max_duty(255);
40 40
@@ -47,9 +47,8 @@ async fn main(_spawner: Spawner) {
47 let rgb = rgb_from_rms(rms); 47 let rgb = rgb_from_rms(rms);
48 48
49 debug!("RMS: {}, RGB: {:?}", rms, rgb); 49 debug!("RMS: {}, RGB: {:?}", rms, rgb);
50 for i in 0..3 { 50 let duties = rgb.map(|byte| DutyCycle::normal(u16::from(byte)));
51 pwm.set_duty(i, rgb[i].into()); 51 pwm.set_all_duties([duties[0], duties[1], duties[2], DutyCycle::normal(0)]);
52 }
53 52
54 if let Err(err) = input_stream.receive().await { 53 if let Err(err) = input_stream.receive().await {
55 error!("{}", err); 54 error!("{}", err);
diff --git a/examples/nrf52840/src/bin/pwm.rs b/examples/nrf52840/src/bin/pwm.rs
index a5bb1347a..02f9b4191 100644
--- a/examples/nrf52840/src/bin/pwm.rs
+++ b/examples/nrf52840/src/bin/pwm.rs
@@ -3,7 +3,7 @@
3 3
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_nrf::pwm::{Prescaler, SimplePwm}; 6use embassy_nrf::pwm::{DutyCycle, Prescaler, SimplePwm};
7use embassy_time::Timer; 7use embassy_time::Timer;
8use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
9 9
@@ -71,7 +71,7 @@ static DUTY: [u16; 1024] = [
71#[embassy_executor::main] 71#[embassy_executor::main]
72async fn main(_spawner: Spawner) { 72async fn main(_spawner: Spawner) {
73 let p = embassy_nrf::init(Default::default()); 73 let p = embassy_nrf::init(Default::default());
74 let mut pwm = SimplePwm::new_4ch(p.PWM0, p.P0_13, p.P0_14, p.P0_16, p.P0_15); 74 let mut pwm = SimplePwm::new_4ch(p.PWM0, p.P0_13, p.P0_14, p.P0_16, p.P0_15, &Default::default());
75 pwm.set_prescaler(Prescaler::Div1); 75 pwm.set_prescaler(Prescaler::Div1);
76 pwm.set_max_duty(32767); 76 pwm.set_max_duty(32767);
77 info!("pwm initialized!"); 77 info!("pwm initialized!");
@@ -79,10 +79,12 @@ async fn main(_spawner: Spawner) {
79 let mut i = 0; 79 let mut i = 0;
80 loop { 80 loop {
81 i += 1; 81 i += 1;
82 pwm.set_duty(0, DUTY[i % 1024]); 82 pwm.set_all_duties([
83 pwm.set_duty(1, DUTY[(i + 256) % 1024]); 83 DutyCycle::normal(DUTY[i % 1024]),
84 pwm.set_duty(2, DUTY[(i + 512) % 1024]); 84 DutyCycle::normal(DUTY[(i + 256) % 1024]),
85 pwm.set_duty(3, DUTY[(i + 768) % 1024]); 85 DutyCycle::normal(DUTY[(i + 512) % 1024]),
86 DutyCycle::normal(DUTY[(i + 768) % 1024]),
87 ]);
86 Timer::after_millis(3).await; 88 Timer::after_millis(3).await;
87 } 89 }
88} 90}
diff --git a/examples/nrf52840/src/bin/pwm_servo.rs b/examples/nrf52840/src/bin/pwm_servo.rs
index d772d2f5d..93cb984e6 100644
--- a/examples/nrf52840/src/bin/pwm_servo.rs
+++ b/examples/nrf52840/src/bin/pwm_servo.rs
@@ -3,14 +3,14 @@
3 3
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_nrf::pwm::{Prescaler, SimplePwm}; 6use embassy_nrf::pwm::{DutyCycle, Prescaler, SimplePwm};
7use embassy_time::Timer; 7use embassy_time::Timer;
8use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
9 9
10#[embassy_executor::main] 10#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 11async fn main(_spawner: Spawner) {
12 let p = embassy_nrf::init(Default::default()); 12 let p = embassy_nrf::init(Default::default());
13 let mut pwm = SimplePwm::new_1ch(p.PWM0, p.P0_05); 13 let mut pwm = SimplePwm::new_1ch(p.PWM0, p.P0_05, &Default::default());
14 // sg90 microervo requires 50hz or 20ms period 14 // sg90 microervo requires 50hz or 20ms period
15 // set_period can only set down to 125khz so we cant use it directly 15 // set_period can only set down to 125khz so we cant use it directly
16 // Div128 is 125khz or 0.000008s or 0.008ms, 20/0.008 is 2500 is top 16 // Div128 is 125khz or 0.000008s or 0.008ms, 20/0.008 is 2500 is top
@@ -24,23 +24,23 @@ async fn main(_spawner: Spawner) {
24 loop { 24 loop {
25 info!("45 deg"); 25 info!("45 deg");
26 // poor mans inverting, subtract our value from max_duty 26 // poor mans inverting, subtract our value from max_duty
27 pwm.set_duty(0, 2500 - 156); 27 pwm.set_duty(0, DutyCycle::normal(2500 - 156));
28 Timer::after_millis(5000).await; 28 Timer::after_millis(5000).await;
29 29
30 info!("90 deg"); 30 info!("90 deg");
31 pwm.set_duty(0, 2500 - 187); 31 pwm.set_duty(0, DutyCycle::normal(2500 - 187));
32 Timer::after_millis(5000).await; 32 Timer::after_millis(5000).await;
33 33
34 info!("135 deg"); 34 info!("135 deg");
35 pwm.set_duty(0, 2500 - 218); 35 pwm.set_duty(0, DutyCycle::normal(2500 - 218));
36 Timer::after_millis(5000).await; 36 Timer::after_millis(5000).await;
37 37
38 info!("180 deg"); 38 info!("180 deg");
39 pwm.set_duty(0, 2500 - 250); 39 pwm.set_duty(0, DutyCycle::normal(2500 - 250));
40 Timer::after_millis(5000).await; 40 Timer::after_millis(5000).await;
41 41
42 info!("0 deg"); 42 info!("0 deg");
43 pwm.set_duty(0, 2500 - 125); 43 pwm.set_duty(0, DutyCycle::normal(2500 - 125));
44 Timer::after_millis(5000).await; 44 Timer::after_millis(5000).await;
45 } 45 }
46} 46}