aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/timer
diff options
context:
space:
mode:
authorxoviat <[email protected]>2025-11-26 08:44:03 -0600
committerGitHub <[email protected]>2025-11-26 08:44:03 -0600
commit1045738fa3e2f2f6b2968d4b35a4b618e6235d2e (patch)
tree6cf0abaca5f946cac1598f34d8f98a922f6c10e6 /embassy-stm32/src/timer
parent3ba8bb866a19a09f25e0b21419a068fd765a9033 (diff)
parent9fa4f7309895bab81eb0e398d8f457ee528aad69 (diff)
Merge branch 'main' into time
Diffstat (limited to 'embassy-stm32/src/timer')
-rw-r--r--embassy-stm32/src/timer/complementary_pwm.rs17
-rw-r--r--embassy-stm32/src/timer/low_level.rs207
-rw-r--r--embassy-stm32/src/timer/mod.rs1
-rw-r--r--embassy-stm32/src/timer/ringbuffered.rs169
-rw-r--r--embassy-stm32/src/timer/simple_pwm.rs45
5 files changed, 290 insertions, 149 deletions
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs
index 6d4c70dff..77f19a37b 100644
--- a/embassy-stm32/src/timer/complementary_pwm.rs
+++ b/embassy-stm32/src/timer/complementary_pwm.rs
@@ -220,9 +220,11 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
220 /// 220 ///
221 /// Note: 221 /// Note:
222 /// 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.
223 #[inline(always)]
224 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]) {
225 self.inner.waveform_up(dma, channel, duty).await 224 self.inner.enable_channel(channel, true);
225 self.inner.enable_update_dma(true);
226 self.inner.setup_update_dma(dma, channel, duty).await;
227 self.inner.enable_update_dma(false);
226 } 228 }
227 229
228 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. 230 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.
@@ -254,7 +256,6 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
254 /// Also be aware that embassy timers use one of timers internally. It is possible to 256 /// Also be aware that embassy timers use one of timers internally. It is possible to
255 /// switch this timer by using `time-driver-timX` feature. 257 /// switch this timer by using `time-driver-timX` feature.
256 /// 258 ///
257 #[inline(always)]
258 pub async fn waveform_up_multi_channel( 259 pub async fn waveform_up_multi_channel(
259 &mut self, 260 &mut self,
260 dma: Peri<'_, impl super::UpDma<T>>, 261 dma: Peri<'_, impl super::UpDma<T>>,
@@ -262,15 +263,11 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
262 ending_channel: Channel, 263 ending_channel: Channel,
263 duty: &[u16], 264 duty: &[u16],
264 ) { 265 ) {
266 self.inner.enable_update_dma(true);
265 self.inner 267 self.inner
266 .waveform_up_multi_channel(dma, starting_channel, ending_channel, duty) 268 .setup_update_dma_burst(dma, starting_channel, ending_channel, duty)
267 .await; 269 .await;
268 } 270 self.inner.enable_update_dma(false);
269
270 /// Generate a sequence of PWM waveform
271 #[inline(always)]
272 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
273 self.inner.waveform(dma, duty).await;
274 } 271 }
275} 272}
276 273
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs
index f0105ece8..aba08081f 100644
--- a/embassy-stm32/src/timer/low_level.rs
+++ b/embassy-stm32/src/timer/low_level.rs
@@ -13,9 +13,10 @@ 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;
18use crate::rcc;
17use crate::time::Hertz; 19use crate::time::Hertz;
18use crate::{dma, rcc};
19 20
20/// Input capture mode. 21/// Input capture mode.
21#[derive(Clone, Copy)] 22#[derive(Clone, Copy)]
@@ -659,29 +660,88 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
659 } 660 }
660 } 661 }
661 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
662 /// Generate a sequence of PWM waveform 696 /// Generate a sequence of PWM waveform
663 /// 697 ///
664 /// Note: 698 /// Note:
665 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 699 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
666 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { 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> {
667 #[allow(clippy::let_unit_value)] // eg. stm32f334 706 #[allow(clippy::let_unit_value)] // eg. stm32f334
668 let req = dma.request(); 707 let req = dma.request();
669 708
670 let original_update_dma_state = self.get_update_dma_state(); 709 unsafe {
710 #[cfg(not(any(bdma, gpdma)))]
711 use crate::dma::{Burst, FifoThreshold};
712 use crate::dma::{Transfer, TransferOptions};
671 713
672 if !original_update_dma_state { 714 let dma_transfer_option = TransferOptions {
673 self.enable_update_dma(true); 715 #[cfg(not(any(bdma, gpdma)))]
674 } 716 fifo_threshold: Some(FifoThreshold::Full),
717 #[cfg(not(any(bdma, gpdma)))]
718 mburst: Burst::Incr8,
719 ..Default::default()
720 };
675 721
676 self.waveform_helper(dma, req, channel, duty).await; 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");
677 734
678 // Since DMA is closed before timer update event trigger DMA is turn off, 735 #[cfg(any(bdma, gpdma))]
679 // this can almost always trigger a DMA FIFO error. 736 Transfer::new_write(
680 // 737 dma,
681 // optional TODO: 738 req,
682 // clean FEIF after disable UDE 739 duty,
683 if !original_update_dma_state { 740 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32,
684 self.enable_update_dma(false); 741 dma_transfer_option,
742 )
743 }
744 }
685 } 745 }
686 } 746 }
687 747
@@ -714,13 +774,13 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
714 /// Also be aware that embassy timers use one of timers internally. It is possible to 774 /// Also be aware that embassy timers use one of timers internally. It is possible to
715 /// switch this timer by using `time-driver-timX` feature. 775 /// switch this timer by using `time-driver-timX` feature.
716 /// 776 ///
717 pub async fn waveform_up_multi_channel( 777 pub fn setup_update_dma_burst<'a>(
718 &mut self, 778 &mut self,
719 dma: Peri<'_, impl super::UpDma<T>>, 779 dma: Peri<'a, impl super::UpDma<T>>,
720 starting_channel: Channel, 780 starting_channel: Channel,
721 ending_channel: Channel, 781 ending_channel: Channel,
722 duty: &[u16], 782 duty: &'a [u16],
723 ) { 783 ) -> Transfer<'a> {
724 let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; 784 let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32;
725 let start_ch_index = starting_channel.index(); 785 let start_ch_index = starting_channel.index();
726 let end_ch_index = ending_channel.index(); 786 let end_ch_index = ending_channel.index();
@@ -738,11 +798,6 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
738 #[allow(clippy::let_unit_value)] // eg. stm32f334 798 #[allow(clippy::let_unit_value)] // eg. stm32f334
739 let req = dma.request(); 799 let req = dma.request();
740 800
741 let original_update_dma_state = self.get_update_dma_state();
742 if !original_update_dma_state {
743 self.enable_update_dma(true);
744 }
745
746 unsafe { 801 unsafe {
747 #[cfg(not(any(bdma, gpdma)))] 802 #[cfg(not(any(bdma, gpdma)))]
748 use crate::dma::{Burst, FifoThreshold}; 803 use crate::dma::{Burst, FifoThreshold};
@@ -763,115 +818,9 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
763 self.regs_gp16().dmar().as_ptr() as *mut u16, 818 self.regs_gp16().dmar().as_ptr() as *mut u16,
764 dma_transfer_option, 819 dma_transfer_option,
765 ) 820 )
766 .await
767 };
768
769 if !original_update_dma_state {
770 self.enable_update_dma(false);
771 } 821 }
772 } 822 }
773 823
774 /// Generate a sequence of PWM waveform
775 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
776 use crate::pac::timer::vals::Ccds;
777
778 #[allow(clippy::let_unit_value)] // eg. stm32f334
779 let req = dma.request();
780
781 let cc_channel = C::CHANNEL;
782
783 let original_cc_dma_on_update = self.get_cc_dma_selection() == Ccds::ON_UPDATE;
784 let original_cc_dma_enabled = self.get_cc_dma_enable_state(cc_channel);
785
786 // redirect CC DMA request onto Update Event
787 if !original_cc_dma_on_update {
788 self.set_cc_dma_selection(Ccds::ON_UPDATE)
789 }
790
791 if !original_cc_dma_enabled {
792 self.set_cc_dma_enable_state(cc_channel, true);
793 }
794
795 self.waveform_helper(dma, req, cc_channel, duty).await;
796
797 // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
798 // this can almost always trigger a DMA FIFO error.
799 //
800 // optional TODO:
801 // clean FEIF after disable UDE
802 if !original_cc_dma_enabled {
803 self.set_cc_dma_enable_state(cc_channel, false);
804 }
805
806 if !original_cc_dma_on_update {
807 self.set_cc_dma_selection(Ccds::ON_COMPARE)
808 }
809 }
810
811 async fn waveform_helper(
812 &mut self,
813 dma: Peri<'_, impl dma::Channel>,
814 req: dma::Request,
815 channel: Channel,
816 duty: &[u16],
817 ) {
818 let original_duty_state = self.get_compare_value(channel);
819 let original_enable_state = self.get_channel_enable_state(channel);
820
821 if !original_enable_state {
822 self.enable_channel(channel, true);
823 }
824
825 unsafe {
826 #[cfg(not(any(bdma, gpdma)))]
827 use crate::dma::{Burst, FifoThreshold};
828 use crate::dma::{Transfer, TransferOptions};
829
830 let dma_transfer_option = TransferOptions {
831 #[cfg(not(any(bdma, gpdma)))]
832 fifo_threshold: Some(FifoThreshold::Full),
833 #[cfg(not(any(bdma, gpdma)))]
834 mburst: Burst::Incr8,
835 ..Default::default()
836 };
837
838 match self.bits() {
839 TimerBits::Bits16 => {
840 Transfer::new_write(
841 dma,
842 req,
843 duty,
844 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16,
845 dma_transfer_option,
846 )
847 .await
848 }
849 #[cfg(not(any(stm32l0)))]
850 TimerBits::Bits32 => {
851 #[cfg(not(any(bdma, gpdma)))]
852 panic!("unsupported timer bits");
853
854 #[cfg(any(bdma, gpdma))]
855 Transfer::new_write(
856 dma,
857 req,
858 duty,
859 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32,
860 dma_transfer_option,
861 )
862 .await
863 }
864 };
865 };
866
867 // restore output compare state
868 if !original_enable_state {
869 self.enable_channel(channel, false);
870 }
871
872 self.set_compare_value(channel, original_duty_state);
873 }
874
875 /// Get capture value for a channel. 824 /// Get capture value for a channel.
876 pub fn get_capture_value(&self, channel: Channel) -> u32 { 825 pub fn get_capture_value(&self, channel: Channel) -> u32 {
877 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/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 6c9ef17e0..484e9fd81 100644
--- a/embassy-stm32/src/timer/simple_pwm.rs
+++ b/embassy-stm32/src/timer/simple_pwm.rs
@@ -4,6 +4,7 @@ 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::ringbuffered::RingBufferedPwmChannel;
7use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; 8use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin};
8use crate::Peri; 9use crate::Peri;
9#[cfg(gpio_v2)] 10#[cfg(gpio_v2)]
@@ -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`].
@@ -316,9 +344,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
316 /// 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.
317 /// Also be aware that embassy timers use one of timers internally. It is possible to 345 /// Also be aware that embassy timers use one of timers internally. It is possible to
318 /// switch this timer by using `time-driver-timX` feature. 346 /// switch this timer by using `time-driver-timX` feature.
319 #[inline(always)]
320 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]) {
321 self.inner.waveform_up(dma, channel, duty).await; 348 self.inner.enable_channel(channel, true);
349 self.inner.enable_update_dma(true);
350 self.inner.setup_update_dma(dma, channel, duty).await;
351 self.inner.enable_update_dma(false);
322 } 352 }
323 353
324 /// 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.
@@ -350,7 +380,6 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
350 /// Also be aware that embassy timers use one of timers internally. It is possible to 380 /// Also be aware that embassy timers use one of timers internally. It is possible to
351 /// switch this timer by using `time-driver-timX` feature. 381 /// switch this timer by using `time-driver-timX` feature.
352 /// 382 ///
353 #[inline(always)]
354 pub async fn waveform_up_multi_channel( 383 pub async fn waveform_up_multi_channel(
355 &mut self, 384 &mut self,
356 dma: Peri<'_, impl super::UpDma<T>>, 385 dma: Peri<'_, impl super::UpDma<T>>,
@@ -358,15 +387,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
358 ending_channel: Channel, 387 ending_channel: Channel,
359 duty: &[u16], 388 duty: &[u16],
360 ) { 389 ) {
390 self.inner.enable_update_dma(true);
361 self.inner 391 self.inner
362 .waveform_up_multi_channel(dma, starting_channel, ending_channel, duty) 392 .setup_update_dma_burst(dma, starting_channel, ending_channel, duty)
363 .await; 393 .await;
364 } 394 self.inner.enable_update_dma(false);
365
366 /// Generate a sequence of PWM waveform
367 #[inline(always)]
368 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
369 self.inner.waveform(dma, duty).await;
370 } 395 }
371} 396}
372 397