@@ -1096,12 +1096,18 @@ void jshSetSystemTime(JsSysTime time) {
1096
1096
1097
1097
/// Convert a time in Milliseconds to one in ticks.
1098
1098
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 ;
1100
1106
}
1101
1107
1102
1108
/// Convert ticks to a time in Milliseconds.
1103
1109
JsVarFloat jshGetMillisecondsFromTime (JsSysTime time ) {
1104
- return time * (1000.0 / SYSCLK_FREQ );
1110
+ return ( JsVarFloat ) time * (1000.0 / ( JsVarFloat ) SYSCLK_FREQ );
1105
1111
}
1106
1112
1107
1113
void jshInterruptOff () {
@@ -2762,13 +2768,14 @@ bool jshSleep(JsSysTime timeUntilWake) {
2762
2768
return true;
2763
2769
}
2764
2770
2765
- bool utilTimerActive = false;
2771
+ volatile bool utilTimerActive = false;
2772
+ volatile uint32_t utilTimerTriggerTime = 0 ;
2766
2773
2767
2774
/// Reschedule the timer (it should already be running) to interrupt after 'period'
2768
2775
void jshUtilTimerReschedule (JsSysTime period ) {
2769
2776
if (period < JSSYSTIME_MAX / NRF_TIMER_FREQ ) {
2770
2777
period = period * NRF_TIMER_FREQ / (long long )SYSCLK_FREQ ;
2771
- if (period < 1 ) period = 1 ;
2778
+ if (period < 0 ) period = 0 ;
2772
2779
if (period > NRF_TIMER_MAX ) period = NRF_TIMER_MAX ;
2773
2780
} else {
2774
2781
// it's too big to do maths on... let's just use the maximum period
@@ -2779,35 +2786,45 @@ void jshUtilTimerReschedule(JsSysTime period) {
2779
2786
/* Setting the timer is complicated because the compare register only compares for equality,
2780
2787
so if we set the compare register even 1 less than the current timer it won't fire for 2^32 microsec
2781
2788
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 .
2784
2791
*/
2785
2792
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
2793
// 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
+ }
2803
2813
} else {
2804
2814
// timer is off, it'll be cleared to literally just set the period
2815
+ utilTimerTriggerTime = period ;
2805
2816
nrf_timer_cc_write (NRF_TIMER1 , NRF_TIMER_CC_CHANNEL0 , (uint32_t )period );
2806
2817
}
2807
2818
}
2808
2819
2809
2820
/// Start the timer and get it to interrupt after 'period'
2810
2821
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
+ }
2811
2828
jshUtilTimerReschedule (period );
2812
2829
if (!utilTimerActive ) {
2813
2830
utilTimerActive = true;
0 commit comments