aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/timer
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-stm32/src/timer')
-rw-r--r--embassy-stm32/src/timer/complementary_pwm.rs210
-rw-r--r--embassy-stm32/src/timer/input_capture.rs50
-rw-r--r--embassy-stm32/src/timer/low_level.rs40
-rw-r--r--embassy-stm32/src/timer/mod.rs101
-rw-r--r--embassy-stm32/src/timer/one_pulse.rs70
-rw-r--r--embassy-stm32/src/timer/pwm_input.rs25
-rw-r--r--embassy-stm32/src/timer/qei.rs56
-rw-r--r--embassy-stm32/src/timer/simple_pwm.rs342
8 files changed, 605 insertions, 289 deletions
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs
index 8eec6c0c7..484aae1d0 100644
--- a/embassy-stm32/src/timer/complementary_pwm.rs
+++ b/embassy-stm32/src/timer/complementary_pwm.rs
@@ -2,71 +2,70 @@
2 2
3use core::marker::PhantomData; 3use core::marker::PhantomData;
4 4
5use stm32_metapac::timer::vals::Ckd; 5pub use stm32_metapac::timer::vals::{Ckd, Ossi, Ossr};
6 6
7use super::low_level::{CountingMode, OutputPolarity, Timer}; 7use super::low_level::{CountingMode, OutputPolarity, Timer};
8use super::simple_pwm::{Ch1, Ch2, Ch3, Ch4, PwmPin}; 8use super::simple_pwm::PwmPin;
9use super::{ 9use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin};
10 AdvancedInstance4Channel, Channel, Channel1ComplementaryPin, Channel2ComplementaryPin, Channel3ComplementaryPin,
11 Channel4ComplementaryPin,
12};
13use crate::gpio::{AnyPin, OutputType}; 10use crate::gpio::{AnyPin, OutputType};
14use crate::time::Hertz; 11use crate::time::Hertz;
15use crate::timer::low_level::OutputCompareMode; 12use crate::timer::low_level::OutputCompareMode;
13use crate::timer::TimerChannel;
16use crate::Peri; 14use crate::Peri;
17 15
18/// Complementary PWM pin wrapper. 16/// Complementary PWM pin wrapper.
19/// 17///
20/// This wraps a pin to make it usable with PWM. 18/// This wraps a pin to make it usable with PWM.
21pub struct ComplementaryPwmPin<'d, T, C> { 19pub struct ComplementaryPwmPin<'d, T, C, #[cfg(afio)] A> {
22 _pin: Peri<'d, AnyPin>, 20 #[allow(unused)]
23 phantom: PhantomData<(T, C)>, 21 pin: Peri<'d, AnyPin>,
22 phantom: PhantomData<if_afio!((T, C, A))>,
24} 23}
25 24
26macro_rules! complementary_channel_impl { 25impl<'d, T: AdvancedInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(ComplementaryPwmPin<'d, T, C, A>) {
27 ($new_chx:ident, $channel:ident, $pin_trait:ident) => { 26 /// Create a new complementary PWM pin instance.
28 impl<'d, T: AdvancedInstance4Channel> ComplementaryPwmPin<'d, T, $channel> { 27 pub fn new(pin: Peri<'d, if_afio!(impl TimerComplementaryPin<T, C, A>)>, output_type: OutputType) -> Self {
29 #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")] 28 critical_section::with(|_| {
30 pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, output_type: OutputType) -> Self { 29 pin.set_low();
31 critical_section::with(|_| { 30 set_as_af!(
32 pin.set_low(); 31 pin,
33 pin.set_as_af( 32 crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh)
34 pin.af_num(), 33 );
35 crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh), 34 });
36 ); 35 ComplementaryPwmPin {
37 }); 36 pin: pin.into(),
38 ComplementaryPwmPin { 37 phantom: PhantomData,
39 _pin: pin.into(),
40 phantom: PhantomData,
41 }
42 }
43 } 38 }
44 }; 39 }
45} 40}
46 41
47complementary_channel_impl!(new_ch1, Ch1, Channel1ComplementaryPin);
48complementary_channel_impl!(new_ch2, Ch2, Channel2ComplementaryPin);
49complementary_channel_impl!(new_ch3, Ch3, Channel3ComplementaryPin);
50complementary_channel_impl!(new_ch4, Ch4, Channel4ComplementaryPin);
51
52/// PWM driver with support for standard and complementary outputs. 42/// PWM driver with support for standard and complementary outputs.
53pub struct ComplementaryPwm<'d, T: AdvancedInstance4Channel> { 43pub struct ComplementaryPwm<'d, T: AdvancedInstance4Channel> {
54 inner: Timer<'d, T>, 44 inner: Timer<'d, T>,
55} 45}
56 46
47#[derive(Copy, Clone, Debug, PartialEq, Eq)]
48/// Determines which outputs are active when PWM is in idle mode
49pub enum IdlePolarity {
50 /// Normal channels are forced active and complementary channels are forced inactive
51 OisActive,
52 /// Normal channels are forced inactive and complementary channels are forced active
53 OisnActive,
54}
55
57impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { 56impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
58 /// Create a new complementary PWM driver. 57 /// Create a new complementary PWM driver.
59 #[allow(clippy::too_many_arguments)] 58 #[allow(clippy::too_many_arguments, unused)]
60 pub fn new( 59 pub fn new<#[cfg(afio)] A>(
61 tim: Peri<'d, T>, 60 tim: Peri<'d, T>,
62 _ch1: Option<PwmPin<'d, T, Ch1>>, 61 ch1: Option<if_afio!(PwmPin<'d, T, Ch1, A>)>,
63 _ch1n: Option<ComplementaryPwmPin<'d, T, Ch1>>, 62 ch1n: Option<if_afio!(ComplementaryPwmPin<'d, T, Ch1, A>)>,
64 _ch2: Option<PwmPin<'d, T, Ch2>>, 63 ch2: Option<if_afio!(PwmPin<'d, T, Ch2, A>)>,
65 _ch2n: Option<ComplementaryPwmPin<'d, T, Ch2>>, 64 ch2n: Option<if_afio!(ComplementaryPwmPin<'d, T, Ch2, A>)>,
66 _ch3: Option<PwmPin<'d, T, Ch3>>, 65 ch3: Option<if_afio!(PwmPin<'d, T, Ch3, A>)>,
67 _ch3n: Option<ComplementaryPwmPin<'d, T, Ch3>>, 66 ch3n: Option<if_afio!(ComplementaryPwmPin<'d, T, Ch3, A>)>,
68 _ch4: Option<PwmPin<'d, T, Ch4>>, 67 ch4: Option<if_afio!(PwmPin<'d, T, Ch4, A>)>,
69 _ch4n: Option<ComplementaryPwmPin<'d, T, Ch4>>, 68 ch4n: Option<if_afio!(ComplementaryPwmPin<'d, T, Ch4, A>)>,
70 freq: Hertz, 69 freq: Hertz,
71 counting_mode: CountingMode, 70 counting_mode: CountingMode,
72 ) -> Self { 71 ) -> Self {
@@ -88,10 +87,55 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
88 this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); 87 this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1);
89 this.inner.set_output_compare_preload(channel, true); 88 this.inner.set_output_compare_preload(channel, true);
90 }); 89 });
90 this.inner.set_autoreload_preload(true);
91 91
92 this 92 this
93 } 93 }
94 94
95 /// Sets the idle output state for the given channels.
96 pub fn set_output_idle_state(&mut self, channels: &[Channel], polarity: IdlePolarity) {
97 let ois_active = matches!(polarity, IdlePolarity::OisActive);
98 for &channel in channels {
99 self.inner.set_ois(channel, ois_active);
100 self.inner.set_oisn(channel, !ois_active);
101 }
102 }
103
104 /// Set state of OSSI-bit in BDTR register
105 pub fn set_off_state_selection_idle(&mut self, val: Ossi) {
106 self.inner.set_ossi(val);
107 }
108
109 /// Get state of OSSI-bit in BDTR register
110 pub fn get_off_state_selection_idle(&self) -> Ossi {
111 self.inner.get_ossi()
112 }
113
114 /// Set state of OSSR-bit in BDTR register
115 pub fn set_off_state_selection_run(&mut self, val: Ossr) {
116 self.inner.set_ossr(val);
117 }
118
119 /// Get state of OSSR-bit in BDTR register
120 pub fn get_off_state_selection_run(&self) -> Ossr {
121 self.inner.get_ossr()
122 }
123
124 /// Trigger break input from software
125 pub fn trigger_software_break(&mut self, n: usize) {
126 self.inner.trigger_software_break(n);
127 }
128
129 /// Set Master Output Enable
130 pub fn set_master_output_enable(&mut self, enable: bool) {
131 self.inner.set_moe(enable);
132 }
133
134 /// Get Master Output Enable
135 pub fn get_master_output_enable(&self) -> bool {
136 self.inner.get_moe()
137 }
138
95 /// Enable the given channel. 139 /// Enable the given channel.
96 pub fn enable(&mut self, channel: Channel) { 140 pub fn enable(&mut self, channel: Channel) {
97 self.inner.enable_channel(channel, true); 141 self.inner.enable_channel(channel, true);
@@ -121,7 +165,11 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
121 /// 165 ///
122 /// This value depends on the configured frequency and the timer's clock rate from RCC. 166 /// This value depends on the configured frequency and the timer's clock rate from RCC.
123 pub fn get_max_duty(&self) -> u16 { 167 pub fn get_max_duty(&self) -> u16 {
124 self.inner.get_max_compare_value() as u16 + 1 168 if self.inner.get_counting_mode().is_center_aligned() {
169 self.inner.get_max_compare_value() as u16
170 } else {
171 self.inner.get_max_compare_value() as u16 + 1
172 }
125 } 173 }
126 174
127 /// Set the duty for a given channel. 175 /// Set the duty for a given channel.
@@ -138,6 +186,16 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
138 self.inner.set_complementary_output_polarity(channel, polarity); 186 self.inner.set_complementary_output_polarity(channel, polarity);
139 } 187 }
140 188
189 /// Set the main output polarity for a given channel.
190 pub fn set_main_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
191 self.inner.set_output_polarity(channel, polarity);
192 }
193
194 /// Set the complementary output polarity for a given channel.
195 pub fn set_complementary_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
196 self.inner.set_complementary_output_polarity(channel, polarity);
197 }
198
141 /// Set the dead time as a proportion of max_duty 199 /// Set the dead time as a proportion of max_duty
142 pub fn set_dead_time(&mut self, value: u16) { 200 pub fn set_dead_time(&mut self, value: u16) {
143 let (ckd, value) = compute_dead_time_value(value); 201 let (ckd, value) = compute_dead_time_value(value);
@@ -145,6 +203,66 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
145 self.inner.set_dead_time_clock_division(ckd); 203 self.inner.set_dead_time_clock_division(ckd);
146 self.inner.set_dead_time_value(value); 204 self.inner.set_dead_time_value(value);
147 } 205 }
206
207 /// Generate a sequence of PWM waveform
208 ///
209 /// Note:
210 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
211 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) {
212 #[allow(clippy::let_unit_value)] // eg. stm32f334
213 let req = dma.request();
214
215 let original_duty_state = self.inner.get_compare_value(channel);
216 let original_enable_state = self.inner.get_channel_enable_state(channel);
217 let original_update_dma_state = self.inner.get_update_dma_state();
218
219 if !original_update_dma_state {
220 self.inner.enable_update_dma(true);
221 }
222
223 if !original_enable_state {
224 self.inner.enable_channel(channel, true);
225 }
226
227 unsafe {
228 #[cfg(not(any(bdma, gpdma)))]
229 use crate::dma::{Burst, FifoThreshold};
230 use crate::dma::{Transfer, TransferOptions};
231
232 let dma_transfer_option = TransferOptions {
233 #[cfg(not(any(bdma, gpdma)))]
234 fifo_threshold: Some(FifoThreshold::Full),
235 #[cfg(not(any(bdma, gpdma)))]
236 mburst: Burst::Incr8,
237 ..Default::default()
238 };
239
240 Transfer::new_write(
241 dma,
242 req,
243 duty,
244 self.inner.regs_gp16().ccr(channel.index()).as_ptr() as *mut u16,
245 dma_transfer_option,
246 )
247 .await
248 };
249
250 // restore output compare state
251 if !original_enable_state {
252 self.inner.enable_channel(channel, false);
253 }
254
255 self.inner.set_compare_value(channel, original_duty_state);
256
257 // Since DMA is closed before timer update event trigger DMA is turn off,
258 // this can almost always trigger a DMA FIFO error.
259 //
260 // optional TODO:
261 // clean FEIF after disable UDE
262 if !original_update_dma_state {
263 self.inner.enable_update_dma(false);
264 }
265 }
148} 266}
149 267
150impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> { 268impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> {
@@ -171,7 +289,11 @@ impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<
171 } 289 }
172 290
173 fn get_max_duty(&self) -> Self::Duty { 291 fn get_max_duty(&self) -> Self::Duty {
174 self.inner.get_max_compare_value() as u16 + 1 292 if self.inner.get_counting_mode().is_center_aligned() {
293 self.inner.get_max_compare_value() as u16
294 } else {
295 self.inner.get_max_compare_value() as u16 + 1
296 }
175 } 297 }
176 298
177 fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { 299 fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {
diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs
index ec8b1ddf1..7a25e6c21 100644
--- a/embassy-stm32/src/timer/input_capture.rs
+++ b/embassy-stm32/src/timer/input_capture.rs
@@ -6,44 +6,33 @@ use core::pin::Pin;
6use core::task::{Context, Poll}; 6use core::task::{Context, Poll};
7 7
8use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer}; 8use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer};
9use super::{ 9use super::{CaptureCompareInterruptHandler, Channel, GeneralInstance4Channel, TimerPin};
10 CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin,
11 GeneralInstance4Channel,
12};
13pub use super::{Ch1, Ch2, Ch3, Ch4}; 10pub use super::{Ch1, Ch2, Ch3, Ch4};
14use crate::gpio::{AfType, AnyPin, Pull}; 11use crate::gpio::{AfType, AnyPin, Pull};
15use crate::interrupt::typelevel::{Binding, Interrupt}; 12use crate::interrupt::typelevel::{Binding, Interrupt};
16use crate::time::Hertz; 13use crate::time::Hertz;
14use crate::timer::TimerChannel;
17use crate::Peri; 15use crate::Peri;
18 16
19/// Capture pin wrapper. 17/// Capture pin wrapper.
20/// 18///
21/// This wraps a pin to make it usable with capture. 19/// This wraps a pin to make it usable with capture.
22pub struct CapturePin<'d, T, C> { 20pub struct CapturePin<'d, T, C, #[cfg(afio)] A> {
23 _pin: Peri<'d, AnyPin>, 21 #[allow(unused)]
24 phantom: PhantomData<(T, C)>, 22 pin: Peri<'d, AnyPin>,
23 phantom: PhantomData<if_afio!((T, C, A))>,
25} 24}
26 25impl<'d, T: GeneralInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(CapturePin<'d, T, C, A>) {
27macro_rules! channel_impl { 26 /// Create a new capture pin instance.
28 ($new_chx:ident, $channel:ident, $pin_trait:ident) => { 27 pub fn new(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>, pull: Pull) -> Self {
29 impl<'d, T: GeneralInstance4Channel> CapturePin<'d, T, $channel> { 28 set_as_af!(pin, AfType::input(pull));
30 #[doc = concat!("Create a new ", stringify!($channel), " capture pin instance.")] 29 CapturePin {
31 pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, pull: Pull) -> Self { 30 pin: pin.into(),
32 pin.set_as_af(pin.af_num(), AfType::input(pull)); 31 phantom: PhantomData,
33 CapturePin {
34 _pin: pin.into(),
35 phantom: PhantomData,
36 }
37 }
38 } 32 }
39 }; 33 }
40} 34}
41 35
42channel_impl!(new_ch1, Ch1, Channel1Pin);
43channel_impl!(new_ch2, Ch2, Channel2Pin);
44channel_impl!(new_ch3, Ch3, Channel3Pin);
45channel_impl!(new_ch4, Ch4, Channel4Pin);
46
47/// Input capture driver. 36/// Input capture driver.
48pub struct InputCapture<'d, T: GeneralInstance4Channel> { 37pub struct InputCapture<'d, T: GeneralInstance4Channel> {
49 inner: Timer<'d, T>, 38 inner: Timer<'d, T>,
@@ -51,12 +40,13 @@ pub struct InputCapture<'d, T: GeneralInstance4Channel> {
51 40
52impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { 41impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> {
53 /// Create a new input capture driver. 42 /// Create a new input capture driver.
54 pub fn new( 43 #[allow(unused)]
44 pub fn new<#[cfg(afio)] A>(
55 tim: Peri<'d, T>, 45 tim: Peri<'d, T>,
56 _ch1: Option<CapturePin<'d, T, Ch1>>, 46 ch1: Option<if_afio!(CapturePin<'d, T, Ch1, A>)>,
57 _ch2: Option<CapturePin<'d, T, Ch2>>, 47 ch2: Option<if_afio!(CapturePin<'d, T, Ch2, A>)>,
58 _ch3: Option<CapturePin<'d, T, Ch3>>, 48 ch3: Option<if_afio!(CapturePin<'d, T, Ch3, A>)>,
59 _ch4: Option<CapturePin<'d, T, Ch4>>, 49 ch4: Option<if_afio!(CapturePin<'d, T, Ch4, A>)>,
60 _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, 50 _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,
61 freq: Hertz, 51 freq: Hertz,
62 counting_mode: CountingMode, 52 counting_mode: CountingMode,
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs
index dc8ceb725..ac039bb0d 100644
--- a/embassy-stm32/src/timer/low_level.rs
+++ b/embassy-stm32/src/timer/low_level.rs
@@ -686,10 +686,35 @@ impl<'d, T: AdvancedInstance1Channel> Timer<'d, T> {
686 self.regs_1ch_cmp().bdtr().modify(|w| w.set_dtg(value)); 686 self.regs_1ch_cmp().bdtr().modify(|w| w.set_dtg(value));
687 } 687 }
688 688
689 /// Set state of OSSI-bit in BDTR register
690 pub fn set_ossi(&self, val: vals::Ossi) {
691 self.regs_1ch_cmp().bdtr().modify(|w| w.set_ossi(val));
692 }
693
694 /// Get state of OSSI-bit in BDTR register
695 pub fn get_ossi(&self) -> vals::Ossi {
696 self.regs_1ch_cmp().bdtr().read().ossi()
697 }
698
699 /// Set state of OSSR-bit in BDTR register
700 pub fn set_ossr(&self, val: vals::Ossr) {
701 self.regs_1ch_cmp().bdtr().modify(|w| w.set_ossr(val));
702 }
703
704 /// Get state of OSSR-bit in BDTR register
705 pub fn get_ossr(&self) -> vals::Ossr {
706 self.regs_1ch_cmp().bdtr().read().ossr()
707 }
708
689 /// Set state of MOE-bit in BDTR register to en-/disable output 709 /// Set state of MOE-bit in BDTR register to en-/disable output
690 pub fn set_moe(&self, enable: bool) { 710 pub fn set_moe(&self, enable: bool) {
691 self.regs_1ch_cmp().bdtr().modify(|w| w.set_moe(enable)); 711 self.regs_1ch_cmp().bdtr().modify(|w| w.set_moe(enable));
692 } 712 }
713
714 /// Get state of MOE-bit in BDTR register
715 pub fn get_moe(&self) -> bool {
716 self.regs_1ch_cmp().bdtr().read().moe()
717 }
693} 718}
694 719
695#[cfg(not(stm32l0))] 720#[cfg(not(stm32l0))]
@@ -725,4 +750,19 @@ impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> {
725 .ccer() 750 .ccer()
726 .modify(|w| w.set_ccne(channel.index(), enable)); 751 .modify(|w| w.set_ccne(channel.index(), enable));
727 } 752 }
753
754 /// Set Output Idle State
755 pub fn set_ois(&self, channel: Channel, val: bool) {
756 self.regs_advanced().cr2().modify(|w| w.set_ois(channel.index(), val));
757 }
758 /// Set Output Idle State Complementary Channel
759 pub fn set_oisn(&self, channel: Channel, val: bool) {
760 self.regs_advanced().cr2().modify(|w| w.set_oisn(channel.index(), val));
761 }
762
763 /// Trigger software break 1 or 2
764 /// Setting this bit generates a break event. This bit is automatically cleared by the hardware.
765 pub fn trigger_software_break(&self, n: usize) {
766 self.regs_advanced().egr().write(|r| r.set_bg(n, true));
767 }
728} 768}
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs
index b29382fc8..b09bc7166 100644
--- a/embassy-stm32/src/timer/mod.rs
+++ b/embassy-stm32/src/timer/mod.rs
@@ -51,6 +51,80 @@ pub enum Ch3 {}
51/// Channel 4 marker type. 51/// Channel 4 marker type.
52pub enum Ch4 {} 52pub enum Ch4 {}
53 53
54/// Timer channel trait.
55#[allow(private_bounds)]
56pub trait TimerChannel: SealedTimerChannel {
57 /// The runtime channel.
58 const CHANNEL: Channel;
59}
60
61trait SealedTimerChannel {}
62
63impl TimerChannel for Ch1 {
64 const CHANNEL: Channel = Channel::Ch1;
65}
66
67impl TimerChannel for Ch2 {
68 const CHANNEL: Channel = Channel::Ch2;
69}
70
71impl TimerChannel for Ch3 {
72 const CHANNEL: Channel = Channel::Ch3;
73}
74
75impl TimerChannel for Ch4 {
76 const CHANNEL: Channel = Channel::Ch4;
77}
78
79impl SealedTimerChannel for Ch1 {}
80impl SealedTimerChannel for Ch2 {}
81impl SealedTimerChannel for Ch3 {}
82impl SealedTimerChannel for Ch4 {}
83
84/// Timer break input.
85#[derive(Clone, Copy)]
86pub enum BkIn {
87 /// Break input 1.
88 BkIn1,
89 /// Break input 2.
90 BkIn2,
91}
92
93impl BkIn {
94 /// Get the channel index (0..3)
95 pub fn index(&self) -> usize {
96 match self {
97 BkIn::BkIn1 => 0,
98 BkIn::BkIn2 => 1,
99 }
100 }
101}
102
103/// Break input 1 marker type.
104pub enum BkIn1 {}
105/// Break input 2 marker type.
106pub enum BkIn2 {}
107
108/// Timer channel trait.
109#[allow(private_bounds)]
110pub trait BreakInput: SealedBreakInput {
111 /// The runtim timer channel.
112 const INPUT: BkIn;
113}
114
115trait SealedBreakInput {}
116
117impl BreakInput for BkIn1 {
118 const INPUT: BkIn = BkIn::BkIn1;
119}
120
121impl BreakInput for BkIn2 {
122 const INPUT: BkIn = BkIn::BkIn2;
123}
124
125impl SealedBreakInput for BkIn1 {}
126impl SealedBreakInput for BkIn2 {}
127
54/// Amount of bits of a timer. 128/// Amount of bits of a timer.
55#[derive(Clone, Copy, PartialEq, Eq, Debug)] 129#[derive(Clone, Copy, PartialEq, Eq, Debug)]
56#[cfg_attr(feature = "defmt", derive(defmt::Format))] 130#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -149,33 +223,20 @@ pub trait AdvancedInstance2Channel: BasicInstance + GeneralInstance2Channel + Ad
149/// Advanced 16-bit timer with 4 channels instance. 223/// Advanced 16-bit timer with 4 channels instance.
150pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {} 224pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {}
151 225
152pin_trait!(Channel1Pin, GeneralInstance4Channel); 226pin_trait!(TimerPin, GeneralInstance4Channel, TimerChannel, @A);
153pin_trait!(Channel2Pin, GeneralInstance4Channel); 227pin_trait!(ExternalTriggerPin, GeneralInstance4Channel, @A);
154pin_trait!(Channel3Pin, GeneralInstance4Channel);
155pin_trait!(Channel4Pin, GeneralInstance4Channel);
156pin_trait!(ExternalTriggerPin, GeneralInstance4Channel);
157
158pin_trait!(Channel1ComplementaryPin, AdvancedInstance4Channel);
159pin_trait!(Channel2ComplementaryPin, AdvancedInstance4Channel);
160pin_trait!(Channel3ComplementaryPin, AdvancedInstance4Channel);
161pin_trait!(Channel4ComplementaryPin, AdvancedInstance4Channel);
162 228
163pin_trait!(BreakInputPin, AdvancedInstance4Channel); 229pin_trait!(TimerComplementaryPin, AdvancedInstance4Channel, TimerChannel, @A);
164pin_trait!(BreakInput2Pin, AdvancedInstance4Channel);
165 230
166pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel); 231pin_trait!(BreakInputPin, AdvancedInstance4Channel, BreakInput, @A);
167pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel);
168 232
169pin_trait!(BreakInput2Comparator1Pin, AdvancedInstance4Channel); 233pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel, BreakInput, @A);
170pin_trait!(BreakInput2Comparator2Pin, AdvancedInstance4Channel); 234pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel, BreakInput, @A);
171 235
172// Update Event trigger DMA for every timer 236// Update Event trigger DMA for every timer
173dma_trait!(UpDma, BasicInstance); 237dma_trait!(UpDma, BasicInstance);
174 238
175dma_trait!(Ch1Dma, GeneralInstance4Channel); 239dma_trait!(Dma, GeneralInstance4Channel, TimerChannel);
176dma_trait!(Ch2Dma, GeneralInstance4Channel);
177dma_trait!(Ch3Dma, GeneralInstance4Channel);
178dma_trait!(Ch4Dma, GeneralInstance4Channel);
179 240
180#[allow(unused)] 241#[allow(unused)]
181macro_rules! impl_core_timer { 242macro_rules! impl_core_timer {
diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs
index 933165ef9..a75b41bd7 100644
--- a/embassy-stm32/src/timer/one_pulse.rs
+++ b/embassy-stm32/src/timer/one_pulse.rs
@@ -7,16 +7,15 @@ use core::pin::Pin;
7use core::task::{Context, Poll}; 7use core::task::{Context, Poll};
8 8
9use super::low_level::{ 9use super::low_level::{
10 CountingMode, FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource, 10 CountingMode, FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource as Ts,
11};
12use super::{
13 CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, ExternalTriggerPin, GeneralInstance4Channel,
14}; 11};
12use super::{CaptureCompareInterruptHandler, Channel, ExternalTriggerPin, GeneralInstance4Channel, TimerPin};
15pub use super::{Ch1, Ch2}; 13pub use super::{Ch1, Ch2};
16use crate::gpio::{AfType, AnyPin, Pull}; 14use crate::gpio::{AfType, AnyPin, Pull};
17use crate::interrupt::typelevel::{Binding, Interrupt}; 15use crate::interrupt::typelevel::{Binding, Interrupt};
18use crate::pac::timer::vals::Etp; 16use crate::pac::timer::vals::Etp;
19use crate::time::Hertz; 17use crate::time::Hertz;
18use crate::timer::TimerChannel;
20use crate::Peri; 19use crate::Peri;
21 20
22/// External input marker type. 21/// External input marker type.
@@ -44,28 +43,46 @@ impl From<ExternalTriggerPolarity> for Etp {
44/// 43///
45/// This wraps a pin to make it usable as a timer trigger. 44/// This wraps a pin to make it usable as a timer trigger.
46pub struct TriggerPin<'d, T, C> { 45pub struct TriggerPin<'d, T, C> {
47 _pin: Peri<'d, AnyPin>, 46 #[allow(unused)]
47 pin: Peri<'d, AnyPin>,
48 phantom: PhantomData<(T, C)>, 48 phantom: PhantomData<(T, C)>,
49} 49}
50 50
51macro_rules! channel_impl { 51trait SealedTriggerSource {}
52 ($new_chx:ident, $channel:ident, $pin_trait:ident) => { 52
53 impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, $channel> { 53/// Marker trait for a trigger source.
54 #[doc = concat!("Create a new ", stringify!($channel), " trigger pin instance.")] 54#[expect(private_bounds)]
55 pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, pull: Pull) -> Self { 55pub trait TriggerSource: SealedTriggerSource {}
56 pin.set_as_af(pin.af_num(), AfType::input(pull)); 56
57 TriggerPin { 57impl TriggerSource for Ch1 {}
58 _pin: pin.into(), 58impl TriggerSource for Ch2 {}
59 phantom: PhantomData, 59impl TriggerSource for Ext {}
60 } 60
61 } 61impl SealedTriggerSource for Ch1 {}
62impl SealedTriggerSource for Ch2 {}
63impl SealedTriggerSource for Ext {}
64
65impl<'d, T: GeneralInstance4Channel, C: TriggerSource + TimerChannel> TriggerPin<'d, T, C> {
66 /// Create a new Channel trigger pin instance.
67 pub fn new<#[cfg(afio)] A>(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>, pull: Pull) -> Self {
68 set_as_af!(pin, AfType::input(pull));
69 TriggerPin {
70 pin: pin.into(),
71 phantom: PhantomData,
62 } 72 }
63 }; 73 }
64} 74}
65 75
66channel_impl!(new_ch1, Ch1, Channel1Pin); 76impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, Ext> {
67channel_impl!(new_ch2, Ch2, Channel2Pin); 77 /// Create a new external trigger pin instance.
68channel_impl!(new_ext, Ext, ExternalTriggerPin); 78 pub fn new_external<#[cfg(afio)] A>(pin: Peri<'d, if_afio!(impl ExternalTriggerPin<T, A>)>, pull: Pull) -> Self {
79 set_as_af!(pin, AfType::input(pull));
80 TriggerPin {
81 pin: pin.into(),
82 phantom: PhantomData,
83 }
84 }
85}
69 86
70/// One pulse driver. 87/// One pulse driver.
71/// 88///
@@ -79,9 +96,10 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
79 /// 96 ///
80 /// The pulse is triggered by a channel 1 input pin on both rising and 97 /// The pulse is triggered by a channel 1 input pin on both rising and
81 /// falling edges. Channel 1 will unusable as an output. 98 /// falling edges. Channel 1 will unusable as an output.
99 #[allow(unused)]
82 pub fn new_ch1_edge_detect( 100 pub fn new_ch1_edge_detect(
83 tim: Peri<'d, T>, 101 tim: Peri<'d, T>,
84 _pin: TriggerPin<'d, T, Ch1>, 102 pin: TriggerPin<'d, T, Ch1>,
85 _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, 103 _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,
86 freq: Hertz, 104 freq: Hertz,
87 pulse_end: u32, 105 pulse_end: u32,
@@ -89,7 +107,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
89 ) -> Self { 107 ) -> Self {
90 let mut this = Self { inner: Timer::new(tim) }; 108 let mut this = Self { inner: Timer::new(tim) };
91 109
92 this.inner.set_trigger_source(TriggerSource::TI1F_ED); 110 this.inner.set_trigger_source(Ts::TI1F_ED);
93 this.inner 111 this.inner
94 .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal); 112 .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal);
95 this.inner 113 this.inner
@@ -114,7 +132,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
114 ) -> Self { 132 ) -> Self {
115 let mut this = Self { inner: Timer::new(tim) }; 133 let mut this = Self { inner: Timer::new(tim) };
116 134
117 this.inner.set_trigger_source(TriggerSource::TI1FP1); 135 this.inner.set_trigger_source(Ts::TI1FP1);
118 this.inner 136 this.inner
119 .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal); 137 .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal);
120 this.inner 138 this.inner
@@ -131,7 +149,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
131 /// as an output. 149 /// as an output.
132 pub fn new_ch2( 150 pub fn new_ch2(
133 tim: Peri<'d, T>, 151 tim: Peri<'d, T>,
134 _pin: TriggerPin<'d, T, Ch1>, 152 _pin: TriggerPin<'d, T, Ch2>,
135 _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, 153 _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,
136 freq: Hertz, 154 freq: Hertz,
137 pulse_end: u32, 155 pulse_end: u32,
@@ -140,7 +158,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
140 ) -> Self { 158 ) -> Self {
141 let mut this = Self { inner: Timer::new(tim) }; 159 let mut this = Self { inner: Timer::new(tim) };
142 160
143 this.inner.set_trigger_source(TriggerSource::TI2FP2); 161 this.inner.set_trigger_source(Ts::TI2FP2);
144 this.inner 162 this.inner
145 .set_input_ti_selection(Channel::Ch2, InputTISelection::Normal); 163 .set_input_ti_selection(Channel::Ch2, InputTISelection::Normal);
146 this.inner 164 this.inner
@@ -172,7 +190,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
172 // No filtering 190 // No filtering
173 r.set_etf(FilterValue::NO_FILTER); 191 r.set_etf(FilterValue::NO_FILTER);
174 }); 192 });
175 this.inner.set_trigger_source(TriggerSource::ETRF); 193 this.inner.set_trigger_source(Ts::ETRF);
176 this.new_inner(freq, pulse_end, counting_mode); 194 this.new_inner(freq, pulse_end, counting_mode);
177 195
178 this 196 this
diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs
index 98b798634..159b5a177 100644
--- a/embassy-stm32/src/timer/pwm_input.rs
+++ b/embassy-stm32/src/timer/pwm_input.rs
@@ -1,12 +1,16 @@
1//! PWM Input driver. 1//! PWM Input driver.
2 2
3use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; 3use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource};
4use super::{Channel, Channel1Pin, Channel2Pin, GeneralInstance4Channel}; 4use super::{Ch1, Ch2, Channel, GeneralInstance4Channel, TimerPin};
5use crate::gpio::{AfType, Pull}; 5use crate::gpio::{AfType, Pull};
6use crate::time::Hertz; 6use crate::time::Hertz;
7use crate::Peri; 7use crate::Peri;
8 8
9/// PWM Input driver. 9/// PWM Input driver.
10///
11/// Only works with CH1 or CH2
12/// Note: Not all timer peripherals are supported
13/// Double check your chips reference manual
10pub struct PwmInput<'d, T: GeneralInstance4Channel> { 14pub struct PwmInput<'d, T: GeneralInstance4Channel> {
11 channel: Channel, 15 channel: Channel,
12 inner: Timer<'d, T>, 16 inner: Timer<'d, T>,
@@ -14,15 +18,25 @@ pub struct PwmInput<'d, T: GeneralInstance4Channel> {
14 18
15impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { 19impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> {
16 /// Create a new PWM input driver. 20 /// Create a new PWM input driver.
17 pub fn new(tim: Peri<'d, T>, pin: Peri<'d, impl Channel1Pin<T>>, pull: Pull, freq: Hertz) -> Self { 21 pub fn new_ch1<#[cfg(afio)] A>(
18 pin.set_as_af(pin.af_num(), AfType::input(pull)); 22 tim: Peri<'d, T>,
23 pin: Peri<'d, if_afio!(impl TimerPin<T, Ch1, A>)>,
24 pull: Pull,
25 freq: Hertz,
26 ) -> Self {
27 set_as_af!(pin, AfType::input(pull));
19 28
20 Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) 29 Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2)
21 } 30 }
22 31
23 /// Create a new PWM input driver. 32 /// Create a new PWM input driver.
24 pub fn new_alt(tim: Peri<'d, T>, pin: Peri<'d, impl Channel2Pin<T>>, pull: Pull, freq: Hertz) -> Self { 33 pub fn new_ch2<#[cfg(afio)] A>(
25 pin.set_as_af(pin.af_num(), AfType::input(pull)); 34 tim: Peri<'d, T>,
35 pin: Peri<'d, if_afio!(impl TimerPin<T, Ch2, A>)>,
36 pull: Pull,
37 freq: Hertz,
38 ) -> Self {
39 set_as_af!(pin, AfType::input(pull));
26 40
27 Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1) 41 Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1)
28 } 42 }
@@ -37,6 +51,7 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> {
37 51
38 // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6 52 // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6
39 // or ST RM0008 (STM32F103) chapter 15.3.6 Input capture mode 53 // or ST RM0008 (STM32F103) chapter 15.3.6 Input capture mode
54 // or ST RM0440 (STM32G4) chapter 30.4.8 PWM input mode
40 inner.set_input_ti_selection(ch1, InputTISelection::Normal); 55 inner.set_input_ti_selection(ch1, InputTISelection::Normal);
41 inner.set_input_capture_mode(ch1, InputCaptureMode::Rising); 56 inner.set_input_capture_mode(ch1, InputCaptureMode::Rising);
42 57
diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs
index f3c81667c..82b5968b0 100644
--- a/embassy-stm32/src/timer/qei.rs
+++ b/embassy-stm32/src/timer/qei.rs
@@ -6,8 +6,9 @@ use stm32_metapac::timer::vals;
6 6
7use super::low_level::Timer; 7use super::low_level::Timer;
8pub use super::{Ch1, Ch2}; 8pub use super::{Ch1, Ch2};
9use super::{Channel1Pin, Channel2Pin, GeneralInstance4Channel}; 9use super::{GeneralInstance4Channel, TimerPin};
10use crate::gpio::{AfType, AnyPin, Pull}; 10use crate::gpio::{AfType, AnyPin, Pull};
11use crate::timer::TimerChannel;
11use crate::Peri; 12use crate::Peri;
12 13
13/// Counting direction 14/// Counting direction
@@ -19,31 +20,37 @@ pub enum Direction {
19} 20}
20 21
21/// Wrapper for using a pin with QEI. 22/// Wrapper for using a pin with QEI.
22pub struct QeiPin<'d, T, Channel> { 23pub struct QeiPin<'d, T, Channel, #[cfg(afio)] A> {
23 _pin: Peri<'d, AnyPin>, 24 #[allow(unused)]
24 phantom: PhantomData<(T, Channel)>, 25 pin: Peri<'d, AnyPin>,
26 phantom: PhantomData<if_afio!((T, Channel, A))>,
25} 27}
26 28
27macro_rules! channel_impl { 29impl<'d, T: GeneralInstance4Channel, C: QeiChannel, #[cfg(afio)] A> if_afio!(QeiPin<'d, T, C, A>) {
28 ($new_chx:ident, $channel:ident, $pin_trait:ident) => { 30 /// Create a new QEI pin instance.
29 impl<'d, T: GeneralInstance4Channel> QeiPin<'d, T, $channel> { 31 pub fn new(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>) -> Self {
30 #[doc = concat!("Create a new ", stringify!($channel), " QEI pin instance.")] 32 critical_section::with(|_| {
31 pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { 33 pin.set_low();
32 critical_section::with(|_| { 34 set_as_af!(pin, AfType::input(Pull::None));
33 pin.set_low(); 35 });
34 pin.set_as_af(pin.af_num(), AfType::input(Pull::None)); 36 QeiPin {
35 }); 37 pin: pin.into(),
36 QeiPin { 38 phantom: PhantomData,
37 _pin: pin.into(),
38 phantom: PhantomData,
39 }
40 }
41 } 39 }
42 }; 40 }
43} 41}
44 42
45channel_impl!(new_ch1, Ch1, Channel1Pin); 43trait SealedQeiChannel: TimerChannel {}
46channel_impl!(new_ch2, Ch2, Channel2Pin); 44
45/// Marker trait for a timer channel eligible for use with QEI.
46#[expect(private_bounds)]
47pub trait QeiChannel: SealedQeiChannel {}
48
49impl QeiChannel for Ch1 {}
50impl QeiChannel for Ch2 {}
51
52impl SealedQeiChannel for Ch1 {}
53impl SealedQeiChannel for Ch2 {}
47 54
48/// Quadrature decoder driver. 55/// Quadrature decoder driver.
49pub struct Qei<'d, T: GeneralInstance4Channel> { 56pub struct Qei<'d, T: GeneralInstance4Channel> {
@@ -52,7 +59,12 @@ pub struct Qei<'d, T: GeneralInstance4Channel> {
52 59
53impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { 60impl<'d, T: GeneralInstance4Channel> Qei<'d, T> {
54 /// Create a new quadrature decoder driver. 61 /// Create a new quadrature decoder driver.
55 pub fn new(tim: Peri<'d, T>, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self { 62 #[allow(unused)]
63 pub fn new<#[cfg(afio)] A>(
64 tim: Peri<'d, T>,
65 ch1: if_afio!(QeiPin<'d, T, Ch1, A>),
66 ch2: if_afio!(QeiPin<'d, T, Ch2, A>),
67 ) -> Self {
56 Self::new_inner(tim) 68 Self::new_inner(tim)
57 } 69 }
58 70
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs
index 8fd7e8df4..e6165e42b 100644
--- a/embassy-stm32/src/timer/simple_pwm.rs
+++ b/embassy-stm32/src/timer/simple_pwm.rs
@@ -4,28 +4,20 @@ 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::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel, TimerBits}; 7use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin};
8#[cfg(gpio_v2)] 8#[cfg(gpio_v2)]
9use crate::gpio::Pull; 9use crate::gpio::Pull;
10use crate::gpio::{AfType, AnyPin, OutputType, Speed}; 10use crate::gpio::{AfType, AnyPin, OutputType, Speed};
11use crate::time::Hertz; 11use crate::time::Hertz;
12use crate::Peri; 12use crate::Peri;
13 13
14/// Channel 1 marker type.
15pub enum Ch1 {}
16/// Channel 2 marker type.
17pub enum Ch2 {}
18/// Channel 3 marker type.
19pub enum Ch3 {}
20/// Channel 4 marker type.
21pub enum Ch4 {}
22
23/// PWM pin wrapper. 14/// PWM pin wrapper.
24/// 15///
25/// This wraps a pin to make it usable with PWM. 16/// This wraps a pin to make it usable with PWM.
26pub struct PwmPin<'d, T, C> { 17pub struct PwmPin<'d, T, C, #[cfg(afio)] A> {
27 _pin: Peri<'d, AnyPin>, 18 #[allow(unused)]
28 phantom: PhantomData<(T, C)>, 19 pub(crate) pin: Peri<'d, AnyPin>,
20 phantom: PhantomData<if_afio!((T, C, A))>,
29} 21}
30 22
31/// PWM pin config 23/// PWM pin config
@@ -43,46 +35,37 @@ pub struct PwmPinConfig {
43 pub pull: Pull, 35 pub pull: Pull,
44} 36}
45 37
46macro_rules! channel_impl { 38impl<'d, T: GeneralInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(PwmPin<'d, T, C, A>) {
47 ($new_chx:ident, $new_chx_with_config:ident, $channel:ident, $pin_trait:ident) => { 39 /// Create a new PWM pin instance.
48 impl<'d, T: GeneralInstance4Channel> PwmPin<'d, T, $channel> { 40 pub fn new(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>, output_type: OutputType) -> Self {
49 #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] 41 critical_section::with(|_| {
50 pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, output_type: OutputType) -> Self { 42 pin.set_low();
51 critical_section::with(|_| { 43 set_as_af!(pin, AfType::output(output_type, Speed::VeryHigh));
52 pin.set_low(); 44 });
53 pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh)); 45 PwmPin {
54 }); 46 pin: pin.into(),
55 PwmPin { 47 phantom: PhantomData,
56 _pin: pin.into(),
57 phantom: PhantomData,
58 }
59 }
60
61 #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance with config.")]
62 pub fn $new_chx_with_config(pin: Peri<'d, impl $pin_trait<T>>, pin_config: PwmPinConfig) -> Self {
63 critical_section::with(|_| {
64 pin.set_low();
65 pin.set_as_af(
66 pin.af_num(),
67 #[cfg(gpio_v1)]
68 AfType::output(pin_config.output_type, pin_config.speed),
69 #[cfg(gpio_v2)]
70 AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull),
71 );
72 });
73 PwmPin {
74 _pin: pin.into(),
75 phantom: PhantomData,
76 }
77 }
78 } 48 }
79 }; 49 }
80}
81 50
82channel_impl!(new_ch1, new_ch1_with_config, Ch1, Channel1Pin); 51 /// Create a new PWM pin instance with a specific configuration.
83channel_impl!(new_ch2, new_ch2_with_config, Ch2, Channel2Pin); 52 pub fn new_with_config(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>, pin_config: PwmPinConfig) -> Self {
84channel_impl!(new_ch3, new_ch3_with_config, Ch3, Channel3Pin); 53 critical_section::with(|_| {
85channel_impl!(new_ch4, new_ch4_with_config, Ch4, Channel4Pin); 54 pin.set_low();
55 #[cfg(gpio_v1)]
56 set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed));
57 #[cfg(gpio_v2)]
58 set_as_af!(
59 pin,
60 AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull)
61 );
62 });
63 PwmPin {
64 pin: pin.into(),
65 phantom: PhantomData,
66 }
67 }
68}
86 69
87/// A single channel of a pwm, obtained from [`SimplePwm::split`], 70/// A single channel of a pwm, obtained from [`SimplePwm::split`],
88/// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc. 71/// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc.
@@ -196,12 +179,13 @@ pub struct SimplePwm<'d, T: GeneralInstance4Channel> {
196 179
197impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { 180impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
198 /// Create a new simple PWM driver. 181 /// Create a new simple PWM driver.
199 pub fn new( 182 #[allow(unused)]
183 pub fn new<#[cfg(afio)] A>(
200 tim: Peri<'d, T>, 184 tim: Peri<'d, T>,
201 _ch1: Option<PwmPin<'d, T, Ch1>>, 185 ch1: Option<if_afio!(PwmPin<'d, T, Ch1, A>)>,
202 _ch2: Option<PwmPin<'d, T, Ch2>>, 186 ch2: Option<if_afio!(PwmPin<'d, T, Ch2, A>)>,
203 _ch3: Option<PwmPin<'d, T, Ch3>>, 187 ch3: Option<if_afio!(PwmPin<'d, T, Ch3, A>)>,
204 _ch4: Option<PwmPin<'d, T, Ch4>>, 188 ch4: Option<if_afio!(PwmPin<'d, T, Ch4, A>)>,
205 freq: Hertz, 189 freq: Hertz,
206 counting_mode: CountingMode, 190 counting_mode: CountingMode,
207 ) -> Self { 191 ) -> Self {
@@ -381,109 +365,183 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
381 self.inner.enable_update_dma(false); 365 self.inner.enable_update_dma(false);
382 } 366 }
383 } 367 }
368
369 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.
370 ///
371 /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers
372 /// in sequence on each update event (UEV). The data is written via the DMAR register using the
373 /// DMA base address (DBA) and burst length (DBL) configured in the DCR register.
374 ///
375 /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row
376 /// represents a single update event and each column corresponds to a specific timer channel (starting
377 /// from `starting_channel` up to and including `ending_channel`).
378 ///
379 /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like:
380 ///
381 /// let dma_buf: [u16; 16] = [
382 /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1
383 /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2
384 /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3
385 /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4
386 /// ];
387 ///
388 /// Each group of N values (where N = number of channels) is transferred on one update event,
389 /// updating the duty cycles of all selected channels simultaneously.
390 ///
391 /// Note:
392 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
393 pub async fn waveform_up_multi_channel(
394 &mut self,
395 dma: Peri<'_, impl super::UpDma<T>>,
396 starting_channel: Channel,
397 ending_channel: Channel,
398 duty: &[u16],
399 ) {
400 let cr1_addr = self.inner.regs_gp16().cr1().as_ptr() as u32;
401 let start_ch_index = starting_channel.index();
402 let end_ch_index = ending_channel.index();
403
404 assert!(start_ch_index <= end_ch_index);
405
406 let ccrx_addr = self.inner.regs_gp16().ccr(start_ch_index).as_ptr() as u32;
407 self.inner
408 .regs_gp16()
409 .dcr()
410 .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8));
411 self.inner
412 .regs_gp16()
413 .dcr()
414 .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8));
415
416 #[allow(clippy::let_unit_value)] // eg. stm32f334
417 let req = dma.request();
418
419 let original_update_dma_state = self.inner.get_update_dma_state();
420 if !original_update_dma_state {
421 self.inner.enable_update_dma(true);
422 }
423
424 unsafe {
425 #[cfg(not(any(bdma, gpdma)))]
426 use crate::dma::{Burst, FifoThreshold};
427 use crate::dma::{Transfer, TransferOptions};
428
429 let dma_transfer_option = TransferOptions {
430 #[cfg(not(any(bdma, gpdma)))]
431 fifo_threshold: Some(FifoThreshold::Full),
432 #[cfg(not(any(bdma, gpdma)))]
433 mburst: Burst::Incr4,
434 ..Default::default()
435 };
436
437 Transfer::new_write(
438 dma,
439 req,
440 duty,
441 self.inner.regs_gp16().dmar().as_ptr() as *mut u16,
442 dma_transfer_option,
443 )
444 .await
445 };
446
447 if !original_update_dma_state {
448 self.inner.enable_update_dma(false);
449 }
450 }
384} 451}
385 452
386macro_rules! impl_waveform_chx { 453impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
387 ($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => { 454 /// Generate a sequence of PWM waveform
388 impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { 455 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
389 /// Generate a sequence of PWM waveform 456 use crate::pac::timer::vals::Ccds;
390 pub async fn $fn_name(&mut self, dma: Peri<'_, impl super::$dma_ch<T>>, duty: &[u16]) {
391 use crate::pac::timer::vals::Ccds;
392 457
393 #[allow(clippy::let_unit_value)] // eg. stm32f334 458 #[allow(clippy::let_unit_value)] // eg. stm32f334
394 let req = dma.request(); 459 let req = dma.request();
395 460
396 let cc_channel = Channel::$cc_ch; 461 let cc_channel = C::CHANNEL;
397 462
398 let original_duty_state = self.channel(cc_channel).current_duty_cycle(); 463 let original_duty_state = self.channel(cc_channel).current_duty_cycle();
399 let original_enable_state = self.channel(cc_channel).is_enabled(); 464 let original_enable_state = self.channel(cc_channel).is_enabled();
400 let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE; 465 let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE;
401 let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); 466 let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel);
402 467
403 // redirect CC DMA request onto Update Event 468 // redirect CC DMA request onto Update Event
404 if !original_cc_dma_on_update { 469 if !original_cc_dma_on_update {
405 self.inner.set_cc_dma_selection(Ccds::ON_UPDATE) 470 self.inner.set_cc_dma_selection(Ccds::ON_UPDATE)
406 } 471 }
407 472
408 if !original_cc_dma_enabled { 473 if !original_cc_dma_enabled {
409 self.inner.set_cc_dma_enable_state(cc_channel, true); 474 self.inner.set_cc_dma_enable_state(cc_channel, true);
410 } 475 }
411 476
412 if !original_enable_state { 477 if !original_enable_state {
413 self.channel(cc_channel).enable(); 478 self.channel(cc_channel).enable();
414 } 479 }
415 480
416 unsafe { 481 unsafe {
482 #[cfg(not(any(bdma, gpdma)))]
483 use crate::dma::{Burst, FifoThreshold};
484 use crate::dma::{Transfer, TransferOptions};
485
486 let dma_transfer_option = TransferOptions {
487 #[cfg(not(any(bdma, gpdma)))]
488 fifo_threshold: Some(FifoThreshold::Full),
489 #[cfg(not(any(bdma, gpdma)))]
490 mburst: Burst::Incr8,
491 ..Default::default()
492 };
493
494 match self.inner.bits() {
495 TimerBits::Bits16 => {
496 Transfer::new_write(
497 dma,
498 req,
499 duty,
500 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16,
501 dma_transfer_option,
502 )
503 .await
504 }
505 #[cfg(not(any(stm32l0)))]
506 TimerBits::Bits32 => {
417 #[cfg(not(any(bdma, gpdma)))] 507 #[cfg(not(any(bdma, gpdma)))]
418 use crate::dma::{Burst, FifoThreshold}; 508 panic!("unsupported timer bits");
419 use crate::dma::{Transfer, TransferOptions}; 509
420 510 #[cfg(any(bdma, gpdma))]
421 let dma_transfer_option = TransferOptions { 511 Transfer::new_write(
422 #[cfg(not(any(bdma, gpdma)))] 512 dma,
423 fifo_threshold: Some(FifoThreshold::Full), 513 req,
424 #[cfg(not(any(bdma, gpdma)))] 514 duty,
425 mburst: Burst::Incr8, 515 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32,
426 ..Default::default() 516 dma_transfer_option,
427 }; 517 )
428 518 .await
429 match self.inner.bits() {
430 TimerBits::Bits16 => {
431 Transfer::new_write(
432 dma,
433 req,
434 duty,
435 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16,
436 dma_transfer_option,
437 )
438 .await
439 }
440 #[cfg(not(any(stm32l0)))]
441 TimerBits::Bits32 => {
442 #[cfg(not(any(bdma, gpdma)))]
443 panic!("unsupported timer bits");
444
445 #[cfg(any(bdma, gpdma))]
446 Transfer::new_write(
447 dma,
448 req,
449 duty,
450 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32,
451 dma_transfer_option,
452 )
453 .await
454 }
455 };
456 };
457
458 // restore output compare state
459 if !original_enable_state {
460 self.channel(cc_channel).disable();
461 } 519 }
520 };
521 };
522
523 // restore output compare state
524 if !original_enable_state {
525 self.channel(cc_channel).disable();
526 }
462 527
463 self.channel(cc_channel).set_duty_cycle(original_duty_state); 528 self.channel(cc_channel).set_duty_cycle(original_duty_state);
464 529
465 // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, 530 // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
466 // this can almost always trigger a DMA FIFO error. 531 // this can almost always trigger a DMA FIFO error.
467 // 532 //
468 // optional TODO: 533 // optional TODO:
469 // clean FEIF after disable UDE 534 // clean FEIF after disable UDE
470 if !original_cc_dma_enabled { 535 if !original_cc_dma_enabled {
471 self.inner.set_cc_dma_enable_state(cc_channel, false); 536 self.inner.set_cc_dma_enable_state(cc_channel, false);
472 } 537 }
473 538
474 if !original_cc_dma_on_update { 539 if !original_cc_dma_on_update {
475 self.inner.set_cc_dma_selection(Ccds::ON_COMPARE) 540 self.inner.set_cc_dma_selection(Ccds::ON_COMPARE)
476 }
477 }
478 } 541 }
479 }; 542 }
480} 543}
481 544
482impl_waveform_chx!(waveform_ch1, Ch1Dma, Ch1);
483impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2);
484impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3);
485impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4);
486
487impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> { 545impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> {
488 type Error = core::convert::Infallible; 546 type Error = core::convert::Infallible;
489} 547}