aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/timer/simple_pwm.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-stm32/src/timer/simple_pwm.rs')
-rw-r--r--embassy-stm32/src/timer/simple_pwm.rs341
1 files changed, 118 insertions, 223 deletions
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs
index e6165e42b..4ffa58778 100644
--- a/embassy-stm32/src/timer/simple_pwm.rs
+++ b/embassy-stm32/src/timer/simple_pwm.rs
@@ -4,12 +4,15 @@ use core::marker::PhantomData;
4use core::mem::ManuallyDrop; 4use core::mem::ManuallyDrop;
5 5
6use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; 6use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer};
7use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin}; 7use super::ringbuffered::RingBufferedPwmChannel;
8use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin};
9use crate::Peri;
10use crate::dma::word::Word;
8#[cfg(gpio_v2)] 11#[cfg(gpio_v2)]
9use crate::gpio::Pull; 12use crate::gpio::Pull;
10use crate::gpio::{AfType, AnyPin, OutputType, Speed}; 13use crate::gpio::{AfType, AnyPin, OutputType, Speed};
14use crate::pac::timer::vals::Ccds;
11use crate::time::Hertz; 15use crate::time::Hertz;
12use crate::Peri;
13 16
14/// PWM pin wrapper. 17/// PWM pin wrapper.
15/// 18///
@@ -94,21 +97,24 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> {
94 self.timer.get_channel_enable_state(self.channel) 97 self.timer.get_channel_enable_state(self.channel)
95 } 98 }
96 99
100 /// Get the frequency of the PWM channel.
101 pub fn get_frequency(&self) -> Hertz {
102 self.timer.get_frequency()
103 }
104
97 /// Get max duty value. 105 /// Get max duty value.
98 /// 106 ///
99 /// This value depends on the configured frequency and the timer's clock rate from RCC. 107 /// This value depends on the configured frequency and the timer's clock rate from RCC.
100 pub fn max_duty_cycle(&self) -> u16 { 108 pub fn max_duty_cycle(&self) -> u32 {
101 let max = self.timer.get_max_compare_value(); 109 self.timer.get_max_compare_value().into() + 1
102 assert!(max < u16::MAX as u32);
103 max as u16 + 1
104 } 110 }
105 111
106 /// Set the duty for a given channel. 112 /// Set the duty for a given channel.
107 /// 113 ///
108 /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included. 114 /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included.
109 pub fn set_duty_cycle(&mut self, duty: u16) { 115 pub fn set_duty_cycle(&mut self, duty: u32) {
110 assert!(duty <= (*self).max_duty_cycle()); 116 assert!(duty <= (*self).max_duty_cycle());
111 self.timer.set_compare_value(self.channel, duty.into()) 117 self.timer.set_compare_value(self.channel, unwrap!(duty.try_into()))
112 } 118 }
113 119
114 /// Set the duty cycle to 0%, or always inactive. 120 /// Set the duty cycle to 0%, or always inactive.
@@ -125,21 +131,21 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> {
125 /// 131 ///
126 /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, 132 /// The caller is responsible for ensuring that `num` is less than or equal to `denom`,
127 /// and that `denom` is not zero. 133 /// and that `denom` is not zero.
128 pub fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) { 134 pub fn set_duty_cycle_fraction(&mut self, num: u32, denom: u32) {
129 assert!(denom != 0); 135 assert!(denom != 0);
130 assert!(num <= denom); 136 assert!(num <= denom);
131 let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom); 137 let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom);
132 138
133 // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16) 139 // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16)
134 #[allow(clippy::cast_possible_truncation)] 140 #[allow(clippy::cast_possible_truncation)]
135 self.set_duty_cycle(duty as u16); 141 self.set_duty_cycle(unwrap!(duty.try_into()));
136 } 142 }
137 143
138 /// Set the duty cycle to `percent / 100` 144 /// Set the duty cycle to `percent / 100`
139 /// 145 ///
140 /// The caller is responsible for ensuring that `percent` is less than or equal to 100. 146 /// The caller is responsible for ensuring that `percent` is less than or equal to 100.
141 pub fn set_duty_cycle_percent(&mut self, percent: u8) { 147 pub fn set_duty_cycle_percent(&mut self, percent: u8) {
142 self.set_duty_cycle_fraction(u16::from(percent), 100) 148 self.set_duty_cycle_fraction(percent as u32, 100)
143 } 149 }
144 150
145 /// Get the duty for a given channel. 151 /// Get the duty for a given channel.
@@ -158,6 +164,34 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> {
158 pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) { 164 pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) {
159 self.timer.set_output_compare_mode(self.channel, mode); 165 self.timer.set_output_compare_mode(self.channel, mode);
160 } 166 }
167
168 /// Convert this PWM channel into a ring-buffered PWM channel.
169 ///
170 /// This allows continuous PWM waveform generation using a DMA ring buffer.
171 /// The ring buffer enables dynamic updates to the PWM duty cycle without blocking.
172 ///
173 /// # Arguments
174 /// * `tx_dma` - The DMA channel to use for transferring duty cycle values
175 /// * `dma_buf` - The buffer to use as a ring buffer (must be non-empty and <= 65535 elements)
176 ///
177 /// # Panics
178 /// Panics if `dma_buf` is empty or longer than 65535 elements.
179 pub fn into_ring_buffered_channel<W: Word + Into<T::Word>>(
180 mut self,
181 tx_dma: Peri<'d, impl super::UpDma<T>>,
182 dma_buf: &'d mut [W],
183 ) -> RingBufferedPwmChannel<'d, T, W> {
184 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
185
186 self.timer.clamp_compare_value::<W>(self.channel);
187 self.timer.enable_update_dma(true);
188
189 RingBufferedPwmChannel::new(
190 unsafe { self.timer.clone_unchecked() },
191 self.channel,
192 self.timer.setup_ring_buffer(tx_dma, self.channel, dma_buf),
193 )
194 }
161} 195}
162 196
163/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. 197/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`].
@@ -198,7 +232,6 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
198 this.inner.set_counting_mode(counting_mode); 232 this.inner.set_counting_mode(counting_mode);
199 this.set_frequency(freq); 233 this.set_frequency(freq);
200 this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details 234 this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details
201 this.inner.start();
202 235
203 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] 236 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
204 .iter() 237 .iter()
@@ -207,6 +240,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
207 240
208 this.inner.set_output_compare_preload(channel, true); 241 this.inner.set_output_compare_preload(channel, true);
209 }); 242 });
243 this.inner.set_autoreload_preload(true);
244
245 // Generate update event so pre-load registers are written to the shadow registers
246 this.inner.generate_update_event();
247 this.inner.start();
210 248
211 this 249 this
212 } 250 }
@@ -285,8 +323,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
285 323
286 /// Set PWM frequency. 324 /// Set PWM frequency.
287 /// 325 ///
288 /// Note: when you call this, the max duty value changes, so you will have to 326 /// Note: that the frequency will not be applied in the timer until an update event
289 /// call `set_duty` on all channels with the duty calculated based on the new max duty. 327 /// occurs.
290 pub fn set_frequency(&mut self, freq: Hertz) { 328 pub fn set_frequency(&mut self, freq: Hertz) {
291 // TODO: prevent ARR = u16::MAX? 329 // TODO: prevent ARR = u16::MAX?
292 let multiplier = if self.inner.get_counting_mode().is_center_aligned() { 330 let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
@@ -297,73 +335,54 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
297 self.inner.set_frequency_internal(freq * multiplier, 16); 335 self.inner.set_frequency_internal(freq * multiplier, 16);
298 } 336 }
299 337
338 /// Get the PWM driver frequency.
339 pub fn get_frequency(&self) -> Hertz {
340 self.inner.get_frequency()
341 }
342
300 /// Get max duty value. 343 /// Get max duty value.
301 /// 344 ///
302 /// This value depends on the configured frequency and the timer's clock rate from RCC. 345 /// This value depends on the configured frequency and the timer's clock rate from RCC.
303 pub fn max_duty_cycle(&self) -> u16 { 346 pub fn max_duty_cycle(&self) -> u32 {
304 let max = self.inner.get_max_compare_value(); 347 self.inner.get_max_compare_value().into() + 1
305 assert!(max < u16::MAX as u32);
306 max as u16 + 1
307 } 348 }
308 349
309 /// Generate a sequence of PWM waveform 350 /// Generate a sequence of PWM waveform
310 /// 351 ///
311 /// Note: 352 /// Note:
312 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 353 /// The DMA channel provided does not need to correspond to the requested channel.
313 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { 354 pub async fn waveform<C: TimerChannel, W: Word + Into<T::Word>>(
314 #[allow(clippy::let_unit_value)] // eg. stm32f334 355 &mut self,
315 let req = dma.request(); 356 dma: Peri<'_, impl super::Dma<T, C>>,
316 357 channel: Channel,
317 let original_duty_state = self.channel(channel).current_duty_cycle(); 358 duty: &[W],
318 let original_enable_state = self.channel(channel).is_enabled(); 359 ) {
319 let original_update_dma_state = self.inner.get_update_dma_state(); 360 self.inner.enable_channel(channel, true);
320 361 self.inner.enable_channel(C::CHANNEL, true);
321 if !original_update_dma_state { 362 self.inner.clamp_compare_value::<W>(channel);
322 self.inner.enable_update_dma(true); 363 self.inner.set_cc_dma_selection(Ccds::ON_UPDATE);
323 } 364 self.inner.set_cc_dma_enable_state(C::CHANNEL, true);
324 365 self.inner.setup_channel_update_dma(dma, channel, duty).await;
325 if !original_enable_state { 366 self.inner.set_cc_dma_enable_state(C::CHANNEL, false);
326 self.channel(channel).enable(); 367 }
327 }
328
329 unsafe {
330 #[cfg(not(any(bdma, gpdma)))]
331 use crate::dma::{Burst, FifoThreshold};
332 use crate::dma::{Transfer, TransferOptions};
333
334 let dma_transfer_option = TransferOptions {
335 #[cfg(not(any(bdma, gpdma)))]
336 fifo_threshold: Some(FifoThreshold::Full),
337 #[cfg(not(any(bdma, gpdma)))]
338 mburst: Burst::Incr8,
339 ..Default::default()
340 };
341
342 Transfer::new_write(
343 dma,
344 req,
345 duty,
346 self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16,
347 dma_transfer_option,
348 )
349 .await
350 };
351
352 // restore output compare state
353 if !original_enable_state {
354 self.channel(channel).disable();
355 }
356
357 self.channel(channel).set_duty_cycle(original_duty_state);
358 368
359 // Since DMA is closed before timer update event trigger DMA is turn off, 369 /// Generate a sequence of PWM waveform
360 // this can almost always trigger a DMA FIFO error. 370 ///
361 // 371 /// Note:
362 // optional TODO: 372 /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.
363 // clean FEIF after disable UDE 373 /// Also be aware that embassy timers use one of timers internally. It is possible to
364 if !original_update_dma_state { 374 /// switch this timer by using `time-driver-timX` feature.
365 self.inner.enable_update_dma(false); 375 pub async fn waveform_up<W: Word + Into<T::Word>>(
366 } 376 &mut self,
377 dma: Peri<'_, impl super::UpDma<T>>,
378 channel: Channel,
379 duty: &[W],
380 ) {
381 self.inner.enable_channel(channel, true);
382 self.inner.clamp_compare_value::<W>(channel);
383 self.inner.enable_update_dma(true);
384 self.inner.setup_update_dma(dma, channel, duty).await;
385 self.inner.enable_update_dma(false);
367 } 386 }
368 387
369 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. 388 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.
@@ -378,167 +397,43 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
378 /// 397 ///
379 /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: 398 /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like:
380 /// 399 ///
400 /// ```rust,ignore
381 /// let dma_buf: [u16; 16] = [ 401 /// let dma_buf: [u16; 16] = [
382 /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 402 /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1
383 /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 403 /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2
384 /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 404 /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3
385 /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 405 /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4
386 /// ]; 406 /// ];
407 /// ```
387 /// 408 ///
388 /// Each group of N values (where N = number of channels) is transferred on one update event, 409 /// Each group of `N` values (where `N` is number of channels) is transferred on one update event,
389 /// updating the duty cycles of all selected channels simultaneously. 410 /// updating the duty cycles of all selected channels simultaneously.
390 /// 411 ///
391 /// Note: 412 /// Note:
392 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 413 /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.
393 pub async fn waveform_up_multi_channel( 414 /// Also be aware that embassy timers use one of timers internally. It is possible to
415 /// switch this timer by using `time-driver-timX` feature.
416 ///
417 pub async fn waveform_up_multi_channel<W: Word + Into<T::Word>>(
394 &mut self, 418 &mut self,
395 dma: Peri<'_, impl super::UpDma<T>>, 419 dma: Peri<'_, impl super::UpDma<T>>,
396 starting_channel: Channel, 420 starting_channel: Channel,
397 ending_channel: Channel, 421 ending_channel: Channel,
398 duty: &[u16], 422 duty: &[W],
399 ) { 423 ) {
400 let cr1_addr = self.inner.regs_gp16().cr1().as_ptr() as u32; 424 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
401 let start_ch_index = starting_channel.index(); 425 .iter()
402 let end_ch_index = ending_channel.index(); 426 .filter(|ch| ch.index() >= starting_channel.index())
403 427 .filter(|ch| ch.index() <= ending_channel.index())
404 assert!(start_ch_index <= end_ch_index); 428 .for_each(|ch| {
405 429 self.inner.enable_channel(*ch, true);
406 let ccrx_addr = self.inner.regs_gp16().ccr(start_ch_index).as_ptr() as u32; 430 self.inner.clamp_compare_value::<W>(*ch);
407 self.inner 431 });
408 .regs_gp16() 432 self.inner.enable_update_dma(true);
409 .dcr()
410 .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8));
411 self.inner 433 self.inner
412 .regs_gp16() 434 .setup_update_dma_burst(dma, starting_channel, ending_channel, duty)
413 .dcr() 435 .await;
414 .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8)); 436 self.inner.enable_update_dma(false);
415
416 #[allow(clippy::let_unit_value)] // eg. stm32f334
417 let req = dma.request();
418
419 let original_update_dma_state = self.inner.get_update_dma_state();
420 if !original_update_dma_state {
421 self.inner.enable_update_dma(true);
422 }
423
424 unsafe {
425 #[cfg(not(any(bdma, gpdma)))]
426 use crate::dma::{Burst, FifoThreshold};
427 use crate::dma::{Transfer, TransferOptions};
428
429 let dma_transfer_option = TransferOptions {
430 #[cfg(not(any(bdma, gpdma)))]
431 fifo_threshold: Some(FifoThreshold::Full),
432 #[cfg(not(any(bdma, gpdma)))]
433 mburst: Burst::Incr4,
434 ..Default::default()
435 };
436
437 Transfer::new_write(
438 dma,
439 req,
440 duty,
441 self.inner.regs_gp16().dmar().as_ptr() as *mut u16,
442 dma_transfer_option,
443 )
444 .await
445 };
446
447 if !original_update_dma_state {
448 self.inner.enable_update_dma(false);
449 }
450 }
451}
452
453impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
454 /// Generate a sequence of PWM waveform
455 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
456 use crate::pac::timer::vals::Ccds;
457
458 #[allow(clippy::let_unit_value)] // eg. stm32f334
459 let req = dma.request();
460
461 let cc_channel = C::CHANNEL;
462
463 let original_duty_state = self.channel(cc_channel).current_duty_cycle();
464 let original_enable_state = self.channel(cc_channel).is_enabled();
465 let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE;
466 let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel);
467
468 // redirect CC DMA request onto Update Event
469 if !original_cc_dma_on_update {
470 self.inner.set_cc_dma_selection(Ccds::ON_UPDATE)
471 }
472
473 if !original_cc_dma_enabled {
474 self.inner.set_cc_dma_enable_state(cc_channel, true);
475 }
476
477 if !original_enable_state {
478 self.channel(cc_channel).enable();
479 }
480
481 unsafe {
482 #[cfg(not(any(bdma, gpdma)))]
483 use crate::dma::{Burst, FifoThreshold};
484 use crate::dma::{Transfer, TransferOptions};
485
486 let dma_transfer_option = TransferOptions {
487 #[cfg(not(any(bdma, gpdma)))]
488 fifo_threshold: Some(FifoThreshold::Full),
489 #[cfg(not(any(bdma, gpdma)))]
490 mburst: Burst::Incr8,
491 ..Default::default()
492 };
493
494 match self.inner.bits() {
495 TimerBits::Bits16 => {
496 Transfer::new_write(
497 dma,
498 req,
499 duty,
500 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16,
501 dma_transfer_option,
502 )
503 .await
504 }
505 #[cfg(not(any(stm32l0)))]
506 TimerBits::Bits32 => {
507 #[cfg(not(any(bdma, gpdma)))]
508 panic!("unsupported timer bits");
509
510 #[cfg(any(bdma, gpdma))]
511 Transfer::new_write(
512 dma,
513 req,
514 duty,
515 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32,
516 dma_transfer_option,
517 )
518 .await
519 }
520 };
521 };
522
523 // restore output compare state
524 if !original_enable_state {
525 self.channel(cc_channel).disable();
526 }
527
528 self.channel(cc_channel).set_duty_cycle(original_duty_state);
529
530 // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
531 // this can almost always trigger a DMA FIFO error.
532 //
533 // optional TODO:
534 // clean FEIF after disable UDE
535 if !original_cc_dma_enabled {
536 self.inner.set_cc_dma_enable_state(cc_channel, false);
537 }
538
539 if !original_cc_dma_on_update {
540 self.inner.set_cc_dma_selection(Ccds::ON_COMPARE)
541 }
542 } 437 }
543} 438}
544 439
@@ -548,11 +443,11 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePw
548 443
549impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> { 444impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> {
550 fn max_duty_cycle(&self) -> u16 { 445 fn max_duty_cycle(&self) -> u16 {
551 self.max_duty_cycle() 446 unwrap!(self.max_duty_cycle().try_into())
552 } 447 }
553 448
554 fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { 449 fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
555 self.set_duty_cycle(duty); 450 self.set_duty_cycle(duty.into());
556 Ok(()) 451 Ok(())
557 } 452 }
558 453
@@ -567,7 +462,7 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for Simpl
567 } 462 }
568 463
569 fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { 464 fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> {
570 self.set_duty_cycle_fraction(num, denom); 465 self.set_duty_cycle_fraction(num.into(), denom.into());
571 Ok(()) 466 Ok(())
572 } 467 }
573 468
@@ -595,16 +490,16 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> {
595 } 490 }
596 491
597 fn get_duty(&self, channel: Self::Channel) -> Self::Duty { 492 fn get_duty(&self, channel: Self::Channel) -> Self::Duty {
598 self.inner.get_compare_value(channel) 493 self.inner.get_compare_value(channel).into()
599 } 494 }
600 495
601 fn get_max_duty(&self) -> Self::Duty { 496 fn get_max_duty(&self) -> Self::Duty {
602 self.inner.get_max_compare_value() + 1 497 self.inner.get_max_compare_value().into() + 1
603 } 498 }
604 499
605 fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { 500 fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {
606 assert!(duty <= self.max_duty_cycle() as u32); 501 assert!(duty <= self.max_duty_cycle() as u32);
607 self.inner.set_compare_value(channel, duty) 502 self.inner.set_compare_value(channel, unwrap!(duty.try_into()))
608 } 503 }
609 504
610 fn set_period<P>(&mut self, period: P) 505 fn set_period<P>(&mut self, period: P)