Skip to content

Commit dd408b6

Browse files
committed
Revert "Util Timer now uses time offsets (not variables based on current system time) - avoids drift"
This reverts commit 46b2a48.
1 parent ad34e66 commit dd408b6

5 files changed

Lines changed: 48 additions & 64 deletions

File tree

ChangeLog

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
nRF52: Fix issue where analogRead would stop E.getBattery from working
99
Fix setWatch debounce lastTime regression from 2v07 (fix #1902)
1010
Util Timer no longer uses RTC - works based on estimated time from the hardware timer itself (fix #1749, ref #1444)
11-
Util Timer now uses time offsets (not variables based on current system time) - avoids drift
1211
nRF52: CPU now sleeps when while UART/BLE data is waiting to be sent (fix #1938)
1312
JSON.stringify now checks for potential stack overflow when stringifying (fix #1940)
1413
Check for Stack overflow when Garbage Collecting giant linked list (fix #1765)

src/jsserial.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ void jsserialSoftwareFunc(
5151
JsSysTime time;
5252
UtilTimerTask task;
5353
if (jstGetLastPinTimerTask(inf->pinTX, &task)) {
54-
time = task.timeLeft + bitTime; // leave one bit of time for a stop bit
54+
time = task.time + bitTime; // leave one bit of time for a stop bit
5555
} else {
5656
// no timer - just start in a little while!
57-
time = jshGetTimeFromMilliseconds(1);
57+
time = jshGetSystemTime()+jshGetTimeFromMilliseconds(1);
5858
}
5959
//bool outState = 1;
6060
int outCount = 0;

src/jstimer.c

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,10 @@ unsigned int utilTimerBit;
2929
bool utilTimerInIRQ = false;
3030
unsigned int utilTimerData;
3131
uint16_t utilTimerReload0H, utilTimerReload0L, utilTimerReload1H, utilTimerReload1L;
32-
/// When we last started the timer, how far in the future were we meant to get called?
32+
/// When did the timer last run? This is used for timekeeping. Should be jshGetSystemTime() cropped to 32 bits
33+
volatile int utilTimerTime;
34+
/// When we rescheduled the timer, how far in the future were we meant to get called?
3335
int utilTimerPeriod;
34-
/** When we last started the timer, this was the RTC time. We need this if we need to
35-
reschedule the timer */
36-
JsSysTime utilTimerScheduleTime;
3736

3837

3938
#ifndef SAVE_ON_FLASH
@@ -94,14 +93,11 @@ void jstUtilTimerInterruptHandler() {
9493
jshPinOutput(31,1);
9594
if (utilTimerOn) {
9695
utilTimerInIRQ = true;
97-
// Update the time of each timer task with the actual timer delay
98-
unsigned char t = utilTimerTasksTail;
99-
while (t!=utilTimerTasksHead) {
100-
utilTimerTasks[t].timeLeft -= utilTimerPeriod;
101-
t = (t+1) & (UTILTIMERTASK_TASKS-1);
102-
}
96+
// Increment estimated timer time
97+
// TODO: use SysTick to estimate time in this fn and update utilTimerTime accordingly
98+
utilTimerTime += utilTimerPeriod;
10399
// execute any timers that are due
104-
while (utilTimerTasksTail!=utilTimerTasksHead && utilTimerTasks[utilTimerTasksTail].timeLeft<=0) {
100+
while (utilTimerTasksTail!=utilTimerTasksHead && (utilTimerTasks[utilTimerTasksTail].time - utilTimerTime)<=0) {
105101
UtilTimerTask *task = &utilTimerTasks[utilTimerTasksTail];
106102
void (*executeFn)(JsSysTime time, void* userdata) = 0;
107103
void *executeData = 0;
@@ -171,12 +167,13 @@ void jstUtilTimerInterruptHandler() {
171167
// If we need to repeat
172168
if (task->repeatInterval) {
173169
// update time (we know time > task->time)
174-
task->timeLeft += task->repeatInterval;
170+
task->time += task->repeatInterval;
171+
175172
// do an in-place bubble sort to ensure that times are still in the right order
176173
unsigned char ta = utilTimerTasksTail;
177174
unsigned char tb = (ta+1) & (UTILTIMERTASK_TASKS-1);
178175
while (tb != utilTimerTasksHead) {
179-
if (utilTimerTasks[ta].timeLeft > utilTimerTasks[tb].timeLeft) {
176+
if ((utilTimerTasks[ta].time-utilTimerTime) > (utilTimerTasks[tb].time-utilTimerTime)) {
180177
UtilTimerTask task = utilTimerTasks[ta];
181178
utilTimerTasks[ta] = utilTimerTasks[tb];
182179
utilTimerTasks[tb] = task;
@@ -195,9 +192,8 @@ void jstUtilTimerInterruptHandler() {
195192

196193
// re-schedule the timer if there is something left to do
197194
if (utilTimerTasksTail != utilTimerTasksHead) {
198-
utilTimerPeriod = utilTimerTasks[utilTimerTasksTail].timeLeft;
195+
utilTimerPeriod = utilTimerTasks[utilTimerTasksTail].time - utilTimerTime;
199196
if (utilTimerPeriod<0) utilTimerPeriod=0;
200-
utilTimerScheduleTime = jshGetSystemTime();
201197
jshUtilTimerReschedule(utilTimerPeriod);
202198
} else {
203199
utilTimerOn = false;
@@ -229,13 +225,9 @@ static bool utilTimerIsFull() {
229225
/* Restart the utility timer with the right period. This should not normally
230226
need to be called by anything outside jstimer.c */
231227
void jstRestartUtilTimer() {
232-
// disabling here stops jshUtilTimerStart trying to schedule the timer including the time that has already passed
233-
jshUtilTimerDisable();
234-
// Work out how long we've got to go
235-
utilTimerPeriod = utilTimerTasks[utilTimerTasksTail].timeLeft;
228+
utilTimerTime = (int)jshGetSystemTime();
229+
utilTimerPeriod = utilTimerTasks[utilTimerTasksTail].time - utilTimerTime;
236230
if (utilTimerPeriod<0) utilTimerPeriod=0;
237-
utilTimerScheduleTime = jshGetSystemTime();
238-
// reschedule
239231
jshUtilTimerStart(utilTimerPeriod);
240232
}
241233

@@ -247,11 +239,9 @@ bool utilTimerInsertTask(UtilTimerTask *task) {
247239

248240
if (!utilTimerInIRQ) jshInterruptOff();
249241

250-
// time between when utilTimerTasks was last updated and time now
251-
int timeDiff = jshGetSystemTime()-utilTimerScheduleTime;
252242
// find out where to insert
253243
unsigned char insertPos = utilTimerTasksTail;
254-
while (insertPos != utilTimerTasksHead && (utilTimerTasks[insertPos].timeLeft-timeDiff) < task->timeLeft)
244+
while (insertPos != utilTimerTasksHead && utilTimerTasks[insertPos].time < task->time)
255245
insertPos = (insertPos+1) & (UTILTIMERTASK_TASKS-1);
256246

257247
bool haveChangedTimer = insertPos==utilTimerTasksTail;
@@ -261,21 +251,18 @@ bool utilTimerInsertTask(UtilTimerTask *task) {
261251
while (i != insertPos) {
262252
unsigned char next = (i+UTILTIMERTASK_TASKS-1) & (UTILTIMERTASK_TASKS-1);
263253
utilTimerTasks[i] = utilTimerTasks[next];
264-
if (haveChangedTimer) {
265-
// if we changed the timer, we're rescheduling so must change
266-
// the times in all tasks
267-
utilTimerTasks[i].timeLeft -= timeDiff;
268-
}
269254
i = next;
270255
}
271256
// add new item
272257
utilTimerTasks[insertPos] = *task;
273258
// increase task list size
274259
utilTimerTasksHead = (utilTimerTasksHead+1) & (UTILTIMERTASK_TASKS-1);
260+
261+
//jsiConsolePrint("Head is %d\n", utilTimerTasksHead);
275262
// now set up timer if not already set up...
276263
if (!utilTimerOn || haveChangedTimer) {
277-
jstRestartUtilTimer();
278264
utilTimerOn = true;
265+
jstRestartUtilTimer();
279266
}
280267

281268
if (!utilTimerInIRQ) jshInterruptOn();
@@ -381,7 +368,7 @@ bool jstGetLastBufferTimerTask(JsVar *var, UtilTimerTask *task) {
381368
bool jstPinOutputAtTime(JsSysTime time, Pin *pins, int pinCount, uint8_t value) {
382369
assert(pinCount<=UTILTIMERTASK_PIN_COUNT);
383370
UtilTimerTask task;
384-
task.timeLeft = (int)(time - jshGetSystemTime());
371+
task.time = (int)time;
385372
task.repeatInterval = 0;
386373
task.type = UET_SET;
387374
int i;
@@ -430,10 +417,10 @@ bool jstPinPWM(JsVarFloat freq, JsVarFloat dutyCycle, Pin pin) {
430417
}
431418
if (ptaskon && ptaskoff) {
432419
// Great! We have PWM... Just update it
433-
if (ptaskoff->timeLeft > ptaskon->timeLeft)
434-
ptaskoff->timeLeft = ptaskon->timeLeft + pulseLength;
420+
if (ptaskoff->time > ptaskon->time)
421+
ptaskoff->time = ptaskon->time + pulseLength;
435422
else
436-
ptaskoff->timeLeft = ptaskon->timeLeft + pulseLength - (unsigned int)period;
423+
ptaskoff->time = ptaskon->time + pulseLength - (unsigned int)period;
437424
ptaskon->repeatInterval = (unsigned int)period;
438425
ptaskoff->repeatInterval = (unsigned int)period;
439426
/* don't bother rescheduling - everything will work out next time
@@ -448,12 +435,12 @@ bool jstPinPWM(JsVarFloat freq, JsVarFloat dutyCycle, Pin pin) {
448435
if (ptaskon || ptaskoff) {
449436
while (utilTimerRemoveTask(jstPinTaskChecker, (void*)&pin));
450437
}
451-
438+
JsSysTime time = jshGetSystemTime();
452439
UtilTimerTask taskon, taskoff;
453440
taskon.data.set.value = 1;
454441
taskoff.data.set.value = 0;
455-
taskon.timeLeft = 0;
456-
taskoff.timeLeft = (int)(taskon.timeLeft + pulseLength);
442+
taskon.time = (int)time;
443+
taskoff.time = (int)(time + pulseLength);
457444
taskon.repeatInterval = (unsigned int)period;
458445
taskoff.repeatInterval = (unsigned int)period;
459446
taskon.type = UET_SET;
@@ -475,7 +462,7 @@ bool jstPinPWM(JsVarFloat freq, JsVarFloat dutyCycle, Pin pin) {
475462
/// Execute the given function repeatedly after the given time period
476463
bool jstExecuteFn(UtilTimerTaskExecFn fn, void *userdata, JsSysTime startTime, uint32_t period) {
477464
UtilTimerTask task;
478-
task.timeLeft = (int)(startTime - jshGetSystemTime());
465+
task.time = (int)startTime;
479466
task.repeatInterval = period;
480467
task.type = UET_EXECUTE;
481468
task.data.execute.fn = fn;
@@ -496,7 +483,7 @@ bool jstStopExecuteFn(UtilTimerTaskExecFn fn, void *userdata) {
496483
/// Set the utility timer so we're woken up in whatever time period
497484
bool jstSetWakeUp(JsSysTime period) {
498485
UtilTimerTask task;
499-
task.timeLeft = (int)period;
486+
task.time = (int)(jshGetSystemTime() + period);
500487
task.repeatInterval = 0;
501488
task.type = UET_WAKEUP;
502489

@@ -508,11 +495,11 @@ bool jstSetWakeUp(JsSysTime period) {
508495
jshInterruptOff();
509496
if (utilTimerTasksTail!=utilTimerTasksHead) {
510497
hasTimer = true;
511-
nextTime = utilTimerTasks[utilTimerTasksTail].timeLeft;
498+
nextTime = utilTimerTasks[utilTimerTasksTail].time;
512499
}
513500
jshInterruptOn();
514501

515-
if (hasTimer && task.timeLeft >= nextTime) {
502+
if (hasTimer && (task.time-utilTimerTime) >= (nextTime-utilTimerTime)) {
516503
// we already had a timer, and it's going to wake us up sooner.
517504
// don't create a WAKEUP timer task
518505
return true;
@@ -549,7 +536,7 @@ bool jstStartSignal(JsSysTime startTime, JsSysTime period, Pin pin, JsVar *curre
549536
if (!jshIsPinValid(pin)) return false;
550537
UtilTimerTask task;
551538
task.repeatInterval = (unsigned int)period;
552-
task.timeLeft = (int)(startTime + period - jshGetSystemTime());
539+
task.time = (int)(startTime + period);
553540
task.type = type;
554541
if (UET_IS_BUFFER_WRITE_EVENT(type)) {
555542
task.data.buffer.pinFunction = jshGetCurrentPinFunction(pin);
@@ -589,14 +576,19 @@ bool jstStopBufferTimerTask(JsVar *var) {
589576
void jstReset() {
590577
jshUtilTimerDisable();
591578
utilTimerTasksTail = utilTimerTasksHead = 0;
579+
utilTimerTime = (int)jshGetSystemTime();
592580
utilTimerPeriod = 0;
593-
utilTimerScheduleTime = jshGetSystemTime();
594581
}
595582

596583
/** when system time is changed, also change the time in the timers.
597584
This should be done with interrupts off */
598585
void jstSystemTimeChanged(JsSysTime diff) {
599-
utilTimerScheduleTime += diff;
586+
unsigned char t = utilTimerTasksTail;
587+
while (t!=utilTimerTasksHead) {
588+
utilTimerTasks[t].time += diff;
589+
t = (t+1) & (UTILTIMERTASK_TASKS-1);
590+
}
591+
utilTimerTime += diff;
600592
}
601593

602594
void jstDumpUtilityTimers() {
@@ -607,15 +599,17 @@ void jstDumpUtilityTimers() {
607599
uTimerTasks[i] = utilTimerTasks[i];
608600
unsigned char uTimerTasksHead = utilTimerTasksHead;
609601
unsigned char uTimerTasksTail = utilTimerTasksTail;
602+
int uTimerTime = utilTimerTime;
610603
jshInterruptOn();
611604

605+
jsiConsolePrintf("Current timer difference %d us", (int)(1000*jshGetMillisecondsFromTime(jshGetSystemTime()-uTimerTime)));
612606
unsigned char t = uTimerTasksTail;
613607
bool hadTimers = false;
614608
while (t!=uTimerTasksHead) {
615609
hadTimers = true;
616610

617611
UtilTimerTask task = uTimerTasks[t];
618-
jsiConsolePrintf("%08d us", (int)(1000*jshGetMillisecondsFromTime(task.timeLeft)));
612+
jsiConsolePrintf("%08d us", (int)(1000*jshGetMillisecondsFromTime(task.time-uTimerTime)));
619613
jsiConsolePrintf(", repeat %08d us", (int)(1000*jshGetMillisecondsFromTime(task.repeatInterval)));
620614
jsiConsolePrintf(" : ");
621615

src/jstimer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ typedef union UtilTimerTaskData {
8989
} UtilTimerTaskData;
9090

9191
typedef struct UtilTimerTask {
92-
int timeLeft; // how far in the future should we execute the timer (JshSysTime, cropped to 32 bits)
92+
int time; // time at which to set pins (JshSysTime, cropped to 32 bits)
9393
unsigned int repeatInterval; // if nonzero, repeat the timer
9494
UtilTimerTaskData data; // data used when timer is hit
9595
UtilTimerEventType type; // the type of this task - do we set pin(s) or read/write data

targets/nrf5x/jshardware.c

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,10 +1398,10 @@ void jshPinPulse(Pin pin, bool pulsePolarity, JsVarFloat pulseTime) {
13981398
if (!jstGetLastPinTimerTask(pin, &task)) {
13991399
// no timer - just start the pulse now!
14001400
jshPinOutput(pin, pulsePolarity);
1401-
task.timeLeft = 0;
1401+
task.time = jshGetSystemTime();
14021402
}
14031403
// Now set the end of the pulse to happen on a timer
1404-
jstPinOutputAtTime(jshGetSystemTime() + task.timeLeft + jshGetTimeFromMilliseconds(pulseTime), &pin, 1, !pulsePolarity);
1404+
jstPinOutputAtTime(task.time + jshGetTimeFromMilliseconds(pulseTime), &pin, 1, !pulsePolarity);
14051405
}
14061406
}
14071407

@@ -2300,19 +2300,10 @@ void jshUtilTimerReschedule(JsSysTime period) {
23002300
period = NRF_TIMER_MAX;
23012301
}
23022302
//jsiConsolePrintf("Sleep for %d %d -> %d\n", (uint32_t)(t>>32), (uint32_t)(t), (uint32_t)(period));
2303-
if (utilTimerActive) {
2304-
/* If running, get the current timer value and attempt to use it to schedule the period
2305-
* based on what it is right now */
2306-
nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_STOP);
2307-
nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_CAPTURE3);
2308-
uint32_t currentValue = nrf_timer_cc_read(NRF_TIMER1, NRF_TIMER_CC_CHANNEL3);
2309-
if (period < currentValue+2) period = currentValue+2;
2310-
nrf_timer_cc_write(NRF_TIMER1, NRF_TIMER_CC_CHANNEL0, (uint32_t)period);
2311-
nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_START);
2312-
} else {
2313-
nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_CLEAR);
2314-
nrf_timer_cc_write(NRF_TIMER1, NRF_TIMER_CC_CHANNEL0, (uint32_t)period);
2315-
}
2303+
if (utilTimerActive) nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_STOP);
2304+
nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_CLEAR);
2305+
nrf_timer_cc_write(NRF_TIMER1, NRF_TIMER_CC_CHANNEL0, (uint32_t)period);
2306+
if (utilTimerActive) nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_START);
23162307
}
23172308

23182309
/// Start the timer and get it to interrupt after 'period'

0 commit comments

Comments
 (0)