Skip to content

Commit 7c6531d

Browse files
committed
nRF52: Further improve software timer accuracy
1 parent 2bdf05f commit 7c6531d

File tree

1 file changed

+39
-22
lines changed

1 file changed

+39
-22
lines changed

targets/nrf5x/jshardware.c

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,12 +1096,18 @@ void jshSetSystemTime(JsSysTime time) {
10961096

10971097
/// Convert a time in Milliseconds to one in ticks.
10981098
JsSysTime jshGetTimeFromMilliseconds(JsVarFloat ms) {
1099-
return (JsSysTime) (ms * (SYSCLK_FREQ / 1000.0));
1099+
// round to nearest system tick
1100+
JsVarFloat ticksFloat = ms * ((JsVarFloat)SYSCLK_FREQ / 1000.0);
1101+
JsSysTime ticks = (JsSysTime)ticksFloat;
1102+
if ((ticksFloat - (JsVarFloat)ticks) >= 0.5) {
1103+
ticks += 1;
1104+
}
1105+
return ticks;
11001106
}
11011107

11021108
/// Convert ticks to a time in Milliseconds.
11031109
JsVarFloat jshGetMillisecondsFromTime(JsSysTime time) {
1104-
return time * (1000.0 / SYSCLK_FREQ);
1110+
return (JsVarFloat)time * (1000.0 / (JsVarFloat)SYSCLK_FREQ);
11051111
}
11061112

11071113
void jshInterruptOff() {
@@ -2762,13 +2768,14 @@ bool jshSleep(JsSysTime timeUntilWake) {
27622768
return true;
27632769
}
27642770

2765-
bool utilTimerActive = false;
2771+
volatile bool utilTimerActive = false;
2772+
volatile uint32_t utilTimerTriggerTime = 0;
27662773

27672774
/// Reschedule the timer (it should already be running) to interrupt after 'period'
27682775
void jshUtilTimerReschedule(JsSysTime period) {
27692776
if (period < JSSYSTIME_MAX / NRF_TIMER_FREQ) {
27702777
period = period * NRF_TIMER_FREQ / (long long)SYSCLK_FREQ;
2771-
if (period < 1) period=1;
2778+
if (period < 0) period=0;
27722779
if (period > NRF_TIMER_MAX) period=NRF_TIMER_MAX;
27732780
} else {
27742781
// it's too big to do maths on... let's just use the maximum period
@@ -2779,35 +2786,45 @@ void jshUtilTimerReschedule(JsSysTime period) {
27792786
/* Setting the timer is complicated because the compare register only compares for equality,
27802787
so if we set the compare register even 1 less than the current timer it won't fire for 2^32 microsec
27812788
2782-
That would be fine but we're not ever allowed to totally disable interrupts so we have to check *after*
2783-
we set it just to make sure it hasn't overflowed and if so to redo it.
2789+
That would be fine but we're not ever allowed to totally disable the timer so we have to check *after*
2790+
we set it just to make sure it hasn't overflowed and if so go ahead and trigger the interrupt ourselves.
27842791
*/
27852792
if (utilTimerActive) { // Reschedule an active timer...
2786-
// Find out what our last trigger time was
2787-
uint32_t lastCC = nrf_timer_cc_read(NRF_TIMER1, NRF_TIMER_CC_CHANNEL0);
27882793
// schedule timer to trigger at the last time we triggered PLUS our period
2789-
uint32_t thisCC = lastCC + period;
2790-
bool needsReschedule;
2791-
do {
2792-
// set up the timer
2793-
nrf_timer_cc_write(NRF_TIMER1, NRF_TIMER_CC_CHANNEL0, (uint32_t)thisCC);
2794-
needsReschedule = false;
2795-
// Check that the timer hasn't already passed this value? Reschedule it 2us in the future
2796-
NRF_TIMER1->TASKS_CAPTURE[1] = 1; // get current timer value
2797-
uint32_t current = NRF_TIMER1->CC[1];
2798-
if (((int32_t)thisCC - (int32_t)current) < 2) { // it it's closer than 2us (or has already passed!)
2799-
thisCC = current+2; // reschedule into the future
2800-
needsReschedule = true;
2801-
}
2802-
} while (needsReschedule);
2794+
uint32_t thisCC = utilTimerTriggerTime + period;
2795+
// set up the timer
2796+
nrf_timer_cc_write(NRF_TIMER1, NRF_TIMER_CC_CHANNEL0, (uint32_t)thisCC);
2797+
// Check that the timer hasn't already passed this value?
2798+
NRF_TIMER1->TASKS_CAPTURE[1] = 1; // get current timer value
2799+
uint32_t current = NRF_TIMER1->CC[1];
2800+
if (((int32_t)thisCC - (int32_t)current) <= -(int32_t)(NRF_TIMER_MAX >> 2)) { // if we can't keep up and are already fairly far behind
2801+
// skip ahead to avoid falling victim to timer wraparound
2802+
// and potentially forgetting to schedule stuff altogether for some time
2803+
thisCC = current;
2804+
nrf_timer_cc_write(NRF_TIMER1, NRF_TIMER_CC_CHANNEL0, (uint32_t)current);
2805+
}
2806+
utilTimerTriggerTime = thisCC;
2807+
if (((int32_t)thisCC - (int32_t)current) < 2) { // if it's closer than 2us (or has already passed!)
2808+
// make sure that the timer doesn't trigger the interrupt shortly afterwards
2809+
nrf_timer_cc_write(NRF_TIMER1, NRF_TIMER_CC_CHANNEL0, (uint32_t)(current - 1));
2810+
// and manually trigger the interrupt now
2811+
NVIC_SetPendingIRQ(TIMER1_IRQn);
2812+
}
28032813
} else {
28042814
// timer is off, it'll be cleared to literally just set the period
2815+
utilTimerTriggerTime = period;
28052816
nrf_timer_cc_write(NRF_TIMER1, NRF_TIMER_CC_CHANNEL0, (uint32_t)period);
28062817
}
28072818
}
28082819

28092820
/// Start the timer and get it to interrupt after 'period'
28102821
void jshUtilTimerStart(JsSysTime period) {
2822+
if (utilTimerActive) {
2823+
// schedule at the current time + period
2824+
// by pretending that the last time we wanted to be called at is now
2825+
NRF_TIMER1->TASKS_CAPTURE[1] = 1; // get current timer value
2826+
utilTimerTriggerTime = NRF_TIMER1->CC[1];
2827+
}
28112828
jshUtilTimerReschedule(period);
28122829
if (!utilTimerActive) {
28132830
utilTimerActive = true;

0 commit comments

Comments
 (0)