From: doctor@kronos.arc.nasa.gov (Terry Fong)
Subject: summary--how to implement a controller
Summary: followup to "how to implement a controller"
Keywords: controller, periodic tasks
Organization: NASA/ARC Information Sciences Division
Date: Sat, 5 Sep 1992 01:08:38 GMT

Hi all,

Last week I asked for suggestions on how to implement a digital
controller using VxWorks. The problem is essentially to determine what
is the most efficient method for periodically executing a task (or a
section of code). Depending on how fast you want to run (i.e., how
short a period is involved) and how much latency (jitter) you can tolerate,
there are three basic approaches:

1) LOW FREQUENCY

If the controller runs at less than about 60 Hz, and latency on the
order of a few milliseconds is not a problem, the easiest method is to
run a high priority task that uses taskDelay() to wait between cycles.
The caveats are that taskDelay() runs at system clock granularity and
that the task needs to complete in less than a clock tick (or "time"
will be lost). It is possible to run faster than 60 Hz if you change
the system clock speed (though this will mess up other timing
functions), or if you use an auxiliary clock (if your board has one).

2) MEDIUM - HIGH FREQUENCIES 

For controllers that run between 30 and 10,000 Hz, a better method
than using taskDelay() is to setup a controller task that synchs on a
binary semaphore. The semaphore is given at the required frequency by
an ISR tied to either the system or auxiliary clock. This is the
recommended method for task synchronization by Wind River Systems, and
is covered in the VxWorks programmer's guide in the synchronization
using semaphores section. Sample code is: 

    -----------------------------------------------------------------------

    SEM_ID SynchSemaphore;

    /*
     * This routine runs at interrupt level.  The semaphore lets the loop in
     * Controller execute one time.
     */
    int ControllerISR (void)
    {
	semGive(SynchSemaphore);
	return(0); 
    }

    /*
     * Here is the controller. Spawn this task to start the controller
     */
    STATUS Controller (void)
    {
	SynchSemaphore = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);

	/* Connect the timer to the aux. clock interrupt */
	if (sysAuxClkConnect((FUNCPTR) ControllerISR, 0) == OK) {
	    sysAuxClkRateSet (100);  /* trigger interrupt at 100 Hz */
	    sysAuxClkEnable();
	    rebootHookAdd((FUNCPTR) sysAuxClkDisable);
	}
	else {
	    printErr ("Controller: the aux clock is not available\n");
	    return ERROR;
	}

	while (TRUE) {
	    /* wait for the synch */
	    semTake(SynchSemaphore, WAIT_FOREVER);

	    /* do control function here ... */
	}

	return OK;	/* NOT REACHED */
    }

    -----------------------------------------------------------------------

3) HIGH FREQUENCY / LOW LATENCY

If the controller needs to run *really* fast (greater than 10 KHz) or
if low latency (jitter) is critical, then as much processing as
possible should be done at the interrupt level (since this runs at the
highest priority with the lowest latency). One method is to do
everything in the ISR. This has problems, however, if other concurrent tasks
can't handle interrupts that long. Another approach is to do things
similiar to the previous method (i.e., synch a task off of a
semaphore), but to try to do some/all of the calcuations in the ISR
and to disable the system clock (since the kernel steals 100-300 usecs
servicing the clock interrupt). 


Many thanks to Fred Roeber, Georg Feil, Stan Schneider, Andrew
Mitchell, and Clark Briggs!!!


-Terry
-- 
_______________________________________________________________________________
 "You do not understand anything     | Terry Fong <terry@ptolemy.arc.nasa.gov>
  until you understand it in more    | NASA Ames, M/S 269-3, Moffett Field, CA
  than one way..." -- Marvin Minsky  | (415) 604-6063, FTS-464-6063
Newsgroups: comp.os.vxworks