Skip to main content

gd32c1x3_hal/timer/
pwm_input.rs

1//! This module allows Timer peripherals to be configured as pwm input.
2//! In this mode, the timer sample a squared signal to find it's frequency and duty cycle.
3
4use core::marker::PhantomData;
5use core::mem;
6
7use crate::pac::Dbg;
8use crate::pac::{Timer0, Timer1, Timer2, Timer3, Timer4};
9
10use crate::afio::PCF0;
11use crate::gpio::{self, Input};
12use crate::rcu::{BusTimerClock, Clocks};
13use crate::time::Hertz;
14use crate::timer::Timer;
15
16pub trait Pins<REMAP> {}
17
18use super::pins::{sealed::Remap, CPin};
19
20impl<TIM, REMAP, P1, P2, MODE1, MODE2> Pins<REMAP> for (P1, P2)
21where
22    REMAP: Remap<Periph = TIM>,
23    P1: CPin<REMAP, 0> + gpio::PinExt<Mode = Input<MODE1>>,
24    P2: CPin<REMAP, 1> + gpio::PinExt<Mode = Input<MODE2>>,
25{
26}
27
28/// PWM Input
29pub struct PwmInput<TIM, REMAP, PINS> {
30    _timer: PhantomData<TIM>,
31    _remap: PhantomData<REMAP>,
32    _pins: PhantomData<PINS>,
33}
34
35/// How the data is read from the timer
36pub enum ReadMode {
37    /// Return the latest captured data
38    Instant,
39    /// Wait for one period of the signal before computing the frequency and the duty cycle
40    /// The microcontroller will be halted for at most two period of the input signal.
41    WaitForNextCapture,
42}
43
44/// The error returned when reading a frequency from a timer
45#[derive(Debug)]
46pub enum Error {
47    /// The signal frequency is too low to be sampled by the current timer configuration
48    FrequencyTooLow,
49}
50
51/// Which frequency the timer will try to sample
52pub enum Configuration {
53    /// In this mode an algorithm calculates the optimal value for the autoreload register and the
54    /// prescaler register in order to be able to sample a wide range of frequency, at the expense
55    /// of resolution.
56    ///
57    /// The minimum frequency that can be sampled is 20% the provided frequency.
58    ///
59    /// Use this mode if you do not know what to choose.
60    Frequency(Hertz),
61
62    /// In this mode an algorithm calculates the optimal value for the autoreload register and the
63    /// prescaler register in order to sample the duty cycle with a high resolution.
64    /// This will limit the frequency range where the timer can operate.
65    ///
66    /// The minimum frequency that can be sampled is 90% the provided frequency
67    DutyCycle(Hertz),
68
69    /// In this mode an algorithm calculates the optimal value for the autoreload register and the
70    /// prescaler register in order to be able to sample signal with a frequency higher than the
71    /// provided value : there is no margin for lower frequencies.
72    RawFrequency(Hertz),
73
74    /// In this mode, the provided arr and presc are directly programmed in the register.
75    RawValues { arr: u16, presc: u16 },
76}
77impl Timer<Timer0> {
78    pub fn pwm_input<REMAP, PINS>(
79        mut self,
80        pins: PINS,
81        pcf0: &mut PCF0,
82        dbg: &mut Dbg,
83        mode: Configuration,
84    ) -> PwmInput<Timer0, REMAP, PINS>
85    where
86        REMAP: Remap<Periph = Timer0>,
87        PINS: Pins<REMAP>,
88    {
89        REMAP::remap(pcf0);
90        self.stop_in_debug(dbg, false);
91        let Self { tim, clk } = self;
92        tim0(tim, pins, clk, mode)
93    }
94}
95impl Timer<Timer1> {
96    pub fn pwm_input<REMAP, PINS>(
97        mut self,
98        pins: PINS,
99        pcf0: &mut PCF0,
100        dbg: &mut Dbg,
101        mode: Configuration,
102    ) -> PwmInput<Timer1, REMAP, PINS>
103    where
104        REMAP: Remap<Periph = Timer1>,
105        PINS: Pins<REMAP>,
106    {
107        REMAP::remap(pcf0);
108        self.stop_in_debug(dbg, false);
109        let Self { tim, clk } = self;
110        tim1(tim, pins, clk, mode)
111    }
112}
113
114impl Timer<Timer2> {
115    pub fn pwm_input<REMAP, PINS>(
116        mut self,
117        pins: PINS,
118        pcf0: &mut PCF0,
119        dbg: &mut Dbg,
120        mode: Configuration,
121    ) -> PwmInput<Timer2, REMAP, PINS>
122    where
123        REMAP: Remap<Periph = Timer2>,
124        PINS: Pins<REMAP>,
125    {
126        REMAP::remap(pcf0);
127        self.stop_in_debug(dbg, false);
128        let Self { tim, clk } = self;
129        tim2(tim, pins, clk, mode)
130    }
131}
132
133impl Timer<Timer3> {
134    pub fn pwm_input<REMAP, PINS>(
135        mut self,
136        pins: PINS,
137        pcf0: &mut PCF0,
138        dbg: &mut Dbg,
139        mode: Configuration,
140    ) -> PwmInput<Timer3, REMAP, PINS>
141    where
142        REMAP: Remap<Periph = Timer3>,
143        PINS: Pins<REMAP>,
144    {
145        REMAP::remap(pcf0);
146        self.stop_in_debug(dbg, false);
147        let Self { tim, clk } = self;
148        tim3(tim, pins, clk, mode)
149    }
150}
151
152impl Timer<Timer4> {
153    pub fn pwm_input<REMAP, PINS>(
154        mut self,
155        pins: PINS,
156        pcf0: &mut PCF0,
157        dbg: &mut Dbg,
158        mode: Configuration,
159    ) -> PwmInput<Timer4, REMAP, PINS>
160    where
161        REMAP: Remap<Periph = Timer4>,
162        PINS: Pins<REMAP>,
163    {
164        REMAP::remap(pcf0);
165        self.stop_in_debug(dbg, false);
166        let Self { tim, clk } = self;
167        tim4(tim, pins, clk, mode)
168    }
169}
170
171/// Courtesy of @TeXitoi (https://github.com/stm32-rs/stm32f1xx-hal/pull/10#discussion_r259535503)
172fn compute_arr_presc(freq: u32, clock: u32) -> (u16, u16) {
173    if freq == 0 {
174        return (core::u16::MAX, core::u16::MAX);
175    }
176    let presc = clock / freq.saturating_mul(core::u16::MAX as u32 + 1);
177    let arr = clock / freq.saturating_mul(presc + 1);
178    (core::cmp::max(1, arr as u16), presc as u16)
179}
180macro_rules! hal {
181    ($($TIMX:ident: ($timX:ident),)+) => {
182        $(
183            fn $timX<REMAP, PINS>(
184                tim: $TIMX,
185                _pins: PINS,
186                clk: Hertz,
187                mode : Configuration,
188            ) -> PwmInput<$TIMX, REMAP, PINS>
189            where
190                REMAP: Remap<Periph = $TIMX>,
191                PINS: Pins<REMAP>,
192            {
193                use Configuration::*;
194                // Disable capture on both channels during setting
195                // (for Channel X bit is CCXE)
196                tim.chctl2().modify(|_,w| w.ch0en().clear_bit().ch1en().clear_bit()
197                                       .ch0p().clear_bit().ch1p().set_bit());
198
199                // Define the direction of the channel (input/output)
200                // and the used input
201                tim.chctl0_input().modify( |_,w| w.ch0ms().ci0().ch1ms().ci0());
202
203                tim.dmainten().write(|w| w.ch0ie().set_bit());
204
205                // Configure slave mode control register
206                // Selects the trigger input to be used to synchronize the counter
207                // 101: Filtered Timer Input 1 (TI1FP1)
208                // ---------------------------------------
209                // Slave Mode Selection :
210                //  100: Reset Mode - Rising edge of the selected trigger input (TRGI)
211                //  reinitializes the counter and generates an update of the registers.
212                tim.smcfg().modify( |_,w| unsafe {w.trgs().bits(0b101).smc().bits(0b100)});
213
214                match mode {
215                    Frequency(f)  => {
216                        let freq = f.raw();
217                        let max_freq = if freq > 5 {freq/5} else {1};
218                        let (arr,presc) = compute_arr_presc(max_freq, clk.raw());
219                        tim.car().write(|w| w.car().bits(arr));
220                        tim.psc().write(|w| w.psc().bits(presc));
221                    },
222                    DutyCycle(f) => {
223                        let freq = f.raw();
224                        let max_freq = if freq > 2 {freq/2 + freq/4 + freq/8} else {1};
225                        let (arr,presc) = compute_arr_presc(max_freq, clk.raw());
226                        tim.car().write(|w| w.car().bits(arr));
227                        tim.psc().write(|w| w.psc().bits(presc));
228                    },
229                    RawFrequency(f) => {
230                        let freq = f.raw();
231                        let (arr,presc) = compute_arr_presc(freq, clk.raw());
232                        tim.car().write(|w| w.car().bits(arr));
233                        tim.psc().write(|w| w.psc().bits(presc));
234                    }
235                    RawValues{arr, presc} => {
236                        tim.car().write(|w| w.car().bits(arr));
237                        tim.psc().write(|w| w.psc().bits(presc));
238                    }
239                }
240
241                // Enable Capture on both channels
242                tim.chctl2().modify(|_,w| w.ch0en().set_bit().ch1en().set_bit());
243
244                tim.ctl0().modify(|_,w| w.cen().set_bit());
245                unsafe { mem::MaybeUninit::uninit().assume_init() }
246            }
247
248            impl<REMAP, PINS> PwmInput<$TIMX, REMAP, PINS>
249            where
250                REMAP: Remap<Periph = $TIMX>,
251                PINS: Pins<REMAP>,
252            {
253                /// Return the frequency sampled by the timer
254                pub fn read_frequency(&self, mode : ReadMode, clocks : &Clocks) -> Result<Hertz,Error> {
255                    if let ReadMode::WaitForNextCapture = mode {
256                        self.wait_for_capture();
257                    }
258
259                    let presc = unsafe { (*$TIMX::ptr()).psc().read().bits() as u16};
260                    let ccr1 = unsafe { (*$TIMX::ptr()).ch0cv().read().bits() as u16};
261
262                    // Formulas :
263                    //
264                    // F_timer = F_pclk / (PSC+1)*(ARR+1)
265                    // Frac_arr = (CCR1+1)/(ARR+1)
266                    // F_signal = F_timer/Frac_arr
267                    // <=> F_signal = [(F_plck)/((PSC+1)*(ARR+1))] * [(ARR+1)/(CCR1+1)]
268                    // <=> F_signal = F_pclk / ((PSC+1)*(CCR1+1))
269                    //
270                    // Where :
271                    // * PSC is the prescaler register
272                    // * ARR is the auto-reload register
273                    // * F_timer is the number of time per second where the timer overflow under normal
274                    // condition
275                    //
276                    if ccr1 == 0 {
277                        Err(Error::FrequencyTooLow)
278                    } else {
279                        let clk = <$TIMX>::timer_clock(&clocks);
280                        Ok(clk/((presc+1) as u32*(ccr1 + 1)as u32))
281                    }
282                }
283
284                /// Return the duty in the form of a fraction : (duty_cycle/period)
285                pub fn read_duty(&self, mode : ReadMode) -> Result<(u16,u16),Error> {
286                    if let ReadMode::WaitForNextCapture = mode {
287                        self.wait_for_capture();
288                    }
289
290                    // Formulas :
291                    // Duty_cycle = (CCR2+1)/(CCR1+1)
292                    let ccr1 = unsafe { (*$TIMX::ptr()).ch0cv().read().bits() as u16};
293                    let ccr2 = unsafe { (*$TIMX::ptr()).ch1cv().read().bits() as u16};
294                    if ccr1 == 0 {
295                        Err(Error::FrequencyTooLow)
296                    } else {
297                        Ok((ccr2,ccr1))
298                    }
299                }
300
301                /// Wait until the timer has captured a period
302                fn wait_for_capture(&self) {
303                    unsafe { (*$TIMX::ptr()).intf().write(|w| w.upif().clear_bit().ch0if().clear_bit().ch0of().clear_bit())};
304                    while unsafe { (*$TIMX::ptr()).intf().read().ch0if().bit_is_clear()} {}
305                }
306            }
307        )+
308    }
309}
310hal! {
311    Timer0: (tim0),
312}
313
314hal! {
315    Timer1: (tim1),
316}
317
318hal! {
319    Timer2: (tim2),
320    Timer3: (tim3),
321}
322
323hal! {
324    Timer4: (tim4),
325}