45
45
#include "mbed_assert.h"
46
46
#include "lp_ticker_api.h"
47
47
48
- static uint16_t SubSecond ;
49
- static uint64_t LastRtcTimeus ;
48
+ static volatile uint64_t last_time_read ;
49
+
50
+ /**
51
+ * Convert sub seconds ticks to micro seconds.
52
+ * The clock running at 32kHz, a tick is 1/32768 of a second.
53
+ */
54
+ static inline uint32_t ticks_to_us (uint16_t ticks ) {
55
+ return (((uint64_t )ticks * RTC_SEC_TO_US ) / RTC_CLOCK_HZ );
56
+ }
57
+
58
+ /**
59
+ * Convert us into sub seconds ticks.
60
+ * @note result might be troncated to be in the range [0 - RTC_SUB_SEC_MASK].
61
+ */
62
+ static inline uint16_t us_to_ticks (uint32_t us ) {
63
+ return (((uint64_t ) us * RTC_CLOCK_HZ ) / RTC_SEC_TO_US ) & RTC_SUB_SEC_MASK ;
64
+ }
65
+
66
+ #define RTC_TICK_THRESHOLD 5
50
67
51
68
/* See rtc.h for details */
52
69
void fRtcInit (void )
@@ -55,115 +72,131 @@ void fRtcInit(void)
55
72
CLOCKREG -> CCR .BITS .RTCEN = True ; /* Enable RTC clock 32K */
56
73
57
74
/* Reset RTC control register */
58
- RTCREG -> CONTROL .WORD = False ;
75
+ RTCREG -> CONTROL .WORD = 0 ;
59
76
60
77
/* Initialize all counters */
61
- RTCREG -> SECOND_COUNTER = False ;
62
- RTCREG -> SUB_SECOND_COUNTER = False ;
63
- RTCREG -> SECOND_ALARM = False ;
64
- RTCREG -> SUB_SECOND_ALARM = False ;
65
- LastRtcTimeus = 0 ;
78
+ RTCREG -> SECOND_COUNTER = 0 ;
79
+ RTCREG -> SUB_SECOND_COUNTER = 0 ;
80
+ RTCREG -> SECOND_ALARM = 0 ;
81
+ RTCREG -> SUB_SECOND_ALARM = 0 ;
82
+ last_time_read = 0 ;
66
83
67
84
/* Reset RTC Status register */
68
- RTCREG -> STATUS .WORD = False ;
85
+ RTCREG -> STATUS .WORD = 0 ;
69
86
70
87
/* Clear interrupt status */
71
- RTCREG -> INT_CLEAR .WORD = False ;
88
+ RTCREG -> INT_CLEAR .WORD = (
89
+ (1 << RTC_INT_CLR_SUB_SEC_BIT_POS ) |
90
+ (1 << RTC_INT_CLR_SEC_BIT_POS )
91
+ );
72
92
93
+ /* Wait previous write to complete */
94
+ while (RTCREG -> STATUS .BITS .BSY_CTRL_REG_WRT == True );
73
95
/* Start sec & sub_sec counter */
74
- while (RTCREG -> STATUS .BITS .BSY_CTRL_REG_WRT == True );/* Wait previous write to complete */
75
- RTCREG -> CONTROL .WORD |= ((True << RTC_CONTROL_SUBSEC_CNT_START_BIT_POS ) |
76
- (True << RTC_CONTROL_SEC_CNT_START_BIT_POS ));
96
+ RTCREG -> CONTROL .WORD |= (
97
+ (True << RTC_CONTROL_SUBSEC_CNT_START_BIT_POS ) |
98
+ (True << RTC_CONTROL_SEC_CNT_START_BIT_POS )
99
+ );
77
100
78
101
/* enable interruption associated with the rtc at NVIC level */
79
- NVIC_SetVector (Rtc_IRQn ,(uint32_t )fRtcHandler ); /* TODO define lp_ticker_isr */
102
+ NVIC_SetVector (Rtc_IRQn ,(uint32_t ) fRtcHandler ); /* TODO define lp_ticker_isr */
80
103
NVIC_ClearPendingIRQ (Rtc_IRQn );
81
104
NVIC_EnableIRQ (Rtc_IRQn );
82
105
83
- while (RTCREG -> STATUS .BITS .BSY_CTRL_REG_WRT == True ); /* Wait for RTC to finish writing register - RTC operates on 32K clock as compared to 32M core*/
84
-
85
- return ;
106
+ /* Wait for RTC to finish writing register */
107
+ while (RTCREG -> STATUS .BITS .BSY_CTRL_REG_WRT == True );
86
108
}
87
109
88
110
/* See rtc.h for details */
89
111
void fRtcFree (void )
90
112
{
91
- /* Reset RTC control register */
92
- RTCREG -> CONTROL .WORD = False ;
113
+ /* Disable interrupts and counter */
114
+ RTCREG -> CONTROL .WORD = 0 ;
93
115
94
116
/* disable interruption associated with the rtc */
95
117
NVIC_DisableIRQ (Rtc_IRQn );
96
118
97
- while (RTCREG -> STATUS .BITS .BSY_CTRL_REG_WRT == True ); /* Wait for RTC to finish writing register - RTC operates on 32K clock as compared to 32M core*/
119
+ /* Wait for RTC to finish writing register */
120
+ while (RTCREG -> STATUS .BITS .BSY_CTRL_REG_WRT == True );
98
121
}
99
122
100
123
/* See rtc.h for details */
101
124
void fRtcSetInterrupt (uint32_t timestamp )
102
125
{
103
- SubSecond = False ;
104
- uint32_t Second = False , EnableInterrupt = False ;
105
- uint8_t DividerAdjust = 1 ;
106
-
107
- if (timestamp ) {
108
- if (timestamp >= RTC_SEC_TO_US ) {
109
- /* TimeStamp is big enough to set second alarm */
110
- Second = ((timestamp / RTC_SEC_TO_US ) & RTC_SEC_MASK ); /* Convert micro second to second */
111
- RTCREG -> SECOND_ALARM = Second ; /* Write to alarm register */
112
-
113
- /* Enable second interrupt */
114
- EnableInterrupt = True << RTC_CONTROL_SEC_CNT_INT_BIT_POS ;
115
- }
116
- timestamp = timestamp - Second * RTC_SEC_TO_US ; /* Take out micro second for sub second alarm */
117
- if (timestamp > False ) {
118
- /* We have some thing for sub second */
119
-
120
- /* Convert micro second to sub_seconds(each count = 30.5 us) */
121
- if (timestamp > 131000 ) {
122
- DividerAdjust = 100 ;
123
- }
124
-
125
- volatile uint64_t Temp = (timestamp / DividerAdjust * RTC_CLOCK_HZ );
126
- Temp = (uint64_t )(Temp / RTC_SEC_TO_US * DividerAdjust );
127
- SubSecond = Temp & RTC_SUB_SEC_MASK ;
128
-
129
- if (SubSecond <= 5 ) {
130
- SubSecond = 0 ;
131
- }
132
-
133
- if (SubSecond > False ) {
134
- /* Second interrupt not enabled */
135
-
136
- /* Set SUB SEC_ALARM */
137
- RTCREG -> SUB_SECOND_ALARM = SubSecond ; /* Write to sub second alarm */
138
-
139
- /* Enable sub second interrupt */
140
- EnableInterrupt |= (True << RTC_CONTROL_SUBSEC_CNT_INT_BIT_POS );
141
- }
142
- }
143
-
144
- RTCREG -> CONTROL .WORD |= EnableInterrupt ;
145
- /* Enable RTC interrupt */
146
- NVIC_EnableIRQ (Rtc_IRQn );
147
-
148
- /* Wait for RTC to finish writing register - RTC operates on 32K clock as compared to 32M core*/
149
- while ((RTCREG -> STATUS .WORD & ((True << RTC_STATUS_SUB_SEC_ALARM_WRT_BIT_POS ) |
150
- (True << RTC_STATUS_SEC_ALARM_WRT_BIT_POS ) |
151
- (True << RTC_STATUS_CONTROL_WRT_BIT_POS ))));
152
- }
153
- return ;
126
+ uint64_t current_time = fRtcRead ();
127
+
128
+ /* compute delta between current time and timestamp.
129
+ * Note: the current time used to compute the delta is relative (truncated
130
+ * to 32 bits).
131
+ */
132
+ int32_t delta = timestamp - (uint32_t ) current_time ;
133
+ if (delta <= 0 ) {
134
+ // event considered in the past, set the interrupt as pending.
135
+ NVIC_SetPendingIRQ (Rtc_IRQn );
136
+ return ;
137
+ }
138
+
139
+ uint64_t full_timestamp = (current_time & ~UINT32_MAX ) | timestamp ;
140
+ if ( (uint32_t )current_time > timestamp ) {
141
+ full_timestamp += ((uint64_t ) UINT32_MAX ) + 1 ;
142
+ }
143
+
144
+ uint32_t target_seconds = full_timestamp / RTC_SEC_TO_US ;
145
+ uint16_t target_ticks = us_to_ticks (full_timestamp );
146
+
147
+ /*
148
+ * If the interrupt is in more than one second from now then use the
149
+ * second alarm, otherwise use the subsecond alarm.
150
+ * In case of the second alarm is used, there is no need to preserve the
151
+ * remaining subsecond because the irq handler should manage spurious
152
+ * interrupts (like when the timestamp is in the past). In such case, irq
153
+ * handler will schedule a new interrupt with the remaining us.
154
+ */
155
+ NVIC_DisableIRQ (Rtc_IRQn );
156
+ if (target_seconds != RTCREG -> SECOND_COUNTER ) {
157
+ RTCREG -> SECOND_ALARM = target_seconds ;
158
+
159
+ uint32_t rtc_control = RTCREG -> CONTROL .WORD ;
160
+ rtc_control |= (1 << RTC_CONTROL_SEC_CNT_INT_BIT_POS ); // enable seconds interrupt
161
+ rtc_control &= ~(1 << RTC_CONTROL_SUBSEC_CNT_INT_BIT_POS ); // disable sub sec interrupt
162
+ RTCREG -> CONTROL .WORD = rtc_control ;
163
+ } else {
164
+ uint16_t current_ticks = RTCREG -> SUB_SECOND_COUNTER ;
165
+ if (current_ticks == target_ticks ||
166
+ ((target_ticks > current_ticks ) && ((target_ticks - current_ticks ) < RTC_TICK_THRESHOLD )) ||
167
+ ((target_ticks < current_ticks ) && ((RTC_SUB_SEC_MASK - (current_ticks - target_ticks )) < RTC_TICK_THRESHOLD ))) {
168
+ // target ticks too close; schedule the interrupt immediately
169
+ NVIC_SetPendingIRQ (Rtc_IRQn );
170
+ } else {
171
+ RTCREG -> SUB_SECOND_ALARM = target_ticks ;
172
+
173
+ uint32_t rtc_control = RTCREG -> CONTROL .WORD ;
174
+ rtc_control &= ~(1 << RTC_CONTROL_SEC_CNT_INT_BIT_POS ); // disable seconds interrupt
175
+ rtc_control |= (1 << RTC_CONTROL_SUBSEC_CNT_INT_BIT_POS ); // enable sub sec interrupt
176
+ RTCREG -> CONTROL .WORD = rtc_control ;
177
+ }
178
+ }
179
+ NVIC_EnableIRQ (Rtc_IRQn );
180
+
181
+ /* Wait for RTC to finish writing register - RTC operates on 32K clock as compared to 32M core*/
182
+ while (RTCREG -> STATUS .WORD &
183
+ (
184
+ (True << RTC_STATUS_SUB_SEC_ALARM_WRT_BIT_POS ) |
185
+ (True << RTC_STATUS_SEC_ALARM_WRT_BIT_POS ) |
186
+ (True << RTC_STATUS_CONTROL_WRT_BIT_POS )
187
+ )
188
+ );
154
189
}
155
190
156
191
/* See rtc.h for details */
157
192
void fRtcDisableInterrupt (void )
158
193
{
159
- /* Disable RTC interrupt */
160
194
NVIC_DisableIRQ (Rtc_IRQn );
161
195
}
162
196
163
197
/* See rtc.h for details */
164
198
void fRtcEnableInterrupt (void )
165
199
{
166
- /* Enable RTC interrupt */
167
200
NVIC_EnableIRQ (Rtc_IRQn );
168
201
}
169
202
@@ -182,33 +215,30 @@ void fRtcClearInterrupt(void)
182
215
/* See rtc.h for details */
183
216
uint64_t fRtcRead (void )
184
217
{
185
- uint32_t Second ;
186
- uint16_t SubSecond ;
187
-
188
218
/* Hardware Bug fix: The rollover of the sub-second counter initiates the increment of the second counter.
189
219
* That means there is one cycle where the sub-second has rolled back to zero and the second counter has not incremented
190
220
* and a read during that cycle will be incorrect. That will occur for one RTC cycle and that is about 31us of exposure.
191
221
* If you read a zero in the sub-second counter then increment the second counter by 1.
192
222
* Alternatively, subtract 1 from the Sub-seconds counter to align the Second and Sub-Second rollover.
193
223
*/
224
+ uint32_t seconds = RTCREG -> SECOND_COUNTER ;
225
+ uint16_t ticks = (RTCREG -> SUB_SECOND_COUNTER - 1 ) & SUB_SEC_MASK ;
194
226
195
- /* Read the Second and Sub-second counters, then read the Second counter again.
196
- * If it changed, then the Second rolled over while reading Sub-seconds, so go back and read them both again.
227
+ /*
228
+ * If seconds has changed while reading ticks, read them both again.
197
229
*/
230
+ while (seconds != RTCREG -> SECOND_COUNTER ) {
231
+ seconds = RTCREG -> SECOND_COUNTER ;
232
+ ticks = (RTCREG -> SUB_SECOND_COUNTER - 1 ) & SUB_SEC_MASK ;
233
+ }
198
234
199
- do {
200
- Second = RTCREG -> SECOND_COUNTER ; /* Get SEC_COUNTER reg value */
201
- SubSecond = (RTCREG -> SUB_SECOND_COUNTER - 1 ) & SUB_SEC_MASK ; /* Get SUB_SEC_COUNTER reg value */
202
- } while (Second != RTCREG -> SECOND_COUNTER ); /* Repeat if the second has changed */
203
-
204
- //note: casting to float removed to avoid reduction in resolution
205
- uint64_t RtcTimeus = ((uint64_t )SubSecond * RTC_SEC_TO_US / RTC_CLOCK_HZ ) + ((uint64_t )Second * RTC_SEC_TO_US );
235
+ uint64_t current_time = ((uint64_t ) seconds * RTC_SEC_TO_US ) + ticks_to_us (ticks );
206
236
207
237
/*check that the time did not go backwards */
208
- MBED_ASSERT (RtcTimeus >= LastRtcTimeus );
209
- LastRtcTimeus = RtcTimeus ;
238
+ MBED_ASSERT (current_time >= last_time_read );
239
+ last_time_read = current_time ;
210
240
211
- return RtcTimeus ;
241
+ return current_time ;
212
242
}
213
243
214
244
/* See rtc.h for details */
@@ -244,43 +274,31 @@ void fRtcWrite(uint64_t RtcTimeus)
244
274
/* See rtc.h for details */
245
275
void fRtcHandler (void )
246
276
{
247
- /* SUB_SECOND/SECOND interrupt occured */
248
- volatile uint32_t TempStatus = RTCREG -> STATUS .WORD ;
249
-
250
277
/* Disable RTC interrupt */
251
278
NVIC_DisableIRQ (Rtc_IRQn );
252
279
253
280
/* Clear sec & sub_sec interrupts */
254
- RTCREG -> INT_CLEAR .WORD = ((True << RTC_INT_CLR_SUB_SEC_BIT_POS ) |
255
- (True << RTC_INT_CLR_SEC_BIT_POS ));
281
+ RTCREG -> INT_CLEAR .WORD = (
282
+ (True << RTC_INT_CLR_SUB_SEC_BIT_POS ) |
283
+ (True << RTC_INT_CLR_SEC_BIT_POS )
284
+ );
256
285
257
- /* TODO ANDing SUB_SEC & SEC interrupt - work around for RTC issue - will be resolved in REV G */
258
- if (TempStatus & RTC_SEC_INT_STATUS_MASK ) {
259
- /* Second interrupt occured */
260
- if (SubSecond > False ) {
261
- /* Set SUB SEC_ALARM */
262
- RTCREG -> SUB_SECOND_ALARM = SubSecond + RTCREG -> SUB_SECOND_COUNTER ;
263
- /* Enable sub second interrupt */
264
- RTCREG -> CONTROL .WORD |= (True << RTC_CONTROL_SUBSEC_CNT_INT_BIT_POS );
265
- } else {
266
- /* We reach here after second interrupt is occured */
267
- RTCREG -> CONTROL .WORD &= ~(True << RTC_CONTROL_SUBSEC_CNT_INT_BIT_POS ) |
268
- (True << RTC_CONTROL_SEC_CNT_INT_BIT_POS );
269
- }
270
- } else {
271
- /* We reach here after sub_second or (Sub second + second) interrupt occured */
272
- /* Disable Second and sub_second interrupt */
273
- RTCREG -> CONTROL .WORD &= ~(True << RTC_CONTROL_SUBSEC_CNT_INT_BIT_POS ) |
274
- (True << RTC_CONTROL_SEC_CNT_INT_BIT_POS );
275
- }
286
+ /* Disable sub seconds and seconds interrupts */
287
+ RTCREG -> CONTROL .WORD &= ~(
288
+ (True << RTC_CONTROL_SUBSEC_CNT_INT_BIT_POS ) |
289
+ (True << RTC_CONTROL_SEC_CNT_INT_BIT_POS )
290
+ );
276
291
277
292
NVIC_EnableIRQ (Rtc_IRQn );
278
293
279
- /* Wait for RTC to finish writing register - RTC operates on 32K clock as compared to 32M core*/
280
- while ((RTCREG -> STATUS .WORD & ((True << RTC_STATUS_SUB_SEC_ALARM_WRT_BIT_POS ) |
281
- (True << RTC_STATUS_CONTROL_WRT_BIT_POS ) |
282
- (True << RTC_STATUS_SUB_SEC_INT_CLR_WRT_BIT_POS ) |
283
- (True << RTC_STATUS_SEC_INT_CLR_WRT_BIT_POS ))));
294
+ /* Wait for RTC to finish writing registers */
295
+ while (RTCREG -> STATUS .WORD &
296
+ (
297
+ (True << RTC_STATUS_CONTROL_WRT_BIT_POS ) |
298
+ (True << RTC_STATUS_SUB_SEC_INT_CLR_WRT_BIT_POS ) |
299
+ (True << RTC_STATUS_SEC_INT_CLR_WRT_BIT_POS )
300
+ )
301
+ );
284
302
285
303
lp_ticker_irq_handler ();
286
304
}
0 commit comments