aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGordon Tyler <[email protected]>2025-10-28 09:08:53 -0400
committerGitHub <[email protected]>2025-10-28 09:08:53 -0400
commitf1053b4dabb9035831787cbc5f2626ee04d39337 (patch)
treef8a92345bb2cf3c88a1fa6570d2cd0f4bf4ee795
parent3923f881c63a483c1593cc345079581ffcce5ff1 (diff)
parent41ff72bbce343c1e3c5efa2939e08a73b9122552 (diff)
Merge branch 'embassy-rs:main' into gpio-nointr
-rw-r--r--embassy-nrf/CHANGELOG.md4
-rw-r--r--embassy-nrf/src/pwm.rs324
-rw-r--r--embassy-rp/CHANGELOG.md2
-rw-r--r--embassy-rp/src/block.rs4
-rw-r--r--embassy-rp/src/clocks.rs2
-rw-r--r--embassy-rp/src/i2c_slave.rs4
-rw-r--r--embassy-rp/src/multicore.rs2
-rw-r--r--embassy-rp/src/pio/mod.rs2
-rw-r--r--embassy-rp/src/pio_programs/i2s.rs18
-rw-r--r--embassy-rp/src/pio_programs/pwm.rs2
-rw-r--r--embassy-rp/src/pio_programs/spi.rs4
-rw-r--r--embassy-rp/src/pio_programs/uart.rs2
-rw-r--r--embassy-rp/src/rom_data/rp2040.rs2
-rw-r--r--embassy-rp/src/rtc/mod.rs2
-rw-r--r--embassy-rp/src/spi.rs2
-rw-r--r--embassy-rp/src/uart/mod.rs2
-rw-r--r--embassy-stm32/CHANGELOG.md1
-rw-r--r--embassy-stm32/src/timer/low_level.rs69
-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
21 files changed, 346 insertions, 139 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/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md
index a99d04aa4..57ec13658 100644
--- a/embassy-rp/CHANGELOG.md
+++ b/embassy-rp/CHANGELOG.md
@@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- Fix typo in interrupt comment 11- Fix several minor typos in documentation
12- Add PIO SPI 12- Add PIO SPI
13- Add PIO I2S input 13- Add PIO I2S input
14- Add PIO onewire parasite power strong pullup 14- Add PIO onewire parasite power strong pullup
diff --git a/embassy-rp/src/block.rs b/embassy-rp/src/block.rs
index a3e1ad925..745883b83 100644
--- a/embassy-rp/src/block.rs
+++ b/embassy-rp/src/block.rs
@@ -240,7 +240,7 @@ impl UnpartitionedSpace {
240 } 240 }
241 } 241 }
242 242
243 /// Create a new unpartition space from run-time values. 243 /// Create a new unpartitioned space from run-time values.
244 /// 244 ///
245 /// Get these from the ROM function `get_partition_table_info` with an argument of `PT_INFO`. 245 /// Get these from the ROM function `get_partition_table_info` with an argument of `PT_INFO`.
246 pub const fn from_raw(permissions_and_location: u32, permissions_and_flags: u32) -> Self { 246 pub const fn from_raw(permissions_and_location: u32, permissions_and_flags: u32) -> Self {
@@ -714,7 +714,7 @@ impl PartitionTableBlock {
714 new_table 714 new_table
715 } 715 }
716 716
717 /// Add a a SHA256 hash of the Block 717 /// Add a SHA256 hash of the Block
718 /// 718 ///
719 /// Adds a `HASH_DEF` covering all the previous items in the Block, and a 719 /// Adds a `HASH_DEF` covering all the previous items in the Block, and a
720 /// `HASH_VALUE` with a SHA-256 hash of them. 720 /// `HASH_VALUE` with a SHA-256 hash of them.
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs
index 56892d7a2..8bfb5129a 100644
--- a/embassy-rp/src/clocks.rs
+++ b/embassy-rp/src/clocks.rs
@@ -267,7 +267,7 @@ impl CoreVoltage {
267 } 267 }
268} 268}
269 269
270/// CLock configuration. 270/// Clock configuration.
271#[non_exhaustive] 271#[non_exhaustive]
272pub struct ClockConfig { 272pub struct ClockConfig {
273 /// Ring oscillator configuration. 273 /// Ring oscillator configuration.
diff --git a/embassy-rp/src/i2c_slave.rs b/embassy-rp/src/i2c_slave.rs
index 770087bc8..0853709df 100644
--- a/embassy-rp/src/i2c_slave.rs
+++ b/embassy-rp/src/i2c_slave.rs
@@ -52,7 +52,7 @@ pub enum ReadStatus {
52 Done, 52 Done,
53 /// Transaction Incomplete, controller trying to read more bytes than were provided 53 /// Transaction Incomplete, controller trying to read more bytes than were provided
54 NeedMoreBytes, 54 NeedMoreBytes,
55 /// Transaction Complere, but controller stopped reading bytes before we ran out 55 /// Transaction Complete, but controller stopped reading bytes before we ran out
56 LeftoverBytes(u16), 56 LeftoverBytes(u16),
57} 57}
58 58
@@ -240,7 +240,7 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
240 240
241 if p.ic_rxflr().read().rxflr() > 0 || me.pending_byte.is_some() { 241 if p.ic_rxflr().read().rxflr() > 0 || me.pending_byte.is_some() {
242 me.drain_fifo(buffer, &mut len); 242 me.drain_fifo(buffer, &mut len);
243 // we're recieving data, set rx fifo watermark to 12 bytes (3/4 full) to reduce interrupt noise 243 // we're receiving data, set rx fifo watermark to 12 bytes (3/4 full) to reduce interrupt noise
244 p.ic_rx_tl().write(|w| w.set_rx_tl(11)); 244 p.ic_rx_tl().write(|w| w.set_rx_tl(11));
245 } 245 }
246 246
diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs
index 3b120e349..572d8db91 100644
--- a/embassy-rp/src/multicore.rs
+++ b/embassy-rp/src/multicore.rs
@@ -58,7 +58,7 @@ const PAUSE_TOKEN: u32 = 0xDEADBEEF;
58const RESUME_TOKEN: u32 = !0xDEADBEEF; 58const RESUME_TOKEN: u32 = !0xDEADBEEF;
59static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false); 59static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false);
60 60
61/// Represents a partiticular CPU core (SIO_CPUID) 61/// Represents a particular CPU core (SIO_CPUID)
62#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] 62#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
63#[cfg_attr(feature = "defmt", derive(defmt::Format))] 63#[cfg_attr(feature = "defmt", derive(defmt::Format))]
64#[repr(u8)] 64#[repr(u8)]
diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs
index 38ee1f97c..92b2c603e 100644
--- a/embassy-rp/src/pio/mod.rs
+++ b/embassy-rp/src/pio/mod.rs
@@ -62,7 +62,7 @@ pub enum FifoJoin {
62 #[cfg(feature = "_rp235x")] 62 #[cfg(feature = "_rp235x")]
63 RxAsControl, 63 RxAsControl,
64 /// FJOIN_RX_PUT | FJOIN_RX_GET: RX can be used as a scratch register, 64 /// FJOIN_RX_PUT | FJOIN_RX_GET: RX can be used as a scratch register,
65 /// not accesible from the CPU 65 /// not accessible from the CPU
66 #[cfg(feature = "_rp235x")] 66 #[cfg(feature = "_rp235x")]
67 PioScratch, 67 PioScratch,
68} 68}
diff --git a/embassy-rp/src/pio_programs/i2s.rs b/embassy-rp/src/pio_programs/i2s.rs
index 7e5f68ad6..5c49beecb 100644
--- a/embassy-rp/src/pio_programs/i2s.rs
+++ b/embassy-rp/src/pio_programs/i2s.rs
@@ -1,4 +1,4 @@
1//! Pio backed I2s output and output drivers 1//! Pio backed I2S output and output drivers
2 2
3use fixed::traits::ToFixed; 3use fixed::traits::ToFixed;
4 4
@@ -9,7 +9,7 @@ use crate::pio::{
9 Common, Config, Direction, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, 9 Common, Config, Direction, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine,
10}; 10};
11 11
12/// This struct represents an i2s receiver & controller driver program 12/// This struct represents an I2S receiver & controller driver program
13pub struct PioI2sInProgram<'d, PIO: Instance> { 13pub struct PioI2sInProgram<'d, PIO: Instance> {
14 prg: LoadedProgram<'d, PIO>, 14 prg: LoadedProgram<'d, PIO>,
15} 15}
@@ -35,7 +35,7 @@ impl<'d, PIO: Instance> PioI2sInProgram<'d, PIO> {
35 } 35 }
36} 36}
37 37
38/// Pio backed I2s input driver 38/// Pio backed I2S input driver
39pub struct PioI2sIn<'d, P: Instance, const S: usize> { 39pub struct PioI2sIn<'d, P: Instance, const S: usize> {
40 dma: Peri<'d, AnyChannel>, 40 dma: Peri<'d, AnyChannel>,
41 sm: StateMachine<'d, P, S>, 41 sm: StateMachine<'d, P, S>,
@@ -50,7 +50,7 @@ impl<'d, P: Instance, const S: usize> PioI2sIn<'d, P, S> {
50 // Whether or not to use the MCU's internal pull-down resistor, as the 50 // Whether or not to use the MCU's internal pull-down resistor, as the
51 // Pico 2 is known to have problems with the inbuilt pulldowns, many 51 // Pico 2 is known to have problems with the inbuilt pulldowns, many
52 // opt to just use an external pull down resistor to meet requirements of common 52 // opt to just use an external pull down resistor to meet requirements of common
53 // i2s microphones such as the INMP441 53 // I2S microphones such as the INMP441
54 data_pulldown: bool, 54 data_pulldown: bool,
55 data_pin: Peri<'d, impl PioPin>, 55 data_pin: Peri<'d, impl PioPin>,
56 bit_clock_pin: Peri<'d, impl PioPin>, 56 bit_clock_pin: Peri<'d, impl PioPin>,
@@ -90,13 +90,13 @@ impl<'d, P: Instance, const S: usize> PioI2sIn<'d, P, S> {
90 Self { dma: dma.into(), sm } 90 Self { dma: dma.into(), sm }
91 } 91 }
92 92
93 /// Return an in-prograss dma transfer future. Awaiting it will guarentee a complete transfer. 93 /// Return an in-progress dma transfer future. Awaiting it will guarantee a complete transfer.
94 pub fn read<'b>(&'b mut self, buff: &'b mut [u32]) -> Transfer<'b, AnyChannel> { 94 pub fn read<'b>(&'b mut self, buff: &'b mut [u32]) -> Transfer<'b, AnyChannel> {
95 self.sm.rx().dma_pull(self.dma.reborrow(), buff, false) 95 self.sm.rx().dma_pull(self.dma.reborrow(), buff, false)
96 } 96 }
97} 97}
98 98
99/// This struct represents an i2s output driver program 99/// This struct represents an I2S output driver program
100/// 100///
101/// The sample bit-depth is set through scratch register `Y`. 101/// The sample bit-depth is set through scratch register `Y`.
102/// `Y` has to be set to sample bit-depth - 2. 102/// `Y` has to be set to sample bit-depth - 2.
@@ -128,14 +128,14 @@ impl<'d, PIO: Instance> PioI2sOutProgram<'d, PIO> {
128 } 128 }
129} 129}
130 130
131/// Pio backed I2s output driver 131/// Pio backed I2S output driver
132pub struct PioI2sOut<'d, P: Instance, const S: usize> { 132pub struct PioI2sOut<'d, P: Instance, const S: usize> {
133 dma: Peri<'d, AnyChannel>, 133 dma: Peri<'d, AnyChannel>,
134 sm: StateMachine<'d, P, S>, 134 sm: StateMachine<'d, P, S>,
135} 135}
136 136
137impl<'d, P: Instance, const S: usize> PioI2sOut<'d, P, S> { 137impl<'d, P: Instance, const S: usize> PioI2sOut<'d, P, S> {
138 /// Configure a state machine to output I2s 138 /// Configure a state machine to output I2S
139 pub fn new( 139 pub fn new(
140 common: &mut Common<'d, P>, 140 common: &mut Common<'d, P>,
141 mut sm: StateMachine<'d, P, S>, 141 mut sm: StateMachine<'d, P, S>,
@@ -179,7 +179,7 @@ impl<'d, P: Instance, const S: usize> PioI2sOut<'d, P, S> {
179 Self { dma: dma.into(), sm } 179 Self { dma: dma.into(), sm }
180 } 180 }
181 181
182 /// Return an in-prograss dma transfer future. Awaiting it will guarentee a complete transfer. 182 /// Return an in-progress dma transfer future. Awaiting it will guarantee a complete transfer.
183 pub fn write<'b>(&'b mut self, buff: &'b [u32]) -> Transfer<'b, AnyChannel> { 183 pub fn write<'b>(&'b mut self, buff: &'b [u32]) -> Transfer<'b, AnyChannel> {
184 self.sm.tx().dma_push(self.dma.reborrow(), buff, false) 184 self.sm.tx().dma_push(self.dma.reborrow(), buff, false)
185 } 185 }
diff --git a/embassy-rp/src/pio_programs/pwm.rs b/embassy-rp/src/pio_programs/pwm.rs
index ba06bb3c1..e4ad4a6f0 100644
--- a/embassy-rp/src/pio_programs/pwm.rs
+++ b/embassy-rp/src/pio_programs/pwm.rs
@@ -67,7 +67,7 @@ impl<'d, T: Instance, const SM: usize> PioPwm<'d, T, SM> {
67 Self { sm, pin } 67 Self { sm, pin }
68 } 68 }
69 69
70 /// Enable's the PIO program, continuing the wave generation from the PIO program. 70 /// Enables the PIO program, continuing the wave generation from the PIO program.
71 pub fn start(&mut self) { 71 pub fn start(&mut self) {
72 self.sm.set_enable(true); 72 self.sm.set_enable(true);
73 } 73 }
diff --git a/embassy-rp/src/pio_programs/spi.rs b/embassy-rp/src/pio_programs/spi.rs
index b10fc6628..765ffaa06 100644
--- a/embassy-rp/src/pio_programs/spi.rs
+++ b/embassy-rp/src/pio_programs/spi.rs
@@ -1,4 +1,4 @@
1//! PIO backed SPi drivers 1//! PIO backed SPI drivers
2 2
3use core::marker::PhantomData; 3use core::marker::PhantomData;
4 4
@@ -83,7 +83,7 @@ pub enum Error {
83 // No errors for now 83 // No errors for now
84} 84}
85 85
86/// PIO based Spi driver. 86/// PIO based SPI driver.
87/// Unlike other PIO programs, the PIO SPI driver owns and holds a reference to 87/// Unlike other PIO programs, the PIO SPI driver owns and holds a reference to
88/// the PIO memory it uses. This is so that it can be reconfigured at runtime if 88/// the PIO memory it uses. This is so that it can be reconfigured at runtime if
89/// desired. 89/// desired.
diff --git a/embassy-rp/src/pio_programs/uart.rs b/embassy-rp/src/pio_programs/uart.rs
index 444efb5db..d59596dd1 100644
--- a/embassy-rp/src/pio_programs/uart.rs
+++ b/embassy-rp/src/pio_programs/uart.rs
@@ -130,7 +130,7 @@ impl<'d, PIO: Instance> PioUartRxProgram<'d, PIO> {
130 } 130 }
131} 131}
132 132
133/// PIO backed Uart reciever 133/// PIO backed Uart receiver
134pub struct PioUartRx<'d, PIO: Instance, const SM: usize> { 134pub struct PioUartRx<'d, PIO: Instance, const SM: usize> {
135 sm_rx: StateMachine<'d, PIO, SM>, 135 sm_rx: StateMachine<'d, PIO, SM>,
136} 136}
diff --git a/embassy-rp/src/rom_data/rp2040.rs b/embassy-rp/src/rom_data/rp2040.rs
index 5a74eddd6..27a8d8981 100644
--- a/embassy-rp/src/rom_data/rp2040.rs
+++ b/embassy-rp/src/rom_data/rp2040.rs
@@ -30,7 +30,7 @@ const DATA_TABLE: *const u16 = 0x0000_0016 as _;
30/// Address of the version number of the ROM. 30/// Address of the version number of the ROM.
31const VERSION_NUMBER: *const u8 = 0x0000_0013 as _; 31const VERSION_NUMBER: *const u8 = 0x0000_0013 as _;
32 32
33/// Retrive rom content from a table using a code. 33/// Retrieve rom content from a table using a code.
34fn rom_table_lookup<T>(table: *const u16, tag: RomFnTableCode) -> T { 34fn rom_table_lookup<T>(table: *const u16, tag: RomFnTableCode) -> T {
35 unsafe { 35 unsafe {
36 let rom_table_lookup_ptr: *const u32 = rom_hword_as_ptr(ROM_TABLE_LOOKUP_PTR); 36 let rom_table_lookup_ptr: *const u32 = rom_hword_as_ptr(ROM_TABLE_LOOKUP_PTR);
diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs
index 68fb3b765..054572903 100644
--- a/embassy-rp/src/rtc/mod.rs
+++ b/embassy-rp/src/rtc/mod.rs
@@ -47,7 +47,7 @@ impl<'d, T: Instance> Rtc<'d, T> {
47 Self { inner } 47 Self { inner }
48 } 48 }
49 49
50 /// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisable by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check. 50 /// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisible by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check.
51 /// 51 ///
52 /// Leap year checking is enabled by default. 52 /// Leap year checking is enabled by default.
53 pub fn set_leap_year_check(&mut self, leap_year_check_enabled: bool) { 53 pub fn set_leap_year_check(&mut self, leap_year_check_enabled: bool) {
diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs
index 559b3b909..d9410e78d 100644
--- a/embassy-rp/src/spi.rs
+++ b/embassy-rp/src/spi.rs
@@ -157,7 +157,7 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> {
157 157
158 /// Private function to apply SPI configuration (phase, polarity, frequency) settings. 158 /// Private function to apply SPI configuration (phase, polarity, frequency) settings.
159 /// 159 ///
160 /// Driver should be disabled before making changes and reenabled after the modifications 160 /// Driver should be disabled before making changes and re-enabled after the modifications
161 /// are applied. 161 /// are applied.
162 fn apply_config(inner: &Peri<'d, T>, config: &Config) { 162 fn apply_config(inner: &Peri<'d, T>, config: &Config) {
163 let p = inner.regs(); 163 let p = inner.regs();
diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs
index 43187df2d..8be87a5d2 100644
--- a/embassy-rp/src/uart/mod.rs
+++ b/embassy-rp/src/uart/mod.rs
@@ -315,7 +315,7 @@ impl<'d, M: Mode> UartRx<'d, M> {
315 } 315 }
316 316
317 /// Returns Ok(len) if no errors occurred. Returns Err((len, err)) if an error was 317 /// Returns Ok(len) if no errors occurred. Returns Err((len, err)) if an error was
318 /// encountered. in both cases, `len` is the number of *good* bytes copied into 318 /// encountered. In both cases, `len` is the number of *good* bytes copied into
319 /// `buffer`. 319 /// `buffer`.
320 fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result<usize, (usize, Error)> { 320 fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result<usize, (usize, Error)> {
321 let r = self.info.regs; 321 let r = self.info.regs;
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index 9848daf49..000d215b7 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
35- feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668)) 35- feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668))
36- feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options 36- feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options
37- change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer 37- change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer
38- change: timer: added output compare values
38 39
39## 0.4.0 - 2025-08-26 40## 0.4.0 - 2025-08-26
40 41
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs
index ac039bb0d..7c02e7e62 100644
--- a/embassy-stm32/src/timer/low_level.rs
+++ b/embassy-stm32/src/timer/low_level.rs
@@ -143,20 +143,69 @@ pub enum OutputCompareMode {
143 /// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as 143 /// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as
144 /// TIMx_CNT>TIMx_CCRx else inactive. 144 /// TIMx_CNT>TIMx_CCRx else inactive.
145 PwmMode2, 145 PwmMode2,
146 // TODO: there's more modes here depending on the chip family. 146
147 #[cfg(timer_v2)]
148 /// In up-counting mode, the channel is active until a trigger
149 /// event is detected (on tim_trgi signal). Then, a comparison is performed as in PWM
150 /// mode 1 and the channels becomes active again at the next update. In down-counting
151 /// mode, the channel is inactive until a trigger event is detected (on tim_trgi signal).
152 /// Then, a comparison is performed as in PWM mode 1 and the channels becomes
153 /// inactive again at the next update.
154 OnePulseMode1,
155
156 #[cfg(timer_v2)]
157 /// In up-counting mode, the channel is inactive until a
158 /// trigger event is detected (on tim_trgi signal). Then, a comparison is performed as in
159 /// PWM mode 2 and the channels becomes inactive again at the next update. In down
160 /// counting mode, the channel is active until a trigger event is detected (on tim_trgi
161 /// signal). Then, a comparison is performed as in PWM mode 1 and the channels
162 /// becomes active again at the next update.
163 OnePulseMode2,
164
165 #[cfg(timer_v2)]
166 /// Combined PWM mode 1 - tim_oc1ref has the same behavior as in PWM mode 1.
167 /// tim_oc1refc is the logical OR between tim_oc1ref and tim_oc2ref.
168 CombinedPwmMode1,
169
170 #[cfg(timer_v2)]
171 /// Combined PWM mode 2 - tim_oc1ref has the same behavior as in PWM mode 2.
172 /// tim_oc1refc is the logical AND between tim_oc1ref and tim_oc2ref.
173 CombinedPwmMode2,
174
175 #[cfg(timer_v2)]
176 /// tim_oc1ref has the same behavior as in PWM mode 1. tim_oc1refc outputs tim_oc1ref
177 /// when the counter is counting up, tim_oc2ref when it is counting down.
178 AsymmetricPwmMode1,
179
180 #[cfg(timer_v2)]
181 /// tim_oc1ref has the same behavior as in PWM mode 2. tim_oc1refc outputs tim_oc1ref
182 /// when the counter is counting up, tim_oc2ref when it is counting down.
183 AsymmetricPwmMode2,
147} 184}
148 185
149impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm { 186impl From<OutputCompareMode> for crate::pac::timer::vals::Ocm {
150 fn from(mode: OutputCompareMode) -> Self { 187 fn from(mode: OutputCompareMode) -> Self {
151 match mode { 188 match mode {
152 OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, 189 OutputCompareMode::Frozen => crate::pac::timer::vals::Ocm::FROZEN,
153 OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVE_ON_MATCH, 190 OutputCompareMode::ActiveOnMatch => crate::pac::timer::vals::Ocm::ACTIVE_ON_MATCH,
154 OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVE_ON_MATCH, 191 OutputCompareMode::InactiveOnMatch => crate::pac::timer::vals::Ocm::INACTIVE_ON_MATCH,
155 OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, 192 OutputCompareMode::Toggle => crate::pac::timer::vals::Ocm::TOGGLE,
156 OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCE_INACTIVE, 193 OutputCompareMode::ForceInactive => crate::pac::timer::vals::Ocm::FORCE_INACTIVE,
157 OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCE_ACTIVE, 194 OutputCompareMode::ForceActive => crate::pac::timer::vals::Ocm::FORCE_ACTIVE,
158 OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWM_MODE1, 195 OutputCompareMode::PwmMode1 => crate::pac::timer::vals::Ocm::PWM_MODE1,
159 OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWM_MODE2, 196 OutputCompareMode::PwmMode2 => crate::pac::timer::vals::Ocm::PWM_MODE2,
197 #[cfg(timer_v2)]
198 OutputCompareMode::OnePulseMode1 => crate::pac::timer::vals::Ocm::RETRIGERRABLE_OPM_MODE_1,
199 #[cfg(timer_v2)]
200 OutputCompareMode::OnePulseMode2 => crate::pac::timer::vals::Ocm::RETRIGERRABLE_OPM_MODE_2,
201 #[cfg(timer_v2)]
202 OutputCompareMode::CombinedPwmMode1 => crate::pac::timer::vals::Ocm::COMBINED_PWM_MODE_1,
203 #[cfg(timer_v2)]
204 OutputCompareMode::CombinedPwmMode2 => crate::pac::timer::vals::Ocm::COMBINED_PWM_MODE_2,
205 #[cfg(timer_v2)]
206 OutputCompareMode::AsymmetricPwmMode1 => crate::pac::timer::vals::Ocm::ASYMMETRIC_PWM_MODE_1,
207 #[cfg(timer_v2)]
208 OutputCompareMode::AsymmetricPwmMode2 => crate::pac::timer::vals::Ocm::ASYMMETRIC_PWM_MODE_2,
160 } 209 }
161 } 210 }
162} 211}
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}