Time measurement by TIMER

published: 2 October 2020 / updated 2 October 2020

Lire cette page en français

 

This article is inspired by the msecTimer.ff program produced by Klaus KUENEL:
Custom Timer Functions on Arduino Nano

La mesure du temps est une nécessité qui revient fréquemment en programmation:

Tous les mots FORTH sont conçus et testés pour la carte ARDUINO NANO. Le code est valable pour ARDUINO NANO et ARDUINO MEGA.

The wrong way to measure elapsed time

The wrong way to estimate the elapsed time for a process is to appeal to a variable whose content is incremented by a function:

variable nbCounts 
: incCounts  ( -- ) 
    1 nbCounts +! ;  \ increment 1 unit content of nbCounts 
: test01  ( -- ) 
    0 nbCounts ! 
    begin 
        incCounts 
        key?         \ continue if no key pressed on keyboard 
    until 
    nbCounts @ .     \ display content of nbCounts 
  ; 

It may be suitable for certain uses. But why is this the wrong way? It's a bad way:

A much better idea would be to go through an independent multitasking process taking care of incrementing the content of our variable nbCounts.

In fact, the microcontrollers fitted to the ARDUINO boards are equipped with timers which can be exploited via software interrupts.

The good idea is therefore to exploit a TIMER interrupt acting on the content of a count variable.

The advantage of a timer to count the elapsed time is that the program can achieve something else during this time.

Les registres TIMER pour ARDUINO NANO

To realize exact timing Arduino has several timers which can be used with interrupts.

Timer0 is used for ms. Timer 1 is used for the CPU load measurement. Timer2 is free.

In this article, we use Timer2 to generate a 10 ms output compare interrupt which increments a variable counter each 10 ms.

Un timer est un registre à l’intérieur du microcontrôleur qui s’incrémente (ou se décrémente) chaque fois qu’il reçoit une impulsion d’un signal d’horloge. Ce signal d’horloge peut être propre au microcontrôleur ou bien extérieur à celui-ci. Un timer est donc un compteur capable de compter le temps qui s’écoule, d’où son nom anglais de timer counter.

Le tableau suivant donne les différents registres de contrôle associés à chaque timer.

Timer 0 Timer 1 Timer 2 rôle / role
TCNT0TCNT1LTCNT2Timer (bit 0 à 7)
-TCNT1H-Timer (bit 8 à 15)
TCCR0ATCCR1ATCCR2ARegistre de contrôle
TCCR0BTCCR1BTCCR2BRegistre de contrôle
-TCCR1C-Registre de contrôle
OCR0AOCR1ALOCR2AOutput Compare (bit 0 à 7)
-OCR1AH-Output Compare (bit 8 à 15)
OCR0BOCR1BLOCR2BOutput Compare (bit 0 à 7)
-OCR1BH-Output Compare (bit 8 à 15)
-ICR1L-Input Capture (bit 0 à 7)
-ICR1H-Input Capture (bit 8 à 15)
TIMSK0TIMSK1TIMSK2Interrupt Mask
TIFR0TIFR1TIFR2Interrupt Flag

Signification des abbréviations

Les registres TCCR2A, TCCR2B et TIMSK2

TCCR2A - Timer/Counter2 Control Register A

bit 76543210
COM2A1COM2A0 COM2B1COM2B0 -------------------- WGM21WGM20

TCCR2B - Timer/Counter2 Control Register B

bit 76543210
FOC2AFOC2B -------------------- WGM22 CS22CS21 CS20

Pour ralentir un timer, il suffit d'utiliser le prédiviseur pour diviser la fréquence du signal d'horloge. Pour faire cela, il suffit de positionner certains bits du registre TCCR2B

TIMSK2 - Timer/Counter Interrupt Mask register

bit 76543210
------------------------------ -------------------- OCIE2BOCIE2ATOIE2

Rôle du registre OCR2A

On peut charger un nombre dans le registre OCR2A. Quand le timer 2, en comptant, devient égal à OCR2A, ça provoque la mise à 1 d’un flag appelé OCF2A. Si on a autorisé les interruptions, ce flag va commander une interruption qui sera traitée par la routine d'interruption.

\ Disable interrupt before removing the interrupt code 
T2_COMPA_Dis 
 
-msectimer 
marker -msectimer 
 
\ Timer2 Register Definitions 
$b0 constant TCCR2A 
$b1 constant TCCR2B 
$70 constant TIMSK2 
$b3 constant OCR2A 
         
\ counter incremented by interruption 
variable counter 
 
\ Interrupt routine for Timer1 CompareA 
: T2_COMPA_ISR 
    1 counter +! 
  ;i 
         
#8  constant OC2Aaddr \ interrupt vect no 8 
: setupInt  ( -- ) 
    ['] T2_COMPA_ISR OC2Aaddr int! 
  ; 
 
 
\ display content TIMERs registers 
: dispRegs ( -- ) 
    hex 
    cr ." OCR2A:  " OCR2A  c@ 2 u.r 
    cr ." TCCR2A: " TCCR2A c@ 2 u.r 
    cr ." TCCR2B: " TCCR2B c@ 2 u.r 
    cr ." TIMSK2: " TIMSK2 c@ 2 u.r 
    cr 
    decimal 
  ; 
 
\ OCR2A Reload for 10 ms@16 MHz clock & prescaler 256 
$9c constant RELOAD 
 
\ general setup TIMER 2 
: setupTimer ( -- ) 
    0 TCCR2B c!       \ Stop Timer2 
    RELOAD OCR2A c!   \ 
 
    0 counter ! 
    setupInt 
 
    %0010 TCCR2A c!   \ CTC Mode 
    %0111 TCCR2B c!   \ Prescaler = 1024 
    %0010 TIMSK2 c!   \ Timer2 CompareA Interrupt enable 
    dispRegs 
  ; 
         
 
\ Disable interrupt before removing the interrupt code 
: T2_COMPA_Dis 
    1 TIMSK2 mclr 
  ;