diff options
Diffstat (limited to 'embassy-stm32/src/timer/complementary_pwm.rs')
| -rw-r--r-- | embassy-stm32/src/timer/complementary_pwm.rs | 108 |
1 files changed, 52 insertions, 56 deletions
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 9a56a41fb..77f19a37b 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs | |||
| @@ -77,8 +77,6 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 77 | 77 | ||
| 78 | this.inner.set_counting_mode(counting_mode); | 78 | this.inner.set_counting_mode(counting_mode); |
| 79 | this.set_frequency(freq); | 79 | this.set_frequency(freq); |
| 80 | this.inner.start(); | ||
| 81 | |||
| 82 | this.inner.enable_outputs(); | 80 | this.inner.enable_outputs(); |
| 83 | 81 | ||
| 84 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] | 82 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] |
| @@ -89,6 +87,10 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 89 | }); | 87 | }); |
| 90 | this.inner.set_autoreload_preload(true); | 88 | this.inner.set_autoreload_preload(true); |
| 91 | 89 | ||
| 90 | // Generate update event so pre-load registers are written to the shadow registers | ||
| 91 | this.inner.generate_update_event(); | ||
| 92 | this.inner.start(); | ||
| 93 | |||
| 92 | this | 94 | this |
| 93 | } | 95 | } |
| 94 | 96 | ||
| @@ -160,8 +162,8 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 160 | 162 | ||
| 161 | /// Set PWM frequency. | 163 | /// Set PWM frequency. |
| 162 | /// | 164 | /// |
| 163 | /// Note: when you call this, the max duty value changes, so you will have to | 165 | /// Note: that the frequency will not be applied in the timer until an update event |
| 164 | /// call `set_duty` on all channels with the duty calculated based on the new max duty. | 166 | /// occurs. |
| 165 | pub fn set_frequency(&mut self, freq: Hertz) { | 167 | pub fn set_frequency(&mut self, freq: Hertz) { |
| 166 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { | 168 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { |
| 167 | 2u8 | 169 | 2u8 |
| @@ -219,59 +221,53 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 219 | /// Note: | 221 | /// Note: |
| 220 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 222 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. |
| 221 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { | 223 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { |
| 222 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | 224 | self.inner.enable_channel(channel, true); |
| 223 | let req = dma.request(); | 225 | self.inner.enable_update_dma(true); |
| 224 | 226 | self.inner.setup_update_dma(dma, channel, duty).await; | |
| 225 | let original_duty_state = self.inner.get_compare_value(channel); | 227 | self.inner.enable_update_dma(false); |
| 226 | let original_enable_state = self.inner.get_channel_enable_state(channel); | 228 | } |
| 227 | let original_update_dma_state = self.inner.get_update_dma_state(); | ||
| 228 | |||
| 229 | if !original_update_dma_state { | ||
| 230 | self.inner.enable_update_dma(true); | ||
| 231 | } | ||
| 232 | |||
| 233 | if !original_enable_state { | ||
| 234 | self.inner.enable_channel(channel, true); | ||
| 235 | } | ||
| 236 | |||
| 237 | unsafe { | ||
| 238 | #[cfg(not(any(bdma, gpdma)))] | ||
| 239 | use crate::dma::{Burst, FifoThreshold}; | ||
| 240 | use crate::dma::{Transfer, TransferOptions}; | ||
| 241 | |||
| 242 | let dma_transfer_option = TransferOptions { | ||
| 243 | #[cfg(not(any(bdma, gpdma)))] | ||
| 244 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 245 | #[cfg(not(any(bdma, gpdma)))] | ||
| 246 | mburst: Burst::Incr8, | ||
| 247 | ..Default::default() | ||
| 248 | }; | ||
| 249 | |||
| 250 | Transfer::new_write( | ||
| 251 | dma, | ||
| 252 | req, | ||
| 253 | duty, | ||
| 254 | self.inner.regs_gp16().ccr(channel.index()).as_ptr() as *mut u16, | ||
| 255 | dma_transfer_option, | ||
| 256 | ) | ||
| 257 | .await | ||
| 258 | }; | ||
| 259 | |||
| 260 | // restore output compare state | ||
| 261 | if !original_enable_state { | ||
| 262 | self.inner.enable_channel(channel, false); | ||
| 263 | } | ||
| 264 | |||
| 265 | self.inner.set_compare_value(channel, original_duty_state); | ||
| 266 | 229 | ||
| 267 | // Since DMA is closed before timer update event trigger DMA is turn off, | 230 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. |
| 268 | // this can almost always trigger a DMA FIFO error. | 231 | /// |
| 269 | // | 232 | /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers |
| 270 | // optional TODO: | 233 | /// in sequence on each update event (UEV). The data is written via the DMAR register using the |
| 271 | // clean FEIF after disable UDE | 234 | /// DMA base address (DBA) and burst length (DBL) configured in the DCR register. |
| 272 | if !original_update_dma_state { | 235 | /// |
| 273 | self.inner.enable_update_dma(false); | 236 | /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row |
| 274 | } | 237 | /// represents a single update event and each column corresponds to a specific timer channel (starting |
| 238 | /// from `starting_channel` up to and including `ending_channel`). | ||
| 239 | /// | ||
| 240 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: | ||
| 241 | /// | ||
| 242 | /// ```rust,ignore | ||
| 243 | /// let dma_buf: [u16; 16] = [ | ||
| 244 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 | ||
| 245 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 | ||
| 246 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 | ||
| 247 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 | ||
| 248 | /// ]; | ||
| 249 | /// ``` | ||
| 250 | /// | ||
| 251 | /// Each group of `N` values (where `N` is number of channels) is transferred on one update event, | ||
| 252 | /// updating the duty cycles of all selected channels simultaneously. | ||
| 253 | /// | ||
| 254 | /// Note: | ||
| 255 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. | ||
| 256 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 257 | /// switch this timer by using `time-driver-timX` feature. | ||
| 258 | /// | ||
| 259 | pub async fn waveform_up_multi_channel( | ||
| 260 | &mut self, | ||
| 261 | dma: Peri<'_, impl super::UpDma<T>>, | ||
| 262 | starting_channel: Channel, | ||
| 263 | ending_channel: Channel, | ||
| 264 | duty: &[u16], | ||
| 265 | ) { | ||
| 266 | self.inner.enable_update_dma(true); | ||
| 267 | self.inner | ||
| 268 | .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) | ||
| 269 | .await; | ||
| 270 | self.inner.enable_update_dma(false); | ||
| 275 | } | 271 | } |
| 276 | } | 272 | } |
| 277 | 273 | ||
