embassy_stm32/timer/
complementary_pwm.rs

1//! PWM driver with complementary output support.
2
3use core::marker::PhantomData;
4
5pub use stm32_metapac::timer::vals::{Ckd, Ossi, Ossr};
6
7use super::low_level::{CountingMode, OutputPolarity, Timer};
8use super::simple_pwm::PwmPin;
9use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin};
10use crate::gpio::{AnyPin, OutputType};
11use crate::time::Hertz;
12use crate::timer::low_level::OutputCompareMode;
13use crate::timer::TimerChannel;
14use crate::Peri;
15
16/// Complementary PWM pin wrapper.
17///
18/// This wraps a pin to make it usable with PWM.
19pub struct ComplementaryPwmPin<'d, T, C> {
20    _pin: Peri<'d, AnyPin>,
21    phantom: PhantomData<(T, C)>,
22}
23
24impl<'d, T: AdvancedInstance4Channel, C: TimerChannel> ComplementaryPwmPin<'d, T, C> {
25    /// Create a new  complementary PWM pin instance.
26    pub fn new(pin: Peri<'d, impl TimerComplementaryPin<T, C>>, output_type: OutputType) -> Self {
27        critical_section::with(|_| {
28            pin.set_low();
29            pin.set_as_af(
30                pin.af_num(),
31                crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh),
32            );
33        });
34        ComplementaryPwmPin {
35            _pin: pin.into(),
36            phantom: PhantomData,
37        }
38    }
39}
40
41/// PWM driver with support for standard and complementary outputs.
42pub struct ComplementaryPwm<'d, T: AdvancedInstance4Channel> {
43    inner: Timer<'d, T>,
44}
45
46#[derive(Copy, Clone, Debug, PartialEq, Eq)]
47/// Determines which outputs are active when PWM is in idle mode
48pub enum IdlePolarity {
49    /// Normal channels are forced active and complementary channels are forced inactive
50    OisActive,
51    /// Normal channels are forced inactive and complementary channels are forced active
52    OisnActive,
53}
54
55impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
56    /// Create a new complementary PWM driver.
57    #[allow(clippy::too_many_arguments)]
58    pub fn new(
59        tim: Peri<'d, T>,
60        _ch1: Option<PwmPin<'d, T, Ch1>>,
61        _ch1n: Option<ComplementaryPwmPin<'d, T, Ch1>>,
62        _ch2: Option<PwmPin<'d, T, Ch2>>,
63        _ch2n: Option<ComplementaryPwmPin<'d, T, Ch2>>,
64        _ch3: Option<PwmPin<'d, T, Ch3>>,
65        _ch3n: Option<ComplementaryPwmPin<'d, T, Ch3>>,
66        _ch4: Option<PwmPin<'d, T, Ch4>>,
67        _ch4n: Option<ComplementaryPwmPin<'d, T, Ch4>>,
68        freq: Hertz,
69        counting_mode: CountingMode,
70    ) -> Self {
71        Self::new_inner(tim, freq, counting_mode)
72    }
73
74    fn new_inner(tim: Peri<'d, T>, freq: Hertz, counting_mode: CountingMode) -> Self {
75        let mut this = Self { inner: Timer::new(tim) };
76
77        this.inner.set_counting_mode(counting_mode);
78        this.set_frequency(freq);
79        this.inner.start();
80
81        this.inner.enable_outputs();
82
83        [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
84            .iter()
85            .for_each(|&channel| {
86                this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1);
87                this.inner.set_output_compare_preload(channel, true);
88            });
89        this.inner.set_autoreload_preload(true);
90
91        this
92    }
93
94    /// Sets the idle output state for the given channels.
95    pub fn set_output_idle_state(&mut self, channels: &[Channel], polarity: IdlePolarity) {
96        let ois_active = matches!(polarity, IdlePolarity::OisActive);
97        for &channel in channels {
98            self.inner.set_ois(channel, ois_active);
99            self.inner.set_oisn(channel, !ois_active);
100        }
101    }
102
103    /// Set state of OSSI-bit in BDTR register
104    pub fn set_off_state_selection_idle(&mut self, val: Ossi) {
105        self.inner.set_ossi(val);
106    }
107
108    /// Get state of OSSI-bit in BDTR register
109    pub fn get_off_state_selection_idle(&self) -> Ossi {
110        self.inner.get_ossi()
111    }
112
113    /// Set state of OSSR-bit in BDTR register
114    pub fn set_off_state_selection_run(&mut self, val: Ossr) {
115        self.inner.set_ossr(val);
116    }
117
118    /// Get state of OSSR-bit in BDTR register
119    pub fn get_off_state_selection_run(&self) -> Ossr {
120        self.inner.get_ossr()
121    }
122
123    /// Trigger break input from software
124    pub fn trigger_software_break(&mut self, n: usize) {
125        self.inner.trigger_software_break(n);
126    }
127
128    /// Set Master Output Enable
129    pub fn set_master_output_enable(&mut self, enable: bool) {
130        self.inner.set_moe(enable);
131    }
132
133    /// Get Master Output Enable
134    pub fn get_master_output_enable(&self) -> bool {
135        self.inner.get_moe()
136    }
137
138    /// Enable the given channel.
139    pub fn enable(&mut self, channel: Channel) {
140        self.inner.enable_channel(channel, true);
141        self.inner.enable_complementary_channel(channel, true);
142    }
143
144    /// Disable the given channel.
145    pub fn disable(&mut self, channel: Channel) {
146        self.inner.enable_complementary_channel(channel, false);
147        self.inner.enable_channel(channel, false);
148    }
149
150    /// Set PWM frequency.
151    ///
152    /// Note: when you call this, the max duty value changes, so you will have to
153    /// call `set_duty` on all channels with the duty calculated based on the new max duty.
154    pub fn set_frequency(&mut self, freq: Hertz) {
155        let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
156            2u8
157        } else {
158            1u8
159        };
160        self.inner.set_frequency_internal(freq * multiplier, 16);
161    }
162
163    /// Get max duty value.
164    ///
165    /// This value depends on the configured frequency and the timer's clock rate from RCC.
166    pub fn get_max_duty(&self) -> u16 {
167        if self.inner.get_counting_mode().is_center_aligned() {
168            self.inner.get_max_compare_value() as u16
169        } else {
170            self.inner.get_max_compare_value() as u16 + 1
171        }
172    }
173
174    /// Set the duty for a given channel.
175    ///
176    /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.
177    pub fn set_duty(&mut self, channel: Channel, duty: u16) {
178        assert!(duty <= self.get_max_duty());
179        self.inner.set_compare_value(channel, duty as _)
180    }
181
182    /// Set the output polarity for a given channel.
183    pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
184        self.inner.set_output_polarity(channel, polarity);
185        self.inner.set_complementary_output_polarity(channel, polarity);
186    }
187
188    /// Set the dead time as a proportion of max_duty
189    pub fn set_dead_time(&mut self, value: u16) {
190        let (ckd, value) = compute_dead_time_value(value);
191
192        self.inner.set_dead_time_clock_division(ckd);
193        self.inner.set_dead_time_value(value);
194    }
195}
196
197impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> {
198    type Channel = Channel;
199    type Time = Hertz;
200    type Duty = u16;
201
202    fn disable(&mut self, channel: Self::Channel) {
203        self.inner.enable_complementary_channel(channel, false);
204        self.inner.enable_channel(channel, false);
205    }
206
207    fn enable(&mut self, channel: Self::Channel) {
208        self.inner.enable_channel(channel, true);
209        self.inner.enable_complementary_channel(channel, true);
210    }
211
212    fn get_period(&self) -> Self::Time {
213        self.inner.get_frequency()
214    }
215
216    fn get_duty(&self, channel: Self::Channel) -> Self::Duty {
217        self.inner.get_compare_value(channel) as u16
218    }
219
220    fn get_max_duty(&self) -> Self::Duty {
221        if self.inner.get_counting_mode().is_center_aligned() {
222            self.inner.get_max_compare_value() as u16
223        } else {
224            self.inner.get_max_compare_value() as u16 + 1
225        }
226    }
227
228    fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {
229        assert!(duty <= self.get_max_duty());
230        self.inner.set_compare_value(channel, duty as u32)
231    }
232
233    fn set_period<P>(&mut self, period: P)
234    where
235        P: Into<Self::Time>,
236    {
237        self.inner.set_frequency(period.into());
238    }
239}
240
241fn compute_dead_time_value(value: u16) -> (Ckd, u8) {
242    /*
243        Dead-time = T_clk * T_dts * T_dtg
244
245        T_dts:
246        This bit-field indicates the division ratio between the timer clock (CK_INT) frequency and the
247        dead-time and sampling clock (tDTS)used by the dead-time generators and the digital filters
248        (ETR, TIx),
249        00: tDTS=tCK_INT
250        01: tDTS=2*tCK_INT
251        10: tDTS=4*tCK_INT
252
253        T_dtg:
254        This bit-field defines the duration of the dead-time inserted between the complementary
255        outputs. DT correspond to this duration.
256        DTG[7:5]=0xx => DT=DTG[7:0]x tdtg with tdtg=tDTS.
257        DTG[7:5]=10x => DT=(64+DTG[5:0])xtdtg with Tdtg=2xtDTS.
258        DTG[7:5]=110 => DT=(32+DTG[4:0])xtdtg with Tdtg=8xtDTS.
259        DTG[7:5]=111 => DT=(32+DTG[4:0])xtdtg with Tdtg=16xtDTS.
260        Example if TDTS=125ns (8MHz), dead-time possible values are:
261        0 to 15875 ns by 125 ns steps,
262        16 us to 31750 ns by 250 ns steps,
263        32 us to 63us by 1 us steps,
264        64 us to 126 us by 2 us steps
265    */
266
267    let mut error = u16::MAX;
268    let mut ckd = Ckd::DIV1;
269    let mut bits = 0u8;
270
271    for this_ckd in [Ckd::DIV1, Ckd::DIV2, Ckd::DIV4] {
272        let outdiv = match this_ckd {
273            Ckd::DIV1 => 1,
274            Ckd::DIV2 => 2,
275            Ckd::DIV4 => 4,
276            _ => unreachable!(),
277        };
278
279        // 127
280        // 128
281        // ..
282        // 254
283        // 256
284        // ..
285        // 504
286        // 512
287        // ..
288        // 1008
289
290        let target = value / outdiv;
291        let (these_bits, result) = if target < 128 {
292            (target as u8, target)
293        } else if target < 255 {
294            ((64 + (target / 2) as u8) | 128, (target - target % 2))
295        } else if target < 508 {
296            ((32 + (target / 8) as u8) | 192, (target - target % 8))
297        } else if target < 1008 {
298            ((32 + (target / 16) as u8) | 224, (target - target % 16))
299        } else {
300            (u8::MAX, 1008)
301        };
302
303        let this_error = value.abs_diff(result * outdiv);
304        if error > this_error {
305            ckd = this_ckd;
306            bits = these_bits;
307            error = this_error;
308        }
309
310        if error == 0 {
311            break;
312        }
313    }
314
315    (ckd, bits)
316}
317
318#[cfg(test)]
319mod tests {
320    use super::{compute_dead_time_value, Ckd};
321
322    #[test]
323    fn test_compute_dead_time_value() {
324        struct TestRun {
325            value: u16,
326            ckd: Ckd,
327            bits: u8,
328        }
329
330        let fn_results = [
331            TestRun {
332                value: 1,
333                ckd: Ckd::DIV1,
334                bits: 1,
335            },
336            TestRun {
337                value: 125,
338                ckd: Ckd::DIV1,
339                bits: 125,
340            },
341            TestRun {
342                value: 245,
343                ckd: Ckd::DIV1,
344                bits: 64 + 245 / 2,
345            },
346            TestRun {
347                value: 255,
348                ckd: Ckd::DIV2,
349                bits: 127,
350            },
351            TestRun {
352                value: 400,
353                ckd: Ckd::DIV1,
354                bits: 210,
355            },
356            TestRun {
357                value: 600,
358                ckd: Ckd::DIV4,
359                bits: 64 + (600u16 / 8) as u8,
360            },
361        ];
362
363        for test_run in fn_results {
364            let (ckd, bits) = compute_dead_time_value(test_run.value);
365
366            assert_eq!(ckd.to_bits(), test_run.ckd.to_bits());
367            assert_eq!(bits, test_run.bits);
368        }
369    }
370}