diff options
Diffstat (limited to 'embassy-stm32/src/timer/simple_pwm.rs')
| -rw-r--r-- | embassy-stm32/src/timer/simple_pwm.rs | 341 |
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; | |||
| 4 | use core::mem::ManuallyDrop; | 4 | use core::mem::ManuallyDrop; |
| 5 | 5 | ||
| 6 | use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; | 6 | use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; |
| 7 | use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin}; | 7 | use super::ringbuffered::RingBufferedPwmChannel; |
| 8 | use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; | ||
| 9 | use crate::Peri; | ||
| 10 | use crate::dma::word::Word; | ||
| 8 | #[cfg(gpio_v2)] | 11 | #[cfg(gpio_v2)] |
| 9 | use crate::gpio::Pull; | 12 | use crate::gpio::Pull; |
| 10 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 13 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
| 14 | use crate::pac::timer::vals::Ccds; | ||
| 11 | use crate::time::Hertz; | 15 | use crate::time::Hertz; |
| 12 | use 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 | |||
| 453 | impl<'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 | ||
| 549 | impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> { | 444 | impl<'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) |
