CMSIS-RTOS RTX  Version 4.78
CMSIS-RTOS RTX: Real-Time Operating System for Cortex-M processor-based devices
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
CMSIS-RTOS RTX Tutorial

This tutorial gives you an easy to follow step-by-step introduction into the basic concepts of a real-time operating system. It is based on CMSIS-RTOS RTX and tries to be generic so that it can be used with any underlying Cortex-M based hardware. The tutorial is divided into the following sections:

  • RTOS Blinky : Provides an easy start for beginners. At the end of this tutorial, an LED will flash on your development board.
  • Idle Demon : Shows how to use the Idle task to manage the runtime and energy consumption of your program.
  • Signals : Demonstrates how to use signals for inter-thread communication.
  • Supervisor Calls (SVC) : Gives an example of how to integrate ISRs with an RTOS.
  • Semaphores : Explains how to manage and protect access to shared resources.
  • Mutexes : Shows how to synchronize the execution of threads using mutexes.
  • Messages : Demonstrates how to pass messages from one thread to another.
  • Mailbox : Explains how to send mails from one thread to another.

RTOS Blinky

Start by opening uVision. Select Project and then New uVision Project.... In the window that opens, press New Folder and name the new folder RTOS. Click Open. Then, enter in the File name box RTOS and click on Save.

rtosTut_CreateNewProject.png
Create new project dialog

A new window opens that asks you to select your target device. In this tutorial, the LPC1857 will be used, but any supported device will be ok (please do not forget to use Pack Installer to install support for your target device before creating a new project).

Enter LPC1857 in the Search box, click on the device and click OK:

rtosTut_SelectDevice.png
Select your target device

The Manage Run-Time Environment window opens that allows us to select the required software components for the project. Select the following components:

  • Board Support (Variant MCB1800):LED (API):LED
  • CMSIS:RTOS (API):Keil RTX

Then, press the Resolve button and then OK.

rtosTut_ManageRTE.png
Select required software components
Note
If you development board does not have a Board Support entry, simply create two functions (LED_On/LED_Off) so that the rest of the code of this tutorial will run on your hardware. Refer to the Board Support documentation for more information.

The Project window will show all software components that are added to the project:

rtosTut_ProjWindow.png
Software components in the Project window

The green diamonds are the software component packages. The files with yellow keys are linked from the software pack and write protected. The remaining files are configuration files:

  • startup_lpc1800xx.s for stack and heap configuration
  • RTE_Device.h to configure the device's peripheral interfaces
  • RTX_Conf_CM.c for configuring CMSIS-RTOS RTX

The Keil RTX software pack provides various user code templates. We add the template for the main function. Right-click on Source Group 1 and select Add New Item to Group ‘Source Group 1’....

rtosTut_AddNewItem.png
Add New Item to Group

Click on User Code Template and select CMSIS-RTOS ‘main’ function. Press Add. This adds the file main.c to Source Group 1.

rtosTut_AddNewItemUCT.png
Add user code template 'CMSIS-RTOS main function'

Add a second user code template: select CMSIS-RTOS Thread and press Add. Add a third user code template by selecting CMSIS-RTOS Thread again. Name it Thread_1.c before pressing the Add button.

rtosTut_AddNewItemUCTThread_1.png
Add user code template 'CMSIS-RTOS Thread'

Source Group 1 should look like this:

rtosTut_SourceGroup1.png
Source Group 1 after adding the three templates

Adapt Thread_1.c

The file Thread_1.c should be open. Change the code as follows:

void Thread_1 (void const *argument);
osThreadId tid_Thread_1;
osThreadDef (Thread_1, osPriorityNormal, 1, 0);
int Init_Thread_1 (void) {
tid_Thread_1 = osThreadCreate (osThread(Thread_1), NULL);
if(!tid_Thread_1) return(-1);
return(0);
}
void Thread_1 (void const *argument) {
while (1) {
; // Insert thread code here...
}
}
Note
  • Change the object names to Thread_1 as shown above. This creates two threads: Thread and Thread_1
  • Warning: in Init_Thread_1 (void) make sure to change osThread(Thread_1) as well.

Adapt main.c

Click on the main.c tab and change the code as follows:

#define osObjectsPublic
#include "osObjects.h"
extern int Init_Thread (void);
extern int Init_Thread_1 (void);
/* main: initialize and start the system */
int main (void) {
// initialize peripherals here
// create 'thread' functions that start executing,
Init_Thread();
Init_Thread_1();
// example: tid_name = osThreadCreate (osThread(name), NULL);
}

Adapt Thread.c

Click on the Thread.c tab and change the code as follows:

#include <cmsis_os.h>
#include "Board_LED.h"
:
void Thread (void const *argument) {
LED_Initialize();
while (1) {
LED_On(0);
osDelay(1000);
LED_Off(0);
osDelay(1000);
}
}
Note
  • Add the header file #include “Board_LED.h”
  • In the thread code add the LED_initilize() function
  • In the while loop add the code to flash the user LED
  • osDelay() is a CMSIS-RTOS function which provides a delay in milliseconds
  • Warning: If you forget to add LED_Initilize(), the code will build but the LEDs will not flash

Adapt Thread_1.c (again)

Click on the Thread_1.c tab and do the same as for Thread.c. Change the LED number to 1:

#include <cmsis_os.h>
#include "Board_LED.h"
:
void Thread_1 (void const *argument) {
LED_Initialize();
while (1) {
LED_On(1);
osDelay(1000);
LED_Off(1);
osDelay(1000);
}
}

Configure CMSIS-RTOS RTX

Double-click on RTX_Conf_CM.c and select the Configuration Wizard. Press Expand All.

rtosTut_RTX_Conf_CM.png
Configuration of CMSIS-RTOS RTX

Set the RTOS Kernel Timer input clock frequency [Hz] to 180000000 as this is the core clock frequency of our target device. Not setting this correctly, will result in a wrong LED flashing interval.

Save and build the program

Select File and then Save All (you might see an info box that the project is saved in the new .uvprojx format). Select Project and then Build target or press F7 to build the complete project. It should build without any errors or warnings:

rtosTut_BuildOutput.png
Build output

Configure the debugger

Select Project and then Options for Target 'Target 1'... or press ALT+F7 to set the options for your target.

Debugging

  • Press CTRL+F5 to start a Debug session.
  • Run the code (F5).

Idle Demon

If no thread is running, CMSIS-RTOS RTX will enter an idle thread. The default idle thread is stored in RTX_Conf_CM.c. It is called os_idle_demon. Typically, power management code can be added here:

/*--------------------------- os_idle_demon ---------------------------------*/
void os_idle_demon (void) {
/* The idle demon is a system thread, running when no other thread is */
/* ready to run. */
for (;;) {
/* HERE: include optional user code to be executed when no thread runs.*/
}
}

The idle demon tutorial runs in simulator mode so that no actual hardware is required. Install the Keil::STM32F1xx_DFP containing the STM32F103RB device. Setup the project:

  1. Create a project called Idle and select the STM32F103RB as the target device.
  2. Select the CMSIS:RTOS (API):Keil RTX software component and resolve any validation problems.
  3. Add the CMSIS-RTOS 'main' function user code template to the project.
  4. Add a C source file called led.c to the project.
  5. Add a header file called led.h to the project.

Adapt led.h

Click on the led.h tab and add these lines of code:

extern void LED_Init (void);
extern void LED_On(uint32_t output);
extern void LED_Off(uint32_t output);

Adapt led.c

Click on the led.c tab and add the following:

#include "stm32f10x.h"
void LED_Init (void) {
GPIOB->CRH = 0x33333333;
}
void LED_On(uint32_t output) {
GPIOB->ODR |= output << 8;
}
void LED_Off(uint32_t output) {
GPIOB->ODR &= ~(output << 8);
}

Adapt main.c

Click on the main.c tab and edit the code as follows:

/*----------------------------------------------------------------------------
CMSIS-RTOS 'main' function template
*---------------------------------------------------------------------------*/
#define osObjectsPublic // define objects in main module
#include "osObjects.h" // RTOS object definitions
#include "stm32f10x.h" // Device header
#include "led.h"
/*----------------------------------------------------------------------------
Thread 1 'led1': toggles the LED 1
*---------------------------------------------------------------------------*/
void led1 (void const *argument)
{
for (;;)
{
LED_On(1); // Toggle LED1
osDelay(500);
LED_Off(1);
osDelay(500);
}
}
/*----------------------------------------------------------------------------
Thread 2 'led2': toggles the LED 2
*---------------------------------------------------------------------------*/
void led2 (void const *argument)
{
for (;;)
{
LED_On(2); // Toggle LED2
osDelay(100);
LED_Off(2);
osDelay(100);
}
}
osThreadId main_ID;
/*
main: initialize and start the system
*/
int main (void) {
osKernelInitialize (); // initialize CMSIS-RTOS
// initialize peripherals here
LED_Init();
// create 'thread' functions that start executing,
// example: tid_name = osThreadCreate (osThread(name), NULL);
osThreadCreate(osThread(led1), NULL);
osThreadCreate(osThread(led2), NULL);
osKernelStart (); // start thread execution
}

Configure CMSIS-RTOS RTX

Double-click on RTX_Conf_CM.c and select the Configuration Wizard. Press Expand All. Set the RTOS Kernel Timer input clock frequency [Hz] to 16000000.

Save and build the program

Select File and then Save All (you might see an info box that the project is saved in the new .uvprojx format). Select Project and then Build target or press F7 to build the complete project. It should build without any errors or warnings.

Configure the target

Select Project and then Options for Target 'Target 1'... or press ALT+F7 to set the options for your target. Select the Debug tab and select Use Simulator. In the Dialog DLL box enter DARMSTM.DLL and in the Parameter box enter -pSTM32F103RB:

rtosTut_TargetOptionsSimulator.png
Simulator Settings

Debugging

Press CTRL+F5 to start a Debug session. As this program runs in simulator mode, we can make use of the advanced debugging tools of uVision without using a debug probe. Go to View -> Analysis Windows -> Performance Analyzer. Expand the tree to see the time spent in the idle demon.

rtosTut_IdlePerfAnalyzer.png
Performance Analyzer showing the time spent in Idle

Adapt the Idle Demon

Leave the Debug session by pressing CTRL+F5 again and open the RTX_Conf_CM.c file in Text Editor mode. Find the os_idle_demon function and change the code to the following:

/*--------------------------- os_idle_demon ---------------------------------*/
void os_idle_demon (void) {
/* The idle demon is a system thread, running when no other thread is */
/* ready to run. */
for (;;) {
/* HERE: include optional user code to be executed when no thread runs.*/
__wfi();
}
}
Note
The __wfi() function waits for an interrupt to happen. This halts the processor until it needs to restart (on a scheduler tick or peripheral interrupt).

Check the changed behavior

  • Rebuild the code by pressing F7.
  • Enter a Debug session (CTRL+F5).
  • Run the code (F5).
  • Find the Idle demon in the Performance Analysis window:
rtosTut_IdlePerfAnalyzerLowEnergy.png
Less processor cycles with __wfi() during Idle

The processor is now frozen during Idle cycles. Less processor cycles will lead to a lowered energy consumption during runtime.

Signals

In this tutorial, we will be creating two LED threads. led_thread1 will be waiting for a signal that will be set by led_thread2. To be able to see the program flow, led_thread2 will us the osDelay function to wait 500 ms before setting the signal for led_thread1. Here's how the program works:

signal_example_flow_chart.png
Simple signal event communication

To run the signals tutorial, create a project with the following:

  1. Create a project called Signals and select your target device.
  2. Select the Board Support:LED (API):LED (if available; otherwise, create two functions LED_On/LED_Off) and CMSIS:RTOS (API):Keil RTX software components and resolve any validation problems.
  3. Add the CMSIS-RTOS 'main' function user code template to the project.

Adapt main.c

Click on the main.c tab and change the code to:

/*----------------------------------------------------------------------------
CMSIS-RTOS 'main' function template
*---------------------------------------------------------------------------*/
#define osObjectsPublic // define objects in main module
#include "osObjects.h" // RTOS object definitions
#include "Board_LED.h" // ::Board Support:LED
void led_Thread1 (void const *argument);
void led_Thread2 (void const *argument);
osThreadDef(led_Thread1, osPriorityAboveNormal, 1, 0);
osThreadDef(led_Thread2, osPriorityNormal, 1, 0);
osThreadId T_led_ID1;
osThreadId T_led_ID2;
/*----------------------------------------------------------------------------
Flash LED 1 when signaled by the led_Thread2
*---------------------------------------------------------------------------*/
void led_Thread1 (void const *argument) {
for (;;) {
LED_On(0);
LED_Off(0);
}
}
/*----------------------------------------------------------------------------
Flash LED two and synchronize the flashing of LED 1 by setting a signal flag
*---------------------------------------------------------------------------*/
void led_Thread2 (void const *argument) {
for (;;) {
LED_On(1);
osSignalSet (T_led_ID1,0x01);
osDelay(500);
LED_Off(1);
osSignalSet (T_led_ID1,0x01);
osDelay(500);
}
}
/*
main: initialize and start the system
*/
int main (void) {
osKernelInitialize (); // initialize CMSIS-RTOS
// initialize peripherals here
LED_Initialize();
// create 'thread' functions that start executing,
// example: tid_name = osThreadCreate (osThread(name), NULL);
T_led_ID2 = osThreadCreate(osThread(led_Thread2), NULL);
T_led_ID1 = osThreadCreate(osThread(led_Thread1), NULL);
osKernelStart (); // start thread execution
}

osSignalWait() is now used in led_thread1(). It is triggered by osSignalSet() in led_thread2. This synchronizes the execution of both threads.

Save, build and debug the program

  • Select File and then Save All.
  • Select Project and then Build target or press F7 to build the complete project. It should build without any errors or warnings.
  • Enter a Debug session (CTRL+F5).
  • Run the code (F5).

Supervisor Calls (SVC)

This tutorial will do the following:

  • It configures two LED flashing threads
  • The two LED threads flash one user LED (on and off)
  • It configures an external interrupt attached to the user button of the development board (on port A bit 0)
  • The second user LED is flashed when the user button is pressed:
    • The user button is on port A bit 0
    • The external interrupt controller (EXTI) can be configured to make this pin an external interrupt line

To run this tutorial, open the previous one or create it as described in Signals. Then

  • Add the CMSIS-RTOS User SVC user code template to the project.

Adapt SVC_Table.s

Uncomment lines 44 and 49 (remove the ;):

IMPORT __SVC_1
DCD __SVC_1 ; user SVC function
Note
  • The SVC_Table.s file provides a lookup table for the software interrupt calls.
  • For each SVC you use you must import the SVC label and extend the SCV table

Adapt main.c

Open main.c and change it to the following:

/*----------------------------------------------------------------------------
CMSIS-RTOS 'main' function template
*---------------------------------------------------------------------------*/
#define osObjectsPublic // define objects in main module
#include "osObjects.h" // RTOS object definitions
#include "stm32f4xx.h"
#include "stm32f4xx_hal.h"
#include "Board_LED.h"
void led_Thread1 (void const *argument);
void led_Thread2 (void const *argument);
void exti_Thread (void const *argument);
void __svc(1) init_EXTI (void); //Define the EXTI initializing code as a supervisor function
osThreadDef(led_Thread1, osPriorityNormal, 1, 0);
osThreadDef(led_Thread2, osPriorityNormal, 1, 0);
osThreadDef(exti_Thread, osPriorityAboveNormal, 1, 0);
osThreadId T_led_ID1;
osThreadId T_led_ID2;
osThreadId T_exti_ID;
/*----------------------------------------------------------------------------
EXTI interrupt handler
*---------------------------------------------------------------------------*/
void EXTI0_IRQHandler(void) {
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
osSignalSet(T_exti_ID,0x01);
}
void exti_Thread (void const *argument) {
for (;;) {
LED_On(1);
osDelay(500);
LED_Off(1);
}
}
void led_Thread1 (void const *argument) {
for (;;) {
LED_Off(0);
}
}
void led_Thread2 (void const *argument) {
LED_Initialize ();
for (;;) {
LED_On(0);
osDelay(500);
osSignalSet(T_led_ID1,0x01);
osDelay(500);
}
}
/*
main: initialize and start the system
*/
int main (void) {
init_EXTI(); //initialize the external interrupts
T_led_ID1 = osThreadCreate(osThread(led_Thread1), NULL);
T_led_ID2 = osThreadCreate(osThread(led_Thread2), NULL);
T_exti_ID = osThreadCreate(osThread(exti_Thread), NULL);
osKernelStart (); // start thread execution
}
void __svc(1) init_EXTI (void);
void __SVC_1 (void) {
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__GPIOA_CLK_ENABLE();
/* Configure GPIO pin: GPIOA0 */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
NVIC_EnableIRQ( EXTI0_IRQn );
}
Note
This code works on the STM32F429I-Discovery board. It uses the STM32F4xx HAL for configuring the GPIO pin. To do the same for your hardware, please consult the device's documentation.

Save, build and debug the program

  • Select File and then Save All.
  • Select Project and then Build target or press F7 to build the complete project. It should build without any errors or warnings.
  • Enter a Debug session (CTRL+F5).
  • Run the code (F5).

Mutexes

This tutorial is again running in the µVision simulator (refer to the Idle Demon tutorial). Two threads will be created that try to output a stream of either 1's or 2's to the UART.

Install the Keil::STM32F1xx_DFP containing the STM32F103RB device. Setup the project:

  1. Create a project called Mutex and select the STM32F103RB as the target device.
  2. Select the CMSIS:RTOS (API):Keil RTX software component and resolve any validation problems.
  3. Add the CMSIS-RTOS 'main' function user code template to the project.
  4. Add a C source file called uart.c to the project.
  5. Add a header file called uart.h to the project.

Adapt uart.h

Click on the uart.h tab and add these lines of code:

extern void USART1_Init (void);
extern int SendChar (int ch);
extern int GetKey (void);

Adapt uart.c

Click on the uart.c tab and add the following:

#include <stm32F10x.h>
/*----------------------------------------------------------------------------
Initialize UART pins, Baudrate
*----------------------------------------------------------------------------*/
void USART1_Init (void) {
int i;
RCC->APB2ENR |= ( 1UL << 0); /* enable clock Alternate Function */
AFIO->MAPR &= ~( 1UL << 2); /* clear USART1 remap */
RCC->APB2ENR |= ( 1UL << 2); /* enable GPIOA clock */
GPIOA->CRH &= ~(0xFFUL << 4); /* clear PA9, PA10 */
GPIOA->CRH |= (0x0BUL << 4); /* USART1 Tx (PA9) output push-pull */
GPIOA->CRH |= (0x04UL << 8); /* USART1 Rx (PA10) input floating */
RCC->APB2ENR |= ( 1UL << 14); /* enable USART#1 clock */
USART1->BRR = 0x0271; /* 115200 baud @ PCLK2 72MHz */
USART1->CR1 = (( 1UL << 2) | /* enable RX */
( 1UL << 3) | /* enable TX */
( 0UL << 12) ); /* 1 start bit, 8 data bits */
USART1->CR2 = 0x0000; /* 1 stop bit */
USART1->CR3 = 0x0000; /* no flow control */
for (i = 0; i < 0x1000; i++) __NOP(); /* avoid unwanted output */
USART1->CR1 |= (( 1UL << 13) ); /* enable USART */
}
/*----------------------------------------------------------------------------
SendChar
Write character to Serial Port.
*----------------------------------------------------------------------------*/
int SendChar (int ch) {
while (!(USART1->SR & USART_SR_TXE));
USART1->DR = (ch & 0x1FF);
return (ch);
}
/*----------------------------------------------------------------------------
GetKey
Read character to Serial Port.
*----------------------------------------------------------------------------*/
int GetKey (void) {
while (!(USART1->SR & USART_SR_RXNE));
return ((int)(USART1->DR & 0x1FF));
}

Adapt main.c

Click on the main.c tab and edit the code as follows:

/*----------------------------------------------------------------------------
CMSIS-RTOS 'main' function template
*---------------------------------------------------------------------------*/
#define osObjectsPublic // define objects in main module
#include "osObjects.h" // RTOS object definitions
#include "stm32f10x.h"
#include "uart.h"
void uart_Thread1 (void const *argument);
void uart_Thread2 (void const *argument);
osThreadDef(uart_Thread1, osPriorityNormal, 1, 0);
osThreadDef(uart_Thread2, osPriorityNormal, 1, 0);
osThreadId T_uart1;
osThreadId T_uart2;
osMutexId uart_mutex;
osMutexDef(uart_mutex);
/*----------------------------------------------------------------------------
Thread two writes the character '1' to UART 1
*---------------------------------------------------------------------------*/
void uart_Thread1 (void const *argument) {
uint32_t i;
for (;;) {
//osMutexWait(uart_mutex, osWaitForever);
for( i=0;i<10;i++) {
SendChar('1');
}
SendChar('\n');
SendChar('\r');
//osMutexRelease(uart_mutex);
}
}
/*----------------------------------------------------------------------------
Thread two writes the character '2' to UART 1
*---------------------------------------------------------------------------*/
void uart_Thread2 (void const *argument) {
uint32_t i;
for(;;) {
//osMutexWait(uart_mutex, osWaitForever);
for( i=0;i<10;i++) {
SendChar('2');
}
SendChar('\n');
SendChar('\r');
//osMutexRelease(uart_mutex);
}
}
/*
main: initialize and start the system
*/
int main (void) {
osKernelInitialize (); // initialize CMSIS-RTOS
// initialize peripherals here
USART1_Init();
// create 'thread' functions that start executing,
// example: tid_name = osThreadCreate (osThread(name), NULL);
uart_mutex = osMutexCreate(osMutex(uart_mutex));
T_uart1 = osThreadCreate(osThread(uart_Thread1), NULL);
T_uart2 = osThreadCreate(osThread(uart_Thread2), NULL);
osKernelStart (); // start thread execution
}

Configure CMSIS-RTOS RTX

Double-click on RTX_Conf_CM.c and select the Configuration Wizard. Press Expand All. Set the RTOS Kernel Timer input clock frequency [Hz] to 16000000.

Save and build the program

Select File and then Save All (you might see an info box that the project is saved in the new .uvprojx format). Select Project and then Build target or press F7 to build the complete project. It should build without any errors or warnings.

Configure the target

Select Project and then Options for Target 'Target 1'... or press ALT+F7 to set the options for your target. Select the Debug tab and select Use Simulator. In the Dialog DLL box enter DARMSTM.DLL and in the Parameter box enter -pSTM32F103RB:

rtosTut_TargetOptionsSimulator.png
Simulator Settings

Debugging

Press CTRL+F5 to start a Debug session. Open the View -> Serial Windows -> UART #1 window. Press F5 to run the program. You will see that both threads try to output to the UART at the same time. Because of the time slicing, the threads corrupt each others output:

rtosTut_mutex_garbage_output.png

Adding the Mutex

Previously, the Mutex had already been created in the main thread, but has not been used by the two output threads.

  • Exit the debugger (CTRL+F5).
  • Uncomment the osMutexWait and osMutexRelease calls in each thread.
  • Rebuild the code (F7).
  • Restart the debugger (CTRL+F5).
  • Run (F5) the code and observe the new output in the UART window. The Mutex now controls the access to the UART, so that the output streams are now interleaved:
rtosTut_mutex_output_ok.png

Semaphores

In some cases, we would like to allow a limited number of threads to access certain resources. For example, an embedded web server might be able to support a limited number of simultaneous requests (due to memory limitations). In such cases, semaphores are used instead of a Mutex.

In this tutorial, we create four threads that each toggle an LED on a development board (make sure that your hardware supports four LEDs, otherwise reduce the number of blinky threads), and use a semaphore to limit the number of active LEDs to 2.

Project Setup

  1. Create a project called Semaphore and select your target device.
  2. Select the Board Support:LED (API):LED (if available; otherwise, create two functions LED_On/LED_Off) and CMSIS:RTOS (API):Keil RTX software components and resolve any validation problems.
  3. Add the CMSIS-RTOS 'main' function user code template to the project.
  4. Add the CMSIS-RTOS Semaphore user code template to the project.

Adapt Semaphore.c

Click on the Semaphore.c tab and add these lines of code:

#include <cmsis_os.h> // CMSIS RTOS header file
#include "Board_LED.h" // ::Board Support:LED
/*----------------------------------------------------------------------------
Semaphore creation & usage
*---------------------------------------------------------------------------*/
void blinky1 (void const *argument); // thread function blinky1
osThreadId tid_blinky1; // thread id blinky1
osThreadDef (blinky1, osPriorityNormal, 1, 0); // thread object blinky1
void blinky2 (void const *argument); // thread function blinky2
osThreadId tid_blinky2; // thread id blinky2
osThreadDef (blinky2, osPriorityNormal, 1, 0); // thread object blinky2
void blinky3 (void const *argument); // thread function blinky3
osThreadId tid_blinky3; // thread id blinky3
osThreadDef (blinky3, osPriorityNormal, 1, 0); // thread object blinky3
void blinky4 (void const *argument); // thread function blinky4
osThreadId tid_blinky4; // thread id blinky4
osThreadDef (blinky4, osPriorityNormal, 1, 0); // thread object blinky4
osSemaphoreId two_LEDs_id; // semaphore id
osSemaphoreDef (two_LEDs); // semaphore object
int Init_Semaphore (void) {
two_LEDs_id = osSemaphoreCreate(osSemaphore(two_LEDs), 2);
tid_blinky1 = osThreadCreate (osThread(blinky1), NULL);
if(!tid_blinky1) return(-1);
tid_blinky2 = osThreadCreate (osThread(blinky2), NULL);
if(!tid_blinky2) return(-1);
tid_blinky3 = osThreadCreate (osThread(blinky3), NULL);
if(!tid_blinky3) return(-1);
tid_blinky4 = osThreadCreate (osThread(blinky4), NULL);
if(!tid_blinky4) return(-1);
return(0);
}
void blinky1(void const *argument) {
while(1) {
LED_On(1);
osDelay(500);
LED_Off(1);
osSemaphoreRelease(two_LEDs_id);
osDelay(500);
}
}
void blinky2(void const *argument) {
while(1) {
LED_On(1);
osDelay(600);
LED_Off(1);
osSemaphoreRelease(two_LEDs_id);
osDelay(600);
}
}
void blinky3(void const *argument) {
while(1) {
LED_On(1);
osDelay(700);
LED_Off(1);
osSemaphoreRelease(two_LEDs_id);
osDelay(700);
}
}
void blinky4(void const *argument) {
while(1) {
LED_On(1);
osDelay(800);
LED_Off(1);
osSemaphoreRelease(two_LEDs_id);
osDelay(800);
}
}

Adapt main.c

Click on the main.c tab and add these lines of code:

/*----------------------------------------------------------------------------
CMSIS-RTOS 'main' function template
*---------------------------------------------------------------------------*/
#define osObjectsPublic // define objects in main module
#include "osObjects.h" // RTOS object definitions
#include "Board_LED.h" // ::Board Support:LED
extern int Init_Semaphore (void);
/*
main: initialize and start the system
*/
int main (void) {
osKernelInitialize (); // initialize CMSIS-RTOS
// initialize peripherals here
LED_Initialize();
// create 'thread' functions that start executing,
// example: tid_name = osThreadCreate (osThread(name), NULL);
Init_Semaphore();
osKernelStart (); // start thread execution
}

Configure CMSIS-RTOS RTX, save and build the program

  • Double-click on RTX_Conf_CM.c and configure your clock settings.
  • Select File and then Save All (you might see an info box that the project is saved in the new .uvprojx format).
  • Select Project and then Build target or press F7 to build the complete project. It should build without any errors or warnings.
  • Enter a Debug session (CTRL+F5) and run (F5) the program.

Observe how the LEDs are blinking. You can also use the System and Thread Viewer (go to Debug -> OS Support -> System and Thread Viewer) to check how the four threads are waiting for the semaphore token to become available:

rtosTut_sempahore_sys_thread_viewer.png
System and Thread Viewer showing the thread states

Messages

In this tutorial, a message queue is created. led_Thread2 puts data into the queue, while led_Thread1 reads from the queue. A development board with at least two LEDs is required to run the program.

Project Setup

  1. Create a project called Message and select your target device.
  2. Select the Board Support:LED (API):LED (if available; otherwise, create two functions LED_On/LED_SetOut) and CMSIS:RTOS (API):Keil RTX software components and resolve any validation problems.
  3. Add the CMSIS-RTOS 'main' function user code template to the project.
  4. Add the CMSIS-RTOS Message Queue user code template to the project.

Adapt MdgQueue.c

Click on the MdgQueue.c tab and add these lines of code:

#include <cmsis_os.h> // CMSIS RTOS header file
#include "Board_LED.h" // ::Board Support:LED
/*----------------------------------------------------------------------------
Message Queue creation & usage
*---------------------------------------------------------------------------*/
void LED_Thread1 (void const *argument); // thread function LED_Thread1
void LED_Thread2 (void const *argument); // thread function LED_Thread2
osThreadId tid_LED_Thread1; // thread id LED_Thread1
osThreadId tid_LED_Thread2; // thread id LED_Thread2
osThreadDef (LED_Thread1, osPriorityNormal, 1, 0); // thread object LED_Thread1
osThreadDef (LED_Thread2, osPriorityNormal, 1, 0); // thread object LED_Thread2
#define MSGQUEUE_OBJECTS 16 // number of Message Queue Objects
osMessageQId Q_LED; // message queue id
osMessageQDef (Q_LED, MSGQUEUE_OBJECTS, unsigned char); // message queue object
osEvent result;
int Init_MsgQueue (void) {
Q_LED = osMessageCreate(osMessageQ(Q_LED), NULL); // create msg queue
if(!Q_LED) {
; // Message Queue object not created, handle failure
}
tid_LED_Thread1 = osThreadCreate (osThread(LED_Thread1), NULL);
if(!tid_LED_Thread1) return(-1);
tid_LED_Thread2 = osThreadCreate (osThread(LED_Thread2), NULL);
if(!tid_LED_Thread2) return(-1);
return(0);
}
void LED_Thread1(void const *argument) {
for (;;) {
result = osMessageGet(Q_LED,osWaitForever); //wait for a message to arrive
LED_SetOut(result.value.v); // write the data to the LED's
}
}
void LED_Thread2(void const *argument) {
for (;;) {
osMessagePut(Q_LED, 0x1, osWaitForever); //Place a value in the message queue
osDelay(500); //Wait 500 ms
LED_On(3); //switch on the LED
osMessagePut(Q_LED, 0x2, osWaitForever); //Repeat for other bit patterns
osDelay(500);
LED_On(3);
osDelay(500);
LED_On(3);
osDelay(500);
LED_On(3);
}
}

Adapt main.c

Click on the main.c tab and add these lines of code:

/*----------------------------------------------------------------------------
CMSIS-RTOS 'main' function template
*---------------------------------------------------------------------------*/
#define osObjectsPublic // define objects in main module
#include "osObjects.h" // RTOS object definitions
#include "Board_LED.h" // ::Board Support:LED
extern int Init_MsgQueue (void);
/*
main: initialize and start the system
*/
int main (void) {
osKernelInitialize (); // initialize CMSIS-RTOS
// initialize peripherals here
LED_Initialize();
// create 'thread' functions that start executing,
// example: tid_name = osThreadCreate (osThread(name), NULL);
Init_MsgQueue();
osKernelStart (); // start thread execution
}

Configure CMSIS-RTOS RTX, save and build the program

  • Double-click on RTX_Conf_CM.c and configure your clock settings.
  • Select File and then Save All (you might see an info box that the project is saved in the new .uvprojx format).
  • Select Project and then Build target or press F7 to build the complete project. It should build without any errors or warnings.
  • Enter a Debug session (CTRL+F5) and run (F5) the program.

Observe how the two LEDs are blinking. You can also use the System and Thread Viewer (go to Debug -> OS Support -> System and Thread Viewer) to check how thread1 is waiting for mailbox:

rtosTut_message_sys_thread_viewer.png
System and Thread Viewer showing the thread states

Mailbox

In this tutorial, a mailbox will be used to send data from a producer to a consumer thread. The data will be used to flash LEDs in various ways. A development board with at least two LEDs is required to run the program.

Project Setup

  1. Create a project called Mailbox and select your target device.
  2. Select the Board Support:LED (API):LED (if available; otherwise, create a function LED_SetOut) and CMSIS:RTOS (API):Keil RTX software components and resolve any validation problems.
  3. Add the CMSIS-RTOS 'main' function user code template to the project.
  4. Add the CMSIS-RTOS Mail Queue user code template to the project.

Adapt MailQueue.c

Click on the MailQueue.c tab and add these lines of code:

#include <cmsis_os.h> // CMSIS RTOS header file
#include "Board_LED.h" // ::Board Support:LED
/*----------------------------------------------------------------------------
Mail Queue creation & usage
*---------------------------------------------------------------------------*/
void LED_Producer (void const *argument); // thread LED_Producer
void LED_Consumer (void const *argument); // thread LED_Consumer
osThreadId tid_LED_Producer; // thread id LED_Producer
osThreadId tid_LED_Consumer; // thread id LED_Consumer
osThreadDef (LED_Producer, osPriorityNormal, 1, 0); // thread object LED_Producer
osThreadDef (LED_Consumer, osPriorityNormal, 1, 0); // thread object LED_Consumer
#define mail_obj 16 // number of Message Queue Objects
typedef struct { // object data type
uint8_t LED0;
uint8_t LED1;
} mail_format;
osMailQId mail_box; // mail queue id
osMailQDef (mail_box, mail_obj, mail_format); // mail queue object
int Init_MailQueue (void) {
mail_box = osMailCreate(osMailQ(mail_box), NULL); // create mail queue
tid_LED_Producer = osThreadCreate (osThread(LED_Producer), NULL);
if(!tid_LED_Producer) return(-1);
tid_LED_Consumer = osThreadCreate (osThread(LED_Consumer), NULL);
if(!tid_LED_Consumer) return(-1);
return(0);
}
void LED_Producer(void const *argument) {
uint8_t led0[12] = {1,0,0,0,1,1,1,1,0,0,0,0}; //create the patterns to send to the mailbox
uint8_t led1[12] = {0,2,0,0,0,2,2,2,2,0,0,0};
uint8_t index;
mail_format *LEDtx = 0;
while(1) {
for(index=0;index<12;index++) {
LEDtx = (mail_format*)osMailAlloc(mail_box, osWaitForever); //allocate a mailslot
LEDtx->LED0 = led0[index]; //Populate it with data
LEDtx->LED1 = led1[index];
osMailPut(mail_box, LEDtx); //Post the mail to the mailbox
osDelay(100);
}
}
}
void LED_Consumer(void const *argument) {
mail_format *LEDrx = 0;
osEvent evt;
while(1) {
evt = osMailGet(mail_box, osWaitForever); //Wait until a message arrives
if (evt.status == osEventMail) { //Check for a valid message
LEDrx = (mail_format*)evt.value.p; //Set the receive pointer to the mailslot
LED_SetOut(LEDrx->LED0|LEDrx->LED1); //Use the data
osMailFree(mail_box, LEDrx); //Free the mailslot
}
}
}

Adapt main.c

Click on the main.c tab and add these lines of code:

/*----------------------------------------------------------------------------
CMSIS-RTOS 'main' function template
*---------------------------------------------------------------------------*/
#define osObjectsPublic // define objects in main module
#include "osObjects.h" // RTOS object definitions
#include "stm32f4xx.h" // Device header
#include "Board_LED.h" // ::Board Support:LED
extern int Init_MailQueue (void);
/*
main: initialize and start the system
*/
int main (void) {
osKernelInitialize (); // initialize CMSIS-RTOS
// initialize peripherals here
LED_Initialize();
// create 'thread' functions that start executing,
// example: tid_name = osThreadCreate (osThread(name), NULL);
Init_MailQueue();
osKernelStart (); // start thread execution
}

Configure CMSIS-RTOS RTX, save and build the program

  • Double-click on RTX_Conf_CM.c and configure your clock settings.
  • Select File and then Save All (you might see an info box that the project is saved in the new .uvprojx format).
  • Select Project and then Build target or press F7 to build the complete project. It should build without any errors or warnings.
  • Enter a Debug session (CTRL+F5) and run (F5) the program.

Observe how the two LEDs are blinking.