aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-02-01 01:02:01 +0000
committerGitHub <[email protected]>2024-02-01 01:02:01 +0000
commit7e02389995379f6cc6057530cc5e2c8c9d5fb0b0 (patch)
treeb38c4604daf076dc08cc8b6e6ca116df83862f7e
parent2f7c8faf51617ca76f967bfafd51d9cd079e266a (diff)
parentb16cc04036da8bcbfe273cd796c092e8e2a074c9 (diff)
Merge pull request #2410 from eZioPan/waveform-on-CHx
impl waveform with TIM OC Channel DMA
-rw-r--r--embassy-stm32/build.rs21
-rw-r--r--embassy-stm32/src/timer/complementary_pwm.rs3
-rw-r--r--embassy-stm32/src/timer/mod.rs40
-rw-r--r--embassy-stm32/src/timer/simple_pwm.rs93
-rw-r--r--examples/stm32f4/src/bin/ws2812_pwm.rs2
5 files changed, 133 insertions, 26 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index bd4195619..69848762a 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -949,9 +949,9 @@ fn main() {
949 } else if pin.signal.starts_with("INN") { 949 } else if pin.signal.starts_with("INN") {
950 // TODO handle in the future when embassy supports differential measurements 950 // TODO handle in the future when embassy supports differential measurements
951 None 951 None
952 } else if pin.signal.starts_with("IN") && pin.signal.ends_with("b") { 952 } else if pin.signal.starts_with("IN") && pin.signal.ends_with('b') {
953 // we number STM32L1 ADC bank 1 as 0..=31, bank 2 as 32..=63 953 // we number STM32L1 ADC bank 1 as 0..=31, bank 2 as 32..=63
954 let signal = pin.signal.strip_prefix("IN").unwrap().strip_suffix("b").unwrap(); 954 let signal = pin.signal.strip_prefix("IN").unwrap().strip_suffix('b').unwrap();
955 Some(32u8 + signal.parse::<u8>().unwrap()) 955 Some(32u8 + signal.parse::<u8>().unwrap())
956 } else if pin.signal.starts_with("IN") { 956 } else if pin.signal.starts_with("IN") {
957 Some(pin.signal.strip_prefix("IN").unwrap().parse().unwrap()) 957 Some(pin.signal.strip_prefix("IN").unwrap().parse().unwrap())
@@ -1022,6 +1022,10 @@ fn main() {
1022 (("dac", "CH1"), quote!(crate::dac::DacDma1)), 1022 (("dac", "CH1"), quote!(crate::dac::DacDma1)),
1023 (("dac", "CH2"), quote!(crate::dac::DacDma2)), 1023 (("dac", "CH2"), quote!(crate::dac::DacDma2)),
1024 (("timer", "UP"), quote!(crate::timer::UpDma)), 1024 (("timer", "UP"), quote!(crate::timer::UpDma)),
1025 (("timer", "CH1"), quote!(crate::timer::Ch1Dma)),
1026 (("timer", "CH2"), quote!(crate::timer::Ch2Dma)),
1027 (("timer", "CH3"), quote!(crate::timer::Ch3Dma)),
1028 (("timer", "CH4"), quote!(crate::timer::Ch4Dma)),
1025 ] 1029 ]
1026 .into(); 1030 .into();
1027 1031
@@ -1037,16 +1041,6 @@ fn main() {
1037 } 1041 }
1038 1042
1039 if let Some(tr) = signals.get(&(regs.kind, ch.signal)) { 1043 if let Some(tr) = signals.get(&(regs.kind, ch.signal)) {
1040 // TIM6 of stm32f334 is special, DMA channel for TIM6 depending on SYSCFG state
1041 if chip_name.starts_with("stm32f334") && p.name == "TIM6" {
1042 continue;
1043 }
1044
1045 // TIM6 of stm32f378 is special, DMA channel for TIM6 depending on SYSCFG state
1046 if chip_name.starts_with("stm32f378") && p.name == "TIM6" {
1047 continue;
1048 }
1049
1050 let peri = format_ident!("{}", p.name); 1044 let peri = format_ident!("{}", p.name);
1051 1045
1052 let channel = if let Some(channel) = &ch.channel { 1046 let channel = if let Some(channel) = &ch.channel {
@@ -1205,7 +1199,7 @@ fn main() {
1205 ADC3 and higher are assigned to the adc34 clock in the table 1199 ADC3 and higher are assigned to the adc34 clock in the table
1206 The adc3_common cfg directive is added if ADC3_COMMON exists 1200 The adc3_common cfg directive is added if ADC3_COMMON exists
1207 */ 1201 */
1208 let has_adc3 = METADATA.peripherals.iter().find(|p| p.name == "ADC3_COMMON").is_some(); 1202 let has_adc3 = METADATA.peripherals.iter().any(|p| p.name == "ADC3_COMMON");
1209 let set_adc345 = HashSet::from(["ADC3", "ADC4", "ADC5"]); 1203 let set_adc345 = HashSet::from(["ADC3", "ADC4", "ADC5"]);
1210 1204
1211 for m in METADATA 1205 for m in METADATA
@@ -1389,6 +1383,7 @@ fn main() {
1389 1383
1390 // ======= 1384 // =======
1391 // ADC3_COMMON is present 1385 // ADC3_COMMON is present
1386 #[allow(clippy::print_literal)]
1392 if has_adc3 { 1387 if has_adc3 {
1393 println!("cargo:rustc-cfg={}", "adc3_common"); 1388 println!("cargo:rustc-cfg={}", "adc3_common");
1394 } 1389 }
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs
index 71d7110b5..eddce0404 100644
--- a/embassy-stm32/src/timer/complementary_pwm.rs
+++ b/embassy-stm32/src/timer/complementary_pwm.rs
@@ -54,6 +54,7 @@ pub struct ComplementaryPwm<'d, T> {
54 54
55impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { 55impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
56 /// Create a new complementary PWM driver. 56 /// Create a new complementary PWM driver.
57 #[allow(clippy::too_many_arguments)]
57 pub fn new( 58 pub fn new(
58 tim: impl Peripheral<P = T> + 'd, 59 tim: impl Peripheral<P = T> + 'd,
59 _ch1: Option<PwmPin<'d, T, Ch1>>, 60 _ch1: Option<PwmPin<'d, T, Ch1>>,
@@ -165,7 +166,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> embedded_hal_02::Pwm for C
165 } 166 }
166 167
167 fn get_period(&self) -> Self::Time { 168 fn get_period(&self) -> Self::Time {
168 self.inner.get_frequency().into() 169 self.inner.get_frequency()
169 } 170 }
170 171
171 fn get_duty(&self, channel: Self::Channel) -> Self::Duty { 172 fn get_duty(&self, channel: Self::Channel) -> Self::Duty {
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs
index d07fd2776..210bf7153 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.
@@ -450,20 +470,17 @@ pub enum CountingMode {
450impl CountingMode { 470impl CountingMode {
451 /// Return whether this mode is edge-aligned (up or down). 471 /// Return whether this mode is edge-aligned (up or down).
452 pub fn is_edge_aligned(&self) -> bool { 472 pub fn is_edge_aligned(&self) -> bool {
453 match self { 473 matches!(self, CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown)
454 CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown => true,
455 _ => false,
456 }
457 } 474 }
458 475
459 /// Return whether this mode is center-aligned. 476 /// Return whether this mode is center-aligned.
460 pub fn is_center_aligned(&self) -> bool { 477 pub fn is_center_aligned(&self) -> bool {
461 match self { 478 matches!(
479 self,
462 CountingMode::CenterAlignedDownInterrupts 480 CountingMode::CenterAlignedDownInterrupts
463 | CountingMode::CenterAlignedUpInterrupts 481 | CountingMode::CenterAlignedUpInterrupts
464 | CountingMode::CenterAlignedBothInterrupts => true, 482 | CountingMode::CenterAlignedBothInterrupts
465 _ => false, 483 )
466 }
467 } 484 }
468} 485}
469 486
@@ -705,3 +722,8 @@ foreach_interrupt! {
705 722
706// Update Event trigger DMA for every timer 723// Update Event trigger DMA for every timer
707dma_trait!(UpDma, Basic16bitInstance); 724dma_trait!(UpDma, Basic16bitInstance);
725
726dma_trait!(Ch1Dma, CaptureCompare16bitInstance);
727dma_trait!(Ch2Dma, CaptureCompare16bitInstance);
728dma_trait!(Ch3Dma, CaptureCompare16bitInstance);
729dma_trait!(Ch4Dma, CaptureCompare16bitInstance);
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs
index 83a3e9291..0b4c1225f 100644
--- a/embassy-stm32/src/timer/simple_pwm.rs
+++ b/embassy-stm32/src/timer/simple_pwm.rs
@@ -160,7 +160,7 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
160 /// 160 ///
161 /// Note: 161 /// Note:
162 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 162 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
163 pub async fn gen_waveform( 163 pub async fn waveform_up(
164 &mut self, 164 &mut self,
165 dma: impl Peripheral<P = impl super::UpDma<T>>, 165 dma: impl Peripheral<P = impl super::UpDma<T>>,
166 channel: Channel, 166 channel: Channel,
@@ -226,6 +226,95 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
226 } 226 }
227} 227}
228 228
229macro_rules! impl_waveform_chx {
230 ($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => {
231 impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
232 /// Generate a sequence of PWM waveform
233 ///
234 /// Note:
235 /// you will need to provide corresponding TIMx_CHy DMA channel to use this method.
236 pub async fn $fn_name(&mut self, dma: impl Peripheral<P = impl super::$dma_ch<T>>, duty: &[u16]) {
237 use super::vals::Ccds;
238
239 assert!(duty.iter().all(|v| *v <= self.get_max_duty()));
240
241 into_ref!(dma);
242
243 #[allow(clippy::let_unit_value)] // eg. stm32f334
244 let req = dma.request();
245
246 let cc_channel = super::Channel::$cc_ch;
247
248 let original_duty_state = self.get_duty(cc_channel);
249 let original_enable_state = self.is_enabled(cc_channel);
250 let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ONUPDATE;
251 let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel);
252
253 // redirect CC DMA request onto Update Event
254 if !original_cc_dma_on_update {
255 self.inner.set_cc_dma_selection(Ccds::ONUPDATE)
256 }
257
258 if !original_cc_dma_enabled {
259 self.inner.set_cc_dma_enable_state(cc_channel, true);
260 }
261
262 if !original_enable_state {
263 self.enable(cc_channel);
264 }
265
266 unsafe {
267 #[cfg(not(any(bdma, gpdma)))]
268 use crate::dma::{Burst, FifoThreshold};
269 use crate::dma::{Transfer, TransferOptions};
270
271 let dma_transfer_option = TransferOptions {
272 #[cfg(not(any(bdma, gpdma)))]
273 fifo_threshold: Some(FifoThreshold::Full),
274 #[cfg(not(any(bdma, gpdma)))]
275 mburst: Burst::Incr8,
276 ..Default::default()
277 };
278
279 Transfer::new_write(
280 &mut dma,
281 req,
282 duty,
283 T::regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut _,
284 dma_transfer_option,
285 )
286 .await
287 };
288
289 // restore output compare state
290 if !original_enable_state {
291 self.disable(cc_channel);
292 }
293
294 self.set_duty(cc_channel, original_duty_state);
295
296 // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
297 // this can almost always trigger a DMA FIFO error.
298 //
299 // optional TODO:
300 // clean FEIF after disable UDE
301 if !original_cc_dma_enabled {
302 self.inner.set_cc_dma_enable_state(cc_channel, false);
303 }
304
305 if !original_cc_dma_on_update {
306 self.inner.set_cc_dma_selection(Ccds::ONCOMPARE)
307 }
308 }
309 }
310 };
311}
312
313impl_waveform_chx!(waveform_ch1, Ch1Dma, Ch1);
314impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2);
315impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3);
316impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4);
317
229impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d, T> { 318impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d, T> {
230 type Channel = Channel; 319 type Channel = Channel;
231 type Time = Hertz; 320 type Time = Hertz;
@@ -240,7 +329,7 @@ impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d,
240 } 329 }
241 330
242 fn get_period(&self) -> Self::Time { 331 fn get_period(&self) -> Self::Time {
243 self.inner.get_frequency().into() 332 self.inner.get_frequency()
244 } 333 }
245 334
246 fn get_duty(&self, channel: Self::Channel) -> Self::Duty { 335 fn get_duty(&self, channel: Self::Channel) -> Self::Duty {
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