aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/timer/complementary_pwm.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-stm32/src/timer/complementary_pwm.rs')
-rw-r--r--embassy-stm32/src/timer/complementary_pwm.rs210
1 files changed, 166 insertions, 44 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) {