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