1
1
/* mbed Microcontroller Library
2
- * Copyright (c) 2016 ARM Limited
2
+ * Copyright (c) 2016 - 2018 ARM Limited
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
22
22
#include "cmsis.h"
23
23
#include "rtc_api.h"
24
24
25
- #define MAX_SEC_BITS (12)
26
- #define MAX_SEC_MASK ((1 << MAX_SEC_BITS) - 1)
27
- #define SEC_IN_USEC (1000000)
25
+ const ticker_info_t * lp_ticker_get_info ()
26
+ {
27
+ static const ticker_info_t info = {
28
+ 32768 , // 32kHz
29
+ 32 // 32 bit counter
30
+ };
31
+ return & info ;
32
+ }
33
+
34
+ #define SEC_BITS (17)
35
+ #define SEC_SHIFT (15)
36
+ #define SEC_MASK ((1 << SEC_BITS) - 1)
37
+ #define TICKS_BITS (15)
38
+ #define TICKS_SHIFT (0)
39
+ #define TICKS_MASK ((1 << TICKS_BITS) - 1)
40
+
28
41
#define OSC32K_CLK_HZ (32768)
29
42
#define MAX_LPTMR_SLEEP ((1 << 16) - 1)
30
43
31
44
static bool lp_ticker_inited = false;
32
- static int lptmr_schedule = 0 ;
45
+ static timestamp_t lptmr_schedule = 0 ;
33
46
34
47
static void rtc_isr (void )
35
48
{
36
49
uint32_t sr = RTC -> SR ;
37
50
if (sr & RTC_SR_TOF_MASK ) {
38
- // Reset RTC to 0 so it keeps counting
51
+ /* Reset RTC to 0 so it keeps counting. */
39
52
RTC_StopTimer (RTC );
40
53
RTC -> TSR = 0 ;
41
54
RTC_StartTimer (RTC );
42
55
} else if (sr & RTC_SR_TAF_MASK ) {
43
56
RTC_DisableInterrupts (RTC , kRTC_AlarmInterruptEnable );
44
57
RTC -> TAR = 0 ; /* Write clears the IRQ flag */
45
58
46
- /* Wait subsecond remainder if any */
47
- if (lptmr_schedule ) {
48
- LPTMR_SetTimerPeriod (LPTMR0 , lptmr_schedule );
59
+ /* Wait subsecond remainder. */
60
+ const uint32_t now_ticks = lp_ticker_read ();
61
+ uint32_t delta_ticks =
62
+ lptmr_schedule >= now_ticks ? lptmr_schedule - now_ticks : (uint32_t )((uint64_t ) lptmr_schedule + 0xFFFFFFFF - now_ticks );
63
+
64
+ lptmr_schedule = 0 ;
65
+
66
+ if (delta_ticks == 0 ) {
67
+ lp_ticker_irq_handler ();
68
+ } else {
69
+ LPTMR_StopTimer (LPTMR0 );
70
+ LPTMR_ClearStatusFlags (LPTMR0 , kLPTMR_TimerCompareFlag );
71
+ LPTMR_SetTimerPeriod (LPTMR0 , delta_ticks );
49
72
LPTMR_EnableInterrupts (LPTMR0 , kLPTMR_TimerInterruptEnable );
50
73
LPTMR_StartTimer (LPTMR0 );
51
- } else {
52
- lp_ticker_irq_handler ();
53
74
}
75
+
54
76
} else if (sr & RTC_SR_TIF_MASK ) {
55
77
RTC_DisableInterrupts (RTC , kRTC_TimeOverflowInterruptEnable );
56
78
}
@@ -71,102 +93,124 @@ void lp_ticker_init(void)
71
93
{
72
94
lptmr_config_t lptmrConfig ;
73
95
74
- if (lp_ticker_inited ) {
75
- return ;
76
- }
77
- lp_ticker_inited = true;
96
+ if (!lp_ticker_inited ) {
97
+
98
+ /* Setup low resolution clock - RTC */
99
+ if (!rtc_isenabled ()) {
100
+ rtc_init ();
101
+ RTC_StartTimer (RTC );
102
+ }
78
103
79
- /* Setup low resolution clock - RTC */
80
- if (!rtc_isenabled ()) {
81
- rtc_init ();
104
+ RTC -> TAR = 0 ; /* Write clears the IRQ flag */
105
+ NVIC_ClearPendingIRQ (RTC_IRQn );
82
106
RTC_DisableInterrupts (RTC , kRTC_AlarmInterruptEnable | kRTC_SecondsInterruptEnable );
83
- RTC_StartTimer ( RTC );
84
- }
107
+ NVIC_SetVector ( RTC_IRQn , ( uint32_t ) rtc_isr );
108
+ NVIC_EnableIRQ ( RTC_IRQn );
85
109
86
- RTC -> TAR = 0 ; /* Write clears the IRQ flag */
87
- NVIC_ClearPendingIRQ (RTC_IRQn );
88
- NVIC_SetVector (RTC_IRQn , (uint32_t )rtc_isr );
89
- NVIC_EnableIRQ (RTC_IRQn );
90
-
91
- /* Setup high resolution clock - LPTMR */
92
- LPTMR_GetDefaultConfig (& lptmrConfig );
93
- /* Use 32kHz drive */
94
- CLOCK_SetXtal32Freq (OSC32K_CLK_HZ );
95
- lptmrConfig .prescalerClockSource = kLPTMR_PrescalerClock_2 ;
96
- LPTMR_Init (LPTMR0 , & lptmrConfig );
97
- LPTMR_EnableInterrupts (LPTMR0 , kLPTMR_TimerInterruptEnable );
98
- NVIC_ClearPendingIRQ (LPTMR0_IRQn );
99
- NVIC_SetVector (LPTMR0_IRQn , (uint32_t )lptmr_isr );
100
- EnableIRQ (LPTMR0_IRQn );
110
+ /* Setup high resolution clock - LPTMR */
111
+ LPTMR_GetDefaultConfig (& lptmrConfig );
112
+
113
+ /* Use 32kHz drive */
114
+ CLOCK_SetXtal32Freq (OSC32K_CLK_HZ );
115
+ lptmrConfig .prescalerClockSource = kLPTMR_PrescalerClock_2 ;
116
+ LPTMR_Init (LPTMR0 , & lptmrConfig );
117
+ LPTMR_DisableInterrupts (LPTMR0 , kLPTMR_TimerInterruptEnable );
118
+ NVIC_ClearPendingIRQ (LPTMR0_IRQn );
119
+ NVIC_SetVector (LPTMR0_IRQn , (uint32_t ) lptmr_isr );
120
+ EnableIRQ (LPTMR0_IRQn );
121
+
122
+ lptmr_schedule = 0 ;
123
+
124
+ lp_ticker_inited = true;
125
+ } else {
126
+ /* In case of re-init we need to disable lp ticker interrupt. */
127
+ LPTMR_StopTimer (LPTMR0 );
128
+
129
+ RTC_DisableInterrupts (RTC , kRTC_AlarmInterruptEnable );
130
+ RTC -> TAR = 0 ; /* Write clears the IRQ flag */
131
+
132
+ lptmr_schedule = 0 ;
133
+ }
101
134
}
102
135
103
136
/** Read the current counter
104
137
*
105
- * @return The current timer's counter value in microseconds
138
+ * @return The current timer's counter value in ticks
106
139
*/
107
140
uint32_t lp_ticker_read (void )
108
141
{
109
- uint32_t sec , pre ;
110
-
111
- if (!lp_ticker_inited ) {
112
- lp_ticker_init ();
113
- }
114
-
115
- sec = RTC -> TSR ; /* 32b: Seconds */
116
- pre = RTC -> TPR ; /* 16b: Increments every 32.768kHz clock cycle (30us) */
117
-
118
- /* Final value: 11b (4095) for sec and 21b for usec (pre can reach 1,000,000us which is close to 1<<20) */
119
- uint32_t ret = (((sec & MAX_SEC_MASK ) * SEC_IN_USEC ) + (((uint64_t )pre * SEC_IN_USEC ) / OSC32K_CLK_HZ ));
120
-
121
- return ret ;
142
+ uint32_t count ;
143
+ uint32_t last_count ;
144
+
145
+ /* TPR is increments every 32.768 kHz clock cycle. The TSR increments when
146
+ * bit 14 of the TPR transitions from a logic one (32768 ticks - 1 sec).
147
+ * After that TPR starts counting from 0.
148
+ *
149
+ * count value is built as follows:
150
+ * count[0 - 14] - ticks (RTC->TPR)
151
+ * count[15 - 31] - seconds (RTC->TSR)
152
+ */
153
+
154
+ /* Loop until the same tick is read twice since this
155
+ * is ripple counter on a different clock domain.
156
+ */
157
+ count = ((RTC -> TSR << SEC_SHIFT ) | (RTC -> TPR & TICKS_MASK ));
158
+ do {
159
+ last_count = count ;
160
+ count = ((RTC -> TSR << SEC_SHIFT ) | (RTC -> TPR & TICKS_MASK ));
161
+ } while (last_count != count );
162
+
163
+ return count ;
122
164
}
123
165
124
166
/** Set interrupt for specified timestamp
125
167
*
126
- * @param timestamp The time in microseconds to be set
168
+ * @param timestamp The time in ticks to be set
127
169
*/
128
170
void lp_ticker_set_interrupt (timestamp_t timestamp )
129
171
{
130
- uint32_t now_us , delta_us , delta_ticks ;
131
-
132
- if (!lp_ticker_inited ) {
133
- lp_ticker_init ();
134
- }
135
-
136
172
lptmr_schedule = 0 ;
137
- now_us = lp_ticker_read ();
138
- delta_us = timestamp >= now_us ? timestamp - now_us : (uint32_t )((uint64_t )timestamp + 0xFFFFFFFF - now_us );
139
173
140
- /* Checking if LPTRM can handle this sleep */
141
- delta_ticks = USEC_TO_COUNT (delta_us , CLOCK_GetFreq (kCLOCK_Er32kClk ));
174
+ /* We get here absolute interrupt time-stamp in ticks which takes into account counter overflow.
175
+ * Since we use additional count-down timer to generate interrupt we need to calculate
176
+ * load value based on time-stamp.
177
+ */
178
+ const uint32_t now_ticks = lp_ticker_read ();
179
+ uint32_t delta_ticks =
180
+ timestamp >= now_ticks ? timestamp - now_ticks : (uint32_t )((uint64_t ) timestamp + 0xFFFFFFFF - now_ticks );
181
+
142
182
if (delta_ticks == 0 ) {
143
- /* The requested delay is less than the minimum resolution of this counter */
183
+ /* The requested delay is less than the minimum resolution of this counter. */
144
184
delta_ticks = 1 ;
145
185
}
146
186
147
187
if (delta_ticks > MAX_LPTMR_SLEEP ) {
148
- /* Using RTC if wait time is over 16b (2s @32kHz) */
149
- uint32_t delta_sec ;
188
+ /* Using RTC if wait time is over 16b (2s @32kHz). */
189
+ uint32_t delay_sec = delta_ticks >> 15 ;
150
190
151
- delta_us += COUNT_TO_USEC (RTC -> TPR , CLOCK_GetFreq (kCLOCK_Er32kClk )); /* Accounting for started second */
152
- delta_sec = delta_us / SEC_IN_USEC ;
153
- delta_us -= delta_sec * SEC_IN_USEC ;
154
-
155
- RTC -> TAR = RTC -> TSR + delta_sec - 1 ;
191
+ RTC -> TAR = RTC -> TSR + delay_sec - 1 ;
156
192
157
193
RTC_EnableInterrupts (RTC , kRTC_AlarmInterruptEnable );
158
194
159
- /* Set aditional, subsecond, sleep time */
160
- if (delta_us ) {
161
- lptmr_schedule = USEC_TO_COUNT (delta_us , CLOCK_GetFreq (kCLOCK_Er32kClk ));
162
- if (lptmr_schedule == 0 ) {
163
- /* The requested delay is less than the minimum resolution of this counter */
164
- lptmr_schedule = 1 ;
165
- }
166
-
167
- }
195
+ /* Store absolute interrupt time-stamp value for further processing in
196
+ * RTC interrupt handler (schedule remaining ticks using LPTMR). */
197
+ lptmr_schedule = timestamp ;
168
198
} else {
169
- /* Below RTC resolution using LPTMR */
199
+ /* Below RTC resolution using LPTMR. */
200
+
201
+ /* In case of re-schedule we need to disable RTC interrupt. */
202
+ RTC_DisableInterrupts (RTC , kRTC_AlarmInterruptEnable );
203
+ RTC -> TAR = 0 ; /* Write clears the IRQ flag */
204
+
205
+ /* When the LPTMR is enabled, the CMR can be altered only when CSR[TCF] is set. When
206
+ * updating the CMR, the CMR must be written and CSR[TCF] must be cleared before the
207
+ * LPTMR counter has incremented past the new LPTMR compare value.
208
+ *
209
+ * When TEN is clear, it resets the LPTMR internal logic, including the CNR and TCF.
210
+ * When TEN is set, the LPTMR is enabled. While writing 1 to this field, CSR[5:1] must
211
+ * not be altered.
212
+ */
213
+ LPTMR_StopTimer (LPTMR0 );
170
214
LPTMR_SetTimerPeriod (LPTMR0 , delta_ticks );
171
215
LPTMR_EnableInterrupts (LPTMR0 , kLPTMR_TimerInterruptEnable );
172
216
LPTMR_StartTimer (LPTMR0 );
@@ -194,6 +238,7 @@ void lp_ticker_clear_interrupt(void)
194
238
{
195
239
RTC -> TAR = 0 ; /* Write clears the IRQ flag */
196
240
LPTMR_ClearStatusFlags (LPTMR0 , kLPTMR_TimerCompareFlag );
241
+ lptmr_schedule = 0 ;
197
242
}
198
243
199
244
#endif /* DEVICE_LOWPOWERTIMER */
0 commit comments