aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/timer
diff options
context:
space:
mode:
authorxoviat <[email protected]>2025-12-01 08:39:32 -0600
committerxoviat <[email protected]>2025-12-01 08:39:32 -0600
commitb466ba29d2857815e5b25af7d8ab82d5b7e05e30 (patch)
tree36b0439a19e9b3649f89d72db6226b77ae4ec0fc /embassy-stm32/src/timer
parentedb14f8c0966e1d22f396cbd631e5835a9a5104b (diff)
timer: allow 16 bit dma buffers for 32 bit timers.
Diffstat (limited to 'embassy-stm32/src/timer')
-rw-r--r--embassy-stm32/src/timer/complementary_pwm.rs18
-rw-r--r--embassy-stm32/src/timer/low_level.rs20
-rw-r--r--embassy-stm32/src/timer/ringbuffered.rs27
-rw-r--r--embassy-stm32/src/timer/simple_pwm.rs24
4 files changed, 49 insertions, 40 deletions
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs
index 996759060..b9434d37b 100644
--- a/embassy-stm32/src/timer/complementary_pwm.rs
+++ b/embassy-stm32/src/timer/complementary_pwm.rs
@@ -8,6 +8,7 @@ use super::low_level::{CountingMode, OutputPolarity, Timer};
8use super::simple_pwm::PwmPin; 8use super::simple_pwm::PwmPin;
9use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; 9use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin};
10use crate::Peri; 10use crate::Peri;
11use crate::dma::word::Word;
11use crate::gpio::{AnyPin, OutputType}; 12use crate::gpio::{AnyPin, OutputType};
12use crate::time::Hertz; 13use crate::time::Hertz;
13use crate::timer::TimerChannel; 14use crate::timer::TimerChannel;
@@ -220,8 +221,14 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
220 /// 221 ///
221 /// Note: 222 /// Note:
222 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 223 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
223 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[T::Word]) { 224 pub async fn waveform_up<W: Word + Into<T::Word>>(
225 &mut self,
226 dma: Peri<'_, impl super::UpDma<T>>,
227 channel: Channel,
228 duty: &[W],
229 ) {
224 self.inner.enable_channel(channel, true); 230 self.inner.enable_channel(channel, true);
231 self.inner.set_compare_value(channel, 0.into());
225 self.inner.enable_update_dma(true); 232 self.inner.enable_update_dma(true);
226 self.inner.setup_update_dma(dma, channel, duty).await; 233 self.inner.setup_update_dma(dma, channel, duty).await;
227 self.inner.enable_update_dma(false); 234 self.inner.enable_update_dma(false);
@@ -256,18 +263,21 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
256 /// Also be aware that embassy timers use one of timers internally. It is possible to 263 /// Also be aware that embassy timers use one of timers internally. It is possible to
257 /// switch this timer by using `time-driver-timX` feature. 264 /// switch this timer by using `time-driver-timX` feature.
258 /// 265 ///
259 pub async fn waveform_up_multi_channel( 266 pub async fn waveform_up_multi_channel<W: Word + Into<T::Word>>(
260 &mut self, 267 &mut self,
261 dma: Peri<'_, impl super::UpDma<T>>, 268 dma: Peri<'_, impl super::UpDma<T>>,
262 starting_channel: Channel, 269 starting_channel: Channel,
263 ending_channel: Channel, 270 ending_channel: Channel,
264 duty: &[T::Word], 271 duty: &[W],
265 ) { 272 ) {
266 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] 273 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
267 .iter() 274 .iter()
268 .filter(|ch| ch.index() >= starting_channel.index()) 275 .filter(|ch| ch.index() >= starting_channel.index())
269 .filter(|ch| ch.index() <= ending_channel.index()) 276 .filter(|ch| ch.index() <= ending_channel.index())
270 .for_each(|ch| self.inner.enable_channel(*ch, true)); 277 .for_each(|ch| {
278 self.inner.enable_channel(*ch, true);
279 self.inner.set_compare_value(*ch, 0.into());
280 });
271 self.inner.enable_update_dma(true); 281 self.inner.enable_update_dma(true);
272 self.inner 282 self.inner
273 .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) 283 .setup_update_dma_burst(dma, starting_channel, ending_channel, duty)
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs
index 73a81bff1..1af66aec1 100644
--- a/embassy-stm32/src/timer/low_level.rs
+++ b/embassy-stm32/src/timer/low_level.rs
@@ -628,12 +628,12 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
628 } 628 }
629 629
630 /// Setup a ring buffer for the channel 630 /// Setup a ring buffer for the channel
631 pub fn setup_ring_buffer<'a>( 631 pub fn setup_ring_buffer<'a, W: Word + Into<T::Word>>(
632 &mut self, 632 &mut self,
633 dma: Peri<'a, impl super::UpDma<T>>, 633 dma: Peri<'a, impl super::UpDma<T>>,
634 channel: Channel, 634 channel: Channel,
635 dma_buf: &'a mut [T::Word], 635 dma_buf: &'a mut [W],
636 ) -> WritableRingBuffer<'a, T::Word> { 636 ) -> WritableRingBuffer<'a, W> {
637 #[allow(clippy::let_unit_value)] // eg. stm32f334 637 #[allow(clippy::let_unit_value)] // eg. stm32f334
638 let req = dma.request(); 638 let req = dma.request();
639 639
@@ -653,7 +653,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
653 WritableRingBuffer::new( 653 WritableRingBuffer::new(
654 dma, 654 dma,
655 req, 655 req,
656 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut T::Word, 656 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut W,
657 dma_buf, 657 dma_buf,
658 dma_transfer_option, 658 dma_transfer_option,
659 ) 659 )
@@ -664,11 +664,11 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
664 /// 664 ///
665 /// Note: 665 /// Note:
666 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 666 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
667 pub fn setup_update_dma<'a>( 667 pub fn setup_update_dma<'a, W: Word + Into<T::Word>>(
668 &mut self, 668 &mut self,
669 dma: Peri<'a, impl super::UpDma<T>>, 669 dma: Peri<'a, impl super::UpDma<T>>,
670 channel: Channel, 670 channel: Channel,
671 duty: &'a [T::Word], 671 duty: &'a [W],
672 ) -> Transfer<'a> { 672 ) -> Transfer<'a> {
673 #[allow(clippy::let_unit_value)] // eg. stm32f334 673 #[allow(clippy::let_unit_value)] // eg. stm32f334
674 let req = dma.request(); 674 let req = dma.request();
@@ -690,7 +690,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
690 dma, 690 dma,
691 req, 691 req,
692 duty, 692 duty,
693 self.regs_gp16().ccr(channel.index()).as_ptr() as *mut T::Word, 693 self.regs_gp16().ccr(channel.index()).as_ptr() as *mut W,
694 dma_transfer_option, 694 dma_transfer_option,
695 ) 695 )
696 } 696 }
@@ -725,12 +725,12 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
725 /// Also be aware that embassy timers use one of timers internally. It is possible to 725 /// Also be aware that embassy timers use one of timers internally. It is possible to
726 /// switch this timer by using `time-driver-timX` feature. 726 /// switch this timer by using `time-driver-timX` feature.
727 /// 727 ///
728 pub fn setup_update_dma_burst<'a>( 728 pub fn setup_update_dma_burst<'a, W: Word + Into<T::Word>>(
729 &mut self, 729 &mut self,
730 dma: Peri<'a, impl super::UpDma<T>>, 730 dma: Peri<'a, impl super::UpDma<T>>,
731 starting_channel: Channel, 731 starting_channel: Channel,
732 ending_channel: Channel, 732 ending_channel: Channel,
733 duty: &'a [T::Word], 733 duty: &'a [W],
734 ) -> Transfer<'a> { 734 ) -> Transfer<'a> {
735 let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; 735 let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32;
736 let start_ch_index = starting_channel.index(); 736 let start_ch_index = starting_channel.index();
@@ -766,7 +766,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
766 dma, 766 dma,
767 req, 767 req,
768 duty, 768 duty,
769 self.regs_gp16().dmar().as_ptr() as *mut T::Word, 769 self.regs_gp16().dmar().as_ptr() as *mut W,
770 dma_transfer_option, 770 dma_transfer_option,
771 ) 771 )
772 } 772 }
diff --git a/embassy-stm32/src/timer/ringbuffered.rs b/embassy-stm32/src/timer/ringbuffered.rs
index f5a4328d1..fbb6b19ea 100644
--- a/embassy-stm32/src/timer/ringbuffered.rs
+++ b/embassy-stm32/src/timer/ringbuffered.rs
@@ -7,6 +7,7 @@ use super::low_level::Timer;
7use super::{Channel, GeneralInstance4Channel}; 7use super::{Channel, GeneralInstance4Channel};
8use crate::dma::WritableRingBuffer; 8use crate::dma::WritableRingBuffer;
9use crate::dma::ringbuffer::Error; 9use crate::dma::ringbuffer::Error;
10use crate::dma::word::Word;
10 11
11/// A PWM channel that uses a DMA ring buffer for continuous waveform generation. 12/// A PWM channel that uses a DMA ring buffer for continuous waveform generation.
12/// 13///
@@ -23,17 +24,17 @@ use crate::dma::ringbuffer::Error;
23/// channel.start(); // Start DMA transfer 24/// channel.start(); // Start DMA transfer
24/// channel.write(&[100, 200, 300]).ok(); // Update duty cycles 25/// channel.write(&[100, 200, 300]).ok(); // Update duty cycles
25/// ``` 26/// ```
26pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> { 27pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel, W: Word + Into<T::Word>> {
27 timer: ManuallyDrop<Timer<'d, T>>, 28 timer: ManuallyDrop<Timer<'d, T>>,
28 ring_buf: WritableRingBuffer<'d, T::Word>, 29 ring_buf: WritableRingBuffer<'d, W>,
29 channel: Channel, 30 channel: Channel,
30} 31}
31 32
32impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { 33impl<'d, T: GeneralInstance4Channel, W: Word + Into<T::Word>> RingBufferedPwmChannel<'d, T, W> {
33 pub(crate) fn new( 34 pub(crate) fn new(
34 timer: ManuallyDrop<Timer<'d, T>>, 35 timer: ManuallyDrop<Timer<'d, T>>,
35 channel: Channel, 36 channel: Channel,
36 ring_buf: WritableRingBuffer<'d, T::Word>, 37 ring_buf: WritableRingBuffer<'d, W>,
37 ) -> Self { 38 ) -> Self {
38 Self { 39 Self {
39 timer, 40 timer,
@@ -55,18 +56,18 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> {
55 } 56 }
56 57
57 /// Write elements directly to the raw buffer. This can be used to fill the buffer before starting the DMA transfer. 58 /// Write elements directly to the raw buffer. This can be used to fill the buffer before starting the DMA transfer.
58 pub fn write_immediate(&mut self, buf: &[T::Word]) -> Result<(usize, usize), Error> { 59 pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> {
59 self.ring_buf.write_immediate(buf) 60 self.ring_buf.write_immediate(buf)
60 } 61 }
61 62
62 /// Write elements from the ring buffer 63 /// Write elements from the ring buffer
63 /// Return a tuple of the length written and the length remaining in the buffer 64 /// Return a tuple of the length written and the length remaining in the buffer
64 pub fn write(&mut self, buf: &[T::Word]) -> Result<(usize, usize), Error> { 65 pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), Error> {
65 self.ring_buf.write(buf) 66 self.ring_buf.write(buf)
66 } 67 }
67 68
68 /// Write an exact number of elements to the ringbuffer. 69 /// Write an exact number of elements to the ringbuffer.
69 pub async fn write_exact(&mut self, buffer: &[T::Word]) -> Result<usize, Error> { 70 pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, Error> {
70 self.ring_buf.write_exact(buffer).await 71 self.ring_buf.write_exact(buffer).await
71 } 72 }
72 73
@@ -155,15 +156,3 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> {
155 self.timer.set_output_compare_mode(self.channel, mode); 156 self.timer.set_output_compare_mode(self.channel, mode);
156 } 157 }
157} 158}
158
159/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`].
160pub struct RingBufferedPwmChannels<'d, T: GeneralInstance4Channel> {
161 /// Channel 1
162 pub ch1: RingBufferedPwmChannel<'d, T>,
163 /// Channel 2
164 pub ch2: RingBufferedPwmChannel<'d, T>,
165 /// Channel 3
166 pub ch3: RingBufferedPwmChannel<'d, T>,
167 /// Channel 4
168 pub ch4: RingBufferedPwmChannel<'d, T>,
169}
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs
index 466c56db2..3f37ffcd9 100644
--- a/embassy-stm32/src/timer/simple_pwm.rs
+++ b/embassy-stm32/src/timer/simple_pwm.rs
@@ -7,6 +7,7 @@ use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer};
7use super::ringbuffered::RingBufferedPwmChannel; 7use super::ringbuffered::RingBufferedPwmChannel;
8use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; 8use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin};
9use crate::Peri; 9use crate::Peri;
10use crate::dma::word::Word;
10#[cfg(gpio_v2)] 11#[cfg(gpio_v2)]
11use crate::gpio::Pull; 12use crate::gpio::Pull;
12use crate::gpio::{AfType, AnyPin, OutputType, Speed}; 13use crate::gpio::{AfType, AnyPin, OutputType, Speed};
@@ -171,11 +172,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> {
171 /// 172 ///
172 /// # Panics 173 /// # Panics
173 /// Panics if `dma_buf` is empty or longer than 65535 elements. 174 /// Panics if `dma_buf` is empty or longer than 65535 elements.
174 pub fn into_ring_buffered_channel( 175 pub fn into_ring_buffered_channel<W: Word + Into<T::Word>>(
175 mut self, 176 mut self,
176 tx_dma: Peri<'d, impl super::UpDma<T>>, 177 tx_dma: Peri<'d, impl super::UpDma<T>>,
177 dma_buf: &'d mut [T::Word], 178 dma_buf: &'d mut [W],
178 ) -> RingBufferedPwmChannel<'d, T> { 179 ) -> RingBufferedPwmChannel<'d, T, W> {
179 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); 180 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
180 181
181 self.timer.enable_update_dma(true); 182 self.timer.enable_update_dma(true);
@@ -344,8 +345,14 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
344 /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. 345 /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.
345 /// Also be aware that embassy timers use one of timers internally. It is possible to 346 /// Also be aware that embassy timers use one of timers internally. It is possible to
346 /// switch this timer by using `time-driver-timX` feature. 347 /// switch this timer by using `time-driver-timX` feature.
347 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[T::Word]) { 348 pub async fn waveform_up<W: Word + Into<T::Word>>(
349 &mut self,
350 dma: Peri<'_, impl super::UpDma<T>>,
351 channel: Channel,
352 duty: &[W],
353 ) {
348 self.inner.enable_channel(channel, true); 354 self.inner.enable_channel(channel, true);
355 self.inner.set_compare_value(channel, 0.into());
349 self.inner.enable_update_dma(true); 356 self.inner.enable_update_dma(true);
350 self.inner.setup_update_dma(dma, channel, duty).await; 357 self.inner.setup_update_dma(dma, channel, duty).await;
351 self.inner.enable_update_dma(false); 358 self.inner.enable_update_dma(false);
@@ -380,18 +387,21 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
380 /// Also be aware that embassy timers use one of timers internally. It is possible to 387 /// Also be aware that embassy timers use one of timers internally. It is possible to
381 /// switch this timer by using `time-driver-timX` feature. 388 /// switch this timer by using `time-driver-timX` feature.
382 /// 389 ///
383 pub async fn waveform_up_multi_channel( 390 pub async fn waveform_up_multi_channel<W: Word + Into<T::Word>>(
384 &mut self, 391 &mut self,
385 dma: Peri<'_, impl super::UpDma<T>>, 392 dma: Peri<'_, impl super::UpDma<T>>,
386 starting_channel: Channel, 393 starting_channel: Channel,
387 ending_channel: Channel, 394 ending_channel: Channel,
388 duty: &[T::Word], 395 duty: &[W],
389 ) { 396 ) {
390 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] 397 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
391 .iter() 398 .iter()
392 .filter(|ch| ch.index() >= starting_channel.index()) 399 .filter(|ch| ch.index() >= starting_channel.index())
393 .filter(|ch| ch.index() <= ending_channel.index()) 400 .filter(|ch| ch.index() <= ending_channel.index())
394 .for_each(|ch| self.inner.enable_channel(*ch, true)); 401 .for_each(|ch| {
402 self.inner.enable_channel(*ch, true);
403 self.inner.set_compare_value(*ch, 0.into());
404 });
395 self.inner.enable_update_dma(true); 405 self.inner.enable_update_dma(true);
396 self.inner 406 self.inner
397 .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) 407 .setup_update_dma_burst(dma, starting_channel, ending_channel, duty)