diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-01-02 16:32:06 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-01-02 16:32:06 +0000 |
| commit | 79ce34931d5b80dc52485c91c86a85c2f1ed170e (patch) | |
| tree | 7fa7a1b5762d4e5efa5c3ec8bbcee482ca5f1ac4 /embassy-stm32 | |
| parent | 7d037df1261715e216659d8b0efe29e296ae4e23 (diff) | |
| parent | 638aa313d4e5e80649f7f6201fef3154e5b2bbd5 (diff) | |
Merge pull request #2367 from eZioPan/simplepwm-dma
implement PWM waveform generating with DMA
Diffstat (limited to 'embassy-stm32')
| -rw-r--r-- | embassy-stm32/build.rs | 11 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/mod.rs | 39 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/simple_pwm.rs | 96 |
3 files changed, 133 insertions, 13 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 058b8a0fc..de03827e9 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -1008,6 +1008,7 @@ fn main() { | |||
| 1008 | (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), | 1008 | (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), |
| 1009 | (("dac", "CH1"), quote!(crate::dac::DacDma1)), | 1009 | (("dac", "CH1"), quote!(crate::dac::DacDma1)), |
| 1010 | (("dac", "CH2"), quote!(crate::dac::DacDma2)), | 1010 | (("dac", "CH2"), quote!(crate::dac::DacDma2)), |
| 1011 | (("timer", "UP"), quote!(crate::timer::UpDma)), | ||
| 1011 | ] | 1012 | ] |
| 1012 | .into(); | 1013 | .into(); |
| 1013 | 1014 | ||
| @@ -1023,6 +1024,16 @@ fn main() { | |||
| 1023 | } | 1024 | } |
| 1024 | 1025 | ||
| 1025 | if let Some(tr) = signals.get(&(regs.kind, ch.signal)) { | 1026 | if let Some(tr) = signals.get(&(regs.kind, ch.signal)) { |
| 1027 | // TIM6 of stm32f334 is special, DMA channel for TIM6 depending on SYSCFG state | ||
| 1028 | if chip_name.starts_with("stm32f334") && p.name == "TIM6" { | ||
| 1029 | continue; | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | // TIM6 of stm32f378 is special, DMA channel for TIM6 depending on SYSCFG state | ||
| 1033 | if chip_name.starts_with("stm32f378") && p.name == "TIM6" { | ||
| 1034 | continue; | ||
| 1035 | } | ||
| 1036 | |||
| 1026 | let peri = format_ident!("{}", p.name); | 1037 | let peri = format_ident!("{}", p.name); |
| 1027 | 1038 | ||
| 1028 | let channel = if let Some(channel) = &ch.channel { | 1039 | let channel = if let Some(channel) = &ch.channel { |
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 74120adad..d07fd2776 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs | |||
| @@ -91,7 +91,17 @@ pub(crate) mod sealed { | |||
| 91 | 91 | ||
| 92 | /// Enable/disable the update interrupt. | 92 | /// Enable/disable the update interrupt. |
| 93 | fn enable_update_interrupt(&mut self, enable: bool) { | 93 | fn enable_update_interrupt(&mut self, enable: bool) { |
| 94 | Self::regs().dier().write(|r| r.set_uie(enable)); | 94 | Self::regs().dier().modify(|r| r.set_uie(enable)); |
| 95 | } | ||
| 96 | |||
| 97 | /// Enable/disable the update dma. | ||
| 98 | fn enable_update_dma(&mut self, enable: bool) { | ||
| 99 | Self::regs().dier().modify(|r| r.set_ude(enable)); | ||
| 100 | } | ||
| 101 | |||
| 102 | /// Get the update dma enable/disable state. | ||
| 103 | fn get_update_dma_state(&self) -> bool { | ||
| 104 | Self::regs().dier().read().ude() | ||
| 95 | } | 105 | } |
| 96 | 106 | ||
| 97 | /// Enable/disable autoreload preload. | 107 | /// Enable/disable autoreload preload. |
| @@ -269,6 +279,11 @@ pub(crate) mod sealed { | |||
| 269 | Self::regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable)); | 279 | Self::regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable)); |
| 270 | } | 280 | } |
| 271 | 281 | ||
| 282 | /// Get enable/disable state of a channel | ||
| 283 | fn get_channel_enable_state(&self, channel: Channel) -> bool { | ||
| 284 | Self::regs_gp16().ccer().read().cce(channel.index()) | ||
| 285 | } | ||
| 286 | |||
| 272 | /// Set compare value for a channel. | 287 | /// Set compare value for a channel. |
| 273 | fn set_compare_value(&mut self, channel: Channel, value: u16) { | 288 | fn set_compare_value(&mut self, channel: Channel, value: u16) { |
| 274 | Self::regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value)); | 289 | Self::regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value)); |
| @@ -288,6 +303,14 @@ pub(crate) mod sealed { | |||
| 288 | fn get_compare_value(&self, channel: Channel) -> u16 { | 303 | fn get_compare_value(&self, channel: Channel) -> u16 { |
| 289 | Self::regs_gp16().ccr(channel.index()).read().ccr() | 304 | Self::regs_gp16().ccr(channel.index()).read().ccr() |
| 290 | } | 305 | } |
| 306 | |||
| 307 | /// Set output compare preload. | ||
| 308 | fn set_output_compare_preload(&mut self, channel: Channel, preload: bool) { | ||
| 309 | let channel_index = channel.index(); | ||
| 310 | Self::regs_gp16() | ||
| 311 | .ccmr_output(channel_index / 2) | ||
| 312 | .modify(|w| w.set_ocpe(channel_index % 2, preload)); | ||
| 313 | } | ||
| 291 | } | 314 | } |
| 292 | 315 | ||
| 293 | /// Capture/Compare 16-bit timer instance with complementary pin support. | 316 | /// Capture/Compare 16-bit timer instance with complementary pin support. |
| @@ -535,13 +558,16 @@ impl From<OutputPolarity> for bool { | |||
| 535 | pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} | 558 | pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} |
| 536 | 559 | ||
| 537 | /// Gneral-purpose 16-bit timer instance. | 560 | /// Gneral-purpose 16-bit timer instance. |
| 538 | pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {} | 561 | pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + Basic16bitInstance + 'static {} |
| 539 | 562 | ||
| 540 | /// Gneral-purpose 32-bit timer instance. | 563 | /// Gneral-purpose 32-bit timer instance. |
| 541 | pub trait GeneralPurpose32bitInstance: sealed::GeneralPurpose32bitInstance + 'static {} | 564 | pub trait GeneralPurpose32bitInstance: |
| 565 | sealed::GeneralPurpose32bitInstance + GeneralPurpose16bitInstance + 'static | ||
| 566 | { | ||
| 567 | } | ||
| 542 | 568 | ||
| 543 | /// Advanced control timer instance. | 569 | /// Advanced control timer instance. |
| 544 | pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + 'static {} | 570 | pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + GeneralPurpose16bitInstance + 'static {} |
| 545 | 571 | ||
| 546 | /// Capture/Compare 16-bit timer instance. | 572 | /// Capture/Compare 16-bit timer instance. |
| 547 | pub trait CaptureCompare16bitInstance: | 573 | pub trait CaptureCompare16bitInstance: |
| @@ -551,7 +577,7 @@ pub trait CaptureCompare16bitInstance: | |||
| 551 | 577 | ||
| 552 | /// Capture/Compare 16-bit timer instance with complementary pin support. | 578 | /// Capture/Compare 16-bit timer instance with complementary pin support. |
| 553 | pub trait ComplementaryCaptureCompare16bitInstance: | 579 | pub trait ComplementaryCaptureCompare16bitInstance: |
| 554 | sealed::ComplementaryCaptureCompare16bitInstance + AdvancedControlInstance + 'static | 580 | sealed::ComplementaryCaptureCompare16bitInstance + CaptureCompare16bitInstance + AdvancedControlInstance + 'static |
| 555 | { | 581 | { |
| 556 | } | 582 | } |
| 557 | 583 | ||
| @@ -676,3 +702,6 @@ foreach_interrupt! { | |||
| 676 | } | 702 | } |
| 677 | }; | 703 | }; |
| 678 | } | 704 | } |
| 705 | |||
| 706 | // Update Event trigger DMA for every timer | ||
| 707 | dma_trait!(UpDma, Basic16bitInstance); | ||
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index e6072aa15..80f10424c 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs | |||
| @@ -86,14 +86,13 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { | |||
| 86 | 86 | ||
| 87 | this.inner.enable_outputs(); | 87 | this.inner.enable_outputs(); |
| 88 | 88 | ||
| 89 | this.inner | 89 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] |
| 90 | .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1); | 90 | .iter() |
| 91 | this.inner | 91 | .for_each(|&channel| { |
| 92 | .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1); | 92 | this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); |
| 93 | this.inner | 93 | this.inner.set_output_compare_preload(channel, true) |
| 94 | .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); | 94 | }); |
| 95 | this.inner | 95 | |
| 96 | .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); | ||
| 97 | this | 96 | this |
| 98 | } | 97 | } |
| 99 | 98 | ||
| @@ -107,6 +106,11 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { | |||
| 107 | self.inner.enable_channel(channel, false); | 106 | self.inner.enable_channel(channel, false); |
| 108 | } | 107 | } |
| 109 | 108 | ||
| 109 | /// Check whether given channel is enabled | ||
| 110 | pub fn is_enabled(&self, channel: Channel) -> bool { | ||
| 111 | self.inner.get_channel_enable_state(channel) | ||
| 112 | } | ||
| 113 | |||
| 110 | /// Set PWM frequency. | 114 | /// Set PWM frequency. |
| 111 | /// | 115 | /// |
| 112 | /// Note: when you call this, the max duty value changes, so you will have to | 116 | /// Note: when you call this, the max duty value changes, so you will have to |
| @@ -135,10 +139,86 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { | |||
| 135 | self.inner.set_compare_value(channel, duty) | 139 | self.inner.set_compare_value(channel, duty) |
| 136 | } | 140 | } |
| 137 | 141 | ||
| 142 | /// Get the duty for a given channel. | ||
| 143 | /// | ||
| 144 | /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. | ||
| 145 | pub fn get_duty(&self, channel: Channel) -> u16 { | ||
| 146 | self.inner.get_compare_value(channel) | ||
| 147 | } | ||
| 148 | |||
| 138 | /// Set the output polarity for a given channel. | 149 | /// Set the output polarity for a given channel. |
| 139 | pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { | 150 | pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { |
| 140 | self.inner.set_output_polarity(channel, polarity); | 151 | self.inner.set_output_polarity(channel, polarity); |
| 141 | } | 152 | } |
| 153 | |||
| 154 | /// Generate a sequence of PWM waveform | ||
| 155 | /// | ||
| 156 | /// Note: | ||
| 157 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | ||
| 158 | pub async fn gen_waveform( | ||
| 159 | &mut self, | ||
| 160 | dma: impl Peripheral<P = impl super::UpDma<T>>, | ||
| 161 | channel: Channel, | ||
| 162 | duty: &[u16], | ||
| 163 | ) { | ||
| 164 | assert!(duty.iter().all(|v| *v <= self.get_max_duty())); | ||
| 165 | |||
| 166 | into_ref!(dma); | ||
| 167 | |||
| 168 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 169 | let req = dma.request(); | ||
| 170 | |||
| 171 | let original_duty_state = self.get_duty(channel); | ||
| 172 | let original_enable_state = self.is_enabled(channel); | ||
| 173 | let original_update_dma_state = self.inner.get_update_dma_state(); | ||
| 174 | |||
| 175 | if !original_update_dma_state { | ||
| 176 | self.inner.enable_update_dma(true); | ||
| 177 | } | ||
| 178 | |||
| 179 | if !original_enable_state { | ||
| 180 | self.enable(channel); | ||
| 181 | } | ||
| 182 | |||
| 183 | unsafe { | ||
| 184 | #[cfg(not(any(bdma, gpdma)))] | ||
| 185 | use crate::dma::{Burst, FifoThreshold}; | ||
| 186 | use crate::dma::{Transfer, TransferOptions}; | ||
| 187 | |||
| 188 | let dma_transfer_option = TransferOptions { | ||
| 189 | #[cfg(not(any(bdma, gpdma)))] | ||
| 190 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 191 | #[cfg(not(any(bdma, gpdma)))] | ||
| 192 | mburst: Burst::Incr8, | ||
| 193 | ..Default::default() | ||
| 194 | }; | ||
| 195 | |||
| 196 | Transfer::new_write( | ||
| 197 | &mut dma, | ||
| 198 | req, | ||
| 199 | duty, | ||
| 200 | T::regs_gp16().ccr(channel.index()).as_ptr() as *mut _, | ||
| 201 | dma_transfer_option, | ||
| 202 | ) | ||
| 203 | .await | ||
| 204 | }; | ||
| 205 | |||
| 206 | // restore output compare state | ||
| 207 | if !original_enable_state { | ||
| 208 | self.disable(channel); | ||
| 209 | } | ||
| 210 | |||
| 211 | self.set_duty(channel, original_duty_state); | ||
| 212 | |||
| 213 | // Since DMA is closed before timer update event trigger DMA is turn off, | ||
| 214 | // this can almost always trigger a DMA FIFO error. | ||
| 215 | // | ||
| 216 | // optional TODO: | ||
| 217 | // clean FEIF after disable UDE | ||
| 218 | if !original_update_dma_state { | ||
| 219 | self.inner.enable_update_dma(false); | ||
| 220 | } | ||
| 221 | } | ||
| 142 | } | 222 | } |
| 143 | 223 | ||
| 144 | impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d, T> { | 224 | impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d, T> { |
