Skip to content

Commit 43605d8

Browse files
committed
Adapt K64F lp ticker driver to the new standards.
Low power ticker time counter is created based on RTC which is driven by 32KHz clock. Additional low power timer is used to generate interrupts. We need to adapt driver to operate on ticks instead of us. Perform the following updates: - provide lp ticker configuration: 32KHz/32 bit counter. - lp_ticker_init() routine disables lp ticker interrupts . - adapt the driver functions to operate on ticks instead us. - adapt comments. - add us_ticker_free() routine.
1 parent 3a48afa commit 43605d8

File tree

1 file changed

+123
-78
lines changed
  • targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api

1 file changed

+123
-78
lines changed
Lines changed: 123 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* mbed Microcontroller Library
2-
* Copyright (c) 2016 ARM Limited
2+
* Copyright (c) 2016 - 2018 ARM Limited
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,35 +22,57 @@
2222
#include "cmsis.h"
2323
#include "rtc_api.h"
2424

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+
2841
#define OSC32K_CLK_HZ (32768)
2942
#define MAX_LPTMR_SLEEP ((1 << 16) - 1)
3043

3144
static bool lp_ticker_inited = false;
32-
static int lptmr_schedule = 0;
45+
static timestamp_t lptmr_schedule = 0;
3346

3447
static void rtc_isr(void)
3548
{
3649
uint32_t sr = RTC->SR;
3750
if (sr & RTC_SR_TOF_MASK) {
38-
// Reset RTC to 0 so it keeps counting
51+
/* Reset RTC to 0 so it keeps counting. */
3952
RTC_StopTimer(RTC);
4053
RTC->TSR = 0;
4154
RTC_StartTimer(RTC);
4255
} else if (sr & RTC_SR_TAF_MASK) {
4356
RTC_DisableInterrupts(RTC, kRTC_AlarmInterruptEnable);
4457
RTC->TAR = 0; /* Write clears the IRQ flag */
4558

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);
4972
LPTMR_EnableInterrupts(LPTMR0, kLPTMR_TimerInterruptEnable);
5073
LPTMR_StartTimer(LPTMR0);
51-
} else {
52-
lp_ticker_irq_handler();
5374
}
75+
5476
} else if (sr & RTC_SR_TIF_MASK) {
5577
RTC_DisableInterrupts(RTC, kRTC_TimeOverflowInterruptEnable);
5678
}
@@ -71,102 +93,124 @@ void lp_ticker_init(void)
7193
{
7294
lptmr_config_t lptmrConfig;
7395

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+
}
78103

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);
82106
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);
85109

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+
}
101134
}
102135

103136
/** Read the current counter
104137
*
105-
* @return The current timer's counter value in microseconds
138+
* @return The current timer's counter value in ticks
106139
*/
107140
uint32_t lp_ticker_read(void)
108141
{
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;
122164
}
123165

124166
/** Set interrupt for specified timestamp
125167
*
126-
* @param timestamp The time in microseconds to be set
168+
* @param timestamp The time in ticks to be set
127169
*/
128170
void lp_ticker_set_interrupt(timestamp_t timestamp)
129171
{
130-
uint32_t now_us, delta_us, delta_ticks;
131-
132-
if (!lp_ticker_inited) {
133-
lp_ticker_init();
134-
}
135-
136172
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);
139173

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+
142182
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. */
144184
delta_ticks = 1;
145185
}
146186

147187
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;
150190

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;
156192

157193
RTC_EnableInterrupts(RTC, kRTC_AlarmInterruptEnable);
158194

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;
168198
} 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);
170214
LPTMR_SetTimerPeriod(LPTMR0, delta_ticks);
171215
LPTMR_EnableInterrupts(LPTMR0, kLPTMR_TimerInterruptEnable);
172216
LPTMR_StartTimer(LPTMR0);
@@ -194,6 +238,7 @@ void lp_ticker_clear_interrupt(void)
194238
{
195239
RTC->TAR = 0; /* Write clears the IRQ flag */
196240
LPTMR_ClearStatusFlags(LPTMR0, kLPTMR_TimerCompareFlag);
241+
lptmr_schedule = 0;
197242
}
198243

199244
#endif /* DEVICE_LOWPOWERTIMER */

0 commit comments

Comments
 (0)