Feature

An event scheduler for control applications

Event schedulers provide multiple relative and absolute timer services for real-time system applications. They are a valuable part of many computer control systems.

KV Ramakrishnan, DRDO -- EDN, 6/6/1996

EDN Access -- 6.06.96 An event scheduler for control applications
Event schedulers for real-time systems give support for control applications to detect an event. A scheduler manages time dependencies of various tasks, including event detection and polling. You can port it to any microprocessor or microcontroller. The minimum hardware requirement is a programmable timer that periodically interrupts the CPU.

An event scheduler is different from a monolithic control loop. A monolithic control loop is split into a set of logical and autonomous processes. The scheduler uses a split architecture. You submit events at one time, and they are executed in the background in a cyclical manner at the specified time. This process allows the foreground task to handle user interaction apart from external hardware-interrupt handling. An event scheduler makes real-time control-system design easy and manageable. It offers a host of benefits in feasibility and planning, scheduling and breaking tasks, testing and tuning the system, and, finally, easy system maintenance. A typical application for an event scheduler is time control; after all, real-time systems manage the precious and essential resource of time.

Systems that use event schedulers include nuclear reactors; chemical plants; and power-distribution, radar, and sonar systems. You use real-time embedded systems for these control applications. An event scheduler can perform the following tasks:

  • Actions that are cyclic in nature, such as ring generation;
  • Repeating sequences of events, such as waveform generation, in sequential switching on and off;
  • Periodic polling, such as keyboard scanning and watchdog functions;
  • An event that causes an action;
  • Precision delay;
  • Validation of pulse width;
  • Waiting for an event of indefinite or definite time;
  • Turning the status of a flag or variable on or off;
  • Checking the health of a communication link and marking its status, such as loop-back checks.

Figure 1 shows the data structure used to implement the event scheduler. Members in the data structure include:

  • PoolSize: An integer quantity that indicates the maximum number of timer pools available for reservation.
  • CurrentToken: A character used for dynamic token generation. You generate the tokens dynamically to avoid deletion of nonexistent action tasks.
  • AssignedToken: A word representing the dynamically generated token assigned to and reserving a pool. When free, or vacant, the token acquires the value FFFFh. You use the AssignedToken for three purposes: to assign a dynamic token, to see that this particular pool is free in the pool-reservation algorithm, and to increment or decrement CounterValue for each timer count. All AssignedTokens acquire the value FFFFh during initialization.
  • CounterValue: An incremented or decremented word, dependent on whether you assign the pool to a timer or timed-action-task execution.
  • ActionTask: A pointer to ActionTask that you execute for a timed-action task. SubmitTask supplies this pointer.


Timer handler

Almost all contemporary processors provide at least one hardware timer. However, many real-time environments require multiple relative- and multiple absolute-timer services. The hardware may not be able to handle multiple timers and the associated interrupts assigned to them. For accurate and relative time computation, a single timer is best. It is, therefore, necessary to handle control at a central point with the help of a single timer. This timer has "n-timer" pools for independent counting. These pools give the timer the capability of concurrently handling several timer services. All tasks receive any number of required timers. You need to program the timer to periodically issue interrupts to the processor.

The dispatcher, or timer handler, always becomes active at every timer-count event detected by the CPU. Once control is given to the timer, the main job of the task dispatcher is to select all submitted tasks that initiate transition of the task from a ready to an active (run) state. The program runs in multiprogramming mode, which means that several processes exist. All submitted tasks are in ready-to-run mode, and, depending upon the countdown value, the task gains control and executes. After execution, this task goes into a nonexistent state. Figure 2 shows the different states of the submitted task. The sequence of operations done by the task dispatcher is as follows:

  • Decrement the time unit by one for all the ready, or submitted, tasks. Increment for timer service;
  • Check to see whether any time unit is zero. If so, the corresponding action task becomes active. Its entry is made NULL by freeing this action pool for further task registration;
  • Repeat the previous step until all ready tasks are checked;
  • Pass control back to the point where it was interrupted.
Dispatcher algorithm
 Dispatching:Procedure interrupt       For all reserved timer pool           Do                   If it is Task Pool Then                   Do                         decrement CounterValue                         If CounterValue zero Then                         Do                                Run ActionTask                                Free Timer Pool                         End                   End                   Else increment CounterValue  //timer service           End   End    

You perform the following initializations before the foreground task takes control.

  • Initialize the timer to generate a periodic hardware interrupt to the processor;
  • Set the interrupt vector for the dispatch function;
  • Give all AssignedToken FFFFh to indicate that all timer pools are free;
  • Submit all periodic tasks.


RequestDelay

You cannot realize precision time delays with counter loops. The RequestDelay system call provides a precision time delay for the requested amount of timer counts:

RequestDelay algorithm

 RequestDelay:procedure(WaitTime)            Token =             StartTimer              While  true            if ElapsedTime(Token)                         >  = WaitTime then                          Do  //  Requested delay elapsed                                   StopTimer(Token)  // free timer                                   return                          End          End   End    


Multiple timer services

Multiple timer services support StartTimer, StopTimer, RestartTimer, and ElapsedTime system calls. The StartTimer call allows the scheduler to allocate a timer by giving it a token for further manipulations. The timer counter allocates resets and increments for each subsequent timer count. You base other operations with this timer on the token returned by the StartTimer call. An exception is returned if no timer counter is available.

StartTimer algorithm

 StartTimer:procedure           Search For the Free Timer Pool           If Not Found return FFFFh   //fail           Initialize CounterValue to Zero           Compute AssignedToken  //for timers           return AssignedToken    End    

You use the StopTimer call not only to stop the timer, but also to free the timer pool and return the current counter value of the specified timer by returning the StartTimer token.

StopTimer algorithm

 StopTimer:procedure(Token)           Find Timer Pool for the given Token           If Not Found return FFFFh   //fail           Free Timer Pool           return CounterValue   End    

The RestartTimer results in the scheduler's resetting the specified counter, using the token returned by the Start Timer.

RestartTimer algorithm

 RestartTimer:procedure(Token)            Find Timer Pool for the given Token            If Not Found return FFFFh   //fail            Initialize CounterValue to Zero   End    

ElapsedTime returns the current counter value of the specified timer by giving the token returned by StartTimer.

ElapsedTime algorithm

 ElapsedTime:procedure(Token)           Find Timer Pool for the given Token           If Not Found return FFFFh  //fail           return CounterValue   End    


SubmitTask

The scheduler allocates a timer counter and an action-task pointer that you execute at the end of the requested time countdown. You can pass two parameters to this call: the action task and the time unit, or wait time. SubmitTask returns a token used primarily to abruptly withdraw the action task, if necessary, before it gets executed. After you execute this action task, the timer pool is free for further registration. The action task may be recursive in time for periodic or cyclic execution of tasks. For example, you can generate a ringing sound until it is cleared by the operator. Figure 3 shows the GenRing waveform for switching the ringer on and off. The first call to GenRing turns the ringer on and submits a MakeOff task. You execute this task after OnTime, which turns off the ringer. GenRing continues to submit itself while the Clear flag is false, and the process continues until Clear becomes true.

 GenRing:Procedure             MakeRing ON             SubmitTask(MakeOff,OnTime)   //First Submit call             If Not Clear Submit(GenRing,OffTime)  //Second Submit call   End GenRing     MakeOff:procedure                MakeRing OFF   End MakeOff     SubmitTask algorithm  SubmitTask:procedure(CallBackFunction,WaitTime)            Search For the Free Timer Pool       If Not Found return FFFFh   //fail       Initialize CounterValue to WaitTime       Initialize ActionTask to CallBackFunction       Compute AssignedToken  //for tasks       return AssignedToken    End    


WithdrawTask

You use the WithdrawTask system call to withdraw a submitted task using SubmitTask. You use the token returned by SubmitTask as a parameter to the WithdrawTask call to identify the parameter. Because the token is unique, you can't withdraw a task that's already executed. When using this mechanism, you cannot accidentally delete a new action task submitted by other tasks and that is present in this pool.

WithdrawTask algorithm

 WithdrawTask:procedure(Token)           Find Timer Pool for the given Token           If Not Found return FFFFh   //fail           Initialize CounterValue to FFFFh  //free timer pool           Initialize ActionTask to NULL           return True   End    


StopWatch

You can use the StopWatch system call to compute the amount of time that elapses until an event makes the given flag true. The call expects three parameters:

  • A pointer to the event flag that becomes true when the event occurs;
  • A function pointer, or callback function, executed by StopWatch until the flag is true or until you reach the specified maximum wait time;
  • A word that indicates the maximum wait time. This word must be FFFFh for an indefinite wait.

    The call returns the following values:

  • The elapsed time, if it occurs before the MaxWaitTime specified by the user;
  • FFFFh, if the elapsed time is greaterÊthan MaxWaitTime. StopWatch algorithm
     StopWatch:procedure(StopFlagPtr, CallbackFunction, MaxWaitTime)      StopFlag based StopFlagPtr           Token =           StartTimer           While            true           Call                       CallbackFunction  if           StopFlag then                 return StopTimer(Token)                 if MaxWaitTime <> Indefinitely                  then   do if                 ElapsedTime(Token)                       >  = WaitTime then                       Do  // condition not satisfied                              StopTimer(Token)  //free timer                              return(0ffffh)                       End                End           End   End StopWatch    


    Examples

    The following examples can coexist in the same application apart from any other monitored event you want to add. These examples offer the possibility of exploring the use of an event scheduler for designing small, real-time, embedded-control systems. Such a system has:

    • A few tasks : A "Do Forever" program for: Waiting for an Event: * CheckTxEmptyThenTx Continuous Polling: * ReadKb Continuous Display update: * Clock
    • Waveform generation: * OutChar.
    • Event-activated programs: RingGen.
    • Delays: # RequestDelay.
    • Delay measurement: # StopWatch.
    • A few timers for other applications: Time-out, power saver, etc.
    • Mailbox and semaphores: By declaring global variable-task synchronization, you can implement communication and message passing without using sophisticated exchange mechanisms.

    You can directly implement almost all activities using public functions in the EventScheduler program. You can make a task Do Forever by unconditional submission of the same task, by itself, for the specified WaitTime. By doing this procedure at the end of the task's execution, the task is registered again, thereby achieving the Do Forever effect (Listings 1 and 2). The source files for these listings are well-commented (see pg 160). The "*" indicates functions included in the Eventest.plm source file, and "#" indicates public functions in the Event.plm source file.

    StopWatch example

    You can use StopWatch to measure pulse width. The function has three parameters. The first is a pointer to a flag. StopWatch measures the time it takes for the flag to become true. An interrupt program or a call-back function can make the flag true. The second parameter is a pointer to the above callback function. If you use interrupt for making the flag true, then this pointer becomes a null-function pointer. The third specifies maximum wait time. This example uses a call-back function, "CheckStatus," and the previously declared variables, "Status" and "Etime."

    Pseudo code

        CheckStatus:procedure                 /*                This is a callback function called by StopWatch. This        function polls the event and makes the Status flag true.              */                     Status  = input(address)            end CheckStatus;      // An event occurred. Measure the time      Etime = StopWatch(@Status,@CheckStatus,150)      if(Etime >150) then call write(@('Pulse Width High$'))      else if(Etime <15) then call write(@('Pulse Width Small$')) else // valid pulse, Pulse width is in Etime. // Use for further processing. ... ... 


    Waveform-generation example

    This example implements a serial data-transfer machine by converting a byte into asynchronous RS-232C serial format. The protocol assumes no parity, a single stop bit, and 8-bit data at half the rate of the timer counts. The program first makes the Empty flag false, indicating that the transmission is in progress. It then sends 10 bits (1 start bit+8 bits of data+1 stop bit) by submitting 10 tasks. The waveform program gets controls every Timer Count3BaudConstant32 clock intervals. The program uses four local functions to perform serial transmission.

    • TxEmpty: makes Empty true after transmitting 10 bits;
    • MakeOn: makes the assigned RS-232C TX pin high;
    • MakeOff: makes the assigned RS-232C TX pin low;
    • StartTxmtn: sends 10 bits and sets empty true.

    Figure 4 shows the waveforms generated when sending the hex character 93 through the machine.

    How a system copes with I/O depends very much on the nature of the task and the capabilities built into the system's design. Most system designs have to make a compromise among the number of submitted tasks, the time it takes to execute a task, and the maximum number of tasks that are ready to execute at a given timer count.

    Computation of worst-case task runtime is very important. Before selecting a slot time (timer-programming value), you must be sure that there is sufficient computing power to execute the critical workload in that slot time. If you select the incorrect slot time, the response time may exceed certain deadlines, and the controlled process may fail. You must optimize the slot time for both response time and worst-case task runtime to meet current system response. A long computational action task affects the entire system's response. Of course, you can partition a long task into a series of short ones or shift some processes to the foreground task to alleviate this problem.


    Author's biography

    KV Ramakrishnan is a scientist at the microprocessor division of DRDO, Cochin, India. He designs and develops real-time systems for naval applications, including ships, underwater communications, sound-velocity recorders, and health-monitoring systems. Ramakrishnan holds bachelor's degrees in technology and EE and an MS in technology, instrumentation, and control from the Regional Engineering College, Calicut-Kerala.



  • ADVERTISEMENT

    ADVERTISEMENT

    Feedback Loop


    Post a CommentPost a Comment

    There are no comments posted for this article.

    Related Content

     

    By This Author

    There are no additional articles written by this author.


    ADVERTISEMENT

    Knowledge Center



    Technology Quick Links

    EDN Marketplace


    ©1997-2008 Reed Business Information, a division of Reed Elsevier Inc. All rights reserved.
    Use of this Web site is subject to its Terms of Use | Privacy Policy

    Please visit these other Reed Business sites

    ADVERTISEMENT
    You will be redirected to your destination in few seconds.