1use core::marker::PhantomData;
13
14use super::PeripheralGuard;
15use crate::{
16 gpio::interconnect::{OutputConnection, PeripheralOutput},
17 mcpwm::{timer::Timer, PwmPeripheral},
18 pac,
19 peripheral::{Peripheral, PeripheralRef},
20};
21
22#[derive(Copy, Clone)]
24#[allow(clippy::upper_case_acronyms, reason = "peripheral is unstable")]
25pub enum PWMStream {
26 PWMA,
28 PWMB,
30}
31
32#[derive(Copy, Clone)]
35pub struct DeadTimeCfg {
36 cfg_reg: u32,
37}
38
39#[allow(clippy::unusual_byte_groupings)]
40impl DeadTimeCfg {
41 const S0: u32 = 0b01_0000_0000_0000_0000;
47 const S1: u32 = 0b00_1000_0000_0000_0000;
49 const S2: u32 = 0b00_0010_0000_0000_0000;
51 const S3: u32 = 0b00_0100_0000_0000_0000;
53 const S4: u32 = 0b00_0000_1000_0000_0000;
55 const S5: u32 = 0b00_0001_0000_0000_0000;
57 const S6: u32 = 0b00_0000_0010_0000_0000;
59 const S7: u32 = 0b00_0000_0100_0000_0000;
61 const _S8: u32 = 0b00_0000_0001_0000_0000;
63 const CLK_SEL: u32 = 0b10_0000_0000_0000_0000;
65
66 pub const fn new_bypass() -> DeadTimeCfg {
75 DeadTimeCfg {
76 cfg_reg: Self::S0 | Self::S1,
77 }
78 }
79
80 pub const fn new_ahc() -> DeadTimeCfg {
87 DeadTimeCfg { cfg_reg: Self::S3 }
88 }
89 #[must_use]
92 const fn set_flag(mut self, flag: u32, val: bool) -> Self {
93 if val {
94 self.cfg_reg |= flag;
95 } else {
96 self.cfg_reg &= !flag;
97 }
98 self
99 }
100
101 #[must_use]
104 pub const fn invert_output(self, fed: bool, red: bool) -> Self {
105 self.set_flag(Self::S3, fed).set_flag(Self::S2, red)
106 }
107
108 #[must_use]
113 pub const fn set_output_swap(self, stream: PWMStream, swap: bool) -> Self {
114 self.set_flag(
115 match stream {
116 PWMStream::PWMA => Self::S6,
117 PWMStream::PWMB => Self::S7,
118 },
119 swap,
120 )
121 }
122
123 #[must_use]
126 pub const fn set_bypass(self, stream: PWMStream, enable: bool) -> Self {
127 self.set_flag(
128 match stream {
129 PWMStream::PWMA => Self::S1,
130 PWMStream::PWMB => Self::S0,
131 },
132 enable,
133 )
134 }
135
136 #[must_use]
138 pub const fn select_clock(self, pwm_clock: bool) -> Self {
139 self.set_flag(Self::CLK_SEL, pwm_clock)
140 }
141
142 #[must_use]
144 pub const fn select_input(self, fed: PWMStream, red: PWMStream) -> Self {
145 self.set_flag(
146 Self::S5,
147 match fed {
148 PWMStream::PWMA => false,
149 PWMStream::PWMB => true,
150 },
151 )
152 .set_flag(
153 Self::S4,
154 match red {
155 PWMStream::PWMA => false,
156 PWMStream::PWMB => true,
157 },
158 )
159 }
160}
161
162pub struct Operator<'d, const OP: u8, PWM> {
173 phantom: PhantomData<&'d PWM>,
174 _guard: PeripheralGuard,
175}
176
177impl<'d, const OP: u8, PWM: PwmPeripheral> Operator<'d, OP, PWM> {
178 pub(super) fn new() -> Self {
179 let guard = PeripheralGuard::new(PWM::peripheral());
180
181 Operator {
188 phantom: PhantomData,
189 _guard: guard,
190 }
191 }
192
193 pub fn set_timer<const TIM: u8>(&mut self, timer: &Timer<TIM, PWM>) {
198 let _ = timer;
199 let block = unsafe { &*PWM::block() };
202 block.operator_timersel().modify(|_, w| match OP {
203 0 => unsafe { w.operator0_timersel().bits(TIM) },
204 1 => unsafe { w.operator1_timersel().bits(TIM) },
205 2 => unsafe { w.operator2_timersel().bits(TIM) },
206 _ => {
207 unreachable!()
208 }
209 });
210 }
211
212 pub fn with_pin_a(
214 self,
215 pin: impl Peripheral<P = impl PeripheralOutput> + 'd,
216 config: PwmPinConfig<true>,
217 ) -> PwmPin<'d, PWM, OP, true> {
218 PwmPin::new(pin, config)
219 }
220
221 pub fn with_pin_b(
223 self,
224 pin: impl Peripheral<P = impl PeripheralOutput> + 'd,
225 config: PwmPinConfig<false>,
226 ) -> PwmPin<'d, PWM, OP, false> {
227 PwmPin::new(pin, config)
228 }
229
230 pub fn with_pins(
232 self,
233 pin_a: impl Peripheral<P = impl PeripheralOutput> + 'd,
234 config_a: PwmPinConfig<true>,
235 pin_b: impl Peripheral<P = impl PeripheralOutput> + 'd,
236 config_b: PwmPinConfig<false>,
237 ) -> (PwmPin<'d, PWM, OP, true>, PwmPin<'d, PWM, OP, false>) {
238 (PwmPin::new(pin_a, config_a), PwmPin::new(pin_b, config_b))
239 }
240
241 pub fn with_linked_pins(
246 self,
247 pin_a: impl Peripheral<P = impl PeripheralOutput> + 'd,
248 config_a: PwmPinConfig<true>,
249 pin_b: impl Peripheral<P = impl PeripheralOutput> + 'd,
250 config_b: PwmPinConfig<false>,
251 config_dt: DeadTimeCfg,
252 ) -> LinkedPins<'d, PWM, OP> {
253 LinkedPins::new(pin_a, config_a, pin_b, config_b, config_dt)
254 }
255}
256
257pub struct PwmPinConfig<const IS_A: bool> {
260 actions: PwmActions<IS_A>,
261 update_method: PwmUpdateMethod,
262}
263
264impl<const IS_A: bool> PwmPinConfig<IS_A> {
265 pub const UP_ACTIVE_HIGH: Self =
268 Self::new(PwmActions::UP_ACTIVE_HIGH, PwmUpdateMethod::SYNC_ON_ZERO);
269 pub const UP_DOWN_ACTIVE_HIGH: Self = Self::new(
272 PwmActions::UP_DOWN_ACTIVE_HIGH,
273 PwmUpdateMethod::SYNC_ON_ZERO,
274 );
275 pub const EMPTY: Self = Self::new(PwmActions::empty(), PwmUpdateMethod::empty());
278
279 pub const fn new(actions: PwmActions<IS_A>, update_method: PwmUpdateMethod) -> Self {
281 PwmPinConfig {
282 actions,
283 update_method,
284 }
285 }
286}
287
288pub struct PwmPin<'d, PWM, const OP: u8, const IS_A: bool> {
290 pin: PeripheralRef<'d, OutputConnection>,
291 phantom: PhantomData<PWM>,
292 _guard: PeripheralGuard,
293}
294
295impl<'d, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> PwmPin<'d, PWM, OP, IS_A> {
296 fn new(
297 pin: impl Peripheral<P = impl PeripheralOutput> + 'd,
298 config: PwmPinConfig<IS_A>,
299 ) -> Self {
300 crate::into_mapped_ref!(pin);
301
302 let guard = PeripheralGuard::new(PWM::peripheral());
303
304 let mut pin = PwmPin {
305 pin,
306 phantom: PhantomData,
307 _guard: guard,
308 };
309 pin.set_actions(config.actions);
310 pin.set_update_method(config.update_method);
311
312 PWM::output_signal::<OP, IS_A>().connect_to(&mut pin.pin);
313 pin.pin.enable_output(true);
314
315 pin
316 }
317
318 pub fn set_actions(&mut self, value: PwmActions<IS_A>) {
320 let ch = unsafe { Self::ch() };
323 let bits = value.0;
324
325 ch.gen((!IS_A) as usize).write(|w| unsafe { w.bits(bits) });
328 }
329
330 pub fn set_update_method(&mut self, update_method: PwmUpdateMethod) {
332 let ch = unsafe { Self::ch() };
335 let bits = update_method.0;
336
337 #[cfg(esp32s3)]
338 let cfg = ch.cmpr_cfg();
339 #[cfg(any(esp32, esp32c6, esp32h2))]
340 let cfg = ch.gen_stmp_cfg();
341
342 cfg.modify(|_, w| unsafe {
343 if IS_A {
344 w.a_upmethod().bits(bits)
345 } else {
346 w.b_upmethod().bits(bits)
347 }
348 });
349 }
350
351 pub fn set_timestamp(&mut self, value: u16) {
355 let ch = unsafe { Self::ch() };
358
359 #[cfg(esp32s3)]
360 if IS_A {
361 ch.cmpr_value0().write(|w| unsafe { w.a().bits(value) });
362 } else {
363 ch.cmpr_value1().write(|w| unsafe { w.b().bits(value) });
364 }
365
366 #[cfg(any(esp32, esp32c6, esp32h2))]
367 if IS_A {
368 ch.gen_tstmp_a().write(|w| unsafe { w.a().bits(value) });
369 } else {
370 ch.gen_tstmp_b().write(|w| unsafe { w.b().bits(value) });
371 }
372 }
373
374 pub fn timestamp(&self) -> u16 {
378 let ch = unsafe { Self::ch() };
381
382 #[cfg(esp32s3)]
383 if IS_A {
384 ch.cmpr_value0().read().a().bits()
385 } else {
386 ch.cmpr_value1().read().b().bits()
387 }
388
389 #[cfg(any(esp32, esp32c6, esp32h2))]
390 if IS_A {
391 ch.gen_tstmp_a().read().a().bits()
392 } else {
393 ch.gen_tstmp_b().read().b().bits()
394 }
395 }
396
397 pub fn period(&self) -> u16 {
399 let block = unsafe { &*PWM::block() };
402
403 let tim_select = block.operator_timersel().read();
404 let tim = match OP {
405 0 => tim_select.operator0_timersel().bits(),
406 1 => tim_select.operator1_timersel().bits(),
407 2 => tim_select.operator2_timersel().bits(),
408 _ => {
409 unreachable!()
410 }
411 };
412
413 block.timer(tim as usize).cfg0().read().period().bits()
417 }
418
419 unsafe fn ch() -> &'static pac::mcpwm0::CH {
420 let block = unsafe { &*PWM::block() };
421 block.ch(OP as usize)
422 }
423}
424
425impl<PWM: PwmPeripheral, const OP: u8, const IS_A: bool> embedded_hal::pwm::ErrorType
427 for PwmPin<'_, PWM, OP, IS_A>
428{
429 type Error = core::convert::Infallible;
430}
431
432impl<PWM: PwmPeripheral, const OP: u8, const IS_A: bool> embedded_hal::pwm::SetDutyCycle
434 for PwmPin<'_, PWM, OP, IS_A>
435{
436 fn max_duty_cycle(&self) -> u16 {
438 self.period()
439 }
440
441 fn set_duty_cycle(&mut self, duty: u16) -> Result<(), core::convert::Infallible> {
443 self.set_timestamp(duty);
444 Ok(())
445 }
446}
447
448#[doc = crate::before_snippet!()]
457#[cfg_attr(
465 esp32h2,
466 doc = "let clock_cfg = PeripheralClockConfig::with_frequency(Rate::from_mhz(40))?;"
467)]
468#[cfg_attr(
469 not(esp32h2),
470 doc = "let clock_cfg = PeripheralClockConfig::with_frequency(Rate::from_mhz(32))?;"
471)]
472pub struct LinkedPins<'d, PWM, const OP: u8> {
494 pin_a: PwmPin<'d, PWM, OP, true>,
495 pin_b: PwmPin<'d, PWM, OP, false>,
496}
497
498impl<'d, PWM: PwmPeripheral, const OP: u8> LinkedPins<'d, PWM, OP> {
499 fn new(
500 pin_a: impl Peripheral<P = impl PeripheralOutput> + 'd,
501 config_a: PwmPinConfig<true>,
502 pin_b: impl Peripheral<P = impl PeripheralOutput> + 'd,
503 config_b: PwmPinConfig<false>,
504 config_dt: DeadTimeCfg,
505 ) -> Self {
506 #[cfg(esp32s3)]
508 let dt_cfg = unsafe { Self::ch() }.db_cfg();
509 #[cfg(not(esp32s3))]
510 let dt_cfg = unsafe { Self::ch() }.dt_cfg();
511 dt_cfg.write(|w| unsafe { w.bits(config_dt.cfg_reg) });
512
513 let pin_a = PwmPin::new(pin_a, config_a);
514 let pin_b = PwmPin::new(pin_b, config_b);
515
516 LinkedPins { pin_a, pin_b }
517 }
518
519 pub fn set_actions_a(&mut self, value: PwmActions<true>) {
521 self.pin_a.set_actions(value)
522 }
523 pub fn set_actions_b(&mut self, value: PwmActions<false>) {
525 self.pin_b.set_actions(value)
526 }
527
528 pub fn set_update_method_a(&mut self, update_method: PwmUpdateMethod) {
530 self.pin_a.set_update_method(update_method)
531 }
532 pub fn set_update_method_b(&mut self, update_method: PwmUpdateMethod) {
534 self.pin_b.set_update_method(update_method)
535 }
536
537 pub fn set_timestamp_a(&mut self, value: u16) {
541 self.pin_a.set_timestamp(value)
542 }
543 pub fn set_timestamp_b(&mut self, value: u16) {
547 self.pin_a.set_timestamp(value)
548 }
549
550 pub fn set_deadtime_cfg(&mut self, config: DeadTimeCfg) {
552 #[cfg(esp32s3)]
553 let dt_cfg = unsafe { Self::ch() }.db_cfg();
554 #[cfg(not(esp32s3))]
555 let dt_cfg = unsafe { Self::ch() }.dt_cfg();
556 dt_cfg.write(|w| unsafe { w.bits(config.cfg_reg) });
557 }
558
559 pub fn set_rising_edge_deadtime(&mut self, dead_time: u16) {
561 #[cfg(esp32s3)]
562 let dt_red = unsafe { Self::ch() }.db_red_cfg();
563 #[cfg(not(esp32s3))]
564 let dt_red = unsafe { Self::ch() }.dt_red_cfg();
565 dt_red.write(|w| unsafe { w.red().bits(dead_time) });
566 }
567 pub fn set_falling_edge_deadtime(&mut self, dead_time: u16) {
569 #[cfg(esp32s3)]
570 let dt_fed = unsafe { Self::ch() }.db_fed_cfg();
571 #[cfg(not(esp32s3))]
572 let dt_fed = unsafe { Self::ch() }.dt_fed_cfg();
573 dt_fed.write(|w| unsafe { w.fed().bits(dead_time) });
574 }
575
576 unsafe fn ch() -> &'static pac::mcpwm0::CH {
577 let block = unsafe { &*PWM::block() };
578 block.ch(OP as usize)
579 }
580}
581
582#[non_exhaustive]
584#[repr(u32)]
585pub enum UpdateAction {
586 SetLow = 1,
588 SetHigh = 2,
590 Toggle = 3,
593}
594
595pub struct PwmActions<const IS_A: bool>(u32);
602
603impl<const IS_A: bool> PwmActions<IS_A> {
604 pub const UP_ACTIVE_HIGH: Self = Self::empty()
609 .on_up_counting_timer_equals_zero(UpdateAction::SetHigh)
610 .on_up_counting_timer_equals_timestamp(UpdateAction::SetLow);
611
612 pub const UP_DOWN_ACTIVE_HIGH: Self = Self::empty()
617 .on_down_counting_timer_equals_timestamp(UpdateAction::SetHigh)
618 .on_up_counting_timer_equals_timestamp(UpdateAction::SetLow);
619
620 pub const fn empty() -> Self {
622 PwmActions(0)
623 }
624
625 pub const fn on_up_counting_timer_equals_zero(self, action: UpdateAction) -> Self {
627 self.with_value_at_offset(action as u32, 0)
628 }
629
630 pub const fn on_up_counting_timer_equals_period(self, action: UpdateAction) -> Self {
632 self.with_value_at_offset(action as u32, 2)
633 }
634
635 pub const fn on_up_counting_timer_equals_timestamp(self, action: UpdateAction) -> Self {
637 match IS_A {
638 true => self.with_value_at_offset(action as u32, 4),
639 false => self.with_value_at_offset(action as u32, 6),
640 }
641 }
642
643 pub const fn on_up_counting_timer_equals_ch_timestamp<const CH_A: bool>(
646 self,
647 action: UpdateAction,
648 ) -> Self {
649 match CH_A {
650 true => self.with_value_at_offset(action as u32, 4),
651 false => self.with_value_at_offset(action as u32, 6),
652 }
653 }
654
655 pub const fn on_down_counting_timer_equals_zero(self, action: UpdateAction) -> Self {
657 self.with_value_at_offset(action as u32, 12)
658 }
659
660 pub const fn on_down_counting_timer_equals_period(self, action: UpdateAction) -> Self {
662 self.with_value_at_offset(action as u32, 14)
663 }
664
665 pub const fn on_down_counting_timer_equals_timestamp(self, action: UpdateAction) -> Self {
667 match IS_A {
668 true => self.with_value_at_offset(action as u32, 16),
669 false => self.with_value_at_offset(action as u32, 18),
670 }
671 }
672
673 pub const fn on_down_counting_timer_equals_ch_timestamp<const CH_A: bool>(
676 self,
677 action: UpdateAction,
678 ) -> Self {
679 match CH_A {
680 true => self.with_value_at_offset(action as u32, 16),
681 false => self.with_value_at_offset(action as u32, 18),
682 }
683 }
684
685 const fn with_value_at_offset(self, value: u32, offset: u32) -> Self {
686 let mask = !(0b11 << offset);
687 let value = (self.0 & mask) | (value << offset);
688 PwmActions(value)
689 }
690}
691
692pub struct PwmUpdateMethod(u8);
696
697impl PwmUpdateMethod {
698 pub const SYNC_IMMEDIATLY: Self = Self::empty();
700 pub const SYNC_ON_ZERO: Self = Self::empty().sync_on_timer_equals_zero();
702 pub const SYNC_ON_PERIOD: Self = Self::empty().sync_on_timer_equals_period();
704
705 pub const fn empty() -> Self {
708 PwmUpdateMethod(0)
709 }
710
711 pub const fn sync_on_timer_equals_zero(mut self) -> Self {
713 self.0 |= 0b0001;
714 self
715 }
716
717 pub const fn sync_on_timer_equals_period(mut self) -> Self {
719 self.0 |= 0b0010;
720 self
721 }
722}