mirror of https://github.com/ARMmbed/mbed-os.git
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.pull/7009/head
parent
c4bd9d125b
commit
39852f772c
|
@ -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.
|
||||
|
@ -22,20 +22,33 @@
|
|||
#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);
|
||||
|
@ -43,14 +56,23 @@ static void rtc_isr(void)
|
|||
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);
|
||||
}
|
||||
|
@ -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();
|
||||
/* Setup low resolution clock - RTC */
|
||||
if (!rtc_isenabled()) {
|
||||
rtc_init();
|
||||
RTC_StartTimer(RTC);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/** 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;
|
||||
uint32_t count;
|
||||
uint32_t last_count;
|
||||
|
||||
if (!lp_ticker_inited) {
|
||||
lp_ticker_init();
|
||||
}
|
||||
/* 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)
|
||||
*/
|
||||
|
||||
sec = RTC->TSR; /* 32b: Seconds */
|
||||
pre = RTC->TPR; /* 16b: Increments every 32.768kHz clock cycle (30us) */
|
||||
/* 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);
|
||||
|
||||
/* 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;
|
||||
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);
|
||||
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue