@@ -1096,12 +1096,18 @@ void jshSetSystemTime(JsSysTime time) {
10961096
10971097/// Convert a time in Milliseconds to one in ticks.
10981098JsSysTime 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.
11031109JsVarFloat jshGetMillisecondsFromTime (JsSysTime time ) {
1104- return time * (1000.0 / SYSCLK_FREQ );
1110+ return ( JsVarFloat ) time * (1000.0 / ( JsVarFloat ) SYSCLK_FREQ );
11051111}
11061112
11071113void 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'
27682775void 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'
28102821void 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