aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32
diff options
context:
space:
mode:
authoreZio Pan <[email protected]>2023-12-28 16:23:47 +0800
committereZio Pan <[email protected]>2023-12-28 20:09:12 +0800
commit8c2a6df03b852233ef6c774896cbb00c2a15040f (patch)
treec8bd878540fafbf589055a91993b8e3b01661a09 /embassy-stm32
parenteebfee189a592427423d3a3ad22132d59926a0e8 (diff)
implement PWM waveform generating with DMA
Diffstat (limited to 'embassy-stm32')
-rw-r--r--embassy-stm32/build.rs11
-rw-r--r--embassy-stm32/src/timer/mod.rs18
-rw-r--r--embassy-stm32/src/timer/simple_pwm.rs100
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
694dma_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);
55channel_impl!(new_ch4, Ch4, Channel4Pin); 55channel_impl!(new_ch4, Ch4, Channel4Pin);
56 56
57/// Simple PWM driver. 57/// Simple PWM driver.
58pub struct SimplePwm<'d, T> { 58pub struct SimplePwm<'d, T, Dma> {
59 inner: PeripheralRef<'d, T>, 59 inner: PeripheralRef<'d, T>,
60 dma: PeripheralRef<'d, Dma>,
60} 61}
61 62
62impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { 63impl<'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
144impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d, T> { 150impl<'d, T: CaptureCompare16bitInstance + Basic16bitInstance, Dma> SimplePwm<'d, T, Dma>
151where
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
214impl<'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;