[nRF52840] fix alarm problem on timer overflow (#2282)

Millisecond and microsecond timers used by OpenThread can overflow during
application run. This commit sets RTC Compare Channel to correct value
after overflow.
This commit is contained in:
Hubert Miś
2017-10-23 22:28:37 -07:00
committed by Jonathan Hui
parent 4f5fa83194
commit 9c719f2627
2 changed files with 34 additions and 36 deletions
+33 -35
View File
@@ -74,7 +74,7 @@ typedef enum
typedef struct
{
volatile bool mFireAlarm; ///< Information for processing function, that alarm should fire.
uint32_t mTargetTime; ///< Alarm fire time (in millisecond for MsTimer, in microsecond for UsTimer)
uint64_t mTargetTime; ///< Alarm fire time (in millisecond for MsTimer, in microsecond for UsTimer)
} AlarmData;
typedef struct
@@ -85,7 +85,7 @@ typedef struct
nrf_rtc_int_t mCompareInt;
} AlarmChannelData;
static volatile uint32_t sTimeOffset = 0; ///< Time offset to keep track of current time (in millisecond).
static volatile uint64_t sTimeOffset = 0; ///< Time offset to keep track of current time (in millisecond).
static AlarmData sTimerData[kNumTimers]; ///< Data of the timers.
static const AlarmChannelData sChannelData[kNumTimers] =
{
@@ -105,7 +105,7 @@ static const AlarmChannelData sChannelData[kNumTimers] =
static void HandleOverflow(void);
static inline uint32_t TimeToTicks(uint32_t aTime, AlarmIndex aIndex)
static inline uint32_t TimeToTicks(uint64_t aTime, AlarmIndex aIndex)
{
uint32_t ticks;
@@ -126,49 +126,37 @@ static inline uint64_t TicksToTime(uint32_t aTicks)
return CEIL_DIV((US_PER_S * (uint64_t)aTicks), RTC_FREQUENCY);
}
static inline uint32_t AlarmGetCurrentTimeRtcProtected(AlarmIndex aIndex)
static inline uint64_t AlarmGetCurrentTimeRtcProtected(AlarmIndex aIndex)
{
uint64_t usecTime = nrf5AlarmGetCurrentTime() + 2 * US_PER_TICK;
uint32_t currentTime;
uint64_t currentTime;
if (aIndex == kMsTimer)
{
currentTime = (uint32_t)(usecTime / US_PER_MS);
currentTime = usecTime / US_PER_MS;
}
else
{
currentTime = (uint32_t)usecTime;
currentTime = usecTime;
}
return currentTime;
}
static inline bool AlarmShallStrike(uint32_t aNow, AlarmIndex aIndex)
static inline bool AlarmShallStrike(uint64_t aNow, AlarmIndex aIndex)
{
uint32_t diff = aNow - sTimerData[aIndex].mTargetTime;
// Three cases:
// 1) aNow is before mTargetTime => Difference is negative (last bit of difference is set) => Returning false.
// 2) aNow is same as mTargetTime => Difference is zero (last bit of difference is clear) => Returning true.
// 3) aNow is after mTargetTime => Difference is positive (last bit of difference is clear) => Returning true.
return !((diff & (1UL << 31)) != 0);
return aNow >= sTimerData[aIndex].mTargetTime;
}
static void HandleCompareMatch(AlarmIndex aIndex, bool aSkipCheck)
{
nrf_rtc_event_clear(RTC_INSTANCE, sChannelData[aIndex].mCompareEvent);
uint64_t usecTime = nrf5AlarmGetCurrentTime();
uint32_t now;
uint64_t now = nrf5AlarmGetCurrentTime();
if (aIndex == kMsTimer)
{
now = (uint32_t)(usecTime / US_PER_MS);
}
else
{
now = (uint32_t)usecTime;
now /= US_PER_MS;
}
// In case the target time was larger than single overflow,
@@ -188,18 +176,31 @@ static void HandleOverflow(void)
nrf_rtc_event_clear(RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW);
// Increment counter on overflow.
sTimeOffset = sTimeOffset + MS_PER_OVERFLOW;
sTimeOffset += MS_PER_OVERFLOW;
}
static void AlarmStartAt(uint32_t aTargetTime, AlarmIndex aIndex)
static void AlarmStartAt(uint32_t aT0, uint32_t aDt, AlarmIndex aIndex)
{
uint64_t now;
uint32_t targetCounter;
uint32_t now;
nrf_rtc_int_disable(RTC_INSTANCE, sChannelData[aIndex].mCompareInt);
nrf_rtc_event_enable(RTC_INSTANCE, sChannelData[aIndex].mCompareEventMask);
sTimerData[aIndex].mTargetTime = aTargetTime;
now = nrf5AlarmGetCurrentTime();
if (aIndex == kMsTimer)
{
now /= US_PER_MS;
}
// Check if 32 LSB of `now` overflowed between getting aT0 and loading `now` value.
if ((uint32_t)now < aT0)
{
now -= 0x0000000100000000;
}
sTimerData[aIndex].mTargetTime = (now & 0xffffffff00000000) + aT0 + aDt;
targetCounter = TimeToTicks(sTimerData[aIndex].mTargetTime, aIndex);
@@ -228,7 +229,6 @@ static void AlarmStop(AlarmIndex aIndex)
void nrf5AlarmInit(void)
{
sTimeOffset = 0;
memset(sTimerData, 0, sizeof(sTimerData));
// Setup low frequency clock.
@@ -306,11 +306,11 @@ void nrf5AlarmProcess(otInstance *aInstance)
}
}
inline uint64_t nrf5AlarmGetCurrentTime()
inline uint64_t nrf5AlarmGetCurrentTime(void)
{
uint32_t rtcValue1;
uint32_t rtcValue2;
uint32_t offset;
uint64_t offset;
rtcValue1 = nrf_rtc_counter_get(RTC_INSTANCE);
@@ -345,7 +345,7 @@ inline uint64_t nrf5AlarmGetCurrentTime()
offset = sTimeOffset;
}
return US_PER_MS * (uint64_t)offset + TicksToTime(rtcValue2);
return US_PER_MS * offset + TicksToTime(rtcValue2);
}
uint32_t otPlatAlarmMilliGetNow(void)
@@ -356,9 +356,8 @@ uint32_t otPlatAlarmMilliGetNow(void)
void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)
{
(void)aInstance;
uint32_t targetTime = aT0 + aDt;
AlarmStartAt(targetTime, kMsTimer);
AlarmStartAt(aT0, aDt, kMsTimer);
}
void otPlatAlarmMilliStop(otInstance *aInstance)
@@ -376,9 +375,8 @@ uint32_t otPlatAlarmMicroGetNow(void)
void otPlatAlarmMicroStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)
{
(void)aInstance;
uint32_t targetTime = aT0 + aDt;
AlarmStartAt(targetTime, kUsTimer);
AlarmStartAt(aT0, aDt, kUsTimer);
}
void otPlatAlarmMicroStop(otInstance *aInstance)
+1 -1
View File
@@ -81,7 +81,7 @@ void nrf5AlarmProcess(otInstance *aInstance);
* Function for geting current time in mircosecond.
*
*/
uint64_t nrf5AlarmGetCurrentTime();
uint64_t nrf5AlarmGetCurrentTime(void);
/**
* Initialization of Random Number Generator.