updated implementation for us_ticker.c
We are now using a timer that uses HFCLK for timer calls this should mean that we have a higher resolution for interrupts.
This commit is contained in:
parent
93df986f58
commit
6888edef4c
1 changed files with 102 additions and 84 deletions
|
@ -21,120 +21,134 @@
|
|||
#include "nrf_delay.h"
|
||||
|
||||
/*
|
||||
* Note: The micro-second timer API on the nRF51 platform is implemented using
|
||||
* the RTC counter run at 32kHz (sourced from an external oscillator). This is
|
||||
* a trade-off between precision and power. Running a normal 32-bit MCU counter
|
||||
* at high frequency causes the average power consumption to rise to a few
|
||||
* hundred micro-amps, which is prohibitive for typical low-power BLE
|
||||
* applications.
|
||||
* A 32kHz clock doesn't offer the precision needed for keeping u-second time,
|
||||
* but we're assuming that this will not be a problem for the average user.
|
||||
* A higher precision implementation of the Ticker API using Timer 1 and a 1MHz clock.
|
||||
* This implementation should be used when higher precision than can be provided by the
|
||||
* LF clock source is required. i.e. precision of less than 30uS.
|
||||
*/
|
||||
|
||||
#define MAX_RTC_COUNTER_VAL 0x00FFFFFF /**< Maximum value of the RTC counter. */
|
||||
#define RTC_CLOCK_FREQ (uint32_t)(32768)
|
||||
#define RTC1_IRQ_PRI 3 /**< Priority of the RTC1 interrupt (used
|
||||
#define MAX_TMR1_COUNTER_VAL 0x0000FFFF // Run the timer in 16 bit mode, for consistency with RTC algorithm above.
|
||||
#define TMR1_CLOCK_FREQ (uint32_t)(1000000) // Run at 1MHz so the lower power 1MHz clock source can be used.
|
||||
#define TMR1_IRQ_PRI 1 /**< Priority of the TMR1 interrupt (used
|
||||
* for checking for timeouts and executing
|
||||
* timeout handlers). This must be the same
|
||||
* as APP_IRQ_PRIORITY_LOW; taken from the
|
||||
* Nordic SDK. */
|
||||
#define MAX_RTC_TASKS_DELAY 47 /**< Maximum delay until an RTC task is executed. */
|
||||
#define MAX_TMR1_TASKS_DELAY 47 /**< Maximum delay until an RTC task is executed. */
|
||||
|
||||
#define FUZZY_RTC_TICKS 2 /* RTC COMPARE occurs when a CC register is N and the RTC
|
||||
#define FUZZY_TMR1_TICKS 10 /* TMR COMPARE occurs when a CC register is N and the TMR
|
||||
* COUNTER value transitions from N-1 to N. If we're trying to
|
||||
* setup a callback for a time which will arrive very shortly,
|
||||
* there are limits to how short the callback interval may be for us
|
||||
* to rely upon the RTC Compare trigger. If the COUNTER is N,
|
||||
* to rely upon the TMR Compare trigger. If the COUNTER is N,
|
||||
* writing N+2 to a CC register is guaranteed to trigger a COMPARE
|
||||
* event at N+2. */
|
||||
|
||||
#define RTC_UNITS_TO_MICROSECONDS(RTC_UNITS) (((RTC_UNITS) * (uint64_t)1000000) / RTC_CLOCK_FREQ)
|
||||
#define MICROSECONDS_TO_RTC_UNITS(MICROS) ((((uint64_t)(MICROS) * RTC_CLOCK_FREQ) + 999999) / 1000000)
|
||||
#define TMR1_UNITS_TO_MICROSECONDS(TMR1_UNITS) TMR1_UNITS
|
||||
#define MICROSECONDS_TO_TMR1_UNITS(MICROS) MICROS
|
||||
|
||||
static bool us_ticker_inited = false;
|
||||
static volatile uint32_t overflowCount; /**< The number of times the 24-bit RTC counter has overflowed. */
|
||||
static volatile uint32_t overflowCount; /**< The number of times the 24-bit TMR1 counter has overflowed. */
|
||||
static volatile bool us_ticker_callbackPending = false;
|
||||
static uint32_t us_ticker_callbackTimestamp;
|
||||
|
||||
static inline void rtc1_enableCompareInterrupt(void)
|
||||
static inline void tmr1_enableCompareInterrupt(void)
|
||||
{
|
||||
NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
|
||||
NRF_RTC1->INTENSET = RTC_INTENSET_COMPARE0_Msk;
|
||||
NRF_TIMER1->INTENSET = TIMER_INTENSET_COMPARE0_Msk;
|
||||
|
||||
//NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
|
||||
//NRF_RTC1->INTENSET = RTC_INTENSET_COMPARE0_Msk;
|
||||
}
|
||||
|
||||
static inline void rtc1_disableCompareInterrupt(void)
|
||||
static inline void tmr1_disableCompareInterrupt(void)
|
||||
{
|
||||
NRF_RTC1->INTENCLR = RTC_INTENSET_COMPARE0_Msk;
|
||||
NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
|
||||
NRF_TIMER1->INTENCLR = TIMER_INTENSET_COMPARE0_Msk;
|
||||
|
||||
//NRF_RTC1->INTENCLR = RTC_INTENSET_COMPARE0_Msk;
|
||||
//NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
|
||||
}
|
||||
|
||||
static inline void rtc1_enableOverflowInterrupt(void)
|
||||
static inline void tmr1_enableOverflowInterrupt(void)
|
||||
{
|
||||
NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
|
||||
NRF_RTC1->INTENSET = RTC_INTENSET_OVRFLW_Msk;
|
||||
NRF_TIMER1->CC[3] = 0;
|
||||
NRF_TIMER1->INTENSET = (TIMER_INTENSET_COMPARE3_Enabled << TIMER_INTENSET_COMPARE3_Pos);
|
||||
|
||||
//NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
|
||||
//NRF_RTC1->INTENSET = RTC_INTENSET_OVRFLW_Msk;
|
||||
}
|
||||
|
||||
static inline void rtc1_disableOverflowInterrupt(void)
|
||||
static inline void tmr1_disableOverflowInterrupt(void)
|
||||
{
|
||||
NRF_RTC1->INTENCLR = RTC_INTENSET_OVRFLW_Msk;
|
||||
NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
|
||||
NRF_TIMER1->INTENCLR = (TIMER_INTENSET_COMPARE3_Enabled << TIMER_INTENSET_COMPARE3_Pos);
|
||||
|
||||
//NRF_RTC1->INTENCLR = RTC_INTENSET_OVRFLW_Msk;
|
||||
//NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
|
||||
}
|
||||
|
||||
static inline void invokeCallback(void)
|
||||
{
|
||||
us_ticker_callbackPending = false;
|
||||
rtc1_disableCompareInterrupt();
|
||||
//tmr1_disableCompareInterrupt();
|
||||
us_ticker_irq_handler();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function for starting the RTC1 timer. The RTC timer is expected to
|
||||
* @brief Function for starting the TIMER1 timer. The timer is expected to
|
||||
* keep running--some interrupts may be disabled temporarily.
|
||||
*/
|
||||
static void rtc1_start()
|
||||
static void tmr1_start()
|
||||
{
|
||||
NRF_RTC1->PRESCALER = 0; /* for no pre-scaling. */
|
||||
NRF_TIMER1->TASKS_STOP = 1;
|
||||
|
||||
rtc1_enableOverflowInterrupt();
|
||||
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
|
||||
NRF_CLOCK->TASKS_HFCLKSTART = 1;
|
||||
while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) ;
|
||||
|
||||
NVIC_SetPriority(RTC1_IRQn, RTC1_IRQ_PRI);
|
||||
NVIC_ClearPendingIRQ(RTC1_IRQn);
|
||||
NVIC_EnableIRQ(RTC1_IRQn);
|
||||
NRF_TIMER1->MODE = TIMER_MODE_MODE_Timer;
|
||||
NRF_TIMER1->TASKS_CLEAR = 1;
|
||||
NRF_TIMER1->PRESCALER = 4; // Configure for 1MHz operation
|
||||
NRF_TIMER1->BITMODE = TIMER_BITMODE_BITMODE_16Bit;
|
||||
tmr1_enableOverflowInterrupt();
|
||||
tmr1_enableCompareInterrupt();
|
||||
|
||||
NRF_RTC1->TASKS_START = 1;
|
||||
nrf_delay_us(MAX_RTC_TASKS_DELAY);
|
||||
NVIC_SetPriority(TIMER1_IRQn, TMR1_IRQ_PRI);
|
||||
NVIC_ClearPendingIRQ(TIMER1_IRQn);
|
||||
NVIC_EnableIRQ(TIMER1_IRQn);
|
||||
|
||||
NRF_TIMER1->TASKS_START = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function for stopping the RTC1 timer. We don't expect to call this.
|
||||
*/
|
||||
void rtc1_stop(void)
|
||||
void tmr1_stop(void)
|
||||
{
|
||||
NVIC_DisableIRQ(RTC1_IRQn);
|
||||
rtc1_disableCompareInterrupt();
|
||||
rtc1_disableOverflowInterrupt();
|
||||
NVIC_DisableIRQ(TIMER1_IRQn);
|
||||
tmr1_disableCompareInterrupt();
|
||||
tmr1_disableOverflowInterrupt();
|
||||
|
||||
NRF_RTC1->TASKS_STOP = 1;
|
||||
nrf_delay_us(MAX_RTC_TASKS_DELAY);
|
||||
NRF_TIMER1->TASKS_STOP = 1;
|
||||
nrf_delay_us(MAX_TMR1_TASKS_DELAY);
|
||||
|
||||
NRF_RTC1->TASKS_CLEAR = 1;
|
||||
nrf_delay_us(MAX_RTC_TASKS_DELAY);
|
||||
NRF_TIMER1->TASKS_CLEAR = 1;
|
||||
nrf_delay_us(MAX_TMR1_TASKS_DELAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function for returning the current value of the RTC1 counter.
|
||||
*
|
||||
* @return Current RTC1 counter as a 64-bit value with 56-bit precision (even
|
||||
* though the underlying counter is 24-bit)
|
||||
* @return Current RTC1 counter as a 64-bit value with 48-bit precision (even
|
||||
* though the underlying counter is 16-bit)
|
||||
*/
|
||||
static inline uint64_t rtc1_getCounter64(void)
|
||||
static inline uint64_t tmr1_getCounter64(void)
|
||||
{
|
||||
if (NRF_RTC1->EVENTS_OVRFLW) {
|
||||
if (NRF_TIMER1->EVENTS_COMPARE[3]) {
|
||||
overflowCount++;
|
||||
NRF_RTC1->EVENTS_OVRFLW = 0;
|
||||
NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
|
||||
NRF_TIMER1->EVENTS_COMPARE[3] = 0;
|
||||
}
|
||||
return ((uint64_t)overflowCount << 24) | NRF_RTC1->COUNTER;
|
||||
|
||||
NRF_TIMER1->TASKS_CAPTURE[2] = 1;
|
||||
|
||||
return ((uint64_t)overflowCount << 16) | (NRF_TIMER1->CC[2] & MAX_TMR1_COUNTER_VAL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -142,9 +156,9 @@ static inline uint64_t rtc1_getCounter64(void)
|
|||
*
|
||||
* @return Current RTC1 counter as a 32-bit value (even though the underlying counter is 24-bit)
|
||||
*/
|
||||
static inline uint32_t rtc1_getCounter(void)
|
||||
static inline uint32_t tmr1_getCounter(void)
|
||||
{
|
||||
return rtc1_getCounter64();
|
||||
return tmr1_getCounter64();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,17 +166,19 @@ static inline uint32_t rtc1_getCounter(void)
|
|||
*
|
||||
* @details Checks for timeouts, and executes timeout handlers for expired timers.
|
||||
*/
|
||||
void RTC1_IRQHandler(void)
|
||||
void TIMER1_IRQHandler(void)
|
||||
{
|
||||
if (NRF_RTC1->EVENTS_OVRFLW) {
|
||||
if (NRF_TIMER1->EVENTS_COMPARE[0])
|
||||
{
|
||||
NRF_TIMER1->EVENTS_COMPARE[0] = 0;
|
||||
if (us_ticker_callbackPending && ((int)(us_ticker_callbackTimestamp - tmr1_getCounter()) <= 0)) {
|
||||
invokeCallback();
|
||||
}
|
||||
}
|
||||
|
||||
if (NRF_TIMER1->EVENTS_COMPARE[3]) {
|
||||
overflowCount++;
|
||||
NRF_RTC1->EVENTS_OVRFLW = 0;
|
||||
NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
|
||||
}
|
||||
if (NRF_RTC1->EVENTS_COMPARE[0] && us_ticker_callbackPending && ((int)(us_ticker_callbackTimestamp - rtc1_getCounter()) <= 0)) {
|
||||
NRF_RTC1->EVENTS_COMPARE[0] = 0;
|
||||
NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
|
||||
invokeCallback();
|
||||
NRF_TIMER1->EVENTS_COMPARE[3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,7 +188,7 @@ void us_ticker_init(void)
|
|||
return;
|
||||
}
|
||||
|
||||
rtc1_start();
|
||||
tmr1_start();
|
||||
us_ticker_inited = true;
|
||||
}
|
||||
|
||||
|
@ -182,9 +198,7 @@ uint32_t us_ticker_read()
|
|||
us_ticker_init();
|
||||
}
|
||||
|
||||
/* Return a pseudo microsecond counter value. This is only as precise as the
|
||||
* 32khz low-freq clock source, but could be adequate.*/
|
||||
return RTC_UNITS_TO_MICROSECONDS(rtc1_getCounter64());
|
||||
return TMR1_UNITS_TO_MICROSECONDS(tmr1_getCounter64());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -228,12 +242,17 @@ void us_ticker_set_interrupt(timestamp_t timestamp)
|
|||
* additional 32 bits. RTC_UNITS_TO_MICROSECONDS() converts this into
|
||||
* microsecond units (in 64-bits).
|
||||
*/
|
||||
const uint64_t currentTime64 = RTC_UNITS_TO_MICROSECONDS(rtc1_getCounter64());
|
||||
|
||||
/*
|
||||
const uint64_t currentTime64 = TMR1_UNITS_TO_MICROSECONDS(tmr1_getCounter64());
|
||||
uint64_t timestamp64 = (currentTime64 & ~(uint64_t)0xFFFFFFFFULL) + timestamp;
|
||||
if (((uint32_t)currentTime64 > 0x80000000) && (timestamp < 0x80000000)) {
|
||||
timestamp64 += (uint64_t)0x100000000ULL;
|
||||
}
|
||||
uint32_t newCallbackTime = MICROSECONDS_TO_RTC_UNITS(timestamp64);
|
||||
uint32_t newCallbackTime = MICROSECONDS_TO_TMR1_UNITS(timestamp64);
|
||||
*/
|
||||
|
||||
uint32_t newCallbackTime = timestamp;
|
||||
|
||||
/* Check for repeat setup of an existing callback. This is actually not
|
||||
* important; the following code should work even without this check. */
|
||||
|
@ -245,28 +264,27 @@ void us_ticker_set_interrupt(timestamp_t timestamp)
|
|||
* Even if they are immediately pending, they are scheduled to trigger a few
|
||||
* ticks later. This keeps things simple by invoking the callback from an
|
||||
* independent interrupt context. */
|
||||
if ((int)(newCallbackTime - rtc1_getCounter()) <= (int)FUZZY_RTC_TICKS) {
|
||||
newCallbackTime = rtc1_getCounter() + FUZZY_RTC_TICKS;
|
||||
if ((int)(newCallbackTime - tmr1_getCounter()) <= (int)FUZZY_TMR1_TICKS) {
|
||||
newCallbackTime = tmr1_getCounter() + FUZZY_TMR1_TICKS;
|
||||
}
|
||||
|
||||
NRF_RTC1->CC[0] = newCallbackTime & MAX_RTC_COUNTER_VAL;
|
||||
us_ticker_callbackTimestamp = newCallbackTime;
|
||||
if (!us_ticker_callbackPending) {
|
||||
us_ticker_callbackPending = true;
|
||||
rtc1_enableCompareInterrupt();
|
||||
}
|
||||
us_ticker_callbackPending = true;
|
||||
NRF_TIMER1->CC[0] = newCallbackTime & MAX_TMR1_COUNTER_VAL;
|
||||
//tmr1_enableCompareInterrupt();
|
||||
}
|
||||
|
||||
void us_ticker_disable_interrupt(void)
|
||||
{
|
||||
if (us_ticker_callbackPending) {
|
||||
rtc1_disableCompareInterrupt();
|
||||
us_ticker_callbackPending = false;
|
||||
}
|
||||
//if (us_ticker_callbackPending) {
|
||||
// //tmr1_disableCompareInterrupt();
|
||||
// us_ticker_callbackPending = false;
|
||||
//}
|
||||
}
|
||||
|
||||
void us_ticker_clear_interrupt(void)
|
||||
{
|
||||
NRF_RTC1->EVENTS_OVRFLW = 0;
|
||||
NRF_RTC1->EVENTS_COMPARE[0] = 0;
|
||||
//NRF_TIMER1->EVENTS_COMPARE[3] = 0;
|
||||
//NRF_TIMER1->EVENTS_COMPARE[0] = 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue