msp432/
timer.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Timer (TIMER_Ax)
6
7use core::cell::Cell;
8use kernel::hil::time::{
9    Alarm, AlarmClient, Counter, Frequency, OverflowClient, Ticks, Ticks16, Time,
10};
11use kernel::utilities::cells::OptionalCell;
12use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
13use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite};
14use kernel::utilities::StaticRef;
15use kernel::ErrorCode;
16
17pub const TIMER_A0_BASE: StaticRef<TimerRegisters> =
18    unsafe { StaticRef::new(0x4000_0000u32 as *const TimerRegisters) };
19
20pub const TIMER_A1_BASE: StaticRef<TimerRegisters> =
21    unsafe { StaticRef::new(0x4000_0400u32 as *const TimerRegisters) };
22
23pub const TIMER_A2_BASE: StaticRef<TimerRegisters> =
24    unsafe { StaticRef::new(0x4000_0800u32 as *const TimerRegisters) };
25
26pub const TIMER_A3_BASE: StaticRef<TimerRegisters> =
27    unsafe { StaticRef::new(0x4000_0C00u32 as *const TimerRegisters) };
28
29register_structs! {
30    /// Timer_Ax
31    pub TimerRegisters {
32        /// Timer_Ax Control
33        (0x00 => ctl: ReadWrite<u16, TAxCTL::Register>),
34        /// Timer_Ax Capture/Compare Control 0
35        (0x02 => cctl0: ReadWrite<u16, TAxCCTLx::Register>),
36        /// Timer_Ax Capture/Compare Control 1
37        (0x04 => cctl1: ReadWrite<u16, TAxCCTLx::Register>),
38        /// Timer_Ax Capture/Compare Control 2
39        (0x06 => cctl2: ReadWrite<u16, TAxCCTLx::Register>),
40        /// Timer_Ax Capture/Compare Control 3
41        (0x08 => cctl3: ReadWrite<u16, TAxCCTLx::Register>),
42        /// Timer_Ax Capture/Compare Control 4
43        (0x0A => cctl4: ReadWrite<u16, TAxCCTLx::Register>),
44        /// Timer_Ax Capture/Compare Control 5
45        (0x0C => cctl5: ReadWrite<u16, TAxCCTLx::Register>),
46        /// Timer_Ax Capture/Compare Control 6
47        (0x0E => cctl6: ReadWrite<u16, TAxCCTLx::Register>),
48        /// Timer_Ax Counter
49        (0x10 => cnt: ReadWrite<u16>),
50        /// Timer_Ax Capture/Compare 0
51        (0x12 => ccr0: ReadWrite<u16>),
52        /// Timer_Ax Capture/Compare 1
53        (0x14 => ccr1: ReadWrite<u16>),
54        /// Timer_Ax Capture/Compare 2
55        (0x16 => ccr2: ReadWrite<u16>),
56        /// Timer_Ax Capture/Compare 3
57        (0x18 => ccr3: ReadWrite<u16>),
58        /// Timer_Ax Capture/Compare 4
59        (0x1A => ccr4: ReadWrite<u16>),
60        /// Timer_Ax Capture/Compare 5
61        (0x1C => ccr5: ReadWrite<u16>),
62        /// Timer_Ax Capture/Compare 6
63        (0x1E => ccr6: ReadWrite<u16>),
64        /// Timer_Ax Expansion 0
65        (0x20 => ex0: ReadWrite<u16, TAxEX0::Register>),
66        (0x22 => _reserved),
67        /// Timer_Ax Interrupt Vector
68        (0x2E => iv: ReadWrite<u16, TAxIV::Register>),
69        (0x30 => @END),
70    }
71}
72
73register_bitfields! [u16,
74    /// Timer_Ax Control Register
75    TAxCTL [
76        /// Timer_A interrupt flag
77        TAIFG OFFSET(0) NUMBITS(1) [],
78        /// Timer_A interrupt enable
79        TAIE OFFSET(1) NUMBITS(1) [],
80        /// TIMER_A clear. Setting this bit resets TAxR, the timer clock divider logic, and the count direction.
81        TACLR OFFSET(2) NUMBITS(1) [],
82        /// Mode control. Setting MCx=0x00 when Timer_A is not in use conserves power.
83        MC OFFSET(4) NUMBITS(2) [
84            /// Stop mode: Timer is halted
85            StopMode = 0,
86            /// Up mode: Timer counts up to TAxCCR0
87            UpMode = 1,
88            /// Continuous mode: Timer counts up to 0xFFFF
89            ContinuousMode = 2,
90            /// Up/Down mode: Timer counts up to TAxCCR0 then down to 0x0000
91            UpDownMode = 3
92        ],
93        /// Input divider. These bits along with the TAIDEX bits select the divider for the input clock.
94        ID OFFSET(6) NUMBITS(2) [
95            /// Clock divided by 1
96            DividedBy1 = 0,
97            /// Clock divided by 2
98            DividedBy2 = 1,
99            /// Clock divided by 4
100            DividedBy4 = 2,
101            /// Clock divied by 8
102            DividedBy8 = 3
103        ],
104        /// Timer_A clock source Select
105        TASSEL OFFSET(8) NUMBITS(2) [
106            /// TAxCLK
107            TAxCLK = 0,
108            /// ACLK
109            ACLK = 1,
110            /// SMCLK
111            SMCLK = 2,
112            /// INCLK
113            INCLK = 3
114        ]
115    ],
116    /// Timer_Ax Capture/Compare Control Register
117    TAxCCTLx [
118        /// Capture/compare interrupt flag
119        CCIFG OFFSET(0) NUMBITS(1) [],
120        /// Capture overflow. This bit indicates a capture overflow occured. COV must be reset with software.
121        COV OFFSET(1) NUMBITS(1) [],
122        /// Output. For output mode 0, this bit directly controls the state of the output
123        OUT OFFSET(2) NUMBITS(1) [],
124        /// Capture/compare input. The selected input signal can be read by this bit.
125        CCI OFFSET(3) NUMBITS(1) [],
126        /// Capture/compare interrupt enable. This bit enables the interrupt request of the corresponding CCIFG flag.
127        CCIE OFFSET(4) NUMBITS(1) [],
128        /// Output mode. Modes 2, 3, 6 and 7 are not useful for TAxCCR0 because EQUx=EQU0.
129        OUTMOD OFFSET(5) NUMBITS(3) [
130            /// OUT bit value
131            OutBit = 0,
132            /// Set
133            Set = 1,
134            /// Toggle/reset
135            ToggleReset = 2,
136            /// Set/reset
137            SetReset = 3,
138            /// Toggle
139            Toggle = 4,
140            /// Reset
141            Reset = 5,
142            /// Toggle/set
143            ToggleSet = 6,
144            /// Reset/set
145            ResetSet = 7
146        ],
147        /// Capture mode
148        CAP OFFSET(8) NUMBITS(1) [],
149        /// Synchronized capture/compare input
150        SCCI OFFSET(10) NUMBITS(1) [],
151        /// Synchronize capture source. This bit is used to synchronize the capture input signal with the timer clock.
152        SCS OFFSET(11) NUMBITS(1) [
153            /// Asynchronous capture
154            Asynchronous = 0,
155            /// Synchronous capture
156            Synchronous = 1
157        ],
158        /// Capture/compare input select. These bits select the TAxCCR0 input signal.
159        CCIS OFFSET(12) NUMBITS(2) [
160            /// CCIxA
161            CCIxA = 0,
162            /// CCIxB
163            CCIxB = 1,
164            /// GND
165            GND = 2,
166            /// VCC
167            VCC = 3
168        ],
169        /// Capture mode
170        CM OFFSET(14) NUMBITS(2) [
171            /// No capture
172            NoCapture = 0,
173            /// Capture on rising edge
174            CaptureRisingEdge = 1,
175            /// Capture on falling edge
176            CaptureFallingEdge = 2,
177            /// Capture on bith rising and falling edges
178            CaptureBothEdges = 3
179        ]
180    ],
181    /// Timer_Ax Interrupt Vector Register
182    TAxIV [
183        TAIV OFFSET(0) NUMBITS(16) [
184            /// No interrupt pending
185            NoInterrupt = 0x00,
186            /// Capture/compare: TAxCCR1 CCIFG
187            InterruptCCR1 = 0x02,
188            /// Capture/compare: TAxCCR2 CCIFG
189            InterruptCCR2 = 0x04,
190            /// Capture/compare: TAxCCR3 CCIFG
191            InterruptCCR3 = 0x06,
192            /// Capture/compare: TAxCCR4 CCIFG
193            InterruptCCR4 = 0x08,
194            /// Capture/compare: TAxCCR5 CCIFG
195            InterruptCCR5 = 0x0A,
196            /// Capture/compare: TAxCCR6 CCIFG
197            InterruptCCR6 = 0x0C,
198            /// Timer overflow: TAxCTL TAIFG
199            InterruptTimer = 0x0E
200        ]
201    ],
202    /// Timer_Ax Expansion Register
203    TAxEX0 [
204        /// Input divider expansion. These bits along with the ID bits select the divider for the input clock.
205        TAIDEX OFFSET(0) NUMBITS(3) [
206            /// Divide by 1
207            DivideBy1 = 0,
208            /// Divide by 2
209            DivideBy2 = 1,
210            /// Divide by 3
211            DivideBy3 = 2,
212            /// Divide by 4
213            DivideBy4 = 3,
214            /// Divide by 5
215            DivideBy5 = 4,
216            /// Divide by 6
217            DivideBy6 = 5,
218            /// Divide by 7
219            DivideBy7 = 6,
220            /// Divide by 8
221            DivideBy8 = 7
222        ]
223    ]
224];
225
226/// Since this timer-modules will be used for other things than alarm too
227/// (e.g. PWM, Timer, etc.) keep track for what it is used for.
228#[derive(PartialEq, Copy, Clone)]
229enum TimerMode {
230    Disabled,
231    Alarm,
232    InternalTimer,
233}
234
235pub struct TimerAFrequency {}
236
237impl Frequency for TimerAFrequency {
238    fn frequency() -> u32 {
239        crate::cs::ACLK_HZ / 16
240    }
241}
242
243pub enum InternalTrigger {
244    CaptureCompare1,
245    CaptureCompare2,
246    CaptureCompare3,
247    CaptureCompare4,
248    CaptureCompare5,
249    CaptureCompare6,
250}
251
252pub trait InternalTimer {
253    /// Start timer in a given frequency. No interrupts are generated, the signal when the timer
254    /// has elapsed is directly forwarded to the dedicated hardware module.
255    /// Ok(()): timer started successfully
256    /// INVAL: frequency too high or too low
257    /// BUSY: timer already in use
258    fn start(&self, frequency_hz: u32, int_src: InternalTrigger) -> Result<(), ErrorCode>;
259
260    /// Stop the timer
261    fn stop(&self);
262}
263
264pub struct TimerA<'a> {
265    registers: StaticRef<TimerRegisters>,
266    mode: Cell<TimerMode>,
267    alarm_client: OptionalCell<&'a dyn AlarmClient>,
268}
269
270impl<'a> TimerA<'a> {
271    pub const fn new(base: StaticRef<TimerRegisters>) -> TimerA<'a> {
272        TimerA {
273            registers: base,
274            mode: Cell::new(TimerMode::Disabled),
275            alarm_client: OptionalCell::empty(),
276        }
277    }
278
279    // Setup the timer to use it for alarms
280    fn setup_for_alarm(&self) {
281        // Setup the timer to use the ACLK (32.768kHz) as clock source, configure it to continuous
282        // mode, divide the clock down to 2048Hz:
283        // 16bit at 2048Hz: granulation about 0.5ms, maximum interval about 30s.
284
285        // Set ACLK as clock source
286        // Divide the clock source by 8 -> 4096Hz
287        // Setup for continuous mode
288        // Disable interrupts
289        // Clear any pending interrupts
290        self.registers.ctl.modify(
291            TAxCTL::TASSEL::ACLK
292                + TAxCTL::ID::DividedBy8
293                + TAxCTL::MC::ContinuousMode
294                + TAxCTL::TAIE::CLEAR
295                + TAxCTL::TAIFG::CLEAR,
296        );
297
298        // divide the 4096Hz by 2 to get 2048Hz
299        self.registers.ex0.modify(TAxEX0::TAIDEX::DivideBy2);
300        self.mode.set(TimerMode::Alarm);
301    }
302
303    // Stops the timer, no matter how it is configured
304    fn stop_timer(&self) {
305        // Disable interrupt and set timer to stop-mode
306        self.registers
307            .ctl
308            .modify(TAxCTL::MC::StopMode + TAxCTL::TAIE::CLEAR);
309
310        // Reset the configuration and disable interrupts of all capture-compare modules
311        self.registers.cctl0.set(0);
312        self.registers.cctl1.set(0);
313        self.registers.cctl2.set(0);
314        self.registers.cctl3.set(0);
315        self.registers.cctl4.set(0);
316        self.registers.cctl5.set(0);
317        self.registers.cctl6.set(0);
318
319        self.mode.set(TimerMode::Disabled);
320    }
321
322    fn handle_alarm_interrupt(&self) {
323        // Disable the interrupt, since the alarm was fired
324        self.registers.cctl0.modify(TAxCCTLx::CCIE::CLEAR);
325        self.alarm_client.map(|client| client.alarm());
326    }
327
328    pub fn handle_interrupt(&self) {
329        if self.registers.cctl0.is_set(TAxCCTLx::CCIFG) {
330            if self.mode.get() == TimerMode::Alarm {
331                self.handle_alarm_interrupt();
332            }
333            self.registers.cctl0.modify(TAxCCTLx::CCIFG::CLEAR);
334        }
335    }
336}
337
338impl Time for TimerA<'_> {
339    type Frequency = TimerAFrequency;
340    type Ticks = Ticks16;
341
342    fn now(&self) -> Ticks16 {
343        Self::Ticks::from(self.registers.cnt.get())
344    }
345}
346
347impl<'a> Counter<'a> for TimerA<'a> {
348    fn set_overflow_client(&self, _client: &'a dyn OverflowClient) {}
349
350    fn start(&self) -> Result<(), ErrorCode> {
351        self.setup_for_alarm();
352        Ok(())
353    }
354
355    fn stop(&self) -> Result<(), ErrorCode> {
356        self.stop_timer();
357        Ok(())
358    }
359
360    fn reset(&self) -> Result<(), ErrorCode> {
361        self.registers.cnt.set(0);
362        Ok(())
363    }
364
365    fn is_running(&self) -> bool {
366        self.registers.cctl0.is_set(TAxCCTLx::CCIE)
367    }
368}
369
370impl<'a> Alarm<'a> for TimerA<'a> {
371    fn set_alarm_client(&self, client: &'a dyn AlarmClient) {
372        self.alarm_client.set(client);
373    }
374
375    fn set_alarm(&self, reference: Self::Ticks, dt: Self::Ticks) {
376        if self.mode.get() != TimerMode::Alarm {
377            self.setup_for_alarm();
378        }
379        let now = self.now();
380        let mut expire = reference.wrapping_add(dt);
381        if !now.within_range(reference, expire) {
382            expire = now;
383        }
384
385        if expire.wrapping_sub(now) <= self.minimum_dt() {
386            expire = now.wrapping_add(self.minimum_dt());
387        }
388
389        let _ = self.disarm();
390        // Set compare register
391        self.registers.ccr0.set(expire.into_u16());
392        // Enable capture/compare interrupt
393        self.registers.cctl0.modify(TAxCCTLx::CCIE::SET);
394    }
395
396    fn get_alarm(&self) -> Self::Ticks {
397        Self::Ticks::from(self.registers.ccr0.get())
398    }
399
400    fn is_armed(&self) -> bool {
401        let int_enabled = self.registers.cctl0.is_set(TAxCCTLx::CCIE);
402        (self.mode.get() == TimerMode::Alarm) && int_enabled
403    }
404
405    fn disarm(&self) -> Result<(), ErrorCode> {
406        // Disable the capture/compare interrupt
407        self.registers.cctl0.modify(TAxCCTLx::CCIE::CLEAR);
408        // Stop the timer completely
409        //self.stop_timer();
410        Ok(())
411    }
412
413    fn minimum_dt(&self) -> Self::Ticks {
414        Self::Ticks::from(1_u16)
415    }
416}
417
418impl InternalTimer for TimerA<'_> {
419    fn start(&self, frequency_hz: u32, trigger: InternalTrigger) -> Result<(), ErrorCode> {
420        if self.mode.get() != TimerMode::Disabled && self.mode.get() != TimerMode::InternalTimer {
421            return Err(ErrorCode::BUSY);
422        }
423
424        if frequency_hz > crate::cs::SMCLK_HZ {
425            return Err(ErrorCode::INVAL);
426        }
427
428        // Stop timer if a different frequency was configured before
429        self.stop_timer();
430
431        let reg_val = if frequency_hz <= 100 {
432            // Divide the SMCLK by 40 -> 1_500_000 / 40 = 37.5kHz
433            self.registers.ctl.modify(TAxCTL::ID::DividedBy8);
434            self.registers.ex0.modify(TAxEX0::TAIDEX::DivideBy5);
435            (crate::cs::SMCLK_HZ / 40) / frequency_hz
436        } else {
437            self.registers.ctl.modify(TAxCTL::ID::DividedBy1);
438            self.registers.ex0.modify(TAxEX0::TAIDEX::DivideBy1);
439            crate::cs::SMCLK_HZ / frequency_hz
440        };
441
442        // Set SMCLK as clock source
443        // Setup for up-mode
444        // Disable interrupts
445        // Clear any pending interrupts
446        self.registers.ctl.modify(
447            TAxCTL::TASSEL::SMCLK + TAxCTL::MC::UpMode + TAxCTL::TAIE::CLEAR + TAxCTL::TAIFG::CLEAR,
448        );
449
450        // Set timer value
451        self.registers.ccr0.set((reg_val - 1) as u16);
452        self.registers.cctl0.modify(TAxCCTLx::CCIE::CLEAR);
453
454        // Get the correct capture-compare registers depending on the desired trigger
455        let (ccr_reg, cctl_reg) = match trigger {
456            InternalTrigger::CaptureCompare1 => (&self.registers.ccr1, &self.registers.cctl1),
457            InternalTrigger::CaptureCompare2 => (&self.registers.ccr2, &self.registers.cctl2),
458            InternalTrigger::CaptureCompare3 => (&self.registers.ccr3, &self.registers.cctl3),
459            InternalTrigger::CaptureCompare4 => (&self.registers.ccr4, &self.registers.cctl4),
460            InternalTrigger::CaptureCompare5 => (&self.registers.ccr5, &self.registers.cctl5),
461            InternalTrigger::CaptureCompare6 => (&self.registers.ccr6, &self.registers.cctl6),
462        };
463
464        // Set capture value to raise interrupt
465        ccr_reg.set((reg_val - 2) as u16);
466        // Enable CCR interrupt to trigger the corresponding Hardware
467        cctl_reg.modify(TAxCCTLx::OUTMOD::SetReset + TAxCCTLx::OUT::CLEAR + TAxCCTLx::CCIE::CLEAR);
468
469        self.mode.set(TimerMode::InternalTimer);
470        Ok(())
471    }
472
473    fn stop(&self) {
474        self.stop_timer();
475    }
476}