diff options
| -rw-r--r-- | embassy-stm32/build.rs | 14 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/mod.rs | 25 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/simple_pwm.rs | 90 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/ws2812_pwm.rs | 2 |
4 files changed, 119 insertions, 12 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index ef152acd1..7860ee2ff 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -1009,6 +1009,10 @@ fn main() { | |||
| 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 | (("timer", "UP"), quote!(crate::timer::UpDma)), |
| 1012 | (("timer", "CH1"), quote!(crate::timer::Ch1Dma)), | ||
| 1013 | (("timer", "CH2"), quote!(crate::timer::Ch2Dma)), | ||
| 1014 | (("timer", "CH3"), quote!(crate::timer::Ch3Dma)), | ||
| 1015 | (("timer", "CH4"), quote!(crate::timer::Ch4Dma)), | ||
| 1012 | ] | 1016 | ] |
| 1013 | .into(); | 1017 | .into(); |
| 1014 | 1018 | ||
| @@ -1024,16 +1028,6 @@ fn main() { | |||
| 1024 | } | 1028 | } |
| 1025 | 1029 | ||
| 1026 | if let Some(tr) = signals.get(&(regs.kind, ch.signal)) { | 1030 | 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 | |||
| 1037 | let peri = format_ident!("{}", p.name); | 1031 | let peri = format_ident!("{}", p.name); |
| 1038 | 1032 | ||
| 1039 | let channel = if let Some(channel) = &ch.channel { | 1033 | 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 d07fd2776..957098cde 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs | |||
| @@ -311,6 +311,26 @@ pub(crate) mod sealed { | |||
| 311 | .ccmr_output(channel_index / 2) | 311 | .ccmr_output(channel_index / 2) |
| 312 | .modify(|w| w.set_ocpe(channel_index % 2, preload)); | 312 | .modify(|w| w.set_ocpe(channel_index % 2, preload)); |
| 313 | } | 313 | } |
| 314 | |||
| 315 | /// Get capture compare DMA selection | ||
| 316 | fn get_cc_dma_selection(&self) -> super::vals::Ccds { | ||
| 317 | Self::regs_gp16().cr2().read().ccds() | ||
| 318 | } | ||
| 319 | |||
| 320 | /// Set capture compare DMA selection | ||
| 321 | fn set_cc_dma_selection(&mut self, ccds: super::vals::Ccds) { | ||
| 322 | Self::regs_gp16().cr2().modify(|w| w.set_ccds(ccds)) | ||
| 323 | } | ||
| 324 | |||
| 325 | /// Get capture compare DMA enable state | ||
| 326 | fn get_cc_dma_enable_state(&self, channel: Channel) -> bool { | ||
| 327 | Self::regs_gp16().dier().read().ccde(channel.index()) | ||
| 328 | } | ||
| 329 | |||
| 330 | /// Set capture compare DMA enable state | ||
| 331 | fn set_cc_dma_enable_state(&mut self, channel: Channel, ccde: bool) { | ||
| 332 | Self::regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) | ||
| 333 | } | ||
| 314 | } | 334 | } |
| 315 | 335 | ||
| 316 | /// Capture/Compare 16-bit timer instance with complementary pin support. | 336 | /// Capture/Compare 16-bit timer instance with complementary pin support. |
| @@ -705,3 +725,8 @@ foreach_interrupt! { | |||
| 705 | 725 | ||
| 706 | // Update Event trigger DMA for every timer | 726 | // Update Event trigger DMA for every timer |
| 707 | dma_trait!(UpDma, Basic16bitInstance); | 727 | dma_trait!(UpDma, Basic16bitInstance); |
| 728 | |||
| 729 | dma_trait!(Ch1Dma, CaptureCompare16bitInstance); | ||
| 730 | dma_trait!(Ch2Dma, CaptureCompare16bitInstance); | ||
| 731 | dma_trait!(Ch3Dma, CaptureCompare16bitInstance); | ||
| 732 | dma_trait!(Ch4Dma, CaptureCompare16bitInstance); | ||
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 80f10424c..665557354 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs | |||
| @@ -155,7 +155,7 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { | |||
| 155 | /// | 155 | /// |
| 156 | /// Note: | 156 | /// Note: |
| 157 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 157 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. |
| 158 | pub async fn gen_waveform( | 158 | pub async fn waveform_up( |
| 159 | &mut self, | 159 | &mut self, |
| 160 | dma: impl Peripheral<P = impl super::UpDma<T>>, | 160 | dma: impl Peripheral<P = impl super::UpDma<T>>, |
| 161 | channel: Channel, | 161 | channel: Channel, |
| @@ -221,6 +221,94 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { | |||
| 221 | } | 221 | } |
| 222 | } | 222 | } |
| 223 | 223 | ||
| 224 | macro_rules! impl_waveform_chx { | ||
| 225 | ($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => { | ||
| 226 | impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { | ||
| 227 | /// Generate a sequence of PWM waveform | ||
| 228 | /// | ||
| 229 | /// Note: | ||
| 230 | /// you will need to provide corresponding TIMx_CHy DMA channel to use this method. | ||
| 231 | pub async fn $fn_name(&mut self, dma: impl Peripheral<P = impl super::$dma_ch<T>>, duty: &[u16]) { | ||
| 232 | use super::vals::Ccds; | ||
| 233 | |||
| 234 | assert!(duty.iter().all(|v| *v <= self.get_max_duty())); | ||
| 235 | |||
| 236 | into_ref!(dma); | ||
| 237 | |||
| 238 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 239 | let req = dma.request(); | ||
| 240 | |||
| 241 | let cc_channel = super::Channel::$cc_ch; | ||
| 242 | |||
| 243 | let original_duty_state = self.get_duty(cc_channel); | ||
| 244 | let original_enable_state = self.is_enabled(cc_channel); | ||
| 245 | let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ONUPDATE; | ||
| 246 | let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); | ||
| 247 | |||
| 248 | if original_cc_dma_on_update { | ||
| 249 | self.inner.set_cc_dma_selection(Ccds::ONUPDATE) | ||
| 250 | } | ||
| 251 | |||
| 252 | if !original_cc_dma_enabled { | ||
| 253 | self.inner.set_cc_dma_enable_state(cc_channel, true); | ||
| 254 | } | ||
| 255 | |||
| 256 | if !original_enable_state { | ||
| 257 | self.enable(cc_channel); | ||
| 258 | } | ||
| 259 | |||
| 260 | unsafe { | ||
| 261 | #[cfg(not(any(bdma, gpdma)))] | ||
| 262 | use crate::dma::{Burst, FifoThreshold}; | ||
| 263 | use crate::dma::{Transfer, TransferOptions}; | ||
| 264 | |||
| 265 | let dma_transfer_option = TransferOptions { | ||
| 266 | #[cfg(not(any(bdma, gpdma)))] | ||
| 267 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 268 | #[cfg(not(any(bdma, gpdma)))] | ||
| 269 | mburst: Burst::Incr8, | ||
| 270 | ..Default::default() | ||
| 271 | }; | ||
| 272 | |||
| 273 | Transfer::new_write( | ||
| 274 | &mut dma, | ||
| 275 | req, | ||
| 276 | duty, | ||
| 277 | T::regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut _, | ||
| 278 | dma_transfer_option, | ||
| 279 | ) | ||
| 280 | .await | ||
| 281 | }; | ||
| 282 | |||
| 283 | // restore output compare state | ||
| 284 | if !original_enable_state { | ||
| 285 | self.disable(cc_channel); | ||
| 286 | } | ||
| 287 | |||
| 288 | self.set_duty(cc_channel, original_duty_state); | ||
| 289 | |||
| 290 | // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, | ||
| 291 | // this can almost always trigger a DMA FIFO error. | ||
| 292 | // | ||
| 293 | // optional TODO: | ||
| 294 | // clean FEIF after disable UDE | ||
| 295 | if !original_cc_dma_enabled { | ||
| 296 | self.inner.set_cc_dma_enable_state(cc_channel, false); | ||
| 297 | } | ||
| 298 | |||
| 299 | if !original_cc_dma_on_update { | ||
| 300 | self.inner.set_cc_dma_selection(Ccds::ONCOMPARE) | ||
| 301 | } | ||
| 302 | } | ||
| 303 | } | ||
| 304 | }; | ||
| 305 | } | ||
| 306 | |||
| 307 | impl_waveform_chx!(waveform_ch1, Ch1Dma, Ch1); | ||
| 308 | impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2); | ||
| 309 | impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3); | ||
| 310 | impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4); | ||
| 311 | |||
| 224 | impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d, T> { | 312 | impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d, T> { |
| 225 | type Channel = Channel; | 313 | type Channel = Channel; |
| 226 | type Time = Hertz; | 314 | type Time = Hertz; |
diff --git a/examples/stm32f4/src/bin/ws2812_pwm.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs index 239709253..6122cea2d 100644 --- a/examples/stm32f4/src/bin/ws2812_pwm.rs +++ b/examples/stm32f4/src/bin/ws2812_pwm.rs | |||
| @@ -91,7 +91,7 @@ async fn main(_spawner: Spawner) { | |||
| 91 | loop { | 91 | loop { |
| 92 | for &color in color_list { | 92 | for &color in color_list { |
| 93 | // with &mut, we can easily reuse same DMA channel multiple times | 93 | // with &mut, we can easily reuse same DMA channel multiple times |
| 94 | ws2812_pwm.gen_waveform(&mut dp.DMA1_CH2, pwm_channel, color).await; | 94 | ws2812_pwm.waveform_up(&mut dp.DMA1_CH2, pwm_channel, color).await; |
| 95 | // ws2812 need at least 50 us low level input to confirm the input data and change it's state | 95 | // ws2812 need at least 50 us low level input to confirm the input data and change it's state |
| 96 | Timer::after_micros(50).await; | 96 | Timer::after_micros(50).await; |
| 97 | // wait until ticker tick | 97 | // wait until ticker tick |
