Timer Ramblings

Timer functions use the low overhead timeGetTime() function to retrieve the elapsed time since Windows was loaded. This value is used to compute both the elapsed time between two events and the time remaining until the next scheduled event.

The time is returned as a 32-bit integer. This means that it will roll over (i.e. go from the maximum unsigned long value of #FFFFFFFFLU to #00000000LU on the next tick) every 49.71 days - i.e. 4,294,967,296 (2^32) divided by 86,400,000 (24 hours/day * 60 minutes/hour * 60 seconds/minute * 1,000 milliseconds/second) is 49.71 days. This can cause problems with the timer math calculations.

Timer demonstrates that clock rollover doesn't have to effect timer operations. Elapsed time and time until an event can both be correctly calculated in spite of clock rollover. The period cannot exceed the maximum positive integer value since larger values would be interpreted as a sign change. You must update the expiration time every time you expire the timer so that the code knows when to expire the timer the next timer tick.

This code simulates a timer rollover. The function GetTime() replaces timeGetTime() and was set up to start at just before a rollover and to return the "time" incremented by one each time it is called. Obviously, you would not be printing out information at every tick at kilohertz frequencies, but this simulation allows you to see what is going on and to experiment with different periods.

Timer.c

/************************************************************************************
*                                                                                   *
*  Copyright (c) 2011 Cass Tyler, PO Box 1026, Willard, NM 87063, (505) 384-5342    *
*                                                                                   *
*  A 32-bit millisecond clock time will roll over every 49.7 days.                  *
*                                                                                   *
*  This program demonstrates that clock rollover doesn't have to effect timer       *
*  operations.  Elapsed time and time until an event can both be correctly          *
*  calculated in spite of clock rollover.  The period cannot exceed the maximum     *
*  positive integer value since larger values would be interpreted as a sign        *
*  change.                                                                          *
*                                                                                   *
*  Add the period to the next expiration time each time the timer expires to turn   *
*  the timer into a periodic timer that is not affected by rollover.                *
*                                                                                   *
*  The accumulated time, nAcc, tells you where you are within the period.  This can *
*  be important, when outputting a sawtooth ramp for instance.                      *
*                                                                                   *
*  Note the observation of the sign bit, ( nYet & 0x80000000 ), to determine that   *
*  the timer has expired.  This is like the countdown on a NASA launch.  This       *
*  method assures that "at least" the specified amount of time will have expired    *
*  and that we will recognize the timer expiration even if we overrun thet time     *
*  somewhat.  This implies that the timed code will be executed at the earliest     *
*  possible time after expiration.                                                  *
*                                                                                   *
*  An example of the output is as follows:                                          *
*                                                                                   *
*     F:\Projects\Try\Timer>try                                                     *
*     uExp = 0x00000007, uNow = 0xFFFFFFF9, nAcc =  1, nYet = -14                   *
*     uExp = 0x00000007, uNow = 0xFFFFFFFA, nAcc =  2, nYet = -13                   *
*     uExp = 0x00000007, uNow = 0xFFFFFFFB, nAcc =  3, nYet = -12                   *
*     uExp = 0x00000007, uNow = 0xFFFFFFFC, nAcc =  4, nYet = -11                   *
*     uExp = 0x00000007, uNow = 0xFFFFFFFD, nAcc =  5, nYet = -10                   *
*     uExp = 0x00000007, uNow = 0xFFFFFFFE, nAcc =  6, nYet =  -9                   *
*     uExp = 0x00000007, uNow = 0xFFFFFFFF, nAcc =  7, nYet =  -8                   *
*     uExp = 0x00000007, uNow = 0x00000000, nAcc =  8, nYet =  -7                   *
*     uExp = 0x00000007, uNow = 0x00000001, nAcc =  9, nYet =  -6                   *
*     uExp = 0x00000007, uNow = 0x00000002, nAcc = 10, nYet =  -5                   *
*     uExp = 0x00000007, uNow = 0x00000003, nAcc = 11, nYet =  -4                   *
*     uExp = 0x00000007, uNow = 0x00000004, nAcc = 12, nYet =  -3                   *
*     uExp = 0x00000007, uNow = 0x00000005, nAcc = 13, nYet =  -2                   *
*     uExp = 0x00000007, uNow = 0x00000006, nAcc = 14, nYet =  -1                   *
*     uExp = 0x00000007, uNow = 0x00000007, nAcc = 15, nYet =   0                   *
*     The Timer Expired!                                                            *
*                                                                                   *
*     F:\Projects\Try\Timer>                                                        *
*                                                                                   *
*  The program will pause after each timer expiration.  Use the space bar to        *
*  continue or any other key to exit.                                               *
*                                                                                   *
*  To compile it from the command line:  "CL Try.c"                                 *
*                                                                                   *
************************************************************************************/

#include <STDIO.H>
#include <CONIO.H>

unsigned long GetTime ( void )

   {
   static unsigned long uTimeNow = { 0xFFFFFFF8LU };

   return ( unsigned long ) uTimeNow++;

   }  // unsigned long GetTime ( void )

void main ( void )

   {
   unsigned long  uBeg; // The Beginning Time
   unsigned long  uPer; // The Period
   unsigned long  uExp; // The Expire Time
   unsigned long  uNow; // The Time Now
   long           nAcc; // The Accumulated Time
   long           nYet; // The Time Yet to Go

   // Now as a Timer

   uPer = 0x0000000FLU;
   uBeg = uNow = GetTime();
   uExp = ( unsigned long ) ( uNow + uPer );

   for (;;)
      {
      uNow = GetTime();

      nAcc = ( long ) ( uNow - uBeg );
      nYet = ( long ) ( uNow - uExp );

      printf ( "uExp = 0x%8.8X, uNow = 0x%8.8X, nAcc = %2hd, nYet = %3hd\n", uExp, uNow, nAcc, nYet );

      if ( nYet & 0x80000000 ) continue;  // nYet is Negative

      printf ( "The Timer Expired!\n" );

      uExp += uPer;  // Set the Next Periodic Expiration Time

      uBeg = uNow;   // Reset the Start of Timer Time

      if ( getch() != ' ' ) break;
      }

   }  // void main ( void )