Skip to content

Commit 0b2724c

Browse files
committed
nRF52: Improve software timer accuracy by keeping timer running while executing jstUtilTimerInterruptHandler (fix #2620)
1 parent 35598fe commit 0b2724c

File tree

4 files changed

+38
-10
lines changed

4 files changed

+38
-10
lines changed

ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
Fix potential overflow of locks - favour small memory leak over accidental free if this ever happens (fix #2616)
2626
micro:bit2: Ensure we don't initialise the I2C1 peripheral (we use software I2C internally)
2727
Unlock functionCode early in function execution to reduce locks needed during recursion (#2616)
28+
nRF52: Improve software timer accuracy by keeping timer running while executing jstUtilTimerInterruptHandler (fix #2620)
2829

2930
2v25 : ESP32C3: Get analogRead working correctly
3031
Graphics: Adjust image alignment when rotating images to avoid cropping (fix #2535)

src/jshardware.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,9 @@ size_t jshFlashGetMemMapAddress(size_t ptr);
338338

339339
/// Start the timer and get it to interrupt once after 'period' (i.e. it should not auto-reload)
340340
void jshUtilTimerStart(JsSysTime period);
341-
/// Reschedule the timer (it should already be running) to interrupt after 'period'
341+
/** Reschedule the timer (it should already be running) to interrupt after 'period'.
342+
With the timer running this should ideally set the time period from the last time the
343+
timer triggered (so as to factor out any execution delay in jstUtilTimerInterruptHandler). */
342344
void jshUtilTimerReschedule(JsSysTime period);
343345
/// Stop the timer
344346
void jshUtilTimerDisable();

src/jstimer.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,10 @@ void jstUtilTimerInterruptHandler() {
8787
/* Note: we're using 32 bit times here, even though the real time counter is 64 bit. We
8888
* just make sure nothing is scheduled that far in the future */
8989
if (utilTimerOn) {
90-
// TODO: Keep UtilTimer running and then use the value from it
91-
// to estimate how long utilTimerPeriod really was
92-
// Subtract utilTimerPeriod from all timers' time
90+
utilTimerSetTime = jshGetSystemTime();
91+
/* We keep UtilTimer running and then jshUtilTimerReschedule reschedules
92+
based on the time from when this IRQ was triggered, *not* from the
93+
current time. */
9394
int t = utilTimerTasksTail;
9495
while (t!=utilTimerTasksHead) {
9596
utilTimerTasks[t].time -= utilTimerPeriod;
@@ -218,7 +219,6 @@ void jstUtilTimerInterruptHandler() {
218219
// re-schedule the timer if there is something left to do
219220
if (utilTimerTasksTail != utilTimerTasksHead) {
220221
utilTimerPeriod = utilTimerTasks[utilTimerTasksTail].time;
221-
utilTimerSetTime = jshGetSystemTime();
222222
if (utilTimerPeriod<0) utilTimerPeriod=0;
223223
jshUtilTimerReschedule(utilTimerPeriod);
224224
} else {

targets/nrf5x/jshardware.c

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,6 @@ const nrf_drv_twis_t *jshGetTWIS(IOEventFlags device) {
626626
#endif
627627

628628
void TIMER1_IRQHandler(void) {
629-
nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_CLEAR);
630629
nrf_timer_event_clear(NRF_TIMER1, NRF_TIMER_EVENT_COMPARE0);
631630
jstUtilTimerInterruptHandler();
632631
}
@@ -2776,17 +2775,43 @@ void jshUtilTimerReschedule(JsSysTime period) {
27762775
period = NRF_TIMER_MAX;
27772776
}
27782777
//jsiConsolePrintf("Sleep for %d %d -> %d\n", (uint32_t)(t>>32), (uint32_t)(t), (uint32_t)(period));
2779-
if (utilTimerActive) nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_STOP);
2780-
nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_CLEAR);
2781-
nrf_timer_cc_write(NRF_TIMER1, NRF_TIMER_CC_CHANNEL0, (uint32_t)period);
2782-
if (utilTimerActive) nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_START);
2778+
2779+
/* Setting the timer is complicated because the compare register only compares for equality,
2780+
so if we set the compare register even 1 less than the current timer it won't fire for 2^32 microsec
2781+
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.
2784+
*/
2785+
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);
2788+
// 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);
2803+
} else {
2804+
// timer is off, it'll be cleared to literally just set the period
2805+
nrf_timer_cc_write(NRF_TIMER1, NRF_TIMER_CC_CHANNEL0, (uint32_t)period);
2806+
}
27832807
}
27842808

27852809
/// Start the timer and get it to interrupt after 'period'
27862810
void jshUtilTimerStart(JsSysTime period) {
27872811
jshUtilTimerReschedule(period);
27882812
if (!utilTimerActive) {
27892813
utilTimerActive = true;
2814+
nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_CLEAR);
27902815
nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_START);
27912816
}
27922817
}

0 commit comments

Comments
 (0)