1use core::marker::PhantomData;
13
14use super::PeripheralGuard;
15use crate::{
16 gpio::interconnect::{OutputSignal, PeripheralOutput},
17 mcpwm::{PwmClockGuard, PwmPeripheral, timer::Timer},
18 pac,
19};
20
21#[derive(Copy, Clone)]
23#[allow(clippy::upper_case_acronyms, reason = "peripheral is unstable")]
24pub enum PWMStream {
25 PWMA,
27 PWMB,
29}
30
31#[derive(Copy, Clone)]
34pub struct DeadTimeCfg {
35 cfg_reg: u32,
36}
37
38#[allow(clippy::unusual_byte_groupings)]
39impl DeadTimeCfg {
40 const S0: u32 = 0b01_0000_0000_0000_0000;
46 const S1: u32 = 0b00_1000_0000_0000_0000;
48 const S2: u32 = 0b00_0010_0000_0000_0000;
50 const S3: u32 = 0b00_0100_0000_0000_0000;
52 const S4: u32 = 0b00_0000_1000_0000_0000;
54 const S5: u32 = 0b00_0001_0000_0000_0000;
56 const S6: u32 = 0b00_0000_0010_0000_0000;
58 const S7: u32 = 0b00_0000_0100_0000_0000;
60 const _S8: u32 = 0b00_0000_0001_0000_0000;
62 const CLK_SEL: u32 = 0b10_0000_0000_0000_0000;
64
65 pub const fn new_bypass() -> DeadTimeCfg {
74 DeadTimeCfg {
75 cfg_reg: Self::S0 | Self::S1,
76 }
77 }
78
79 pub const fn new_ahc() -> DeadTimeCfg {
86 DeadTimeCfg { cfg_reg: Self::S3 }
87 }
88 #[must_use]
91 const fn set_flag(mut self, flag: u32, val: bool) -> Self {
92 if val {
93 self.cfg_reg |= flag;
94 } else {
95 self.cfg_reg &= !flag;
96 }
97 self
98 }
99
100 #[must_use]
103 pub const fn invert_output(self, fed: bool, red: bool) -> Self {
104 self.set_flag(Self::S3, fed).set_flag(Self::S2, red)
105 }
106
107 #[must_use]
112 pub const fn set_output_swap(self, stream: PWMStream, swap: bool) -> Self {
113 self.set_flag(
114 match stream {
115 PWMStream::PWMA => Self::S6,
116 PWMStream::PWMB => Self::S7,
117 },
118 swap,
119 )
120 }
121
122 #[must_use]
125 pub const fn set_bypass(self, stream: PWMStream, enable: bool) -> Self {
126 self.set_flag(
127 match stream {
128 PWMStream::PWMA => Self::S1,
129 PWMStream::PWMB => Self::S0,
130 },
131 enable,
132 )
133 }
134
135 #[must_use]
137 pub const fn select_clock(self, pwm_clock: bool) -> Self {
138 self.set_flag(Self::CLK_SEL, pwm_clock)
139 }
140
141 #[must_use]
143 pub const fn select_input(self, fed: PWMStream, red: PWMStream) -> Self {
144 self.set_flag(
145 Self::S5,
146 match fed {
147 PWMStream::PWMA => false,
148 PWMStream::PWMB => true,
149 },
150 )
151 .set_flag(
152 Self::S4,
153 match red {
154 PWMStream::PWMA => false,
155 PWMStream::PWMB => true,
156 },
157 )
158 }
159}
160
161pub struct Operator<'d, const OP: u8, PWM> {
171 phantom: PhantomData<&'d PWM>,
172 _guard: PeripheralGuard,
173 _pwm_clock_guard: PwmClockGuard,
174}
175
176impl<'d, const OP: u8, PWM: PwmPeripheral> Operator<'d, OP, PWM> {
177 pub(super) fn new(guard: PeripheralGuard) -> Self {
178 Operator {
185 phantom: PhantomData,
186 _guard: guard,
187 _pwm_clock_guard: PwmClockGuard::new::<PWM>(),
188 }
189 }
190
191 pub fn set_timer<const TIM: u8>(&mut self, timer: &Timer<TIM, PWM>) {
196 let _ = timer;
197 let block = unsafe { &*PWM::block() };
200 block.operator_timersel().modify(|_, w| match OP {
201 0 => unsafe { w.operator0_timersel().bits(TIM) },
202 1 => unsafe { w.operator1_timersel().bits(TIM) },
203 2 => unsafe { w.operator2_timersel().bits(TIM) },
204 _ => {
205 unreachable!()
206 }
207 });
208 }
209
210 pub fn with_pin_a(
212 self,
213 pin: impl PeripheralOutput<'d>,
214 config: PwmPinConfig<true>,
215 ) -> PwmPin<'d, PWM, OP, true> {
216 PwmPin::new(pin, config)
217 }
218
219 pub fn with_pin_b(
221 self,
222 pin: impl PeripheralOutput<'d>,
223 config: PwmPinConfig<false>,
224 ) -> PwmPin<'d, PWM, OP, false> {
225 PwmPin::new(pin, config)
226 }
227
228 pub fn with_pins(
230 self,
231 pin_a: impl PeripheralOutput<'d>,
232 config_a: PwmPinConfig<true>,
233 pin_b: impl PeripheralOutput<'d>,
234 config_b: PwmPinConfig<false>,
235 ) -> (PwmPin<'d, PWM, OP, true>, PwmPin<'d, PWM, OP, false>) {
236 (PwmPin::new(pin_a, config_a), PwmPin::new(pin_b, config_b))
237 }
238
239 pub fn with_linked_pins(
244 self,
245 pin_a: impl PeripheralOutput<'d>,
246 config_a: PwmPinConfig<true>,
247 pin_b: impl PeripheralOutput<'d>,
248 config_b: PwmPinConfig<false>,
249 config_dt: DeadTimeCfg,
250 ) -> LinkedPins<'d, PWM, OP> {
251 LinkedPins::new(pin_a, config_a, pin_b, config_b, config_dt)
252 }
253}
254
255pub struct PwmPinConfig<const IS_A: bool> {
258 actions: PwmActions<IS_A>,
259 update_method: PwmUpdateMethod,
260}
261
262impl<const IS_A: bool> PwmPinConfig<IS_A> {
263 pub const UP_ACTIVE_HIGH: Self =
266 Self::new(PwmActions::UP_ACTIVE_HIGH, PwmUpdateMethod::SYNC_ON_ZERO);
267 pub const UP_DOWN_ACTIVE_HIGH: Self = Self::new(
270 PwmActions::UP_DOWN_ACTIVE_HIGH,
271 PwmUpdateMethod::SYNC_ON_ZERO,
272 );
273 pub const EMPTY: Self = Self::new(PwmActions::empty(), PwmUpdateMethod::empty());
276
277 pub const fn new(actions: PwmActions<IS_A>, update_method: PwmUpdateMethod) -> Self {
279 PwmPinConfig {
280 actions,
281 update_method,
282 }
283 }
284}
285
286pub struct PwmPin<'d, PWM, const OP: u8, const IS_A: bool> {
288 pin: OutputSignal<'d>,
289 phantom: PhantomData<PWM>,
290 _guard: PeripheralGuard,
291}
292
293impl<'d, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> PwmPin<'d, PWM, OP, IS_A> {
294 fn new(pin: impl PeripheralOutput<'d>, config: PwmPinConfig<IS_A>) -> Self {
295 let pin = pin.into();
296
297 let guard = PeripheralGuard::new(PWM::peripheral());
298
299 let mut pin = PwmPin {
300 pin,
301 phantom: PhantomData,
302 _guard: guard,
303 };
304 pin.set_actions(config.actions);
305 pin.set_update_method(config.update_method);
306
307 PWM::output_signal::<OP, IS_A>().connect_to(&pin.pin);
308 pin.pin.set_output_enable(true);
309
310 pin
311 }
312
313 pub fn set_actions(&mut self, value: PwmActions<IS_A>) {
315 let ch = unsafe { Self::ch() };
318 let bits = value.0;
319
320 ch.gen_((!IS_A) as usize).write(|w| unsafe { w.bits(bits) });
323 }
324
325 pub fn set_update_method(&mut self, update_method: PwmUpdateMethod) {
327 let ch = unsafe { Self::ch() };
330 let bits = update_method.0;
331
332 #[cfg(esp32s3)]
333 let cfg = ch.cmpr_cfg();
334 #[cfg(any(esp32, esp32c6, esp32h2))]
335 let cfg = ch.gen_stmp_cfg();
336
337 cfg.modify(|_, w| unsafe {
338 if IS_A {
339 w.a_upmethod().bits(bits)
340 } else {
341 w.b_upmethod().bits(bits)
342 }
343 });
344 }
345
346 pub fn set_timestamp(&mut self, value: u16) {
350 let ch = unsafe { Self::ch() };
353
354 #[cfg(esp32s3)]
355 if IS_A {
356 ch.cmpr_value0().write(|w| unsafe { w.a().bits(value) });
357 } else {
358 ch.cmpr_value1().write(|w| unsafe { w.b().bits(value) });
359 }
360
361 #[cfg(any(esp32, esp32c6, esp32h2))]
362 if IS_A {
363 ch.gen_tstmp_a().write(|w| unsafe { w.a().bits(value) });
364 } else {
365 ch.gen_tstmp_b().write(|w| unsafe { w.b().bits(value) });
366 }
367 }
368
369 pub fn timestamp(&self) -> u16 {
373 let ch = unsafe { Self::ch() };
376
377 #[cfg(esp32s3)]
378 if IS_A {
379 ch.cmpr_value0().read().a().bits()
380 } else {
381 ch.cmpr_value1().read().b().bits()
382 }
383
384 #[cfg(any(esp32, esp32c6, esp32h2))]
385 if IS_A {
386 ch.gen_tstmp_a().read().a().bits()
387 } else {
388 ch.gen_tstmp_b().read().b().bits()
389 }
390 }
391
392 pub fn period(&self) -> u16 {
394 let block = unsafe { &*PWM::block() };
397
398 let tim_select = block.operator_timersel().read();
399 let tim = match OP {
400 0 => tim_select.operator0_timersel().bits(),
401 1 => tim_select.operator1_timersel().bits(),
402 2 => tim_select.operator2_timersel().bits(),
403 _ => {
404 unreachable!()
405 }
406 };
407
408 block.timer(tim as usize).cfg0().read().period().bits()
412 }
413
414 unsafe fn ch() -> &'static pac::mcpwm0::CH {
415 let block = unsafe { &*PWM::block() };
416 block.ch(OP as usize)
417 }
418}
419
420impl<PWM: PwmPeripheral, const OP: u8, const IS_A: bool> embedded_hal::pwm::ErrorType
422 for PwmPin<'_, PWM, OP, IS_A>
423{
424 type Error = core::convert::Infallible;
425}
426
427impl<PWM: PwmPeripheral, const OP: u8, const IS_A: bool> embedded_hal::pwm::SetDutyCycle
429 for PwmPin<'_, PWM, OP, IS_A>
430{
431 fn max_duty_cycle(&self) -> u16 {
433 self.period()
434 }
435
436 fn set_duty_cycle(&mut self, duty: u16) -> Result<(), core::convert::Infallible> {
438 self.set_timestamp(duty);
439 Ok(())
440 }
441}
442
443#[procmacros::doc_replace(
444 "mcpwm_clk" => {
445 cfg(not(esp32h2)) => "40",
446 cfg(esp32h2) => "32"
447 }
448)]
449pub struct LinkedPins<'d, PWM, const OP: u8> {
490 pin_a: PwmPin<'d, PWM, OP, true>,
491 pin_b: PwmPin<'d, PWM, OP, false>,
492}
493
494impl<'d, PWM: PwmPeripheral, const OP: u8> LinkedPins<'d, PWM, OP> {
495 fn new(
496 pin_a: impl PeripheralOutput<'d>,
497 config_a: PwmPinConfig<true>,
498 pin_b: impl PeripheralOutput<'d>,
499 config_b: PwmPinConfig<false>,
500 config_dt: DeadTimeCfg,
501 ) -> Self {
502 #[cfg(esp32s3)]
504 let dt_cfg = unsafe { Self::ch() }.db_cfg();
505 #[cfg(not(esp32s3))]
506 let dt_cfg = unsafe { Self::ch() }.dt_cfg();
507 dt_cfg.write(|w| unsafe { w.bits(config_dt.cfg_reg) });
508
509 let pin_a = PwmPin::new(pin_a, config_a);
510 let pin_b = PwmPin::new(pin_b, config_b);
511
512 LinkedPins { pin_a, pin_b }
513 }
514
515 pub fn set_actions_a(&mut self, value: PwmActions<true>) {
517 self.pin_a.set_actions(value)
518 }
519 pub fn set_actions_b(&mut self, value: PwmActions<false>) {
521 self.pin_b.set_actions(value)
522 }
523
524 pub fn set_update_method_a(&mut self, update_method: PwmUpdateMethod) {
526 self.pin_a.set_update_method(update_method)
527 }
528 pub fn set_update_method_b(&mut self, update_method: PwmUpdateMethod) {
530 self.pin_b.set_update_method(update_method)
531 }
532
533 pub fn set_timestamp_a(&mut self, value: u16) {
537 self.pin_a.set_timestamp(value)
538 }
539 pub fn set_timestamp_b(&mut self, value: u16) {
543 self.pin_a.set_timestamp(value)
544 }
545
546 pub fn set_deadtime_cfg(&mut self, config: DeadTimeCfg) {
548 #[cfg(esp32s3)]
549 let dt_cfg = unsafe { Self::ch() }.db_cfg();
550 #[cfg(not(esp32s3))]
551 let dt_cfg = unsafe { Self::ch() }.dt_cfg();
552 dt_cfg.write(|w| unsafe { w.bits(config.cfg_reg) });
553 }
554
555 pub fn set_rising_edge_deadtime(&mut self, dead_time: u16) {
557 #[cfg(esp32s3)]
558 let dt_red = unsafe { Self::ch() }.db_red_cfg();
559 #[cfg(not(esp32s3))]
560 let dt_red = unsafe { Self::ch() }.dt_red_cfg();
561 dt_red.write(|w| unsafe { w.red().bits(dead_time) });
562 }
563 pub fn set_falling_edge_deadtime(&mut self, dead_time: u16) {
565 #[cfg(esp32s3)]
566 let dt_fed = unsafe { Self::ch() }.db_fed_cfg();
567 #[cfg(not(esp32s3))]
568 let dt_fed = unsafe { Self::ch() }.dt_fed_cfg();
569 dt_fed.write(|w| unsafe { w.fed().bits(dead_time) });
570 }
571
572 unsafe fn ch() -> &'static pac::mcpwm0::CH {
573 let block = unsafe { &*PWM::block() };
574 block.ch(OP as usize)
575 }
576}
577
578#[non_exhaustive]
580#[repr(u32)]
581pub enum UpdateAction {
582 SetLow = 1,
584 SetHigh = 2,
586 Toggle = 3,
589}
590
591pub struct PwmActions<const IS_A: bool>(u32);
598
599impl<const IS_A: bool> PwmActions<IS_A> {
600 pub const UP_ACTIVE_HIGH: Self = Self::empty()
605 .on_up_counting_timer_equals_zero(UpdateAction::SetHigh)
606 .on_up_counting_timer_equals_timestamp(UpdateAction::SetLow);
607
608 pub const UP_DOWN_ACTIVE_HIGH: Self = Self::empty()
613 .on_down_counting_timer_equals_timestamp(UpdateAction::SetHigh)
614 .on_up_counting_timer_equals_timestamp(UpdateAction::SetLow);
615
616 pub const fn empty() -> Self {
618 PwmActions(0)
619 }
620
621 pub const fn on_up_counting_timer_equals_zero(self, action: UpdateAction) -> Self {
623 self.with_value_at_offset(action as u32, 0)
624 }
625
626 pub const fn on_up_counting_timer_equals_period(self, action: UpdateAction) -> Self {
628 self.with_value_at_offset(action as u32, 2)
629 }
630
631 pub const fn on_up_counting_timer_equals_timestamp(self, action: UpdateAction) -> Self {
633 match IS_A {
634 true => self.with_value_at_offset(action as u32, 4),
635 false => self.with_value_at_offset(action as u32, 6),
636 }
637 }
638
639 pub const fn on_up_counting_timer_equals_ch_timestamp<const CH_A: bool>(
642 self,
643 action: UpdateAction,
644 ) -> Self {
645 match CH_A {
646 true => self.with_value_at_offset(action as u32, 4),
647 false => self.with_value_at_offset(action as u32, 6),
648 }
649 }
650
651 pub const fn on_down_counting_timer_equals_zero(self, action: UpdateAction) -> Self {
653 self.with_value_at_offset(action as u32, 12)
654 }
655
656 pub const fn on_down_counting_timer_equals_period(self, action: UpdateAction) -> Self {
658 self.with_value_at_offset(action as u32, 14)
659 }
660
661 pub const fn on_down_counting_timer_equals_timestamp(self, action: UpdateAction) -> Self {
663 match IS_A {
664 true => self.with_value_at_offset(action as u32, 16),
665 false => self.with_value_at_offset(action as u32, 18),
666 }
667 }
668
669 pub const fn on_down_counting_timer_equals_ch_timestamp<const CH_A: bool>(
672 self,
673 action: UpdateAction,
674 ) -> Self {
675 match CH_A {
676 true => self.with_value_at_offset(action as u32, 16),
677 false => self.with_value_at_offset(action as u32, 18),
678 }
679 }
680
681 const fn with_value_at_offset(self, value: u32, offset: u32) -> Self {
682 let mask = !(0b11 << offset);
683 let value = (self.0 & mask) | (value << offset);
684 PwmActions(value)
685 }
686}
687
688pub struct PwmUpdateMethod(u8);
692
693impl PwmUpdateMethod {
694 pub const SYNC_IMMEDIATLY: Self = Self::empty();
696 pub const SYNC_ON_ZERO: Self = Self::empty().sync_on_timer_equals_zero();
698 pub const SYNC_ON_PERIOD: Self = Self::empty().sync_on_timer_equals_period();
700
701 pub const fn empty() -> Self {
704 PwmUpdateMethod(0)
705 }
706
707 pub const fn sync_on_timer_equals_zero(mut self) -> Self {
709 self.0 |= 0b0001;
710 self
711 }
712
713 pub const fn sync_on_timer_equals_period(mut self) -> Self {
715 self.0 |= 0b0010;
716 self
717 }
718}