From c276da5fcb93ce20da0c2f3bfccdeb7e0fee67a7 Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Tue, 2 Jan 2024 13:30:13 +0800 Subject: [PATCH] ask a DMA Channel only when use .gen_waveform() --- embassy-stm32/src/timer/simple_pwm.rs | 78 +++++++++----------------- examples/stm32f4/src/bin/pwm.rs | 12 +--- examples/stm32f4/src/bin/ws2812_pwm.rs | 6 +- examples/stm32g4/src/bin/pwm.rs | 12 +--- examples/stm32h7/src/bin/pwm.rs | 13 +---- 5 files changed, 35 insertions(+), 86 deletions(-) diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 7a5475c31..77d902e35 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -55,19 +55,12 @@ channel_impl!(new_ch3, Ch3, Channel3Pin); channel_impl!(new_ch4, Ch4, Channel4Pin); /// Simple PWM driver. -pub struct SimplePwm<'d, T, Dma> { +pub struct SimplePwm<'d, T> { inner: PeripheralRef<'d, T>, - dma: PeripheralRef<'d, Dma>, } -impl<'d, T: CaptureCompare16bitInstance, Dma> SimplePwm<'d, T, Dma> { +impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { /// Create a new simple PWM driver. - /// - /// Note: - /// If you want to use [`Self::gen_waveform()`], you need to provide corresponding TIMx_UP DMA channel. - /// Otherwise you can just put a [`dma::NoDma`](crate::dma::NoDma) - /// Currently, you can only use one channel at a time to generate waveform with [`Self::gen_waveform()`]. - /// But you can always use multiple TIM to generate multiple waveform simultaneously. pub fn new( tim: impl Peripheral

+ 'd, _ch1: Option>, @@ -76,22 +69,16 @@ impl<'d, T: CaptureCompare16bitInstance, Dma> SimplePwm<'d, T, Dma> { _ch4: Option>, freq: Hertz, counting_mode: CountingMode, - dma: impl Peripheral

+ 'd, ) -> Self { - Self::new_inner(tim, freq, counting_mode, dma) + Self::new_inner(tim, freq, counting_mode) } - fn new_inner( - tim: impl Peripheral

+ 'd, - freq: Hertz, - counting_mode: CountingMode, - dma: impl Peripheral

+ 'd, - ) -> Self { - into_ref!(tim, dma); + fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, counting_mode: CountingMode) -> Self { + into_ref!(tim); T::enable_and_reset(); - let mut this = Self { inner: tim, dma }; + let mut this = Self { inner: tim }; this.inner.set_counting_mode(counting_mode); this.set_frequency(freq); @@ -165,32 +152,23 @@ impl<'d, T: CaptureCompare16bitInstance, Dma> SimplePwm<'d, T, Dma> { } } -impl<'d, T: CaptureCompare16bitInstance + Basic16bitInstance, Dma> SimplePwm<'d, T, Dma> -where - Dma: super::UpDma, -{ +impl<'d, T: CaptureCompare16bitInstance + Basic16bitInstance> SimplePwm<'d, T> { /// Generate a sequence of PWM waveform - pub async fn gen_waveform(&mut self, channel: Channel, duty: &[u16]) { + /// + /// Note: + /// you will need to provide corresponding TIMx_UP DMA channel to use this method. + pub async fn gen_waveform( + &mut self, + dma: impl Peripheral

>, + channel: Channel, + duty: &[u16], + ) { assert!(duty.iter().all(|v| *v <= self.get_max_duty())); - #[cfg_attr(any(stm32f334, stm32f378), allow(clippy::let_unit_value))] - let req = self.dma.request(); + into_ref!(dma); - #[cfg(not(any(bdma, gpdma)))] - let (isr_bit, isr_reg, ifcr_reg) = { - let dma_regs = self.dma.regs(); - let isr_num = self.dma.num() / 4; - let isr_bit = self.dma.num() % 4; - let isr_reg = dma_regs.isr(isr_num); - let ifcr_reg = dma_regs.ifcr(isr_num); - (isr_bit, isr_reg, ifcr_reg) - }; - - #[cfg(not(any(bdma, gpdma)))] - // clean DMA FIFO error before a transfer - if isr_reg.read().feif(isr_bit) { - ifcr_reg.write(|v| v.set_feif(isr_bit, true)); - } + #[allow(clippy::let_unit_value)] // eg. stm32f334 + let req = dma.request(); let original_duty_state = self.get_duty(channel); let original_enable_state = self.is_enabled(channel); @@ -218,7 +196,7 @@ where }; Transfer::new_write( - &mut self.dma, + &mut dma, req, duty, T::regs_gp16().ccr(channel.index()).as_ptr() as *mut _, @@ -231,21 +209,21 @@ where if !original_enable_state { self.disable(channel); } + self.set_duty(channel, original_duty_state); + + // Since DMA is closed before timer update event trigger DMA is turn off, + // this can almost always trigger a DMA FIFO error. + // + // optional TODO: + // clean FEIF after disable UDE if !original_update_dma_state { self.inner.enable_update_dma(false); - - #[cfg(not(any(bdma, gpdma)))] - // Since DMA could be closed before timer update event trigger DMA is turn off, this can almost always trigger a DMA FIFO error. - // Thus, we will try clean DMA FEIF after each transfer - if isr_reg.read().feif(isr_bit) { - ifcr_reg.write(|v| v.set_feif(isr_bit, true)); - } } } } -impl<'d, T: CaptureCompare16bitInstance, Dma> embedded_hal_02::Pwm for SimplePwm<'d, T, Dma> { +impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d, T> { type Channel = Channel; type Time = Hertz; type Duty = u16; diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs index 92bc42ec8..8844a9f0e 100644 --- a/examples/stm32f4/src/bin/pwm.rs +++ b/examples/stm32f4/src/bin/pwm.rs @@ -3,7 +3,6 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma; use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; @@ -17,16 +16,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); - let mut pwm = SimplePwm::new( - p.TIM1, - Some(ch1), - None, - None, - None, - khz(10), - Default::default(), - dma::NoDma, - ); + let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default()); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1); diff --git a/examples/stm32f4/src/bin/ws2812_pwm.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs index 93a89f16a..239709253 100644 --- a/examples/stm32f4/src/bin/ws2812_pwm.rs +++ b/examples/stm32f4/src/bin/ws2812_pwm.rs @@ -45,7 +45,7 @@ async fn main(_spawner: Spawner) { device_config.rcc.sys = Sysclk::PLL1_P; } - let dp = embassy_stm32::init(device_config); + let mut dp = embassy_stm32::init(device_config); let mut ws2812_pwm = SimplePwm::new( dp.TIM3, @@ -55,7 +55,6 @@ async fn main(_spawner: Spawner) { None, khz(800), // data rate of ws2812 CountingMode::EdgeAlignedUp, - dp.DMA1_CH2, ); // construct ws2812 non-return-to-zero (NRZ) code bit by bit @@ -91,7 +90,8 @@ async fn main(_spawner: Spawner) { loop { for &color in color_list { - ws2812_pwm.gen_waveform(pwm_channel, color).await; + // with &mut, we can easily reuse same DMA channel multiple times + ws2812_pwm.gen_waveform(&mut dp.DMA1_CH2, pwm_channel, color).await; // ws2812 need at least 50 us low level input to confirm the input data and change it's state Timer::after_micros(50).await; // wait until ticker tick diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index 9fa004c3e..d4809a481 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs @@ -3,7 +3,6 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma; use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; @@ -17,16 +16,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let ch1 = PwmPin::new_ch1(p.PC0, OutputType::PushPull); - let mut pwm = SimplePwm::new( - p.TIM1, - Some(ch1), - None, - None, - None, - khz(10), - Default::default(), - dma::NoDma, - ); + let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default()); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1); diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index de155fc94..1e48ba67b 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.rs @@ -7,7 +7,7 @@ use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; use embassy_stm32::timer::Channel; -use embassy_stm32::{dma, Config}; +use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -38,16 +38,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let ch1 = PwmPin::new_ch1(p.PA6, OutputType::PushPull); - let mut pwm = SimplePwm::new( - p.TIM3, - Some(ch1), - None, - None, - None, - khz(10), - Default::default(), - dma::NoDma, - ); + let mut pwm = SimplePwm::new(p.TIM3, Some(ch1), None, None, None, khz(10), Default::default()); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1);