aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/timer
diff options
context:
space:
mode:
authorElias Hanelt <[email protected]>2025-11-25 13:18:00 -0800
committerElias Hanelt <[email protected]>2025-11-25 13:18:00 -0800
commit2b219b7cb59ec3e4370edc88538ea3ea996f37b9 (patch)
treec602a4b7c39d50ada628f2d715c6cc59f2e35c3a /embassy-stm32/src/timer
parent576fb23faabf6df7f2c9ed2039e94d3586a3788f (diff)
parent906eaee53f84381dd10583894edf2de67275f083 (diff)
Merge remote-tracking branch 'origin/main' into feature/spi-bidi
Diffstat (limited to 'embassy-stm32/src/timer')
-rw-r--r--embassy-stm32/src/timer/complementary_pwm.rs108
-rw-r--r--embassy-stm32/src/timer/input_capture.rs1
-rw-r--r--embassy-stm32/src/timer/low_level.rs181
-rw-r--r--embassy-stm32/src/timer/mod.rs1
-rw-r--r--embassy-stm32/src/timer/pwm_input.rs1
-rw-r--r--embassy-stm32/src/timer/ringbuffered.rs169
-rw-r--r--embassy-stm32/src/timer/simple_pwm.rs274
7 files changed, 451 insertions, 284 deletions
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs
index 9a56a41fb..77f19a37b 100644
--- a/embassy-stm32/src/timer/complementary_pwm.rs
+++ b/embassy-stm32/src/timer/complementary_pwm.rs
@@ -77,8 +77,6 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
77 77
78 this.inner.set_counting_mode(counting_mode); 78 this.inner.set_counting_mode(counting_mode);
79 this.set_frequency(freq); 79 this.set_frequency(freq);
80 this.inner.start();
81
82 this.inner.enable_outputs(); 80 this.inner.enable_outputs();
83 81
84 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] 82 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
@@ -89,6 +87,10 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
89 }); 87 });
90 this.inner.set_autoreload_preload(true); 88 this.inner.set_autoreload_preload(true);
91 89
90 // Generate update event so pre-load registers are written to the shadow registers
91 this.inner.generate_update_event();
92 this.inner.start();
93
92 this 94 this
93 } 95 }
94 96
@@ -160,8 +162,8 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
160 162
161 /// Set PWM frequency. 163 /// Set PWM frequency.
162 /// 164 ///
163 /// Note: when you call this, the max duty value changes, so you will have to 165 /// Note: that the frequency will not be applied in the timer until an update event
164 /// call `set_duty` on all channels with the duty calculated based on the new max duty. 166 /// occurs.
165 pub fn set_frequency(&mut self, freq: Hertz) { 167 pub fn set_frequency(&mut self, freq: Hertz) {
166 let multiplier = if self.inner.get_counting_mode().is_center_aligned() { 168 let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
167 2u8 169 2u8
@@ -219,59 +221,53 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
219 /// Note: 221 /// Note:
220 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 222 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
221 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { 223 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) {
222 #[allow(clippy::let_unit_value)] // eg. stm32f334 224 self.inner.enable_channel(channel, true);
223 let req = dma.request(); 225 self.inner.enable_update_dma(true);
224 226 self.inner.setup_update_dma(dma, channel, duty).await;
225 let original_duty_state = self.inner.get_compare_value(channel); 227 self.inner.enable_update_dma(false);
226 let original_enable_state = self.inner.get_channel_enable_state(channel); 228 }
227 let original_update_dma_state = self.inner.get_update_dma_state();
228
229 if !original_update_dma_state {
230 self.inner.enable_update_dma(true);
231 }
232
233 if !original_enable_state {
234 self.inner.enable_channel(channel, true);
235 }
236
237 unsafe {
238 #[cfg(not(any(bdma, gpdma)))]
239 use crate::dma::{Burst, FifoThreshold};
240 use crate::dma::{Transfer, TransferOptions};
241
242 let dma_transfer_option = TransferOptions {
243 #[cfg(not(any(bdma, gpdma)))]
244 fifo_threshold: Some(FifoThreshold::Full),
245 #[cfg(not(any(bdma, gpdma)))]
246 mburst: Burst::Incr8,
247 ..Default::default()
248 };
249
250 Transfer::new_write(
251 dma,
252 req,
253 duty,
254 self.inner.regs_gp16().ccr(channel.index()).as_ptr() as *mut u16,
255 dma_transfer_option,
256 )
257 .await
258 };
259
260 // restore output compare state
261 if !original_enable_state {
262 self.inner.enable_channel(channel, false);
263 }
264
265 self.inner.set_compare_value(channel, original_duty_state);
266 229
267 // Since DMA is closed before timer update event trigger DMA is turn off, 230 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.
268 // this can almost always trigger a DMA FIFO error. 231 ///
269 // 232 /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers
270 // optional TODO: 233 /// in sequence on each update event (UEV). The data is written via the DMAR register using the
271 // clean FEIF after disable UDE 234 /// DMA base address (DBA) and burst length (DBL) configured in the DCR register.
272 if !original_update_dma_state { 235 ///
273 self.inner.enable_update_dma(false); 236 /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row
274 } 237 /// represents a single update event and each column corresponds to a specific timer channel (starting
238 /// from `starting_channel` up to and including `ending_channel`).
239 ///
240 /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like:
241 ///
242 /// ```rust,ignore
243 /// let dma_buf: [u16; 16] = [
244 /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1
245 /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2
246 /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3
247 /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4
248 /// ];
249 /// ```
250 ///
251 /// Each group of `N` values (where `N` is number of channels) is transferred on one update event,
252 /// updating the duty cycles of all selected channels simultaneously.
253 ///
254 /// Note:
255 /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.
256 /// 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.
258 ///
259 pub async fn waveform_up_multi_channel(
260 &mut self,
261 dma: Peri<'_, impl super::UpDma<T>>,
262 starting_channel: Channel,
263 ending_channel: Channel,
264 duty: &[u16],
265 ) {
266 self.inner.enable_update_dma(true);
267 self.inner
268 .setup_update_dma_burst(dma, starting_channel, ending_channel, duty)
269 .await;
270 self.inner.enable_update_dma(false);
275 } 271 }
276} 272}
277 273
diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs
index 2a4ec2db0..9cf0f8c34 100644
--- a/embassy-stm32/src/timer/input_capture.rs
+++ b/embassy-stm32/src/timer/input_capture.rs
@@ -60,6 +60,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> {
60 this.inner.set_counting_mode(counting_mode); 60 this.inner.set_counting_mode(counting_mode);
61 this.inner.set_tick_freq(freq); 61 this.inner.set_tick_freq(freq);
62 this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details 62 this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details
63 this.inner.generate_update_event();
63 this.inner.start(); 64 this.inner.start();
64 65
65 // enable NVIC interrupt 66 // enable NVIC interrupt
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs
index 0122fe4f7..aba08081f 100644
--- a/embassy-stm32/src/timer/low_level.rs
+++ b/embassy-stm32/src/timer/low_level.rs
@@ -13,6 +13,7 @@ use embassy_hal_internal::Peri;
13pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; 13pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource};
14 14
15use super::*; 15use super::*;
16use crate::dma::{Transfer, WritableRingBuffer};
16use crate::pac::timer::vals; 17use crate::pac::timer::vals;
17use crate::rcc; 18use crate::rcc;
18use crate::time::Hertz; 19use crate::time::Hertz;
@@ -272,6 +273,17 @@ impl<'d, T: CoreInstance> Timer<'d, T> {
272 self.regs_core().cr1().modify(|r| r.set_cen(true)); 273 self.regs_core().cr1().modify(|r| r.set_cen(true));
273 } 274 }
274 275
276 /// Generate timer update event from software.
277 ///
278 /// Set URS to avoid generating interrupt or DMA request. This update event is only
279 /// used to load value from pre-load registers. If called when the timer is running,
280 /// it may disrupt the output waveform.
281 pub fn generate_update_event(&self) {
282 self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY));
283 self.regs_core().egr().write(|r| r.set_ug(true));
284 self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT));
285 }
286
275 /// Stop the timer. 287 /// Stop the timer.
276 pub fn stop(&self) { 288 pub fn stop(&self) {
277 self.regs_core().cr1().modify(|r| r.set_cen(false)); 289 self.regs_core().cr1().modify(|r| r.set_cen(false));
@@ -322,10 +334,6 @@ impl<'d, T: CoreInstance> Timer<'d, T> {
322 let regs = self.regs_core(); 334 let regs = self.regs_core();
323 regs.psc().write_value(psc); 335 regs.psc().write_value(psc);
324 regs.arr().write(|r| r.set_arr(arr)); 336 regs.arr().write(|r| r.set_arr(arr));
325
326 regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY));
327 regs.egr().write(|r| r.set_ug(true));
328 regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT));
329 } 337 }
330 #[cfg(not(stm32l0))] 338 #[cfg(not(stm32l0))]
331 TimerBits::Bits32 => { 339 TimerBits::Bits32 => {
@@ -335,10 +343,6 @@ impl<'d, T: CoreInstance> Timer<'d, T> {
335 let regs = self.regs_gp32_unchecked(); 343 let regs = self.regs_gp32_unchecked();
336 regs.psc().write_value(psc); 344 regs.psc().write_value(psc);
337 regs.arr().write_value(arr); 345 regs.arr().write_value(arr);
338
339 regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY));
340 regs.egr().write(|r| r.set_ug(true));
341 regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT));
342 } 346 }
343 } 347 }
344 } 348 }
@@ -656,6 +660,167 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
656 } 660 }
657 } 661 }
658 662
663 /// Setup a ring buffer for the channel
664 pub fn setup_ring_buffer<'a>(
665 &mut self,
666 dma: Peri<'a, impl super::UpDma<T>>,
667 channel: Channel,
668 dma_buf: &'a mut [u16],
669 ) -> WritableRingBuffer<'a, u16> {
670 #[allow(clippy::let_unit_value)] // eg. stm32f334
671 let req = dma.request();
672
673 unsafe {
674 use crate::dma::TransferOptions;
675 #[cfg(not(any(bdma, gpdma)))]
676 use crate::dma::{Burst, FifoThreshold};
677
678 let dma_transfer_option = TransferOptions {
679 #[cfg(not(any(bdma, gpdma)))]
680 fifo_threshold: Some(FifoThreshold::Full),
681 #[cfg(not(any(bdma, gpdma)))]
682 mburst: Burst::Incr8,
683 ..Default::default()
684 };
685
686 WritableRingBuffer::new(
687 dma,
688 req,
689 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16,
690 dma_buf,
691 dma_transfer_option,
692 )
693 }
694 }
695
696 /// Generate a sequence of PWM waveform
697 ///
698 /// Note:
699 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
700 pub fn setup_update_dma<'a>(
701 &mut self,
702 dma: Peri<'a, impl super::UpDma<T>>,
703 channel: Channel,
704 duty: &'a [u16],
705 ) -> Transfer<'a> {
706 #[allow(clippy::let_unit_value)] // eg. stm32f334
707 let req = dma.request();
708
709 unsafe {
710 #[cfg(not(any(bdma, gpdma)))]
711 use crate::dma::{Burst, FifoThreshold};
712 use crate::dma::{Transfer, TransferOptions};
713
714 let dma_transfer_option = TransferOptions {
715 #[cfg(not(any(bdma, gpdma)))]
716 fifo_threshold: Some(FifoThreshold::Full),
717 #[cfg(not(any(bdma, gpdma)))]
718 mburst: Burst::Incr8,
719 ..Default::default()
720 };
721
722 match self.bits() {
723 TimerBits::Bits16 => Transfer::new_write(
724 dma,
725 req,
726 duty,
727 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16,
728 dma_transfer_option,
729 ),
730 #[cfg(not(any(stm32l0)))]
731 TimerBits::Bits32 => {
732 #[cfg(not(any(bdma, gpdma)))]
733 panic!("unsupported timer bits");
734
735 #[cfg(any(bdma, gpdma))]
736 Transfer::new_write(
737 dma,
738 req,
739 duty,
740 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32,
741 dma_transfer_option,
742 )
743 }
744 }
745 }
746 }
747
748 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.
749 ///
750 /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers
751 /// in sequence on each update event (UEV). The data is written via the DMAR register using the
752 /// DMA base address (DBA) and burst length (DBL) configured in the DCR register.
753 ///
754 /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row
755 /// represents a single update event and each column corresponds to a specific timer channel (starting
756 /// from `starting_channel` up to and including `ending_channel`).
757 ///
758 /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like:
759 ///
760 /// ```rust,ignore
761 /// let dma_buf: [u16; 16] = [
762 /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1
763 /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2
764 /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3
765 /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4
766 /// ];
767 /// ```
768 ///
769 /// Each group of `N` values (where `N` is number of channels) is transferred on one update event,
770 /// updating the duty cycles of all selected channels simultaneously.
771 ///
772 /// Note:
773 /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.
774 /// Also be aware that embassy timers use one of timers internally. It is possible to
775 /// switch this timer by using `time-driver-timX` feature.
776 ///
777 pub fn setup_update_dma_burst<'a>(
778 &mut self,
779 dma: Peri<'a, impl super::UpDma<T>>,
780 starting_channel: Channel,
781 ending_channel: Channel,
782 duty: &'a [u16],
783 ) -> Transfer<'a> {
784 let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32;
785 let start_ch_index = starting_channel.index();
786 let end_ch_index = ending_channel.index();
787
788 assert!(start_ch_index <= end_ch_index);
789
790 let ccrx_addr = self.regs_gp16().ccr(start_ch_index).as_ptr() as u32;
791 self.regs_gp16()
792 .dcr()
793 .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8));
794 self.regs_gp16()
795 .dcr()
796 .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8));
797
798 #[allow(clippy::let_unit_value)] // eg. stm32f334
799 let req = dma.request();
800
801 unsafe {
802 #[cfg(not(any(bdma, gpdma)))]
803 use crate::dma::{Burst, FifoThreshold};
804 use crate::dma::{Transfer, TransferOptions};
805
806 let dma_transfer_option = TransferOptions {
807 #[cfg(not(any(bdma, gpdma)))]
808 fifo_threshold: Some(FifoThreshold::Full),
809 #[cfg(not(any(bdma, gpdma)))]
810 mburst: Burst::Incr4,
811 ..Default::default()
812 };
813
814 Transfer::new_write(
815 dma,
816 req,
817 duty,
818 self.regs_gp16().dmar().as_ptr() as *mut u16,
819 dma_transfer_option,
820 )
821 }
822 }
823
659 /// Get capture value for a channel. 824 /// Get capture value for a channel.
660 pub fn get_capture_value(&self, channel: Channel) -> u32 { 825 pub fn get_capture_value(&self, channel: Channel) -> u32 {
661 self.get_compare_value(channel) 826 self.get_compare_value(channel)
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs
index 804d1ef37..3fa363881 100644
--- a/embassy-stm32/src/timer/mod.rs
+++ b/embassy-stm32/src/timer/mod.rs
@@ -12,6 +12,7 @@ pub mod low_level;
12pub mod one_pulse; 12pub mod one_pulse;
13pub mod pwm_input; 13pub mod pwm_input;
14pub mod qei; 14pub mod qei;
15pub mod ringbuffered;
15pub mod simple_pwm; 16pub mod simple_pwm;
16 17
17use crate::interrupt; 18use crate::interrupt;
diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs
index da8a79b09..057ab011a 100644
--- a/embassy-stm32/src/timer/pwm_input.rs
+++ b/embassy-stm32/src/timer/pwm_input.rs
@@ -47,6 +47,7 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> {
47 inner.set_counting_mode(CountingMode::EdgeAlignedUp); 47 inner.set_counting_mode(CountingMode::EdgeAlignedUp);
48 inner.set_tick_freq(freq); 48 inner.set_tick_freq(freq);
49 inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details 49 inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details
50 inner.generate_update_event();
50 inner.start(); 51 inner.start();
51 52
52 // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6 53 // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6
diff --git a/embassy-stm32/src/timer/ringbuffered.rs b/embassy-stm32/src/timer/ringbuffered.rs
new file mode 100644
index 000000000..e8f97bf59
--- /dev/null
+++ b/embassy-stm32/src/timer/ringbuffered.rs
@@ -0,0 +1,169 @@
1//! RingBuffered PWM driver.
2
3use core::mem::ManuallyDrop;
4use core::task::Waker;
5
6use super::low_level::Timer;
7use super::{Channel, GeneralInstance4Channel};
8use crate::dma::WritableRingBuffer;
9use crate::dma::ringbuffer::Error;
10
11/// A PWM channel that uses a DMA ring buffer for continuous waveform generation.
12///
13/// This allows you to continuously update PWM duty cycles via DMA without blocking the CPU.
14/// The ring buffer enables smooth, uninterrupted waveform generation by automatically cycling
15/// through duty cycle values stored in memory.
16///
17/// You can write new duty cycle values to the ring buffer while it's running, enabling
18/// dynamic waveform generation for applications like motor control, LED dimming, or audio output.
19///
20/// # Example
21/// ```ignore
22/// let mut channel = pwm.ch1().into_ring_buffered_channel(dma_ch, &mut buffer);
23/// channel.start(); // Start DMA transfer
24/// channel.write(&[100, 200, 300]).ok(); // Update duty cycles
25/// ```
26pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> {
27 timer: ManuallyDrop<Timer<'d, T>>,
28 ring_buf: WritableRingBuffer<'d, u16>,
29 channel: Channel,
30}
31
32impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> {
33 pub(crate) fn new(
34 timer: ManuallyDrop<Timer<'d, T>>,
35 channel: Channel,
36 ring_buf: WritableRingBuffer<'d, u16>,
37 ) -> Self {
38 Self {
39 timer,
40 ring_buf,
41 channel,
42 }
43 }
44
45 /// Start the ring buffer operation.
46 ///
47 /// You must call this after creating it for it to work.
48 pub fn start(&mut self) {
49 self.ring_buf.start()
50 }
51
52 /// Clear all data in the ring buffer.
53 pub fn clear(&mut self) {
54 self.ring_buf.clear()
55 }
56
57 /// 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: &[u16]) -> Result<(usize, usize), Error> {
59 self.ring_buf.write_immediate(buf)
60 }
61
62 /// Write elements from the ring buffer
63 /// Return a tuple of the length written and the length remaining in the buffer
64 pub fn write(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> {
65 self.ring_buf.write(buf)
66 }
67
68 /// Write an exact number of elements to the ringbuffer.
69 pub async fn write_exact(&mut self, buffer: &[u16]) -> Result<usize, Error> {
70 self.ring_buf.write_exact(buffer).await
71 }
72
73 /// Wait for any ring buffer write error.
74 pub async fn wait_write_error(&mut self) -> Result<usize, Error> {
75 self.ring_buf.wait_write_error().await
76 }
77
78 /// The current length of the ringbuffer
79 pub fn len(&mut self) -> Result<usize, Error> {
80 self.ring_buf.len()
81 }
82
83 /// The capacity of the ringbuffer
84 pub const fn capacity(&self) -> usize {
85 self.ring_buf.capacity()
86 }
87
88 /// Set a waker to be woken when at least one byte is send.
89 pub fn set_waker(&mut self, waker: &Waker) {
90 self.ring_buf.set_waker(waker)
91 }
92
93 /// Request the DMA to reset. The configuration for this channel will not be preserved.
94 ///
95 /// This doesn't immediately stop the transfer, you have to wait until is_running returns false.
96 pub fn request_reset(&mut self) {
97 self.ring_buf.request_reset()
98 }
99
100 /// Request the transfer to pause, keeping the existing configuration for this channel.
101 /// To restart the transfer, call [`start`](Self::start) again.
102 ///
103 /// This doesn't immediately stop the transfer, you have to wait until is_running returns false.
104 pub fn request_pause(&mut self) {
105 self.ring_buf.request_pause()
106 }
107
108 /// Return whether DMA is still running.
109 ///
110 /// If this returns false, it can be because either the transfer finished, or it was requested to stop early with request_stop.
111 pub fn is_running(&mut self) -> bool {
112 self.ring_buf.is_running()
113 }
114
115 /// Stop the DMA transfer and await until the buffer is empty.
116 ///
117 /// This disables the DMA transfer's circular mode so that the transfer stops when all available data has been written.
118 ///
119 /// This is designed to be used with streaming output data such as the I2S/SAI or DAC.
120 pub async fn stop(&mut self) {
121 self.ring_buf.stop().await
122 }
123
124 /// Enable the given channel.
125 pub fn enable(&mut self) {
126 self.timer.enable_channel(self.channel, true);
127 }
128
129 /// Disable the given channel.
130 pub fn disable(&mut self) {
131 self.timer.enable_channel(self.channel, false);
132 }
133
134 /// Check whether given channel is enabled
135 pub fn is_enabled(&self) -> bool {
136 self.timer.get_channel_enable_state(self.channel)
137 }
138
139 /// Get max duty value.
140 ///
141 /// This value depends on the configured frequency and the timer's clock rate from RCC.
142 pub fn max_duty_cycle(&self) -> u16 {
143 let max = self.timer.get_max_compare_value();
144 assert!(max < u16::MAX as u32);
145 max as u16 + 1
146 }
147
148 /// Set the output polarity for a given channel.
149 pub fn set_polarity(&mut self, polarity: super::low_level::OutputPolarity) {
150 self.timer.set_output_polarity(self.channel, polarity);
151 }
152
153 /// Set the output compare mode for a given channel.
154 pub fn set_output_compare_mode(&mut self, mode: super::low_level::OutputCompareMode) {
155 self.timer.set_output_compare_mode(self.channel, mode);
156 }
157}
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 36303aeb4..484e9fd81 100644
--- a/embassy-stm32/src/timer/simple_pwm.rs
+++ b/embassy-stm32/src/timer/simple_pwm.rs
@@ -4,7 +4,8 @@ use core::marker::PhantomData;
4use core::mem::ManuallyDrop; 4use core::mem::ManuallyDrop;
5 5
6use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; 6use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer};
7use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin}; 7use super::ringbuffered::RingBufferedPwmChannel;
8use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin};
8use crate::Peri; 9use crate::Peri;
9#[cfg(gpio_v2)] 10#[cfg(gpio_v2)]
10use crate::gpio::Pull; 11use crate::gpio::Pull;
@@ -158,6 +159,33 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> {
158 pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) { 159 pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) {
159 self.timer.set_output_compare_mode(self.channel, mode); 160 self.timer.set_output_compare_mode(self.channel, mode);
160 } 161 }
162
163 /// Convert this PWM channel into a ring-buffered PWM channel.
164 ///
165 /// This allows continuous PWM waveform generation using a DMA ring buffer.
166 /// The ring buffer enables dynamic updates to the PWM duty cycle without blocking.
167 ///
168 /// # Arguments
169 /// * `tx_dma` - The DMA channel to use for transferring duty cycle values
170 /// * `dma_buf` - The buffer to use as a ring buffer (must be non-empty and <= 65535 elements)
171 ///
172 /// # Panics
173 /// Panics if `dma_buf` is empty or longer than 65535 elements.
174 pub fn into_ring_buffered_channel(
175 mut self,
176 tx_dma: Peri<'d, impl super::UpDma<T>>,
177 dma_buf: &'d mut [u16],
178 ) -> RingBufferedPwmChannel<'d, T> {
179 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
180
181 self.timer.enable_update_dma(true);
182
183 RingBufferedPwmChannel::new(
184 unsafe { self.timer.clone_unchecked() },
185 self.channel,
186 self.timer.setup_ring_buffer(tx_dma, self.channel, dma_buf),
187 )
188 }
161} 189}
162 190
163/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. 191/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`].
@@ -198,7 +226,6 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
198 this.inner.set_counting_mode(counting_mode); 226 this.inner.set_counting_mode(counting_mode);
199 this.set_frequency(freq); 227 this.set_frequency(freq);
200 this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details 228 this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details
201 this.inner.start();
202 229
203 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] 230 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
204 .iter() 231 .iter()
@@ -207,6 +234,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
207 234
208 this.inner.set_output_compare_preload(channel, true); 235 this.inner.set_output_compare_preload(channel, true);
209 }); 236 });
237 this.inner.set_autoreload_preload(true);
238
239 // Generate update event so pre-load registers are written to the shadow registers
240 this.inner.generate_update_event();
241 this.inner.start();
210 242
211 this 243 this
212 } 244 }
@@ -285,8 +317,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
285 317
286 /// Set PWM frequency. 318 /// Set PWM frequency.
287 /// 319 ///
288 /// Note: when you call this, the max duty value changes, so you will have to 320 /// Note: that the frequency will not be applied in the timer until an update event
289 /// call `set_duty` on all channels with the duty calculated based on the new max duty. 321 /// occurs.
290 pub fn set_frequency(&mut self, freq: Hertz) { 322 pub fn set_frequency(&mut self, freq: Hertz) {
291 // TODO: prevent ARR = u16::MAX? 323 // TODO: prevent ARR = u16::MAX?
292 let multiplier = if self.inner.get_counting_mode().is_center_aligned() { 324 let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
@@ -309,80 +341,14 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
309 /// Generate a sequence of PWM waveform 341 /// Generate a sequence of PWM waveform
310 /// 342 ///
311 /// Note: 343 /// Note:
312 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 344 /// 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 /// switch this timer by using `time-driver-timX` feature.
313 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { 347 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) {
314 #[allow(clippy::let_unit_value)] // eg. stm32f334 348 self.inner.enable_channel(channel, true);
315 let req = dma.request(); 349 self.inner.enable_update_dma(true);
316 350 self.inner.setup_update_dma(dma, channel, duty).await;
317 let original_duty_state = self.channel(channel).current_duty_cycle(); 351 self.inner.enable_update_dma(false);
318 let original_enable_state = self.channel(channel).is_enabled();
319 let original_update_dma_state = self.inner.get_update_dma_state();
320
321 if !original_update_dma_state {
322 self.inner.enable_update_dma(true);
323 }
324
325 if !original_enable_state {
326 self.channel(channel).enable();
327 }
328
329 unsafe {
330 #[cfg(not(any(bdma, gpdma)))]
331 use crate::dma::{Burst, FifoThreshold};
332 use crate::dma::{Transfer, TransferOptions};
333
334 let dma_transfer_option = TransferOptions {
335 #[cfg(not(any(bdma, gpdma)))]
336 fifo_threshold: Some(FifoThreshold::Full),
337 #[cfg(not(any(bdma, gpdma)))]
338 mburst: Burst::Incr8,
339 ..Default::default()
340 };
341
342 match self.inner.bits() {
343 TimerBits::Bits16 => {
344 Transfer::new_write(
345 dma,
346 req,
347 duty,
348 self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16,
349 dma_transfer_option,
350 )
351 .await
352 }
353 #[cfg(not(any(stm32l0)))]
354 TimerBits::Bits32 => {
355 #[cfg(not(any(bdma, gpdma)))]
356 panic!("unsupported timer bits");
357
358 #[cfg(any(bdma, gpdma))]
359 Transfer::new_write(
360 dma,
361 req,
362 duty,
363 self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32,
364 dma_transfer_option,
365 )
366 .await
367 }
368 };
369 };
370
371 // restore output compare state
372 if !original_enable_state {
373 self.channel(channel).disable();
374 }
375
376 self.channel(channel).set_duty_cycle(original_duty_state);
377
378 // Since DMA is closed before timer update event trigger DMA is turn off,
379 // this can almost always trigger a DMA FIFO error.
380 //
381 // optional TODO:
382 // clean FEIF after disable UDE
383 if !original_update_dma_state {
384 self.inner.enable_update_dma(false);
385 }
386 } 352 }
387 353
388 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. 354 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.
@@ -397,18 +363,23 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
397 /// 363 ///
398 /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: 364 /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like:
399 /// 365 ///
366 /// ```rust,ignore
400 /// let dma_buf: [u16; 16] = [ 367 /// let dma_buf: [u16; 16] = [
401 /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 368 /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1
402 /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 369 /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2
403 /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 370 /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3
404 /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 371 /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4
405 /// ]; 372 /// ];
373 /// ```
406 /// 374 ///
407 /// Each group of N values (where N = number of channels) is transferred on one update event, 375 /// Each group of `N` values (where `N` is number of channels) is transferred on one update event,
408 /// updating the duty cycles of all selected channels simultaneously. 376 /// updating the duty cycles of all selected channels simultaneously.
409 /// 377 ///
410 /// Note: 378 /// Note:
411 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 379 /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.
380 /// 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.
382 ///
412 pub async fn waveform_up_multi_channel( 383 pub async fn waveform_up_multi_channel(
413 &mut self, 384 &mut self,
414 dma: Peri<'_, impl super::UpDma<T>>, 385 dma: Peri<'_, impl super::UpDma<T>>,
@@ -416,148 +387,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
416 ending_channel: Channel, 387 ending_channel: Channel,
417 duty: &[u16], 388 duty: &[u16],
418 ) { 389 ) {
419 let cr1_addr = self.inner.regs_gp16().cr1().as_ptr() as u32; 390 self.inner.enable_update_dma(true);
420 let start_ch_index = starting_channel.index();
421 let end_ch_index = ending_channel.index();
422
423 assert!(start_ch_index <= end_ch_index);
424
425 let ccrx_addr = self.inner.regs_gp16().ccr(start_ch_index).as_ptr() as u32;
426 self.inner
427 .regs_gp16()
428 .dcr()
429 .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8));
430 self.inner 391 self.inner
431 .regs_gp16() 392 .setup_update_dma_burst(dma, starting_channel, ending_channel, duty)
432 .dcr() 393 .await;
433 .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8)); 394 self.inner.enable_update_dma(false);
434
435 #[allow(clippy::let_unit_value)] // eg. stm32f334
436 let req = dma.request();
437
438 let original_update_dma_state = self.inner.get_update_dma_state();
439 if !original_update_dma_state {
440 self.inner.enable_update_dma(true);
441 }
442
443 unsafe {
444 #[cfg(not(any(bdma, gpdma)))]
445 use crate::dma::{Burst, FifoThreshold};
446 use crate::dma::{Transfer, TransferOptions};
447
448 let dma_transfer_option = TransferOptions {
449 #[cfg(not(any(bdma, gpdma)))]
450 fifo_threshold: Some(FifoThreshold::Full),
451 #[cfg(not(any(bdma, gpdma)))]
452 mburst: Burst::Incr4,
453 ..Default::default()
454 };
455
456 Transfer::new_write(
457 dma,
458 req,
459 duty,
460 self.inner.regs_gp16().dmar().as_ptr() as *mut u16,
461 dma_transfer_option,
462 )
463 .await
464 };
465
466 if !original_update_dma_state {
467 self.inner.enable_update_dma(false);
468 }
469 }
470}
471
472impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
473 /// Generate a sequence of PWM waveform
474 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
475 use crate::pac::timer::vals::Ccds;
476
477 #[allow(clippy::let_unit_value)] // eg. stm32f334
478 let req = dma.request();
479
480 let cc_channel = C::CHANNEL;
481
482 let original_duty_state = self.channel(cc_channel).current_duty_cycle();
483 let original_enable_state = self.channel(cc_channel).is_enabled();
484 let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE;
485 let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel);
486
487 // redirect CC DMA request onto Update Event
488 if !original_cc_dma_on_update {
489 self.inner.set_cc_dma_selection(Ccds::ON_UPDATE)
490 }
491
492 if !original_cc_dma_enabled {
493 self.inner.set_cc_dma_enable_state(cc_channel, true);
494 }
495
496 if !original_enable_state {
497 self.channel(cc_channel).enable();
498 }
499
500 unsafe {
501 #[cfg(not(any(bdma, gpdma)))]
502 use crate::dma::{Burst, FifoThreshold};
503 use crate::dma::{Transfer, TransferOptions};
504
505 let dma_transfer_option = TransferOptions {
506 #[cfg(not(any(bdma, gpdma)))]
507 fifo_threshold: Some(FifoThreshold::Full),
508 #[cfg(not(any(bdma, gpdma)))]
509 mburst: Burst::Incr8,
510 ..Default::default()
511 };
512
513 match self.inner.bits() {
514 TimerBits::Bits16 => {
515 Transfer::new_write(
516 dma,
517 req,
518 duty,
519 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16,
520 dma_transfer_option,
521 )
522 .await
523 }
524 #[cfg(not(any(stm32l0)))]
525 TimerBits::Bits32 => {
526 #[cfg(not(any(bdma, gpdma)))]
527 panic!("unsupported timer bits");
528
529 #[cfg(any(bdma, gpdma))]
530 Transfer::new_write(
531 dma,
532 req,
533 duty,
534 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32,
535 dma_transfer_option,
536 )
537 .await
538 }
539 };
540 };
541
542 // restore output compare state
543 if !original_enable_state {
544 self.channel(cc_channel).disable();
545 }
546
547 self.channel(cc_channel).set_duty_cycle(original_duty_state);
548
549 // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
550 // this can almost always trigger a DMA FIFO error.
551 //
552 // optional TODO:
553 // clean FEIF after disable UDE
554 if !original_cc_dma_enabled {
555 self.inner.set_cc_dma_enable_state(cc_channel, false);
556 }
557
558 if !original_cc_dma_on_update {
559 self.inner.set_cc_dma_selection(Ccds::ON_COMPARE)
560 }
561 } 395 }
562} 396}
563 397