|
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