vorago_shared_hal/timer/
mod.rs

1pub mod regs;
2
3use core::convert::Infallible;
4
5#[cfg(feature = "vor1x")]
6pub use crate::InterruptConfig;
7#[cfg(feature = "vor1x")]
8use crate::sysconfig::enable_peripheral_clock;
9pub use regs::{CascadeSource, InvalidTimerIndex, TimId};
10
11use crate::{enable_nvic_interrupt, sealed::Sealed, time::Hertz};
12use crate::{gpio::DynPinId, ioconfig::regs::FunctionSelect, pins::AnyPin};
13use fugit::RateExtU32;
14
15#[cfg(feature = "vor1x")]
16use crate::PeripheralSelect;
17
18#[cfg(feature = "vor1x")]
19use va108xx as pac;
20#[cfg(feature = "vor4x")]
21use va416xx as pac;
22
23#[cfg(feature = "vor4x")]
24pub const TIM_IRQ_OFFSET: usize = 48;
25
26//==================================================================================================
27// Defintions
28//==================================================================================================
29
30#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
31#[cfg_attr(feature = "defmt", derive(defmt::Format))]
32pub struct CascadeControl {
33    /// Enable Cascade 0 signal active as a requirement for counting
34    pub enable_src_0: bool,
35    /// Invert Cascade 0, making it active low
36    pub inv_src_0: regs::CascadeInvert,
37    /// Enable Cascade 1 signal active as a requirement for counting
38    pub enable_src_1: bool,
39    /// Invert Cascade 1, making it active low
40    pub inv_src_1: regs::CascadeInvert,
41    /// Specify required operation if both Cascade 0 and Cascade 1 are active.
42    /// 0 is a logical AND of both cascade signals, 1 is a logical OR
43    pub dual_operation: regs::DualCascadeOp,
44    /// Enable trigger mode for Cascade 0. In trigger mode, couting will start with the selected
45    /// cascade signal active, but once the counter is active, cascade control will be ignored
46    pub trigger_mode_0: bool,
47    /// Trigger mode, identical to [Self::trigger_mode_0] but for Cascade 1
48    pub trigger_mode_1: bool,
49    /// Enable Cascade 2 signal active as a requirement to stop counting. This mode is similar
50    /// to the REQ_STOP control bit, but signalled by a Cascade source
51    pub enable_stop_src_2: bool,
52    /// Invert Cascade 2, making it active low
53    pub inv_src_2: regs::CascadeInvert,
54    /// The counter is automatically disabled if the corresponding Cascade 2 level-sensitive input
55    /// souce is active when the count reaches 0. If the counter is not 0, the cascade control is
56    /// ignored
57    pub trigger_mode_2: bool,
58}
59
60#[derive(Debug, PartialEq, Eq)]
61#[cfg_attr(feature = "defmt", derive(defmt::Format))]
62pub enum CascadeSelect {
63    Csd0 = 0,
64    Csd1 = 1,
65    Csd2 = 2,
66}
67
68//==================================================================================================
69// Valid TIM and PIN combinations
70//==================================================================================================
71
72pub trait TimPin: AnyPin {
73    const PIN_ID: DynPinId;
74    const FUN_SEL: FunctionSelect;
75    const TIM_ID: TimId;
76}
77
78pub trait TimInstance: Sealed {
79    // TIM ID ranging from 0 to 23 for 24 TIM peripherals
80    const ID: TimId;
81    #[cfg(feature = "vor4x")]
82    const IRQ: va416xx::Interrupt;
83
84    #[cfg(feature = "vor4x")]
85    fn clock(clocks: &crate::clock::Clocks) -> Hertz {
86        if Self::ID.value() <= 15 {
87            clocks.apb1()
88        } else {
89            clocks.apb2()
90        }
91    }
92}
93
94macro_rules! tim_marker {
95    ($TIMX:path, $ID:expr) => {
96        impl TimInstance for $TIMX {
97            const ID: TimId = TimId::new_unchecked($ID);
98        }
99
100        impl Sealed for $TIMX {}
101    };
102    ($TIMX:path, $ID:expr, $IrqId:ident) => {
103        impl TimInstance for $TIMX {
104            const ID: TimId = TimId::new_unchecked($ID);
105            const IRQ: va416xx::Interrupt = va416xx::Interrupt::$IrqId;
106        }
107
108        impl Sealed for $TIMX {}
109    };
110}
111
112cfg_if::cfg_if! {
113    if #[cfg(feature = "vor1x")] {
114        tim_marker!(pac::Tim0, 0);
115        tim_marker!(pac::Tim1, 1);
116        tim_marker!(pac::Tim2, 2);
117        tim_marker!(pac::Tim3, 3);
118        tim_marker!(pac::Tim4, 4);
119        tim_marker!(pac::Tim5, 5);
120        tim_marker!(pac::Tim6, 6);
121        tim_marker!(pac::Tim7, 7);
122        tim_marker!(pac::Tim8, 8);
123        tim_marker!(pac::Tim9, 9);
124        tim_marker!(pac::Tim10, 10);
125        tim_marker!(pac::Tim11, 11);
126        tim_marker!(pac::Tim12, 12);
127        tim_marker!(pac::Tim13, 13);
128        tim_marker!(pac::Tim14, 14);
129        tim_marker!(pac::Tim15, 15);
130        tim_marker!(pac::Tim16, 16);
131        tim_marker!(pac::Tim17, 17);
132        tim_marker!(pac::Tim18, 18);
133        tim_marker!(pac::Tim19, 19);
134        tim_marker!(pac::Tim20, 20);
135        tim_marker!(pac::Tim21, 21);
136        tim_marker!(pac::Tim22, 22);
137        tim_marker!(pac::Tim23, 23);
138    } else if #[cfg(feature = "vor4x")] {
139        tim_marker!(pac::Tim0, 0, TIM0);
140        tim_marker!(pac::Tim1, 1, TIM1);
141        tim_marker!(pac::Tim2, 2, TIM2);
142        tim_marker!(pac::Tim3, 3, TIM3);
143        tim_marker!(pac::Tim4, 4, TIM4);
144        tim_marker!(pac::Tim5, 5, TIM5);
145        tim_marker!(pac::Tim6, 6, TIM6);
146        tim_marker!(pac::Tim7, 7, TIM7);
147        tim_marker!(pac::Tim8, 8, TIM8);
148        tim_marker!(pac::Tim9, 9, TIM9);
149        tim_marker!(pac::Tim10, 10, TIM10);
150        tim_marker!(pac::Tim11, 11, TIM11);
151        tim_marker!(pac::Tim12, 12, TIM12);
152        tim_marker!(pac::Tim13, 13, TIM13);
153        tim_marker!(pac::Tim14, 14, TIM14);
154        tim_marker!(pac::Tim15, 15, TIM15);
155        tim_marker!(pac::Tim16, 16, TIM16);
156        tim_marker!(pac::Tim17, 17, TIM17);
157        tim_marker!(pac::Tim18, 18, TIM18);
158        tim_marker!(pac::Tim19, 19, TIM19);
159        tim_marker!(pac::Tim20, 20, TIM20);
160        tim_marker!(pac::Tim21, 21, TIM21);
161        tim_marker!(pac::Tim22, 22, TIM22);
162        tim_marker!(pac::Tim23, 23, TIM23);
163    }
164}
165
166pub trait ValidTimAndPin<Pin: TimPin, Tim: TimInstance>: Sealed {}
167
168#[macro_use]
169mod macros {
170    macro_rules! pin_and_tim {
171        ($Px:ident, $FunSel:path, $ID:expr) => {
172            impl TimPin for Pin<$Px>
173            where
174                $Px: PinId,
175            {
176                const PIN_ID: DynPinId = $Px::ID;
177                const FUN_SEL: FunctionSelect = $FunSel;
178                const TIM_ID: TimId = TimId::new_unchecked($ID);
179            }
180        };
181    }
182}
183
184#[cfg(feature = "vor1x")]
185pub mod pins_vor1x;
186#[cfg(feature = "vor4x")]
187pub mod pins_vor4x;
188
189//==================================================================================================
190// Timers
191//==================================================================================================
192
193/// Hardware timers
194pub struct CountdownTimer {
195    id: TimId,
196    regs: regs::MmioTimer<'static>,
197    curr_freq: Hertz,
198    ref_clk: Hertz,
199    rst_val: u32,
200    last_cnt: u32,
201}
202
203impl CountdownTimer {
204    /// Create a countdown timer structure for a given TIM peripheral.
205    ///
206    /// This does not enable the timer. You can use the [Self::load], [Self::start],
207    /// [Self::enable_interrupt] and [Self::enable] API to set up and configure the countdown
208    /// timer.
209    #[cfg(feature = "vor1x")]
210    pub fn new<Tim: TimInstance>(_tim: Tim, sys_clk: Hertz) -> Self {
211        enable_tim_clk(Tim::ID);
212        assert_tim_reset_for_cycles(Tim::ID, 2);
213        CountdownTimer {
214            id: Tim::ID,
215            regs: regs::Timer::new_mmio(Tim::ID),
216            ref_clk: sys_clk,
217            rst_val: 0,
218            curr_freq: 0.Hz(),
219            last_cnt: 0,
220        }
221    }
222
223    /// Create a countdown timer structure for a given TIM peripheral.
224    ///
225    /// This does not enable the timer. You can use the [Self::load], [Self::start],
226    /// [Self::enable_interrupt] and [Self::enable] API to set up and configure the countdown
227    /// timer.
228    #[cfg(feature = "vor4x")]
229    pub fn new<Tim: TimInstance>(_tim: Tim, clks: &crate::clock::Clocks) -> Self {
230        enable_tim_clk(Tim::ID);
231        assert_tim_reset_for_cycles(Tim::ID, 2);
232        CountdownTimer {
233            id: Tim::ID,
234            regs: regs::Timer::new_mmio(Tim::ID),
235            ref_clk: clks.apb1(),
236            rst_val: 0,
237            curr_freq: 0.Hz(),
238            last_cnt: 0,
239        }
240    }
241
242    #[inline]
243    pub fn perid(&self) -> u32 {
244        self.regs.read_perid()
245    }
246
247    #[inline(always)]
248    pub fn enable(&mut self) {
249        self.regs
250            .write_enable_control(regs::EnableControl::new_enable());
251    }
252    #[inline(always)]
253    pub fn disable(&mut self) {
254        self.regs
255            .write_enable_control(regs::EnableControl::new_disable());
256    }
257
258    #[cfg(feature = "vor1x")]
259    pub fn enable_interrupt(&mut self, irq_cfg: InterruptConfig) {
260        if irq_cfg.route {
261            let irqsel = unsafe { pac::Irqsel::steal() };
262            enable_peripheral_clock(PeripheralSelect::Irqsel);
263            irqsel
264                .tim(self.id.value() as usize)
265                .write(|w| unsafe { w.bits(irq_cfg.id as u32) });
266        }
267        if irq_cfg.enable_in_nvic {
268            unsafe { enable_nvic_interrupt(irq_cfg.id) };
269        }
270        self.regs.modify_control(|mut value| {
271            value.set_irq_enable(true);
272            value
273        });
274    }
275
276    #[cfg(feature = "vor4x")]
277    #[inline(always)]
278    pub fn enable_interrupt(&mut self, enable_in_nvic: bool) {
279        if enable_in_nvic {
280            unsafe { enable_nvic_interrupt(self.id.interrupt_id()) };
281        }
282        self.regs.modify_control(|mut value| {
283            value.set_irq_enable(true);
284            value
285        });
286    }
287
288    /// This function only clears the interrupt enable bit.
289    ///
290    /// It does not mask the interrupt in the NVIC or un-route the IRQ.
291    #[inline(always)]
292    pub fn disable_interrupt(&mut self) {
293        self.regs.modify_control(|mut value| {
294            value.set_irq_enable(false);
295            value
296        });
297    }
298
299    /// Calls [Self::load] to configure the specified frequency and then calls [Self::enable].
300    pub fn start(&mut self, frequency: impl Into<Hertz>) {
301        self.load(frequency);
302        self.enable();
303    }
304
305    /// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the
306    /// flag and restart the time if configured correctly
307    pub fn wait(&mut self) -> nb::Result<(), Infallible> {
308        let cnt = self.counter();
309        if (cnt > self.last_cnt) || cnt == 0 {
310            self.last_cnt = self.rst_val;
311            Ok(())
312        } else {
313            self.last_cnt = cnt;
314            Err(nb::Error::WouldBlock)
315        }
316    }
317
318    /// Load the count down timer with a timeout but do not start it.
319    pub fn load(&mut self, timeout: impl Into<Hertz>) {
320        self.disable();
321        self.curr_freq = timeout.into();
322        self.rst_val = self.ref_clk.raw() / self.curr_freq.raw();
323        self.set_reload(self.rst_val);
324        self.set_count(self.rst_val);
325    }
326
327    #[inline(always)]
328    pub fn set_reload(&mut self, val: u32) {
329        self.regs.write_reset_value(val);
330    }
331
332    #[inline(always)]
333    pub fn set_count(&mut self, val: u32) {
334        self.regs.write_count_value(val);
335    }
336
337    #[inline(always)]
338    pub fn counter(&self) -> u32 {
339        self.regs.read_count_value()
340    }
341
342    /// Disable the counter, setting both enable and active bit to 0
343    #[inline]
344    pub fn auto_disable(&mut self, enable: bool) {
345        self.regs.modify_control(|mut value| {
346            value.set_auto_disable(enable);
347            value
348        });
349    }
350
351    /// This option only applies when the Auto-Disable functionality is 0.
352    ///
353    /// The active bit is changed to 0 when count reaches 0, but the counter stays
354    /// enabled. When Auto-Disable is 1, Auto-Deactivate is implied
355    #[inline]
356    pub fn auto_deactivate(&mut self, enable: bool) {
357        self.regs.modify_control(|mut value| {
358            value.set_auto_deactivate(enable);
359            value
360        });
361    }
362
363    /// Configure the cascade parameters
364    pub fn cascade_control(&mut self, ctrl: CascadeControl) {
365        self.regs.write_cascade_control(
366            regs::CascadeControl::builder()
367                .with_trigger2(ctrl.trigger_mode_2)
368                .with_inv2(ctrl.inv_src_2)
369                .with_en2(ctrl.enable_stop_src_2)
370                .with_trigger1(ctrl.trigger_mode_1)
371                .with_trigger0(ctrl.trigger_mode_0)
372                .with_dual_cascade_op(ctrl.dual_operation)
373                .with_inv1(ctrl.inv_src_1)
374                .with_en1(ctrl.enable_src_1)
375                .with_inv0(ctrl.inv_src_0)
376                .with_en0(ctrl.enable_src_0)
377                .build(),
378        );
379    }
380
381    pub fn cascade_source(
382        &mut self,
383        cascade_index: CascadeSelect,
384        src: regs::CascadeSource,
385    ) -> Result<(), regs::InvalidCascadeSourceId> {
386        // Safety: Index range safe by enum values.
387        unsafe {
388            self.regs
389                .write_cascade_unchecked(cascade_index as usize, regs::CascadeSourceReg::new(src)?);
390        }
391        Ok(())
392    }
393
394    pub fn curr_freq(&self) -> Hertz {
395        self.curr_freq
396    }
397
398    /// Disables the TIM and the dedicated TIM clock.
399    pub fn stop_with_clock_disable(mut self) {
400        self.disable();
401        unsafe { pac::Sysconfig::steal() }
402            .tim_clk_enable()
403            .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.id.value())) });
404    }
405}
406
407//==================================================================================================
408// Delay implementations
409//==================================================================================================
410//
411impl embedded_hal::delay::DelayNs for CountdownTimer {
412    fn delay_ns(&mut self, ns: u32) {
413        let ticks = (u64::from(ns)) * (u64::from(self.ref_clk.raw())) / 1_000_000_000;
414
415        let full_cycles = ticks >> 32;
416        let mut last_count;
417        let mut new_count;
418        if full_cycles > 0 {
419            self.set_reload(u32::MAX);
420            self.set_count(u32::MAX);
421            self.enable();
422
423            for _ in 0..full_cycles {
424                // Always ensure that both values are the same at the start.
425                new_count = self.counter();
426                last_count = new_count;
427                loop {
428                    new_count = self.counter();
429                    if new_count == 0 {
430                        // Wait till timer has wrapped.
431                        while self.counter() == 0 {
432                            cortex_m::asm::nop()
433                        }
434                        break;
435                    }
436                    // Timer has definitely wrapped.
437                    if new_count > last_count {
438                        break;
439                    }
440                    last_count = new_count;
441                }
442            }
443        }
444        let ticks = (ticks & u32::MAX as u64) as u32;
445        self.disable();
446        if ticks > 1 {
447            self.set_reload(ticks);
448            self.set_count(ticks);
449            self.enable();
450            last_count = ticks;
451
452            loop {
453                new_count = self.counter();
454                if new_count == 0 || (new_count > last_count) {
455                    break;
456                }
457                last_count = new_count;
458            }
459        }
460
461        self.disable();
462    }
463}
464
465#[inline(always)]
466pub fn enable_tim_clk(id: TimId) {
467    unsafe { pac::Sysconfig::steal() }
468        .tim_clk_enable()
469        .modify(|r, w| unsafe { w.bits(r.bits() | (1 << id.value())) });
470}
471
472#[inline(always)]
473pub fn disable_tim_clk(id: TimId) {
474    unsafe { pac::Sysconfig::steal() }
475        .tim_clk_enable()
476        .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << (id.value()))) });
477}
478
479/// Clear the reset bit of the TIM, holding it in reset
480///
481/// # Safety
482///
483/// Only the bit related to the corresponding TIM peripheral is modified
484#[inline]
485pub fn assert_tim_reset(id: TimId) {
486    unsafe { pac::Peripherals::steal() }
487        .sysconfig
488        .tim_reset()
489        .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << id.value())) });
490}
491
492#[inline]
493pub fn deassert_tim_reset(tim: TimId) {
494    unsafe { pac::Peripherals::steal() }
495        .sysconfig
496        .tim_reset()
497        .modify(|r, w| unsafe { w.bits(r.bits() | (1 << tim.value())) });
498}
499
500pub fn assert_tim_reset_for_cycles(tim: TimId, cycles: u32) {
501    assert_tim_reset(tim);
502    cortex_m::asm::delay(cycles);
503    deassert_tim_reset(tim);
504}