DPRG Events Shoptalk

Website design and hosting by NCC

Theory of Operation for SR04 Timing Utilities

by David P. Anderson
09 March 1999

II. Timing Utilities
In section I. the coding of a 1 kHz free running clock, "sysclock", is described. This section adds a few simple utilities that allow this clock to be used to time various robot sensor and output functions.

A. Reset sysclock
void reset_system_clock()
     sysclock = 0;

This function is used to reset the system clock to zero. It is pretty trivial in the HC6811/icc11 environment, but is worth coding as a separate subroutine to ease porting to other hardware/software platforms. In practice this reset is only called immediately after hardware reset during the SR04 initialization sequence.

B. Read sysclock
void mseconds(unsigned long *t)
     *t = sysclock;

This function is likewise trivial, but eases porting. Calling this routine with a pointer to a long sets that long equal to the number of milliseconds that have elapsed since the system clock was reset.

Note that this could also be coded as a function that returns a long. For the HC6811 architecture with it's 16 bit address space, it is more convenient to pass in a 16 bit pointer than to pass back a 32 bit value. This allows for fewer stack maniplulations using the 8 bit hardware stack.

C. Timer done
int timer_done(unsigned long *t)
     return (sysclock >= *t);

The timer_done() function returns a boolean that indicates whether the sysclock has incremented past a particular previously set timer variable. This routine is the basis for all other timing functions. As long as the system clock is less than the timer variable t, the function returns a FALSE, (define as 0). When the system clock is greater than or equal to the timer variable t, the function returns a TRUE (define as NOT 0).

Individual routines that need to use the timing function define their own timer variable(s) which can be set to the system clock plus some delay. Conditional execution can then be subject to a flag returned by timer_done().

D. Delay plus the system clock
void plus_sysclock(unsigned long *t, unsigned int delay)
     *t = sysclock + (unsigned long)delay;

This function provides a quick way for a routine to set a timer delay as an offset to the current system clock. As in the other functions, the timer is passed in as a 16 bit pointer to a long. Adjust this for your hardware. To speed execution, the delay is constrained to 16 bits. In practice this is not much of a constraint, as most timing delays are considerably less than one minute (65,536 ticks). Indeed, most are less than one second.

E. Simple Example
Here is a simple example showing one way in which the timing utilities can be used. In this case two leds are assumed to be attached to the hardware and memory mapped to a location named OUT_PORT, bits 0x1, and 0x2. The following code flashes one led twice per second, and the other led five times per second.

void flash()
     unsigned long t1, t2;                /* define two local timers */

     mseconds(&t1);                       /* set the timers = now */

     while (1) {                          /* endless loop */

          if (timer_done(&t1)) {
               OUT_PORT ^= 0x1;           /* flash led #1 on/off */
               plus_sysclock(&t1,500);    /* wait 500 msecs */

          if (timer_done(&t2)) {
               OUT_PORT ^= 0x2;           /* flash led #2 on/off */
               plus_sysclock(&t2,200);    /* wait 200 msecs */

In this example, both timers will trigger on the first pass through the loop, turning their associated leds on. Timer t1 is then set to the system clock plus 500 msecs, and timer t2 is set to the system clock plus 200 msecs. The loop will then execute many times (many thousands, depending on your processor speed) and each time through the loop the timers will be tested and return a false.

After 200 msecs, the t2 timer will expire, and the timer_done(&t2) call will return a TRUE. It's led will be toggled off by the Exclusive OR statement, and the t2 timer reset for another 200 msecs. 200 msecs later the t2 timer will expire again and it's led toggled back on. 100 msecs after that the t1 timer will expire for the first time and its led will toggle, and so forth.

It becomes apparent that most of the CPU execution here is "idle" time, that is, waiting for a timer to expire. During this time other routines have a chance to run and this can be exploited for multi-tasking, more on this to come. For this reason it is helpful to insure that the timer_done() routine runs as fast as possible. The SR04 version is actually coded in assembly language and hand tweaked for best possible execution speed.

F. Extended Application
The actual SR04 robot software is of course considerably more involved than the above simple example. However, the principle of conditional execution based on local timing variables applies thoughout. In general the sensor and output functions are coded as Finite State Machine (FSM) routines which have been Augmented with a 32 bit timer (AFSM). This is M.I.T.'s Rodney Brook's terminology.

Each AFSM routine has its own STATE variable, to tell it what inputs to look for and what actions to take, and its own TIMER variable, to tell the state machine when to execute and when to defer. These are each called in turn from a single main loop, which runs endlessly as in the example above. SR04's main loop looks something like this:

void sensor_loop()

     while (1) {

Most of these tasks run with delays on the order of 50 to 250 ticks, so they execute about 4 to 20 times per second. The sensor tasks set global flags and arguments based on their states and inputs. It is the job of the arbitration task to determine their priority and pass the appropriate commands on to the motor and servo output subsystems. This is the basis of the "subsumption" architecture.

Home | I. System Timer | II. Timer Utilities
III. Multi-tasking System - Coming Soon!
09 Mar 1999, Dallas, TX, David P. Anderson

Copyright © 1984 - 2016 Dallas Personal Robotics Group. All rights reserved.