1 /*
2  * This file is part of the MicroPython project, http://micropython.org/
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2021 Damien P. George
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24  * THE SOFTWARE.
25  */
26 
27 #include "py/mphal.h"
28 #include "ticks.h"
29 #include "fsl_gpt.h"
30 
31 // General purpose timer for keeping microsecond and millisecond tick values.
32 #define GPTx GPT2
33 #define GPTx_IRQn GPT2_IRQn
34 #define GPTx_IRQHandler GPT2_IRQHandler
35 
36 static uint32_t ticks_us64_upper;
37 static uint32_t ticks_ms_upper;
38 
ticks_init(void)39 void ticks_init(void) {
40     ticks_us64_upper = 0;
41     ticks_ms_upper = 0;
42 
43     gpt_config_t config;
44     config.clockSource = kGPT_ClockSource_Osc;
45     config.divider = 24; // XTAL is 24MHz
46     config.enableFreeRun = true;
47     config.enableRunInWait = true;
48     config.enableRunInStop = true;
49     config.enableRunInDoze = true;
50     config.enableRunInDbg = false;
51     config.enableMode = true;
52     GPT_Init(GPTx, &config);
53 
54     GPT_EnableInterrupts(GPTx, kGPT_RollOverFlagInterruptEnable);
55     NVIC_SetPriority(GPTx_IRQn, 0); // highest priority
56     NVIC_EnableIRQ(GPTx_IRQn);
57 
58     GPT_StartTimer(GPTx);
59 }
60 
GPTx_IRQHandler(void)61 void GPTx_IRQHandler(void) {
62     if (GPT_GetStatusFlags(GPTx, kGPT_OutputCompare1Flag)) {
63         GPT_ClearStatusFlags(GPTx, kGPT_OutputCompare1Flag);
64         GPT_DisableInterrupts(GPTx, kGPT_OutputCompare1InterruptEnable);
65         __SEV();
66     }
67     if (GPT_GetStatusFlags(GPTx, kGPT_RollOverFlag)) {
68         GPT_ClearStatusFlags(GPTx, kGPT_RollOverFlag);
69         ++ticks_us64_upper;
70         if (++ticks_ms_upper >= 1000) {
71             // Wrap upper counter at a multiple of 1000 so that when mp_hal_ticks_ms()
72             // wraps due to overflow it wraps smoothly.
73             ticks_ms_upper = 0;
74         }
75     }
76 }
77 
ticks_wake_after_us32(uint32_t us)78 static void ticks_wake_after_us32(uint32_t us) {
79     if (us < 2) {
80         // Delay too short to guarantee that we won't miss it when setting the OCR below.
81         __SEV();
82     } else {
83         // Disable IRQs so setting the OCR is done without any interruption.
84         uint32_t irq_state = DisableGlobalIRQ();
85         GPT_EnableInterrupts(GPTx, kGPT_OutputCompare1InterruptEnable);
86         uint32_t oc = GPT_GetCurrentTimerCount(GPTx) + us;
87         GPT_SetOutputCompareValue(GPTx, kGPT_OutputCompare_Channel1, oc);
88         EnableGlobalIRQ(irq_state);
89     }
90 }
91 
ticks_us64_with(uint32_t * upper_ptr)92 static uint64_t ticks_us64_with(uint32_t *upper_ptr) {
93     uint32_t irq_state = DisableGlobalIRQ();
94     uint32_t lower = GPT_GetCurrentTimerCount(GPTx);
95     uint32_t upper = *upper_ptr;
96     uint32_t overflow = GPT_GetStatusFlags(GPTx, kGPT_RollOverFlag);
97     EnableGlobalIRQ(irq_state);
98     if (overflow && lower < 0x80000000) {
99         // The timer counter overflowed before reading it but the IRQ handler
100         // has not yet been called, so perform the IRQ arithmetic now.
101         ++upper;
102     }
103     return (uint64_t)upper << 32 | (uint64_t)lower;
104 }
105 
ticks_us32(void)106 uint32_t ticks_us32(void) {
107     return GPT_GetCurrentTimerCount(GPTx);
108 }
109 
ticks_us64(void)110 uint64_t ticks_us64(void) {
111     return ticks_us64_with(&ticks_us64_upper);
112 }
113 
ticks_ms32(void)114 uint32_t ticks_ms32(void) {
115     // This will return a value that only has the lower 32-bits valid.
116     return ticks_us64_with(&ticks_ms_upper) / 1000;
117 }
118 
ticks_delay_us64(uint64_t us)119 void ticks_delay_us64(uint64_t us) {
120     uint64_t t0 = ticks_us64();
121     for (;;) {
122         uint64_t dt = ticks_us64() - t0;
123         if (dt >= us) {
124             return;
125         }
126         dt = us - dt;
127         if (dt > 0xffffffff) {
128             dt = 0xffffffff;
129         }
130         ticks_wake_after_us32((uint32_t)dt);
131         if (dt < 50) {
132             __WFE();
133         } else {
134             MICROPY_EVENT_POLL_HOOK
135         }
136     }
137 }
138