1use core::marker::PhantomData;
16
17use embassy_hal_internal::{Peri, PeripheralType};
18use embassy_sync::waitqueue::AtomicWaker;
19use paste::paste;
20
21use crate::{
22 event_link::InterruptEvent,
23 gpio::{Flex, Pin, PortFunction, WithOpenDrain},
24 module_stop::ModuleStop,
25 pac::{
26 self,
27 gpt::vals::{Ccr, Gtio, Mode, Odty, Tpcs},
28 },
29};
30
31#[cfg_attr(feature = "defmt", derive(defmt::Format))]
33#[non_exhaustive]
34pub struct Config {
35 pub divider: Divider,
37
38 pub compare_a: u16,
40
41 pub compare_b: u16,
43
44 pub top: u16,
46}
47
48#[cfg_attr(feature = "defmt", derive(defmt::Format))]
50pub enum Divider {
51 Div1,
53
54 Div4,
56
57 Div16,
59
60 Div64,
62
63 Div256,
65
66 Div1024,
68}
69
70pub struct Pwm<'d, I: Instance> {
72 _instance: PhantomData<&'d I>,
73 channel_a: Option<Flex<'d, WithOpenDrain>>,
75 channel_b: Option<Flex<'d, WithOpenDrain>>,
76}
77
78#[cfg_attr(feature = "defmt", derive(defmt::Format))]
80#[derive(Debug)]
81pub enum PwmError {
82 InvalidDutyCycle,
84
85 Unknown,
87}
88
89#[allow(private_bounds)]
91pub trait Instance: SealedInstance + ModuleStop + PeripheralType + 'static + Send {}
92
93pub(crate) trait SealedInstance {
94 #[cfg(feature = "defmt")]
95 const PERIPHERAL: &'static str;
96 #[cfg(not(feature = "defmt"))]
97 const PERIPHERAL: () = ();
98
99 const CAPTURE_COMP_A_EVENT: InterruptEvent;
100
101 #[allow(dead_code)]
102 const CAPTURE_COMP_B_EVENT: InterruptEvent;
103
104 #[allow(dead_code)]
105 const COMP_C_EVENT: InterruptEvent;
106
107 fn regs() -> pac::gpt::Gpt;
108
109 fn cmpa_waker() -> &'static AtomicWaker;
110}
111
112#[allow(private_bounds)]
114pub trait PwmChannel: SealedPwmChannel {}
115
116pub(crate) trait SealedPwmChannel {}
117
118#[allow(private_bounds)]
120pub trait PwmPin<I: Instance, C: PwmChannel>: SealedPwmPin<I, C> {}
121
122pub(crate) trait SealedPwmPin<I: SealedInstance, C: PwmChannel>:
123 Pin + PeripheralType
124{
125 const PERIPHERAL_FUNC: PortFunction;
126
127 #[inline(always)]
128 fn set_pfunc(&self) {
129 self.set_as_pf(Self::PERIPHERAL_FUNC);
130 }
131}
132
133trait SealedPwmChansetter {}
134impl<'d, I: Instance> SealedPwmChansetter for Pwm<'d, I> {}
135
136#[allow(private_bounds)]
138pub trait PwmChansetter<'d, C: PwmChannel, I: Instance>: SealedPwmChansetter {
139 fn with_channel<A: PwmPin<I, C>>(self, pin_a: Peri<'d, A>) -> Self;
141}
142
143impl<'d, I: Instance> PwmChansetter<'d, ChanA, I> for Pwm<'d, I> {
144 fn with_channel<A: PwmPin<I, ChanA>>(mut self, pin_a: Peri<'d, A>) -> Self {
146 assert!(self.channel_b.is_none());
147
148 let pwm = I::regs();
149
150 pwm.gtior().modify(|w| {
151 w.set_gtioa(Gtio::_00111);
153 w.set_oae(true);
154 });
155 pwm.gtber().modify(|w| w.set_ccra(Ccr::SingleBuffer));
156
157 pin_a.set_pfunc();
158 let pin_a = Flex::new(pin_a);
159
160 self.channel_a = Some(pin_a);
161 self
162 }
163}
164
165impl<'d, I: Instance> PwmChansetter<'d, ChanB, I> for Pwm<'d, I> {
166 fn with_channel<B: PwmPin<I, ChanB>>(mut self, pin_b: Peri<'d, B>) -> Self {
168 assert!(self.channel_b.is_none());
169
170 let pwm = I::regs();
171
172 pwm.gtior().modify(|w| {
173 w.set_gtiob(Gtio::_00111);
175 w.set_obe(true);
176 });
177 pwm.gtber().modify(|w| w.set_ccrb(Ccr::SingleBuffer));
178
179 pin_b.set_pfunc();
180 let pin_b = Flex::new(pin_b);
181
182 self.channel_b = Some(pin_b);
183 self
184 }
185}
186
187impl Default for Config {
188 fn default() -> Self {
189 Self {
190 divider: Divider::Div4,
191 compare_a: 0,
192 compare_b: 0,
193 top: 0xFFFF,
194 }
195 }
196}
197
198impl From<Divider> for u16 {
199 fn from(value: Divider) -> Self {
200 match value {
201 Divider::Div1 => 1,
202 Divider::Div4 => 4,
203 Divider::Div16 => 16,
204 Divider::Div64 => 64,
205 Divider::Div256 => 256,
206 Divider::Div1024 => 1024,
207 }
208 }
209}
210
211impl From<Tpcs> for Divider {
212 fn from(value: Tpcs) -> Self {
213 match value {
214 Tpcs::Div1 => Self::Div1,
215 Tpcs::Div4 => Self::Div4,
216 Tpcs::Div16 => Self::Div16,
217 Tpcs::Div64 => Self::Div64,
218 Tpcs::Div256 => Self::Div256,
219 Tpcs::Div1024 => Self::Div1024,
220 Tpcs::_RESERVED_6 => unimplemented!(),
221 Tpcs::_RESERVED_7 => unimplemented!(),
222 }
223 }
224}
225
226impl From<Divider> for Tpcs {
227 fn from(value: Divider) -> Self {
228 match value {
229 Divider::Div1 => Self::Div1,
230 Divider::Div4 => Self::Div4,
231 Divider::Div16 => Self::Div16,
232 Divider::Div64 => Self::Div64,
233 Divider::Div256 => Self::Div256,
234 Divider::Div1024 => Self::Div1024,
235 }
236 }
237}
238
239impl<'d, I: Instance> Pwm<'d, I> {
240 pub fn new(peri: Peri<'d, I>, config: Config) -> Self {
250 let _ = peri;
251
252 I::start_module();
253
254 let pwm = I::regs();
255
256 pwm.gtcr().modify(|w| w.set_md(Mode::TrianglePwm1));
257
258 pwm.gtcnt().write_value(0);
259
260 let mut this = Self {
261 _instance: PhantomData,
262 channel_a: None,
263 channel_b: None,
264 };
265
266 pwm.gtssr().modify(|r| r.set_cstrt(true));
267
268 this.set_config(config);
269
270 this
271 }
272
273 pub fn with_channel_a<A: PwmPin<I, ChanA>>(self, pin_a: Peri<'d, A>) -> Self {
275 PwmChansetter::<ChanA, I>::with_channel::<A>(self, pin_a)
276 }
277
278 pub fn with_channel_b<B: PwmPin<I, ChanB>>(self, pin_b: Peri<'d, B>) -> Self {
280 PwmChansetter::<ChanB, I>::with_channel::<B>(self, pin_b)
281 }
282
283 pub fn set_config(&mut self, config: Config) {
286 let pwm = I::regs();
287 pwm.gtcr().modify(|w| w.set_tpcs(config.divider.into()));
288 pwm.gtpr().write_value(config.top as u32);
289
290 pwm.gtuddtyc().modify(|w| w.set_oadty(Odty::CompareMatch));
291 pwm.gtccra().write_value(config.compare_a as u32);
292 pwm.gtccrc().write_value(config.compare_a as u32);
293
294 pwm.gtuddtyc().modify(|w| w.set_obdty(Odty::CompareMatch));
295 pwm.gtccrb().write_value(config.compare_b as u32);
296 pwm.gtccre().write_value(config.compare_b as u32);
297 }
298
299 #[inline]
301 pub fn start(&mut self) {
302 let pwm = I::regs();
303
304 pwm.gtcr().modify(|w| w.set_cst(true));
305 }
306
307 #[inline]
309 pub fn stop(&mut self) {
310 let pwm = I::regs();
311
312 pwm.gtcr().modify(|w| w.set_cst(false));
313 }
314
315 pub fn set_frequency(&mut self, frequency: u32, pct: f32) -> Result<(), PwmError> {
321 let pwm = I::regs();
322 let clocks = crate::clock::clock_status();
323 let divider: Divider = pwm.gtcr().read().tpcs().into();
324 let divider: u16 = divider.into();
325 let divider = divider as f32;
326 let pwm_clk = clocks.peripheral_d.to_Hz() as f32;
327 let period = (pwm_clk / divider) / (frequency as f32 * 2.0);
328
329 trace!(
330 "{}set_freq(freq={}, pct={}%, divider={}, clk={}, period={})",
331 I::PERIPHERAL,
332 frequency,
333 pct * 100.0,
334 divider,
335 pwm_clk,
336 period,
337 );
338
339 if period < 2.0 || period > f32::from(u16::MAX - 2) {
340 error!(
341 "{}Unable to set frequency={} Hz, max allowable={}",
342 I::PERIPHERAL,
343 frequency,
344 (pwm_clk / (divider * 4.0)) as u32,
345 );
346 return Err(PwmError::InvalidDutyCycle);
347 }
348
349 pwm.gtpr().write_value(period as u32);
350 self.set_duty_pct(pct);
351
352 Ok(())
353 }
354
355 #[inline]
360 pub fn set_duty_pct(&mut self, pct: f32) {
361 self.set_duty_pct_a(pct);
362 self.set_duty_pct_b(pct);
363 }
364
365 pub fn set_duty_pct_a(&mut self, pct: f32) {
370 if self.channel_a.is_none() {
371 return;
372 }
373
374 let pwm = I::regs();
375 let pct = pct.clamp(0.0, 1.0);
376 let period = pwm.gtpr().read() as f32;
377 let cmp = (period * (1.0 - pct)) as u32;
378
379 if cmp == 0 {
380 pwm.gtuddtyc().modify(|w| w.set_oadty(Odty::On));
381 } else if cmp >= period as u32 {
382 pwm.gtuddtyc().modify(|w| w.set_oadty(Odty::Off));
383 } else {
384 pwm.gtuddtyc().modify(|w| w.set_oadty(Odty::CompareMatch));
387 pwm.gtccra().write_value(cmp);
388 pwm.gtccrc().write_value(cmp);
389 }
390 }
391
392 pub fn set_duty_a(&mut self, duty: u32) -> Result<(), PwmError> {
397 if self.channel_a.is_none() {
398 return Ok(());
399 }
400
401 let pwm = I::regs();
402 let period = pwm.gtpr().read();
403
404 #[cfg(feature = "strict-assert")]
405 assert!(period <= u32::from(u16::MAX));
406 if period > u32::from(u16::MAX) {
407 Err(PwmError::InvalidDutyCycle)?;
408 }
409
410 if duty == 0 {
411 pwm.gtuddtyc().modify(|w| w.set_oadty(Odty::On));
412 } else if duty >= period {
413 pwm.gtuddtyc().modify(|w| w.set_oadty(Odty::Off));
414 } else {
415 pwm.gtuddtyc().modify(|w| w.set_oadty(Odty::CompareMatch));
418 pwm.gtccra().write_value(duty);
419 pwm.gtccrc().write_value(duty);
420 }
421
422 Ok(())
423 }
424
425 pub fn set_duty_pct_b(&mut self, pct: f32) {
430 if self.channel_b.is_none() {
431 return;
432 }
433
434 let pwm = I::regs();
435 let pct = pct.clamp(0.0, 1.0);
436 let period = pwm.gtpr().read() as f32;
437 let cmp = (period * (1.0 - pct)) as u32;
438
439 if cmp == 0 {
440 pwm.gtuddtyc().modify(|w| w.set_obdty(Odty::On));
441 } else if cmp >= period as u32 {
442 pwm.gtuddtyc().modify(|w| w.set_obdty(Odty::Off));
443 } else {
444 pwm.gtuddtyc().modify(|w| w.set_obdty(Odty::CompareMatch));
447 pwm.gtccrb().write_value(cmp);
448 pwm.gtccre().write_value(cmp);
449 }
450 }
451
452 pub fn set_duty_b(&mut self, duty: u32) -> Result<(), PwmError> {
457 if self.channel_b.is_none() {
458 return Ok(());
459 }
460
461 let pwm = I::regs();
462 let period = pwm.gtpr().read();
463
464 #[cfg(feature = "strict-assert")]
465 assert!(period <= u32::from(u16::MAX));
466 if period > u32::from(u16::MAX) {
467 Err(PwmError::InvalidDutyCycle)?;
468 }
469
470 if duty == 0 {
471 pwm.gtuddtyc().modify(|w| w.set_obdty(Odty::On));
472 } else if duty >= period {
473 pwm.gtuddtyc().modify(|w| w.set_obdty(Odty::Off));
474 } else {
475 pwm.gtuddtyc().modify(|w| w.set_obdty(Odty::CompareMatch));
478 pwm.gtccrb().write_value(duty);
479 pwm.gtccre().write_value(duty);
480 }
481
482 Ok(())
483 }
484}
485
486impl<'d, I: Instance> Drop for Pwm<'d, I> {
487 fn drop(&mut self) {
488 self.stop();
489 I::stop_module();
490 }
491}
492
493macro_rules! declare_pwm_channel {
494 ($chan:ident) => {
495 paste! {
496 #[allow(missing_docs)]
498 pub enum [< Chan $chan >] {}
499 impl PwmChannel for [< Chan $chan >] {}
500 impl SealedPwmChannel for [< Chan $chan >] {}
501 }
502 };
503}
504
505macro_rules! pwm_pin {
513 ($instance:ident, $chan:ident, $pin:ident, $pf:ident) => {
514 impl crate::pwm::PwmPin<crate::peripherals::$instance, crate::pwm::$chan>
515 for crate::peripherals::$pin
516 {
517 }
518
519 impl crate::pwm::SealedPwmPin<crate::peripherals::$instance, crate::pwm::$chan>
520 for crate::peripherals::$pin
521 {
522 const PERIPHERAL_FUNC: crate::gpio::PortFunction = crate::gpio::PortFunction::$pf;
523 }
524 };
525}
526pub(crate) use pwm_pin;
527
528declare_pwm_channel!(A);
529declare_pwm_channel!(B);
530
531macro_rules! gpt_instance {
532 ($peripheral:ident, $ccmpa:ident, $ccmpb:ident, $cmpc:ident, $overflow:ident, $underflow:ident) => {
533 impl crate::pwm::Instance for crate::peripherals::$peripheral {}
534 impl crate::pwm::SealedInstance for crate::peripherals::$peripheral {
535 #[cfg(feature = "defmt")]
536 const PERIPHERAL: &'static str = concat!(stringify!($peripheral), ": ");
537
538 const CAPTURE_COMP_A_EVENT: crate::event_link::InterruptEvent =
539 crate::event_link::InterruptEvent::$ccmpa;
540 const CAPTURE_COMP_B_EVENT: crate::event_link::InterruptEvent =
541 crate::event_link::InterruptEvent::$ccmpb;
542 const COMP_C_EVENT: crate::event_link::InterruptEvent =
543 crate::event_link::InterruptEvent::$cmpc;
544
545 #[inline(always)]
546 fn regs() -> crate::pac::gpt::Gpt {
547 crate::pac::$peripheral
548 }
549
550 fn cmpa_waker() -> &'static embassy_sync::waitqueue::AtomicWaker {
551 static WAKER: embassy_sync::waitqueue::AtomicWaker =
552 embassy_sync::waitqueue::AtomicWaker::new();
553 &WAKER
554 }
555 }
556 };
557}
558pub(crate) use gpt_instance;
559
560impl embedded_hal_1::pwm::Error for PwmError {
561 fn kind(&self) -> embedded_hal_1::pwm::ErrorKind {
562 embedded_hal_1::pwm::ErrorKind::Other
563 }
564}
565
566impl<'d, I: Instance> embedded_hal_1::pwm::ErrorType for Pwm<'d, I> {
567 type Error = PwmError;
568}
569
570impl<'d, I: Instance> embedded_hal_1::pwm::SetDutyCycle for Pwm<'d, I> {
571 fn max_duty_cycle(&self) -> u16 {
572 let pwm = I::regs();
573 let period = pwm.gtpr().read();
574 assert!(period <= u32::from(u16::MAX));
575 period as u16
576 }
577
578 fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
579 if self.channel_a.is_some() {
580 self.set_duty_a(duty as u32)?;
581 }
582
583 if self.channel_b.is_some() {
584 self.set_duty_b(duty as u32)?;
585 }
586
587 Ok(())
588 }
589}