Link here

An Introduction to Multitasking

How to write multitasking C-language programs and configure them to automatically start with each power-up.

This chapter provides an introduction to real time programming on the QCard, a Single Board Computer (SBC) based on the 68HC11 microcontroller. You’ll learn:

  • About the multitasking executive and how to use it;
  • How to configure your completed instrument control application to "autostart" each time the QCard Controller powers up or is reset.
 

Multitasking basics

Many instrumentation and automation applications can be logically conceived of in terms of a set of distinct "tasks" that cooperate to solve the problem at hand. For example, a program that manages a hand-held sensing instrument might have one task that acquires sensory data, another that performs calculations to process the data, and a third task that displays the results on a liquid crystal display.

Using the QCard Controller’s real time operating system (RTOS) with its built-in multitasking executive confers significant advantages when designing real-time systems. Breaking up a complex program into easily understood modular tasks speeds debugging, improves maintainability, and prevents source code modifications of one task from adversely affecting the required real-time performance of another task.

 

The task activation routine

In a multitasking environment, a task is an environment capable of running a program. After declaring (naming) a new task (which also allocates a 1 Kbyte task area), its environment is "built" by initializing its required stacks, buffers and pointers in the 1 Kbyte task area. Then the task is activated by associating it with an activation routine that performs a specified set of actions.

A typical task activation routine is the CalcForever() function in the GETSTART.C file. Its definition is straightforward:

_Q void CalcForever(void)
// this infinite loop function can be used as a task activation routine
{  DimAndInitFPArray(0.0,CIRCLE_ROWS,CIRCLE_COLUMNS,&circle_parameters);
   while(1)                      // infinite loop
   {  IncrementRadius();         // updates radius variable
      area = CalcArea(radius);   // updates area variable
      if(radius%10 == 0)         // on even multiples of 10...
      SaveCircleParameters();    // save data in FORTH_ARRAY
      Pause();                   // give other tasks a chance to run
   }
}

The first thing that this function does is to dimension and initialize the circle_parameters array. Then it enters an infinite loop that increments the radius variable, calculates the corresponding circular area and stores it in the area variable, and saves the radius and area in the circle_parameters array if the radius is an even multiple of 10. The function calls Pause() on every pass through the loop. Pause() is a multitasking function that instructs the multitasking executive to change to the next task (if any) in the round-robin task list. This enables cooperative multitasking, in which a task willingly lets other tasks run by executing Pause(). The other type of multitasking, also supported by the QCard Controller, is pre-emptive multitasking, in which an interrupt-driven timeslice clock forces a task switch on a periodic basis.

In summary, the CalcForever() function is an infinite loop that manages the calculation and storage of the radius and circular area. This function can be the activation routine for a stand-alone task running in a multitasking environment.

 

Declare, build and activate a task

The short section titled "Multitasking" in the GETSTART.C file demonstrates how easy it is to set up a task using the pre-defined macros. First we declare the new task as:

 TASK CalculationTask

The TASK typedef allocates a 1 Kbyte task structure named CalculationTask in the common RAM.

The function SetupTask() builds and activates the new task; its definition is:

void SetupTask()
{  NEXT_TASK = TASKBASE;                           // empty task loop before building
   BUILD_C_TASK(HEAP_START,HEAP_END,&CalculationTask);  // private heap
   ACTIVATE(CalcForever, &CalculationTask);        // define task’s activity
}

The first statement empties the round-robin task loop by setting the NEXT_TASK pointer in the task’s user area to point to the task’s own TASKBASE. The next statement invokes the BUILD_C_TASK() macro which expects starting and ending addresses for the task’s heap, and the address at which the task is located. We have defined the constants HEAP_START and HEAP_END to specify a task-private heap occupying 1 Kbyte on page 0. The task base address is simply &CalculationTask. BUILD_C_TASK() sets up all of the stacks, buffers and pointers required by the task.

The final statement in SetupTask() invokes the ACTIVATE() macro which expects a pointer to the activation function (which is CalcForever) and the TASKBASE address (which is &CalculationTask).

Multiple tasks can be declared, built and activated in the same way.

 

Putting a task asleep

A "sleeping" task remains in the round-robin task loop, but is not entered by the multitasking executive. The status of a task can be changed from AWAKE to ASLEEP and back again by simply storing the appropriate constant in the user_status variable in the task’s USER_AREA. The USER_AREA is a task-private structure initialized by BUILD_C_TASK() that contains the pointers that a task needs to operate; it is defined in the USER.H file in the \MOSAIC\FABIUS\INCLUDE\MOSAIC directory. The USER_AREA structure is the first element in the TASK structure.

The Nap() function in GETSTART.C is a simple function that puts the CalculationTask asleep:

  _Q void Nap(void) %%//%% put calculation task asleep
  { CalculationTask.USER_AREA.user_status = ASLEEP;
  }

This function simply stores the ASLEEP constant into the user_status variable in the CalculationTask’s USER_AREA structure. A similar function named Wakeup() stores the AWAKE constant into user_status to wake up the task. We’ll see how to use these functions in the next section.

 

The main function gets us going

The main() function is the highest level routine in the program. Its definition is:

 void main(void)
// Print starting area and radius, build and activate CalculationTask.
{  int unused_variable; // an example of how warnings are handled!
   InitVars();
   printf(“\nStarting condition:”);
   Announce();         // print starting values of radius and area
   SetupTask();        // build and activate the CalculationTask
}

As you recall, the declaration of the unused_variable was inserted to demonstrate how the Control-C IDE highlights the source code line associated with compiler errors and warnings. InitVars() performs a runtime initialization of the variables used by the program; this is very important, because compile-time initializations won’t ensure that variables are properly initialized after the program has run once, or after the processor is restarted.

After initializing the variables, main() announces the starting values of radius and area and then calls SetupTask() to build and activate the CalculationTask. To execute the program, simply type at your terminal:

 main↓

You’ll see the following message:

 Starting condition:
  The radius is 0; the circular area is 0.
  ok

The ok prompt lets you know that QED-Forth is ready to accept more commands. We have set up a two-task application: the default startup task (named the FORTH_TASK) is still running the QED-Forth interpreter, and the CalculationTask that we built is running the CalcForever() activation routine. At any time we can monitor the current values of radius and area by interactively calling the function:

 Announce( )↓

Remember to type a space after the ( character, and you may have to type slowly so that the multitasking program does not miss any of the incoming characters from the terminal. To view the contents of the circular buffer array named circle_parameters, type the command:

  PrintFPArray( circle_parameters)↓

To suspend the operation of the CalculationTask, type:

  Nap( )↓

Now notice that successive invocations of:

 Announce( )↓

all show the same values of the radius and area; this is because the CalculationTask is no longer updating them. To re-awaken the CalculationTask, simply type:

 Wakeup( )↓

To abort the multitasking program altogether and return to a single task running the QED-Forth monitor, you can perform a warm restart by typing:

 WARM↓

The QED-Forth startup message will be displayed.

Of course, if you want to run the program again, you can type main or any of the interactively callable function names at any time. Remember to type

 WARM↓

or

 COLD↓

before trying to download another program file; the QCard Controller can’t run multiple tasks and accept a download file at the same time. (Both WARM and COLD re-initialize the system, but COLD performs a more thorough initialization and causes QED-Forth to immediately forget the definitions of the C functions that were sent over in the .DLF download file).

 

Autostarting your application

You can configure QED-Forth to automatically execute a specified application program after every reset, restart, and ABORT. This makes it easy to design a production instrument based on the QCard; the instrument will automatically perform its required function when it is turned on or reset.

QED-Forth provides two functions named AUTOSTART and PRIORITY.AUTOSTART that allow you to specify a startup routine. Both write a pattern in memory that instructs QED-Forth to execute a user-specified program. AUTOSTART stores the pattern in EEPROM which is inside the 68HC11 processor chip, and PRIORITY.AUTOSTART stores the pattern near the top of page 4 which is typically in PROM in a final turnkeyed system. The EEPROM-based AUTOSTART function is convenient during program development and debugging, or in the development of one-of-a-kind systems. But because the startup pattern is stored in EEPROM inside the 68HC11, it is cannot be automatically transferred with the application program to a different board.

The PRIORITY.AUTOSTART routine should be used for PROM-based production systems. It installs the startup pattern in PROM, so simply reproducing the PROM and plugging it into any QCard turns that board into a turnkeyed instrument controller. In other words, the startup instructions are stored in the same PROM as the application program itself.

Let’s assume that you want to want to run the main routine every time you turn on, reset, or restart the QCard Controller. The following command:

 CFA.FOR main AUTOSTART

leaves the extended code field address (cfa) of main on the stack. AUTOSTART then writes a pattern into EEPROM comprising a 16-bit flag (equal to 1357H) followed by the 32-bit extended cfa of the specified startup program. All subsequent resets and restarts will call the specified application program after QED-Forth initializes the system.

To specify the startup vector so that it can eventually reside in PROM, we would execute a different command:

 CFA.FOR main PRIORITY.AUTOSTART

PRIORITY.AUTOSTART writes a pattern starting at 7FFAH on page 4 comprising a 16-bit flag (equal to 1357H) followed by the 32-bit extended cfa of the specified startup program. All subsequent resets and restarts will call the specified application program after QED-Forth initializes the system.

The priority autostart and autostart locations are checked each time QED-Forth executes ABORT, which is called after every reset, COLD or WARM restart, or error. ABORT first checks the priority autostart location at 7FFAH\4, and if 1357 is stored there it executes the program whose xcfa is stored in the four bytes starting at 7FFCH\4. If the priority autostart pattern is not present, or if the specified priority startup program finishes executing and returns, ABORT then checks the autostart pattern at AE00H in common memory. If 1357 is stored there it executes the program whose 32-bit xcfa is stored in the four bytes starting at AE02H.

To remove the autostart pattern or patterns, execute:

 NO.AUTOSTART

This command clears the priority startup pattern at 7FFAH\4 and the startup pattern at AE00H.

 

Summary

Now you’ve worked through the GETSTART.C program in detail. You know how to compile, download and execute programs, perform simple floating point calculations, print formatted strings and numbers to the terminal, dimension and access FORTH_ARRAYs in paged memory, define a multitasking application with an interactive terminal interface, and autostart an application. That’s pretty good considering that this is your first C program on the QCard Controller!

 
This page is about: How to Write Multitasking C-language Programs, how to Configure Application Programs to Automatically Start with Each Power-up – How to write multitasking C-language programs and configure them to automatically start with each power-up. real time operating system, RTOS, multitasking executive, autostart
 
 
Navigation