diff options
| author | eZio Pan <[email protected]> | 2023-12-28 16:23:47 +0800 |
|---|---|---|
| committer | eZio Pan <[email protected]> | 2023-12-28 20:09:12 +0800 |
| commit | 8c2a6df03b852233ef6c774896cbb00c2a15040f (patch) | |
| tree | c8bd878540fafbf589055a91993b8e3b01661a09 /embassy-stm32 | |
| parent | eebfee189a592427423d3a3ad22132d59926a0e8 (diff) | |
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 | 18 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/simple_pwm.rs | 100 |
3 files changed, 113 insertions, 16 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..05a0564a3 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs | |||
| @@ -91,7 +91,12 @@ 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)); | ||
| 95 | } | 100 | } |
| 96 | 101 | ||
| 97 | /// Enable/disable autoreload preload. | 102 | /// Enable/disable autoreload preload. |
| @@ -288,6 +293,14 @@ pub(crate) mod sealed { | |||
| 288 | fn get_compare_value(&self, channel: Channel) -> u16 { | 293 | fn get_compare_value(&self, channel: Channel) -> u16 { |
| 289 | Self::regs_gp16().ccr(channel.index()).read().ccr() | 294 | Self::regs_gp16().ccr(channel.index()).read().ccr() |
| 290 | } | 295 | } |
| 296 | |||
| 297 | /// Set output compare preload. | ||
| 298 | fn set_output_compare_preload(&mut self, channel: Channel, preload: bool) { | ||
| 299 | let channel_index = channel.index(); | ||
| 300 | Self::regs_gp16() | ||
| 301 | .ccmr_output(channel_index / 2) | ||
| 302 | .modify(|w| w.set_ocpe(channel_index % 2, preload)); | ||
| 303 | } | ||
| 291 | } | 304 | } |
| 292 | 305 | ||
| 293 | /// Capture/Compare 16-bit timer instance with complementary pin support. | 306 | /// Capture/Compare 16-bit timer instance with complementary pin support. |
| @@ -676,3 +689,6 @@ foreach_interrupt! { | |||
| 676 | } | 689 | } |
| 677 | }; | 690 | }; |
| 678 | } | 691 | } |
| 692 | |||
| 693 | // Update Event trigger DMA for every timer | ||
| 694 | dma_trait!(UpDma, Basic16bitInstance); | ||
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index e6072aa15..1819c7c55 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs | |||
| @@ -55,11 +55,12 @@ channel_impl!(new_ch3, Ch3, Channel3Pin); | |||
| 55 | channel_impl!(new_ch4, Ch4, Channel4Pin); | 55 | channel_impl!(new_ch4, Ch4, Channel4Pin); |
| 56 | 56 | ||
| 57 | /// Simple PWM driver. | 57 | /// Simple PWM driver. |
| 58 | pub struct SimplePwm<'d, T> { | 58 | pub struct SimplePwm<'d, T, Dma> { |
| 59 | inner: PeripheralRef<'d, T>, | 59 | inner: PeripheralRef<'d, T>, |
| 60 | dma: PeripheralRef<'d, Dma>, | ||
| 60 | } | 61 | } |
| 61 | 62 | ||
| 62 | impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { | 63 | impl<'d, T: CaptureCompare16bitInstance, Dma> SimplePwm<'d, T, Dma> { |
| 63 | /// Create a new simple PWM driver. | 64 | /// Create a new simple PWM driver. |
| 64 | pub fn new( | 65 | pub fn new( |
| 65 | tim: impl Peripheral<P = T> + 'd, | 66 | tim: impl Peripheral<P = T> + 'd, |
| @@ -69,16 +70,22 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { | |||
| 69 | _ch4: Option<PwmPin<'d, T, Ch4>>, | 70 | _ch4: Option<PwmPin<'d, T, Ch4>>, |
| 70 | freq: Hertz, | 71 | freq: Hertz, |
| 71 | counting_mode: CountingMode, | 72 | counting_mode: CountingMode, |
| 73 | dma: impl Peripheral<P = Dma> + 'd, | ||
| 72 | ) -> Self { | 74 | ) -> Self { |
| 73 | Self::new_inner(tim, freq, counting_mode) | 75 | Self::new_inner(tim, freq, counting_mode, dma) |
| 74 | } | 76 | } |
| 75 | 77 | ||
| 76 | fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self { | 78 | fn new_inner( |
| 77 | into_ref!(tim); | 79 | tim: impl Peripheral<P = T> + 'd, |
| 80 | freq: Hertz, | ||
| 81 | counting_mode: CountingMode, | ||
| 82 | dma: impl Peripheral<P = Dma> + 'd, | ||
| 83 | ) -> Self { | ||
| 84 | into_ref!(tim, dma); | ||
| 78 | 85 | ||
| 79 | T::enable_and_reset(); | 86 | T::enable_and_reset(); |
| 80 | 87 | ||
| 81 | let mut this = Self { inner: tim }; | 88 | let mut this = Self { inner: tim, dma }; |
| 82 | 89 | ||
| 83 | this.inner.set_counting_mode(counting_mode); | 90 | this.inner.set_counting_mode(counting_mode); |
| 84 | this.set_frequency(freq); | 91 | this.set_frequency(freq); |
| @@ -86,14 +93,13 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { | |||
| 86 | 93 | ||
| 87 | this.inner.enable_outputs(); | 94 | this.inner.enable_outputs(); |
| 88 | 95 | ||
| 89 | this.inner | 96 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] |
| 90 | .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1); | 97 | .iter() |
| 91 | this.inner | 98 | .for_each(|&channel| { |
| 92 | .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1); | 99 | this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); |
| 93 | this.inner | 100 | this.inner.set_output_compare_preload(channel, true) |
| 94 | .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); | 101 | }); |
| 95 | this.inner | 102 | |
| 96 | .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); | ||
| 97 | this | 103 | this |
| 98 | } | 104 | } |
| 99 | 105 | ||
| @@ -141,7 +147,71 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { | |||
| 141 | } | 147 | } |
| 142 | } | 148 | } |
| 143 | 149 | ||
| 144 | impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d, T> { | 150 | impl<'d, T: CaptureCompare16bitInstance + Basic16bitInstance, Dma> SimplePwm<'d, T, Dma> |
| 151 | where | ||
| 152 | Dma: super::UpDma<T>, | ||
| 153 | { | ||
| 154 | /// Generate a sequence of PWM waveform | ||
| 155 | pub async fn gen_waveform(&mut self, channel: Channel, duty: &[u16]) { | ||
| 156 | duty.iter().all(|v| v.le(&self.get_max_duty())); | ||
| 157 | |||
| 158 | self.inner.enable_update_dma(true); | ||
| 159 | |||
| 160 | #[cfg_attr(any(stm32f334, stm32f378), allow(clippy::let_unit_value))] | ||
| 161 | let req = self.dma.request(); | ||
| 162 | |||
| 163 | self.enable(channel); | ||
| 164 | |||
| 165 | #[cfg(not(any(bdma, gpdma)))] | ||
| 166 | let dma_regs = self.dma.regs(); | ||
| 167 | #[cfg(not(any(bdma, gpdma)))] | ||
| 168 | let isr_num = self.dma.num() / 4; | ||
| 169 | #[cfg(not(any(bdma, gpdma)))] | ||
| 170 | let isr_bit = self.dma.num() % 4; | ||
| 171 | |||
| 172 | #[cfg(not(any(bdma, gpdma)))] | ||
| 173 | // clean DMA FIFO error before a transfer | ||
| 174 | if dma_regs.isr(isr_num).read().feif(isr_bit) { | ||
| 175 | dma_regs.ifcr(isr_num).write(|v| v.set_feif(isr_bit, true)); | ||
| 176 | } | ||
| 177 | |||
| 178 | unsafe { | ||
| 179 | #[cfg(not(any(bdma, gpdma)))] | ||
| 180 | use crate::dma::{Burst, FifoThreshold}; | ||
| 181 | use crate::dma::{Transfer, TransferOptions}; | ||
| 182 | |||
| 183 | let dma_transfer_option = TransferOptions { | ||
| 184 | #[cfg(not(any(bdma, gpdma)))] | ||
| 185 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 186 | #[cfg(not(any(bdma, gpdma)))] | ||
| 187 | mburst: Burst::Incr8, | ||
| 188 | ..Default::default() | ||
| 189 | }; | ||
| 190 | |||
| 191 | Transfer::new_write( | ||
| 192 | &mut self.dma, | ||
| 193 | req, | ||
| 194 | duty, | ||
| 195 | T::regs_gp16().ccr(channel.index()).as_ptr() as *mut _, | ||
| 196 | dma_transfer_option, | ||
| 197 | ) | ||
| 198 | .await | ||
| 199 | }; | ||
| 200 | |||
| 201 | self.disable(channel); | ||
| 202 | |||
| 203 | self.inner.enable_update_dma(false); | ||
| 204 | |||
| 205 | #[cfg(not(any(bdma, gpdma)))] | ||
| 206 | // Since DMA is closed before timer update event trigger DMA is turn off, it will almost always trigger a DMA FIFO error. | ||
| 207 | // Thus, we will always clean DMA FEIF after each transfer | ||
| 208 | if dma_regs.isr(isr_num).read().feif(isr_bit) { | ||
| 209 | dma_regs.ifcr(isr_num).write(|v| v.set_feif(isr_bit, true)); | ||
| 210 | } | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | impl<'d, T: CaptureCompare16bitInstance, Dma> embedded_hal_02::Pwm for SimplePwm<'d, T, Dma> { | ||
| 145 | type Channel = Channel; | 215 | type Channel = Channel; |
| 146 | type Time = Hertz; | 216 | type Time = Hertz; |
| 147 | type Duty = u16; | 217 | type Duty = u16; |
