Skip to main content

ra_hal/
timer_gpt.rs

1//! Timer that generates interrupts utilizing the General PWM Timer (`GPT`).
2//!
3//! # Notes
4//!
5//! * 32-bit timers are supported but one instance is used for embassy-time, if support is enabled.
6//! * `GPT` module stop will be disabled on instantiation, but not reenabled on drop.
7
8use core::marker::PhantomData;
9
10use embassy_hal_internal::{Peri, PeripheralType};
11
12use crate::{
13    event_link::InterruptEvent,
14    module_stop::ModuleStop,
15    pac::gpt::{
16        regs::{Gtdnsr, Gtupsr},
17        vals::{Ccr, Mode, Tpcs, Ud},
18    },
19};
20
21/// An [`InterruptTimer`] instance.
22#[allow(private_bounds)]
23pub trait Instance<Width: TimerWidth>: SealedInstance + ModuleStop + PeripheralType {
24    /// Event link event upon a match with capture/compare value A.
25    const COMP_A_EVENT: crate::event_link::InterruptEvent;
26
27    /// Event link event upon a match with capture/compare value B.
28    const COMP_B_EVENT: crate::event_link::InterruptEvent;
29
30    /// Event link event upon a match with compare value C.
31    const COMP_C_EVENT: crate::event_link::InterruptEvent;
32
33    /// Event link event for an overflow event.
34    const OVERFLOW_EVENT: crate::event_link::InterruptEvent;
35
36    /// Event link event for an underflow event.
37    const UNDERFLOW_EVENT: crate::event_link::InterruptEvent;
38}
39
40pub(crate) trait SealedInstance: PeripheralType {
41    const INDEX: usize;
42
43    fn regs() -> crate::pac::gpt::Gpt;
44}
45
46pub(crate) trait TimerWidth: Into<u32> + Send + Sync + 'static {
47    fn max() -> u64;
48}
49
50impl TimerWidth for u32 {
51    #[inline(always)]
52    fn max() -> u64 {
53        u32::MAX.into()
54    }
55}
56
57impl TimerWidth for u16 {
58    #[inline(always)]
59    fn max() -> u64 {
60        u16::MAX.into()
61    }
62}
63
64/// A timer that fires an [`InterruptEvent`] at a fixed interval.
65#[allow(private_bounds)]
66pub struct InterruptTimer<'d, W: TimerWidth, I: Instance<W>> {
67    phantom_i: PhantomData<&'d I>,
68    phantom_w: PhantomData<W>,
69    triangle: bool,
70}
71
72#[allow(private_bounds)]
73impl<'d, W: TimerWidth, I: Instance<W>> InterruptTimer<'d, W, I> {
74    /// Creates a new timer.
75    pub fn new(peri: Peri<'d, I>) -> Self {
76        let _ = peri;
77
78        I::start_module();
79
80        let gpt = I::regs();
81
82        // Disable external things that might modify the counter
83        gpt.gtupsr().write_value(Gtupsr(0));
84        gpt.gtdnsr().write_value(Gtdnsr(0));
85        gpt.gtcnt().write_value(0);
86        gpt.gtssr().modify(|r| r.set_cstrt(true));
87
88        Self {
89            phantom_i: PhantomData,
90            phantom_w: PhantomData,
91            triangle: false,
92        }
93    }
94
95    /// Sets the number of times per second a timer should fire.
96    #[inline(always)]
97    pub fn set_frequency(&mut self, frequency: u32) {
98        let gpt = I::regs();
99
100        // Set clock divider to 16
101        gpt.gtcr().modify(|r| r.set_tpcs(Tpcs::Div16));
102
103        let clocks = crate::clock::clock_status();
104
105        let mut period = (clocks.peripheral_d.to_Hz() as u64 / 16) / (frequency as u64);
106
107        if period > W::max() {
108            period /= 2;
109            self.triangle = true;
110            gpt.gtcr().modify(|r| r.set_md(Mode::TrianglePwm1));
111            gpt.gtber().modify(|w| w.set_ccra(Ccr::NoBuffer));
112        } else {
113            self.triangle = false;
114            gpt.gtcr().modify(|r| r.set_md(Mode::SawWaveOneShot));
115        }
116
117        assert!(period <= W::max());
118
119        self.set_period(period as u32);
120    }
121
122    #[inline(always)]
123    fn set_period(&mut self, period: u32) {
124        let gpt = I::regs();
125
126        // Note: 16-bit instances still use 32-bit registers.
127        gpt.gtpr().write_value(period);
128    }
129
130    /// Starts the timer and resets the counter and returns the associated [`InterruptEvent`].
131    /// The event is either over- or underflow depending on the frequency of the timer.
132    #[inline]
133    pub fn start(&mut self) -> InterruptEvent {
134        let gpt = I::regs();
135
136        gpt.gtuddtyc().write(|r| {
137            r.set_udf(true);
138            r.set_ud(Ud::Up);
139        });
140        gpt.gtuddtyc().write(|r| {
141            r.set_udf(false);
142            r.set_ud(Ud::Up);
143        });
144
145        gpt.gtcnt().write_value(0);
146
147        // Start the timer
148        gpt.gtstr().modify(|r| r.set_cstrt(I::INDEX, true));
149
150        if self.triangle {
151            I::UNDERFLOW_EVENT
152        } else {
153            I::OVERFLOW_EVENT
154        }
155    }
156
157    /// Stops the timer.
158    #[inline(always)]
159    pub fn stop(&mut self) {
160        let gpt = I::regs();
161
162        // Stop the timer
163        gpt.gtstr().modify(|r| r.set_cstrt(I::INDEX, false));
164    }
165}
166
167impl<'d, W: TimerWidth, I: Instance<W>> Drop for InterruptTimer<'d, W, I> {
168    fn drop(&mut self) {
169        error!(
170            "GPT{}: Drop not yet implemented, module will not be stopped",
171            I::INDEX
172        );
173    }
174}
175
176macro_rules! timer_instance {
177    ($peripheral:ident, $width:ident, $index:literal, $cmpa_int:ident, $cmpb_int:ident, $cmpc_int:ident, $overflow_int:ident, $underflow_int:ident) => {
178        impl crate::timer_gpt::Instance<$width> for crate::peripherals::$peripheral {
179            const COMP_A_EVENT: crate::event_link::InterruptEvent =
180                crate::event_link::InterruptEvent::$cmpa_int;
181            const COMP_B_EVENT: crate::event_link::InterruptEvent =
182                crate::event_link::InterruptEvent::$cmpb_int;
183            const COMP_C_EVENT: crate::event_link::InterruptEvent =
184                crate::event_link::InterruptEvent::$cmpc_int;
185            const OVERFLOW_EVENT: crate::event_link::InterruptEvent =
186                crate::event_link::InterruptEvent::$overflow_int;
187            const UNDERFLOW_EVENT: crate::event_link::InterruptEvent =
188                crate::event_link::InterruptEvent::$underflow_int;
189        }
190
191        impl crate::timer_gpt::SealedInstance for crate::peripherals::$peripheral {
192            const INDEX: usize = $index;
193
194            #[inline(always)]
195            fn regs() -> crate::pac::gpt::Gpt {
196                crate::pac::$peripheral
197            }
198        }
199    };
200}
201pub(crate) use timer_instance;