|
32 | 32 | */
|
33 | 33 |
|
34 | 34 | #include "rtc_api.h"
|
| 35 | +#include "lp_ticker_api.h" |
35 | 36 | #include "cmsis.h"
|
36 | 37 | #include "rtc_regs.h"
|
37 | 38 | #include "pwrseq_regs.h"
|
38 | 39 | #include "clkman_regs.h"
|
39 | 40 |
|
| 41 | +#define PRESCALE_VAL MXC_E_RTC_PRESCALE_DIV_2_0 // Set the divider for the 4kHz clock |
| 42 | +#define SHIFT_AMT (MXC_E_RTC_PRESCALE_DIV_2_12 - PRESCALE_VAL) |
| 43 | + |
| 44 | +#define WINDOW 1000 |
| 45 | + |
40 | 46 | static int rtc_inited = 0;
|
41 | 47 | static volatile uint32_t overflow_cnt = 0;
|
42 |
| -static uint32_t overflow_alarm = 0; |
| 48 | + |
| 49 | +static uint64_t rtc_read64(void); |
43 | 50 |
|
44 | 51 | //******************************************************************************
|
45 | 52 | static void overflow_handler(void)
|
46 | 53 | {
|
47 |
| - MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS; |
| 54 | + MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_OVERFLOW; |
| 55 | + MXC_PWRSEQ->flags = MXC_F_PWRSEQ_MSK_FLAGS_RTC_ROLLOVER; |
48 | 56 | overflow_cnt++;
|
49 |
| - |
50 |
| - if (overflow_cnt == overflow_alarm) { |
51 |
| - // Enable the comparator interrupt for the alarm |
52 |
| - MXC_RTCTMR->inten |= MXC_F_RTC_INTEN_COMP0; |
53 |
| - } |
54 |
| -} |
55 |
| - |
56 |
| -//****************************************************************************** |
57 |
| -static void alarm_handler(void) |
58 |
| -{ |
59 |
| - MXC_RTCTMR->inten &= ~MXC_F_RTC_INTEN_COMP0; |
60 |
| - MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS; |
61 | 57 | }
|
62 | 58 |
|
63 | 59 | //******************************************************************************
|
64 | 60 | void rtc_init(void)
|
65 | 61 | {
|
66 |
| - if(rtc_inited) { |
| 62 | + if (rtc_inited) { |
67 | 63 | return;
|
68 | 64 | }
|
69 | 65 | rtc_inited = 1;
|
70 | 66 |
|
| 67 | + overflow_cnt = 0; |
| 68 | + |
71 | 69 | // Enable the clock to the synchronizer
|
72 | 70 | MXC_CLKMAN->clk_ctrl_13_rtc_int_sync = MXC_E_CLKMAN_CLK_SCALE_ENABLED;
|
73 | 71 |
|
74 | 72 | // Enable the clock to the RTC
|
75 | 73 | MXC_PWRSEQ->reg0 |= MXC_F_PWRSEQ_REG0_PWR_RTCEN_RUN;
|
76 | 74 |
|
77 |
| - // Set the divider from the 4kHz clock |
78 |
| - MXC_RTCTMR->prescale = MXC_E_RTC_PRESCALE_DIV_2_0; |
79 |
| - |
80 |
| - // Enable the overflow interrupt |
81 |
| - MXC_RTCTMR->inten |= MXC_F_RTC_FLAGS_OVERFLOW; |
82 |
| - |
83 | 75 | // Prepare interrupt handlers
|
84 |
| - NVIC_SetVector(RTC0_IRQn, (uint32_t)alarm_handler); |
| 76 | + NVIC_SetVector(RTC0_IRQn, (uint32_t)lp_ticker_irq_handler); |
85 | 77 | NVIC_EnableIRQ(RTC0_IRQn);
|
86 | 78 | NVIC_SetVector(RTC3_IRQn, (uint32_t)overflow_handler);
|
87 | 79 | NVIC_EnableIRQ(RTC3_IRQn);
|
88 | 80 |
|
89 |
| - // Enable the RTC |
90 |
| - MXC_RTCTMR->ctrl |= MXC_F_RTC_CTRL_ENABLE; |
| 81 | + // Enable wakeup on RTC rollover |
| 82 | + MXC_PWRSEQ->msk_flags &= ~MXC_F_PWRSEQ_MSK_FLAGS_RTC_ROLLOVER; |
| 83 | + |
| 84 | + /* RTC registers are only reset on a power cycle. Do not reconfigure the RTC |
| 85 | + * if it is already running. |
| 86 | + */ |
| 87 | + if (!(MXC_RTCTMR->ctrl & MXC_F_RTC_CTRL_ENABLE)) { |
| 88 | + // Set the clock divider |
| 89 | + MXC_RTCTMR->prescale = PRESCALE_VAL; |
| 90 | + |
| 91 | + // Enable the overflow interrupt |
| 92 | + MXC_RTCTMR->inten |= MXC_F_RTC_FLAGS_OVERFLOW; |
| 93 | + |
| 94 | + // Restart the timer from 0 |
| 95 | + MXC_RTCTMR->timer = 0; |
| 96 | + |
| 97 | + // Enable the RTC |
| 98 | + MXC_RTCTMR->ctrl |= MXC_F_RTC_CTRL_ENABLE; |
| 99 | + } |
| 100 | +} |
| 101 | + |
| 102 | +//****************************************************************************** |
| 103 | +void lp_ticker_init(void) |
| 104 | +{ |
| 105 | + rtc_init(); |
91 | 106 | }
|
92 | 107 |
|
93 | 108 | //******************************************************************************
|
@@ -118,73 +133,117 @@ int rtc_isenabled(void)
|
118 | 133 | //******************************************************************************
|
119 | 134 | time_t rtc_read(void)
|
120 | 135 | {
|
121 |
| - unsigned int shift_amt; |
122 | 136 | uint32_t ovf_cnt_1, ovf_cnt_2, timer_cnt;
|
123 |
| - |
124 |
| - // Account for a change in the default prescaler |
125 |
| - shift_amt = MXC_E_RTC_PRESCALE_DIV_2_12 - MXC_RTCTMR->prescale; |
| 137 | + uint32_t ovf1, ovf2; |
126 | 138 |
|
127 | 139 | // Ensure coherency between overflow_cnt and timer
|
128 | 140 | do {
|
129 | 141 | ovf_cnt_1 = overflow_cnt;
|
| 142 | + ovf1 = MXC_RTCTMR->flags & MXC_F_RTC_FLAGS_OVERFLOW; |
130 | 143 | timer_cnt = MXC_RTCTMR->timer;
|
| 144 | + ovf2 = MXC_RTCTMR->flags & MXC_F_RTC_FLAGS_OVERFLOW; |
131 | 145 | ovf_cnt_2 = overflow_cnt;
|
132 |
| - } while (ovf_cnt_1 != ovf_cnt_2); |
| 146 | + } while ((ovf_cnt_1 != ovf_cnt_2) || (ovf1 != ovf2)); |
133 | 147 |
|
134 |
| - return (timer_cnt >> shift_amt) + (ovf_cnt_1 << (32 - shift_amt)); |
| 148 | + // Account for an unserviced interrupt |
| 149 | + if (ovf1) { |
| 150 | + ovf_cnt_1++; |
| 151 | + } |
| 152 | + |
| 153 | + return (timer_cnt >> SHIFT_AMT) + (ovf_cnt_1 << (32 - SHIFT_AMT)); |
135 | 154 | }
|
136 | 155 |
|
137 | 156 | //******************************************************************************
|
138 |
| -uint64_t rtc_read_us(void) |
| 157 | +static uint64_t rtc_read64(void) |
139 | 158 | {
|
140 |
| - unsigned int shift_amt; |
141 | 159 | uint32_t ovf_cnt_1, ovf_cnt_2, timer_cnt;
|
142 |
| - uint64_t currentUs; |
143 |
| - |
144 |
| - // Account for a change in the default prescaler |
145 |
| - shift_amt = MXC_E_RTC_PRESCALE_DIV_2_12 - MXC_RTCTMR->prescale; |
| 160 | + uint32_t ovf1, ovf2; |
| 161 | + uint64_t current_us; |
146 | 162 |
|
147 | 163 | // Ensure coherency between overflow_cnt and timer
|
148 | 164 | do {
|
149 | 165 | ovf_cnt_1 = overflow_cnt;
|
| 166 | + ovf1 = MXC_RTCTMR->flags & MXC_F_RTC_FLAGS_OVERFLOW; |
150 | 167 | timer_cnt = MXC_RTCTMR->timer;
|
| 168 | + ovf2 = MXC_RTCTMR->flags & MXC_F_RTC_FLAGS_OVERFLOW; |
151 | 169 | ovf_cnt_2 = overflow_cnt;
|
152 |
| - } while (ovf_cnt_1 != ovf_cnt_2); |
| 170 | + } while ((ovf_cnt_1 != ovf_cnt_2) || (ovf1 != ovf2)); |
| 171 | + |
| 172 | + // Account for an unserviced interrupt |
| 173 | + if (ovf1) { |
| 174 | + ovf_cnt_1++; |
| 175 | + } |
153 | 176 |
|
154 |
| - currentUs = (((uint64_t)timer_cnt * 1000000) >> shift_amt) + (((uint64_t)ovf_cnt_1 * 1000000) << (32 - shift_amt)); |
| 177 | + current_us = (((uint64_t)timer_cnt * 1000000) >> SHIFT_AMT) + (((uint64_t)ovf_cnt_1 * 1000000) << (32 - SHIFT_AMT)); |
155 | 178 |
|
156 |
| - return currentUs; |
| 179 | + return current_us; |
157 | 180 | }
|
158 | 181 |
|
159 | 182 | //******************************************************************************
|
160 | 183 | void rtc_write(time_t t)
|
161 | 184 | {
|
162 |
| - // Account for a change in the default prescaler |
163 |
| - unsigned int shift_amt = MXC_E_RTC_PRESCALE_DIV_2_12 - MXC_RTCTMR->prescale; |
164 |
| - |
165 | 185 | MXC_RTCTMR->ctrl &= ~MXC_F_RTC_CTRL_ENABLE; // disable the timer while updating
|
166 |
| - MXC_RTCTMR->timer = t << shift_amt; |
167 |
| - overflow_cnt = t >> (32 - shift_amt); |
| 186 | + MXC_RTCTMR->timer = t << SHIFT_AMT; |
| 187 | + overflow_cnt = t >> (32 - SHIFT_AMT); |
168 | 188 | MXC_RTCTMR->ctrl |= MXC_F_RTC_CTRL_ENABLE; // enable the timer while updating
|
169 | 189 | }
|
170 | 190 |
|
171 | 191 | //******************************************************************************
|
172 |
| -void rtc_set_wakeup(uint64_t wakeupUs) |
| 192 | +void lp_ticker_set_interrupt(timestamp_t timestamp) |
173 | 193 | {
|
174 |
| - // Account for a change in the default prescaler |
175 |
| - unsigned int shift_amt = MXC_E_RTC_PRESCALE_DIV_2_12 - MXC_RTCTMR->prescale; |
| 194 | + uint32_t comp_value; |
| 195 | + uint64_t curr_ts64; |
| 196 | + uint64_t ts64; |
| 197 | + |
| 198 | + // Note: interrupts are disabled before this function is called. |
176 | 199 |
|
177 | 200 | // Disable the alarm while it is prepared
|
178 | 201 | MXC_RTCTMR->inten &= ~MXC_F_RTC_INTEN_COMP0;
|
179 |
| - MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_COMP0; // clear interrupt |
180 | 202 |
|
181 |
| - overflow_alarm = (wakeupUs >> (32 - shift_amt)) / 1000000; |
| 203 | + curr_ts64 = rtc_read64(); |
| 204 | + ts64 = (uint64_t)timestamp | (curr_ts64 & 0xFFFFFFFF00000000ULL); |
182 | 205 |
|
183 |
| - if (overflow_alarm == overflow_cnt) { |
184 |
| - MXC_RTCTMR->comp[0] = (wakeupUs << shift_amt) / 1000000; |
185 |
| - MXC_RTCTMR->inten |= MXC_F_RTC_INTEN_COMP0; |
| 206 | + // If this event is older than a recent window, it must be in the future |
| 207 | + if ((ts64 < (curr_ts64 - WINDOW)) && ((curr_ts64 - WINDOW) < curr_ts64)) { |
| 208 | + ts64 += 0x100000000ULL; |
186 | 209 | }
|
187 | 210 |
|
| 211 | + uint32_t timer = MXC_RTCTMR->timer; |
| 212 | + if (ts64 <= curr_ts64) { |
| 213 | + // This event has already occurred. Set the alarm to expire immediately. |
| 214 | + comp_value = timer + 1; |
| 215 | + } else { |
| 216 | + comp_value = (ts64 << SHIFT_AMT) / 1000000; |
| 217 | + } |
| 218 | + |
| 219 | + // Ensure that the compare value is far enough in the future to guarantee the interrupt occurs. |
| 220 | + if ((comp_value < (timer + 2)) && (comp_value > (timer - 10))) { |
| 221 | + comp_value = timer + 2; |
| 222 | + } |
| 223 | + |
| 224 | + MXC_RTCTMR->comp[0] = comp_value; |
| 225 | + MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_COMP0; // clear interrupt |
| 226 | + MXC_RTCTMR->inten |= MXC_F_RTC_INTEN_COMP0; // enable the interrupt |
| 227 | + |
188 | 228 | // Enable wakeup from RTC
|
189 |
| - MXC_PWRSEQ->msk_flags &= ~(MXC_F_PWRSEQ_MSK_FLAGS_RTC_ROLLOVER | MXC_F_PWRSEQ_MSK_FLAGS_RTC_CMPR0); |
| 229 | + MXC_PWRSEQ->msk_flags &= ~MXC_F_PWRSEQ_MSK_FLAGS_RTC_CMPR0; |
| 230 | +} |
| 231 | + |
| 232 | +//****************************************************************************** |
| 233 | +inline void lp_ticker_disable_interrupt(void) |
| 234 | +{ |
| 235 | + MXC_RTCTMR->inten &= ~MXC_F_RTC_INTEN_COMP0; |
| 236 | +} |
| 237 | + |
| 238 | +//****************************************************************************** |
| 239 | +inline void lp_ticker_clear_interrupt(void) |
| 240 | +{ |
| 241 | + MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS; |
| 242 | + MXC_PWRSEQ->flags = MXC_F_PWRSEQ_MSK_FLAGS_RTC_CMPR0; |
| 243 | +} |
| 244 | + |
| 245 | +//****************************************************************************** |
| 246 | +inline uint32_t lp_ticker_read(void) |
| 247 | +{ |
| 248 | + return rtc_read64(); |
190 | 249 | }
|
0 commit comments