mirror of https://github.com/ARMmbed/mbed-os.git
use a virtual 64-bit timestamp to avoid wrap-around issues. gosh!
parent
aaf998f5cf
commit
e01cb0ae84
|
|
@ -189,6 +189,11 @@ uint32_t us_ticker_read()
|
||||||
* @Note: If this function is used to setup an interrupt which is immediately
|
* @Note: If this function is used to setup an interrupt which is immediately
|
||||||
* pending--such as for 'now' or a time in the past,--then the callback is
|
* pending--such as for 'now' or a time in the past,--then the callback is
|
||||||
* invoked a few ticks later.
|
* invoked a few ticks later.
|
||||||
|
*
|
||||||
|
* @suggestion: Avoid studying this code unless you're forced to. And please
|
||||||
|
* don't prove me wrong--I've spent too much time trying to get this code to
|
||||||
|
* behave correctly; currently it pretends to do so, and I don't want to learn
|
||||||
|
* anything different unless I'm given an easy way to reproduce the problem.
|
||||||
*/
|
*/
|
||||||
void us_ticker_set_interrupt(timestamp_t timestamp)
|
void us_ticker_set_interrupt(timestamp_t timestamp)
|
||||||
{
|
{
|
||||||
|
|
@ -196,7 +201,50 @@ void us_ticker_set_interrupt(timestamp_t timestamp)
|
||||||
us_ticker_init();
|
us_ticker_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t newCallbackTime = MICROSECONDS_TO_RTC_UNITS(timestamp);
|
/*
|
||||||
|
* Alert: Get yourself a beverage.
|
||||||
|
*
|
||||||
|
* In comes a harmless, merry-looking 32-bit timestamp in units of micro-
|
||||||
|
* seconds from the generic us_ticker_api. On most platforms with 32-bit
|
||||||
|
* hardware timers running at micro-second precision this poses no serious
|
||||||
|
* challenge. But on the nRF51, we use an RTC timer running at 32kHz to
|
||||||
|
* implement a low-power us-ticker. This brings with a problem that's rooted
|
||||||
|
* in the fact that 1000000 is not a multiple of 32768.
|
||||||
|
*
|
||||||
|
* Going from a micro-second based timestamp to a 32kHz based RTC-time is a
|
||||||
|
* linear mapping; but then when the 32-bit micro-second timestamp wraps
|
||||||
|
* around, unfortunately the underlying RTC counter doesn't. The result is
|
||||||
|
* that timestamp expiry checks on micro-second timestamps don't yield the
|
||||||
|
* same result when applied on the corresponding RTC timestamp values.
|
||||||
|
* You'll really need to whip up some numbers to see the problem.
|
||||||
|
*
|
||||||
|
* As far as I understand--and this could just be me turning old and rusty,
|
||||||
|
* --there is no clean solution for this problem at the wrap-around
|
||||||
|
* boundary. I'd be interested to be shown how. My solution is to translate
|
||||||
|
* the incoming 32-bit timestamp into a virtual 64-bit timestamp based on
|
||||||
|
* the knowledge of system-uptime, and then use this 64-bit value to do a
|
||||||
|
* linear mapping to RTC time. 64-bit timestamp values shouldn't wrap around
|
||||||
|
* in a few thousand years. This may be pulling down the theoretical upper
|
||||||
|
* boundary for the shelf life of nRF products when used with mbed; please
|
||||||
|
* accept my two to the power 56 apologies for that. But then life often
|
||||||
|
* forces one to live within constraints. What's the meaning of all this
|
||||||
|
* anyway?
|
||||||
|
*
|
||||||
|
* System uptime on an nRF is maintained using RTC's counter. We track the
|
||||||
|
* overflow count to extend the 24-bit hardware counter by an additional 32
|
||||||
|
* bits. This then forms the basis for system time.
|
||||||
|
* RTC_UNITS_TO_MICROSECONDS() converts this into microsecond units (in
|
||||||
|
* 64-bits). We then shave off the lower 32-bits of it and add in the
|
||||||
|
* incoming timestamp to get a mapping into a virtual 64-bit timestamp.
|
||||||
|
* There's one additional check to handle the case of wraparound for the
|
||||||
|
* 32-bit timestamp.
|
||||||
|
*/
|
||||||
|
uint64_t timestamp64 = (RTC_UNITS_TO_MICROSECONDS(rtc1_getCounter()) & ~(uint64_t)0xFFFFFFFF) + timestamp;
|
||||||
|
if ((us_ticker_read() > 0xF0000000) && (timestamp < 0x10000000)) {
|
||||||
|
timestamp64 += (uint64_t)0x100000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t newCallbackTime = MICROSECONDS_TO_RTC_UNITS(timestamp64);
|
||||||
|
|
||||||
/* Check for repeat setup of an existing callback. This is actually not
|
/* Check for repeat setup of an existing callback. This is actually not
|
||||||
* important; the following code should work even without this check. */
|
* important; the following code should work even without this check. */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue