Skip to content

Commit

Permalink
Adapt K64F lp ticker driver to the new standards.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mprse authored and bulislaw committed May 25, 2018
1 parent c4bd9d1 commit 39852f7
Showing 1 changed file with 123 additions and 78 deletions.
201 changes: 123 additions & 78 deletions targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/lp_ticker.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* mbed Microcontroller Library
* Copyright (c) 2016 ARM Limited
* Copyright (c) 2016 - 2018 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -22,35 +22,57 @@
#include "cmsis.h"
#include "rtc_api.h"

#define MAX_SEC_BITS (12)
#define MAX_SEC_MASK ((1 << MAX_SEC_BITS) - 1)
#define SEC_IN_USEC (1000000)
const ticker_info_t* lp_ticker_get_info()
{
static const ticker_info_t info = {
32768, // 32kHz
32 // 32 bit counter
};
return &info;
}

#define SEC_BITS (17)
#define SEC_SHIFT (15)
#define SEC_MASK ((1 << SEC_BITS) - 1)
#define TICKS_BITS (15)
#define TICKS_SHIFT (0)
#define TICKS_MASK ((1 << TICKS_BITS) - 1)

#define OSC32K_CLK_HZ (32768)
#define MAX_LPTMR_SLEEP ((1 << 16) - 1)

static bool lp_ticker_inited = false;
static int lptmr_schedule = 0;
static timestamp_t lptmr_schedule = 0;

static void rtc_isr(void)
{
uint32_t sr = RTC->SR;
if (sr & RTC_SR_TOF_MASK) {
// Reset RTC to 0 so it keeps counting
/* Reset RTC to 0 so it keeps counting. */
RTC_StopTimer(RTC);
RTC->TSR = 0;
RTC_StartTimer(RTC);
} else if (sr & RTC_SR_TAF_MASK) {
RTC_DisableInterrupts(RTC, kRTC_AlarmInterruptEnable);
RTC->TAR = 0; /* Write clears the IRQ flag */

/* Wait subsecond remainder if any */
if (lptmr_schedule) {
LPTMR_SetTimerPeriod(LPTMR0, lptmr_schedule);
/* Wait subsecond remainder. */
const uint32_t now_ticks = lp_ticker_read();
uint32_t delta_ticks =
lptmr_schedule >= now_ticks ? lptmr_schedule - now_ticks : (uint32_t)((uint64_t) lptmr_schedule + 0xFFFFFFFF - now_ticks);

lptmr_schedule = 0;

if (delta_ticks == 0) {
lp_ticker_irq_handler();
} else {
LPTMR_StopTimer(LPTMR0);
LPTMR_ClearStatusFlags(LPTMR0, kLPTMR_TimerCompareFlag);
LPTMR_SetTimerPeriod(LPTMR0, delta_ticks);
LPTMR_EnableInterrupts(LPTMR0, kLPTMR_TimerInterruptEnable);
LPTMR_StartTimer(LPTMR0);
} else {
lp_ticker_irq_handler();
}

} else if (sr & RTC_SR_TIF_MASK) {
RTC_DisableInterrupts(RTC, kRTC_TimeOverflowInterruptEnable);
}
Expand All @@ -71,102 +93,124 @@ void lp_ticker_init(void)
{
lptmr_config_t lptmrConfig;

if (lp_ticker_inited) {
return;
}
lp_ticker_inited = true;
if (!lp_ticker_inited) {

/* Setup low resolution clock - RTC */
if (!rtc_isenabled()) {
rtc_init();
RTC_StartTimer(RTC);
}

/* Setup low resolution clock - RTC */
if (!rtc_isenabled()) {
rtc_init();
RTC->TAR = 0; /* Write clears the IRQ flag */
NVIC_ClearPendingIRQ(RTC_IRQn);
RTC_DisableInterrupts(RTC, kRTC_AlarmInterruptEnable | kRTC_SecondsInterruptEnable);
RTC_StartTimer(RTC);
}
NVIC_SetVector(RTC_IRQn, (uint32_t) rtc_isr);
NVIC_EnableIRQ(RTC_IRQn);

RTC->TAR = 0; /* Write clears the IRQ flag */
NVIC_ClearPendingIRQ(RTC_IRQn);
NVIC_SetVector(RTC_IRQn, (uint32_t)rtc_isr);
NVIC_EnableIRQ(RTC_IRQn);

/* Setup high resolution clock - LPTMR */
LPTMR_GetDefaultConfig(&lptmrConfig);
/* Use 32kHz drive */
CLOCK_SetXtal32Freq(OSC32K_CLK_HZ);
lptmrConfig.prescalerClockSource = kLPTMR_PrescalerClock_2;
LPTMR_Init(LPTMR0, &lptmrConfig);
LPTMR_EnableInterrupts(LPTMR0, kLPTMR_TimerInterruptEnable);
NVIC_ClearPendingIRQ(LPTMR0_IRQn);
NVIC_SetVector(LPTMR0_IRQn, (uint32_t)lptmr_isr);
EnableIRQ(LPTMR0_IRQn);
/* Setup high resolution clock - LPTMR */
LPTMR_GetDefaultConfig(&lptmrConfig);

/* Use 32kHz drive */
CLOCK_SetXtal32Freq(OSC32K_CLK_HZ);
lptmrConfig.prescalerClockSource = kLPTMR_PrescalerClock_2;
LPTMR_Init(LPTMR0, &lptmrConfig);
LPTMR_DisableInterrupts(LPTMR0, kLPTMR_TimerInterruptEnable);
NVIC_ClearPendingIRQ(LPTMR0_IRQn);
NVIC_SetVector(LPTMR0_IRQn, (uint32_t) lptmr_isr);
EnableIRQ(LPTMR0_IRQn);

lptmr_schedule = 0;

lp_ticker_inited = true;
} else {
/* In case of re-init we need to disable lp ticker interrupt. */
LPTMR_StopTimer(LPTMR0);

RTC_DisableInterrupts(RTC, kRTC_AlarmInterruptEnable);
RTC->TAR = 0; /* Write clears the IRQ flag */

lptmr_schedule = 0;
}
}

/** Read the current counter
*
* @return The current timer's counter value in microseconds
* @return The current timer's counter value in ticks
*/
uint32_t lp_ticker_read(void)
{
uint32_t sec, pre;

if (!lp_ticker_inited) {
lp_ticker_init();
}

sec = RTC->TSR; /* 32b: Seconds */
pre = RTC->TPR; /* 16b: Increments every 32.768kHz clock cycle (30us) */

/* Final value: 11b (4095) for sec and 21b for usec (pre can reach 1,000,000us which is close to 1<<20) */
uint32_t ret = (((sec & MAX_SEC_MASK) * SEC_IN_USEC) + (((uint64_t)pre * SEC_IN_USEC) / OSC32K_CLK_HZ));

return ret;
uint32_t count;
uint32_t last_count;

/* TPR is increments every 32.768 kHz clock cycle. The TSR increments when
* bit 14 of the TPR transitions from a logic one (32768 ticks - 1 sec).
* After that TPR starts counting from 0.
*
* count value is built as follows:
* count[0 - 14] - ticks (RTC->TPR)
* count[15 - 31] - seconds (RTC->TSR)
*/

/* Loop until the same tick is read twice since this
* is ripple counter on a different clock domain.
*/
count = ((RTC->TSR << SEC_SHIFT) | (RTC->TPR & TICKS_MASK));
do {
last_count = count;
count = ((RTC->TSR << SEC_SHIFT) | (RTC->TPR & TICKS_MASK));
} while (last_count != count);

return count;
}

/** Set interrupt for specified timestamp
*
* @param timestamp The time in microseconds to be set
* @param timestamp The time in ticks to be set
*/
void lp_ticker_set_interrupt(timestamp_t timestamp)
{
uint32_t now_us, delta_us, delta_ticks;

if (!lp_ticker_inited) {
lp_ticker_init();
}

lptmr_schedule = 0;
now_us = lp_ticker_read();
delta_us = timestamp >= now_us ? timestamp - now_us : (uint32_t)((uint64_t)timestamp + 0xFFFFFFFF - now_us);

/* Checking if LPTRM can handle this sleep */
delta_ticks = USEC_TO_COUNT(delta_us, CLOCK_GetFreq(kCLOCK_Er32kClk));
/* We get here absolute interrupt time-stamp in ticks which takes into account counter overflow.
* Since we use additional count-down timer to generate interrupt we need to calculate
* load value based on time-stamp.
*/
const uint32_t now_ticks = lp_ticker_read();
uint32_t delta_ticks =
timestamp >= now_ticks ? timestamp - now_ticks : (uint32_t)((uint64_t) timestamp + 0xFFFFFFFF - now_ticks);

if (delta_ticks == 0) {
/* The requested delay is less than the minimum resolution of this counter */
/* The requested delay is less than the minimum resolution of this counter. */
delta_ticks = 1;
}

if (delta_ticks > MAX_LPTMR_SLEEP) {
/* Using RTC if wait time is over 16b (2s @32kHz) */
uint32_t delta_sec;
/* Using RTC if wait time is over 16b (2s @32kHz). */
uint32_t delay_sec = delta_ticks >> 15;

delta_us += COUNT_TO_USEC(RTC->TPR, CLOCK_GetFreq(kCLOCK_Er32kClk)); /* Accounting for started second */
delta_sec = delta_us / SEC_IN_USEC;
delta_us -= delta_sec * SEC_IN_USEC;

RTC->TAR = RTC->TSR + delta_sec - 1;
RTC->TAR = RTC->TSR + delay_sec - 1;

RTC_EnableInterrupts(RTC, kRTC_AlarmInterruptEnable);

/* Set aditional, subsecond, sleep time */
if (delta_us) {
lptmr_schedule = USEC_TO_COUNT(delta_us, CLOCK_GetFreq(kCLOCK_Er32kClk));
if (lptmr_schedule == 0) {
/* The requested delay is less than the minimum resolution of this counter */
lptmr_schedule = 1;
}

}
/* Store absolute interrupt time-stamp value for further processing in
* RTC interrupt handler (schedule remaining ticks using LPTMR). */
lptmr_schedule = timestamp;
} else {
/* Below RTC resolution using LPTMR */
/* Below RTC resolution using LPTMR. */

/* In case of re-schedule we need to disable RTC interrupt. */
RTC_DisableInterrupts(RTC, kRTC_AlarmInterruptEnable);
RTC->TAR = 0; /* Write clears the IRQ flag */

/* When the LPTMR is enabled, the CMR can be altered only when CSR[TCF] is set. When
* updating the CMR, the CMR must be written and CSR[TCF] must be cleared before the
* LPTMR counter has incremented past the new LPTMR compare value.
*
* When TEN is clear, it resets the LPTMR internal logic, including the CNR and TCF.
* When TEN is set, the LPTMR is enabled. While writing 1 to this field, CSR[5:1] must
* not be altered.
*/
LPTMR_StopTimer(LPTMR0);
LPTMR_SetTimerPeriod(LPTMR0, delta_ticks);
LPTMR_EnableInterrupts(LPTMR0, kLPTMR_TimerInterruptEnable);
LPTMR_StartTimer(LPTMR0);
Expand Down Expand Up @@ -194,6 +238,7 @@ void lp_ticker_clear_interrupt(void)
{
RTC->TAR = 0; /* Write clears the IRQ flag */
LPTMR_ClearStatusFlags(LPTMR0, kLPTMR_TimerCompareFlag);
lptmr_schedule = 0;
}

#endif /* DEVICE_LOWPOWERTIMER */

0 comments on commit 39852f7

Please sign in to comment.