diff options
| -rw-r--r-- | embassy-stm32/src/timer/simple_pwm.rs | 78 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/pwm.rs | 12 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/ws2812_pwm.rs | 6 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/pwm.rs | 12 | ||||
| -rw-r--r-- | 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); | |||
| 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, Dma> { | 58 | pub struct SimplePwm<'d, T> { |
| 59 | inner: PeripheralRef<'d, T>, | 59 | inner: PeripheralRef<'d, T>, |
| 60 | dma: PeripheralRef<'d, Dma>, | ||
| 61 | } | 60 | } |
| 62 | 61 | ||
| 63 | impl<'d, T: CaptureCompare16bitInstance, Dma> SimplePwm<'d, T, Dma> { | 62 | impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { |
| 64 | /// Create a new simple PWM driver. | 63 | /// Create a new simple PWM driver. |
| 65 | /// | ||
| 66 | /// Note: | ||
| 67 | /// If you want to use [`Self::gen_waveform()`], you need to provide corresponding TIMx_UP DMA channel. | ||
| 68 | /// Otherwise you can just put a [`dma::NoDma`](crate::dma::NoDma) | ||
| 69 | /// Currently, you can only use one channel at a time to generate waveform with [`Self::gen_waveform()`]. | ||
| 70 | /// But you can always use multiple TIM to generate multiple waveform simultaneously. | ||
| 71 | pub fn new( | 64 | pub fn new( |
| 72 | tim: impl Peripheral<P = T> + 'd, | 65 | tim: impl Peripheral<P = T> + 'd, |
| 73 | _ch1: Option<PwmPin<'d, T, Ch1>>, | 66 | _ch1: Option<PwmPin<'d, T, Ch1>>, |
| @@ -76,22 +69,16 @@ impl<'d, T: CaptureCompare16bitInstance, Dma> SimplePwm<'d, T, Dma> { | |||
| 76 | _ch4: Option<PwmPin<'d, T, Ch4>>, | 69 | _ch4: Option<PwmPin<'d, T, Ch4>>, |
| 77 | freq: Hertz, | 70 | freq: Hertz, |
| 78 | counting_mode: CountingMode, | 71 | counting_mode: CountingMode, |
| 79 | dma: impl Peripheral<P = Dma> + 'd, | ||
| 80 | ) -> Self { | 72 | ) -> Self { |
| 81 | Self::new_inner(tim, freq, counting_mode, dma) | 73 | Self::new_inner(tim, freq, counting_mode) |
| 82 | } | 74 | } |
| 83 | 75 | ||
| 84 | fn new_inner( | 76 | fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self { |
| 85 | tim: impl Peripheral<P = T> + 'd, | 77 | into_ref!(tim); |
| 86 | freq: Hertz, | ||
| 87 | counting_mode: CountingMode, | ||
| 88 | dma: impl Peripheral<P = Dma> + 'd, | ||
| 89 | ) -> Self { | ||
| 90 | into_ref!(tim, dma); | ||
| 91 | 78 | ||
| 92 | T::enable_and_reset(); | 79 | T::enable_and_reset(); |
| 93 | 80 | ||
| 94 | let mut this = Self { inner: tim, dma }; | 81 | let mut this = Self { inner: tim }; |
| 95 | 82 | ||
| 96 | this.inner.set_counting_mode(counting_mode); | 83 | this.inner.set_counting_mode(counting_mode); |
| 97 | this.set_frequency(freq); | 84 | this.set_frequency(freq); |
| @@ -165,32 +152,23 @@ impl<'d, T: CaptureCompare16bitInstance, Dma> SimplePwm<'d, T, Dma> { | |||
| 165 | } | 152 | } |
| 166 | } | 153 | } |
| 167 | 154 | ||
| 168 | impl<'d, T: CaptureCompare16bitInstance + Basic16bitInstance, Dma> SimplePwm<'d, T, Dma> | 155 | impl<'d, T: CaptureCompare16bitInstance + Basic16bitInstance> SimplePwm<'d, T> { |
| 169 | where | ||
| 170 | Dma: super::UpDma<T>, | ||
| 171 | { | ||
| 172 | /// Generate a sequence of PWM waveform | 156 | /// Generate a sequence of PWM waveform |
| 173 | pub async fn gen_waveform(&mut self, channel: Channel, duty: &[u16]) { | 157 | /// |
| 158 | /// Note: | ||
| 159 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | ||
| 160 | pub async fn gen_waveform( | ||
| 161 | &mut self, | ||
| 162 | dma: impl Peripheral<P = impl super::UpDma<T>>, | ||
| 163 | channel: Channel, | ||
| 164 | duty: &[u16], | ||
| 165 | ) { | ||
| 174 | assert!(duty.iter().all(|v| *v <= self.get_max_duty())); | 166 | assert!(duty.iter().all(|v| *v <= self.get_max_duty())); |
| 175 | 167 | ||
| 176 | #[cfg_attr(any(stm32f334, stm32f378), allow(clippy::let_unit_value))] | 168 | into_ref!(dma); |
| 177 | let req = self.dma.request(); | ||
| 178 | |||
| 179 | #[cfg(not(any(bdma, gpdma)))] | ||
| 180 | let (isr_bit, isr_reg, ifcr_reg) = { | ||
| 181 | let dma_regs = self.dma.regs(); | ||
| 182 | let isr_num = self.dma.num() / 4; | ||
| 183 | let isr_bit = self.dma.num() % 4; | ||
| 184 | let isr_reg = dma_regs.isr(isr_num); | ||
| 185 | let ifcr_reg = dma_regs.ifcr(isr_num); | ||
| 186 | (isr_bit, isr_reg, ifcr_reg) | ||
| 187 | }; | ||
| 188 | 169 | ||
| 189 | #[cfg(not(any(bdma, gpdma)))] | 170 | #[allow(clippy::let_unit_value)] // eg. stm32f334 |
| 190 | // clean DMA FIFO error before a transfer | 171 | let req = dma.request(); |
| 191 | if isr_reg.read().feif(isr_bit) { | ||
| 192 | ifcr_reg.write(|v| v.set_feif(isr_bit, true)); | ||
| 193 | } | ||
| 194 | 172 | ||
| 195 | let original_duty_state = self.get_duty(channel); | 173 | let original_duty_state = self.get_duty(channel); |
| 196 | let original_enable_state = self.is_enabled(channel); | 174 | let original_enable_state = self.is_enabled(channel); |
| @@ -218,7 +196,7 @@ where | |||
| 218 | }; | 196 | }; |
| 219 | 197 | ||
| 220 | Transfer::new_write( | 198 | Transfer::new_write( |
| 221 | &mut self.dma, | 199 | &mut dma, |
| 222 | req, | 200 | req, |
| 223 | duty, | 201 | duty, |
| 224 | T::regs_gp16().ccr(channel.index()).as_ptr() as *mut _, | 202 | T::regs_gp16().ccr(channel.index()).as_ptr() as *mut _, |
| @@ -231,21 +209,21 @@ where | |||
| 231 | if !original_enable_state { | 209 | if !original_enable_state { |
| 232 | self.disable(channel); | 210 | self.disable(channel); |
| 233 | } | 211 | } |
| 212 | |||
| 234 | self.set_duty(channel, original_duty_state); | 213 | self.set_duty(channel, original_duty_state); |
| 214 | |||
| 215 | // Since DMA is closed before timer update event trigger DMA is turn off, | ||
| 216 | // this can almost always trigger a DMA FIFO error. | ||
| 217 | // | ||
| 218 | // optional TODO: | ||
| 219 | // clean FEIF after disable UDE | ||
| 235 | if !original_update_dma_state { | 220 | if !original_update_dma_state { |
| 236 | self.inner.enable_update_dma(false); | 221 | self.inner.enable_update_dma(false); |
| 237 | |||
| 238 | #[cfg(not(any(bdma, gpdma)))] | ||
| 239 | // Since DMA could be closed before timer update event trigger DMA is turn off, this can almost always trigger a DMA FIFO error. | ||
| 240 | // Thus, we will try clean DMA FEIF after each transfer | ||
| 241 | if isr_reg.read().feif(isr_bit) { | ||
| 242 | ifcr_reg.write(|v| v.set_feif(isr_bit, true)); | ||
| 243 | } | ||
| 244 | } | 222 | } |
| 245 | } | 223 | } |
| 246 | } | 224 | } |
| 247 | 225 | ||
| 248 | impl<'d, T: CaptureCompare16bitInstance, Dma> embedded_hal_02::Pwm for SimplePwm<'d, T, Dma> { | 226 | impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d, T> { |
| 249 | type Channel = Channel; | 227 | type Channel = Channel; |
| 250 | type Time = Hertz; | 228 | type Time = Hertz; |
| 251 | type Duty = u16; | 229 | 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 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::dma; | ||
| 7 | use embassy_stm32::gpio::OutputType; | 6 | use embassy_stm32::gpio::OutputType; |
| 8 | use embassy_stm32::time::khz; | 7 | use embassy_stm32::time::khz; |
| 9 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; |
| @@ -17,16 +16,7 @@ async fn main(_spawner: Spawner) { | |||
| 17 | info!("Hello World!"); | 16 | info!("Hello World!"); |
| 18 | 17 | ||
| 19 | let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); | 18 | let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); |
| 20 | let mut pwm = SimplePwm::new( | 19 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default()); |
| 21 | p.TIM1, | ||
| 22 | Some(ch1), | ||
| 23 | None, | ||
| 24 | None, | ||
| 25 | None, | ||
| 26 | khz(10), | ||
| 27 | Default::default(), | ||
| 28 | dma::NoDma, | ||
| 29 | ); | ||
| 30 | let max = pwm.get_max_duty(); | 20 | let max = pwm.get_max_duty(); |
| 31 | pwm.enable(Channel::Ch1); | 21 | pwm.enable(Channel::Ch1); |
| 32 | 22 | ||
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) { | |||
| 45 | device_config.rcc.sys = Sysclk::PLL1_P; | 45 | device_config.rcc.sys = Sysclk::PLL1_P; |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | let dp = embassy_stm32::init(device_config); | 48 | let mut dp = embassy_stm32::init(device_config); |
| 49 | 49 | ||
| 50 | let mut ws2812_pwm = SimplePwm::new( | 50 | let mut ws2812_pwm = SimplePwm::new( |
| 51 | dp.TIM3, | 51 | dp.TIM3, |
| @@ -55,7 +55,6 @@ async fn main(_spawner: Spawner) { | |||
| 55 | None, | 55 | None, |
| 56 | khz(800), // data rate of ws2812 | 56 | khz(800), // data rate of ws2812 |
| 57 | CountingMode::EdgeAlignedUp, | 57 | CountingMode::EdgeAlignedUp, |
| 58 | dp.DMA1_CH2, | ||
| 59 | ); | 58 | ); |
| 60 | 59 | ||
| 61 | // construct ws2812 non-return-to-zero (NRZ) code bit by bit | 60 | // construct ws2812 non-return-to-zero (NRZ) code bit by bit |
| @@ -91,7 +90,8 @@ async fn main(_spawner: Spawner) { | |||
| 91 | 90 | ||
| 92 | loop { | 91 | loop { |
| 93 | for &color in color_list { | 92 | for &color in color_list { |
| 94 | ws2812_pwm.gen_waveform(pwm_channel, color).await; | 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; | ||
| 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 |
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 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::dma; | ||
| 7 | use embassy_stm32::gpio::OutputType; | 6 | use embassy_stm32::gpio::OutputType; |
| 8 | use embassy_stm32::time::khz; | 7 | use embassy_stm32::time::khz; |
| 9 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; |
| @@ -17,16 +16,7 @@ async fn main(_spawner: Spawner) { | |||
| 17 | info!("Hello World!"); | 16 | info!("Hello World!"); |
| 18 | 17 | ||
| 19 | let ch1 = PwmPin::new_ch1(p.PC0, OutputType::PushPull); | 18 | let ch1 = PwmPin::new_ch1(p.PC0, OutputType::PushPull); |
| 20 | let mut pwm = SimplePwm::new( | 19 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default()); |
| 21 | p.TIM1, | ||
| 22 | Some(ch1), | ||
| 23 | None, | ||
| 24 | None, | ||
| 25 | None, | ||
| 26 | khz(10), | ||
| 27 | Default::default(), | ||
| 28 | dma::NoDma, | ||
| 29 | ); | ||
| 30 | let max = pwm.get_max_duty(); | 20 | let max = pwm.get_max_duty(); |
| 31 | pwm.enable(Channel::Ch1); | 21 | pwm.enable(Channel::Ch1); |
| 32 | 22 | ||
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; | |||
| 7 | use embassy_stm32::time::khz; | 7 | use embassy_stm32::time::khz; |
| 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; |
| 9 | use embassy_stm32::timer::Channel; | 9 | use embassy_stm32::timer::Channel; |
| 10 | use embassy_stm32::{dma, Config}; | 10 | use embassy_stm32::Config; |
| 11 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 13 | ||
| @@ -38,16 +38,7 @@ async fn main(_spawner: Spawner) { | |||
| 38 | info!("Hello World!"); | 38 | info!("Hello World!"); |
| 39 | 39 | ||
| 40 | let ch1 = PwmPin::new_ch1(p.PA6, OutputType::PushPull); | 40 | let ch1 = PwmPin::new_ch1(p.PA6, OutputType::PushPull); |
| 41 | let mut pwm = SimplePwm::new( | 41 | let mut pwm = SimplePwm::new(p.TIM3, Some(ch1), None, None, None, khz(10), Default::default()); |
| 42 | p.TIM3, | ||
| 43 | Some(ch1), | ||
| 44 | None, | ||
| 45 | None, | ||
| 46 | None, | ||
| 47 | khz(10), | ||
| 48 | Default::default(), | ||
| 49 | dma::NoDma, | ||
| 50 | ); | ||
| 51 | let max = pwm.get_max_duty(); | 42 | let max = pwm.get_max_duty(); |
| 52 | pwm.enable(Channel::Ch1); | 43 | pwm.enable(Channel::Ch1); |
| 53 | 44 | ||
