Multithreaded programs using event flags

This recipe will illustrate how to use CMSIS-RTOS to make an LED blink. We'll define two tasks or threads. The job of one task is to switch the LED ON, and the other one is to switch it OFF. The ON and OFF events are triggered by the tasks sending messages to each other. CMSIS-RTOS supports a number of intertask-communication strategies; our program uses event flags. We can illustrate our program using a state diagram, as follows:

Multithreaded programs using event flags

We'll call our first recipe, RTOS_Blinky_c8v0.

How to do it…

Create a new project (in a new folder) named RTOS_Binky and use the Run-Time Environment manager to select Board SupportLED (API) and CMSISKeil RTX as shown in the following screenshot. As usual, we can select Resolve to fix the warning messages. Note that this RTE is the same as the one that we introduced in Chapter 2, C Language Programming.

How to do it…
  1. Create a new file named RTXBlinky.c, and create a skeleton by adding boilerplate code for SystemClock_Config(), and so on. Add this file to the project.
  2. Select the Configuration Wizard tab for the RTX_Conf_CM.c file and configure the RTOS:
    How to do it…
  3. Open the RTXBlinky.c file and tasks A and B:
    #include "RTXBlinky.h"
    /*--------------------------------------------------
     *      Thread 1 'taskA': Switch LED ON
     *--------------------------------------------------*/
    void taskA (void const *argument) {
      for (;;) {
        /* wait for an event flag 0x0001 */
        osSignalWait(0x0001, osWaitForever);
        LED_On (LED_A);
        osDelay(500);
        /* set signal to taskB thread */
        osSignalSet(tid_taskB, 0x0001);
      }
    }
    
    /*--------------------------------------------------
     *      Thread 2 'taskB': Switch LED OFF
     *--------------------------------------------------*/
    void taskB (void const *argument) {
      for (;;) {
        /* wait for an event flag 0x0001 */
        osSignalWait(0x0001, osWaitForever);
        LED_Off (LED_A);
        osDelay(500);
        /* set signal to taskA thread */
        osSignalSet(tid_taskA, 0x0001);          
      }
    }
    
    /*--------------------------------------------------
     *      Main: Initialize and start RTX Kernel
     *--------------------------------------------------*/
    int main (void) {
    
      HAL_Init ();   /* Init Hardware Abstraction Layer */
      SystemClock_Config ();           /* Config Clocks */
      LED_Initialize();          /* Initialize the LEDs */
    
      tid_taskA = osThreadCreate(osThread(taskA), NULL);
      tid_taskB = osThreadCreate(osThread(taskB), NULL);
    
      /* set signal to taskA thread */
      osSignalSet(tid_taskA, 0x0001); 
    
      osDelay(osWaitForever);
      while(1);
    }
  4. Create the RTXBlinky.h header file and add the following code:
    #ifndef __RTX_BLINKY_H
    #define __RTX_BLINKY_H
    
    #include "stm32f4xx_hal.h"        /* STM32F4xx Defs */
    #include "Board_LED.h"
    #include "cmsis_os.h"
    
    #define LED_A   0
    
    /* Task ids */
    osThreadId tid_taskA;   
    osThreadId tid_taskB;
    
    /* Function Prototypes */
    void taskA (void const *argument);
    void taskB (void const *argument);
    
    /* Define Threads */
    osThreadDef(taskA, osPriorityNormal, 1, 0);
    osThreadDef(taskB, osPriorityNormal, 1, 0);
    
    #endif /* __RTX_BLINKY_H */
  5. Build, download, and run the program.

How it works…

In RTOS, the basic unit of execution is a task. A task is very similar to a C procedure, but it must contain an endless loop:

void taskA (void const *argument) {
  for (;;) {
    /* taskA statements */
            }
}

So, a task never terminates and thus runs forever in a similar manner to the way that a program does. We can think of tasks as small self-contained programs. While each task runs in an endless loop, the task itself may be started by other tasks and stopped by itself or other tasks.

An RTOS-based program is made up of a number of tasks, which are controlled by the RTOS scheduler. The scheduler is essentially a timer interrupter that allots a certain amount of execution time to each task. So, task 1 may run for (say) 100 ms, then be descheduled to allow task 2 to run for a similar period of time; task 2 will give way to task 3; and finally, control passes back to task 1. If we open the Configuration Wizard tab for the RTX_Conf_CM.c file and expand the System Configuration menu, then we'll see that we're allocating slices of runtime to each task in a round-robin fashion, and tasks are switched every 5 ms (refer to the following screenshot):

How it works…

It is useful to think of all tasks running simultaneously, and each of them performing a specific function. This allows each functional block to be coded and tested in isolation and then integrated into a fully running program that, in turn, imposes structure and aids debugging. When a task is created, it is allocated its own task ID. This is a variable, which acts as a handle for each task and is used when we want to manage the activity of the task. We declare two such variables, one for taskA and one for taskB:

osThreadId tid_taskA;
osThread tid_taskB;

When CMSIS-RTOS runs on ARM-Cortex it uses the SysTick timer within the processor to provide the RTOS time reference. Each time we switch running tasks, the RTOS saves the state of all the task variables to a task stack and stores the runtime information about a task in a Task Control Block that is referenced by the task ID. In addition to the task variables, the Task Control Block also contains information about the status of a task. Part of this information is its run state.

A task can be in one of four basic states: RUNNING, READY, WAITING, or INACTIVE. Only one task can be running at a time, so the other tasks must be either READY, WAITING, or INACTIVE. A task is placed in the WAITING state when its execution is suspended. This may occur when it is waiting for an event to occur, such as a signal from another task. CMSIS-RTOS provides a number of mechanisms to enable tasks to communicate with each other, such as events, semaphores, and messages.

There may be many tasks that are READY for execution and it is the job of the scheduler to switch between them. CMSIS-RTOS is preemptive; the active thread with the highest priority becomes the RUNNING thread, provided that it is not waiting for any event. The initial priority of a thread is defined with the osThreadDef() function but may be changed during execution using the osThreadSetPriority() function. The function prototype for osThreadSetPriority() in the cmsis_os.h file identifies the function parameters, as follows:

/// param      name         name of the thread fn.
/// param      priority     initial priority of the thread fn.
/// param      instances    number of possible thread instances.
/// param      stacksz      stack size (bytes) for the thread fn.

Our program uses two threads, one to switch an LED ON and another to switch it OFF, so we define them, as follows:

osThreadDef(taskA, osPriorityNormal, 1, 0);
osThreadDef(taskB, osPriorityNormal, 1, 0);

Tip

The osPriorityNormal argument is a pseudonym for the value, 0 (positive numbers indicate a higher priority, negative numbers a lower one).

Threads are created by the osThreadCreate() function, which returns a pointer to the Task Control Block. This function requires two arguments, a pointer to the thread definition and a pointer to its start argument. In our case, we write the following:

 tid_taskA = osThreadCreate(osThread(taskA), NULL);
 tid_taskB = osThreadCreate(osThread(taskB), NULL);

When each task is first created, it has sixteen event flags stored in the Task Control Block. It is possible to halt the execution of a task until a particular event flag or group of event flags are set by another task in the system. Our A and B tasks are very similar; the first statement in each is as follows:

osSignalWait(0x0001, osWaitForever);

This system call, suspends the execution of the task and places it into the WAIT_EVNT state. Any task can set the event flags of any other task in a system with the osSignalSet() CMSIS-RTOS function call. The main program statement is as follows:

osSignalSet(tid_taskA, 0x0001);

This statement sends a signal to taskA, which has been held by the following statement since this task was created:

osSignalWait(0x0001, osWaitForever); 

The remaining taskA statements are as follows:

LED_on (LED_A);
osDelay(500);
osSignalSet(tid_taskB, 0x0001);

These statements turn the LED ON, invoke a delay, and then signal taskB. As well as running our application code as tasks, CMSIS-RTOS also provides some timing services, which can be accessed through CMSIS-RTOS function calls; osDelay() exemplifies the most basic of them. As CMSIS-RTOS ticks have been set at 1 ms, the delay is set at 0.5 seconds.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.233.205