The CMSIS-RTOS is a common API for Real-Time operating systems. It provides a standardized programming interface that is portable to many RTOS and enables therefore software templates, middleware, libraries, and other components that can work across supported the RTOS systems.
mbed RTOS library
The Thread Management function group allow defining, creating, and controlling thread functions in the system. The function main is a special thread function that is started at system initialization and has the initial priority osPriorityNormal.
00001 #include "mbed.h" 00002 #include "cmsis_os.h" 00003 00004 DigitalOut led1(LED1); 00005 DigitalOut led2(LED2); 00006 00007 void led2_thread(void const *argument) { 00008 while (true) { 00009 led2 = !led2; 00010 osDelay(1000); 00011 } 00012 } 00013 osThreadDef(led2_thread, osPriorityNormal, 1, DEFAULT_STACK_SIZE); 00014 00015 int main() { 00016 osThreadCreate(osThread(led2_thread), NULL); 00017 00018 while (true) { 00019 led1 = !led1; 00020 osDelay(500); 00021 } 00022 }
The main function is already the first thread scheduled by the rtos.
The Mutex Management function group is used to synchronize the execution of threads. This is for example used to protect access to a shared resource, for example a shared memory image.
Mutex Management functions cannot be called from interrupt service routines (ISR).
00001 #include "mbed.h" 00002 #include "cmsis_os.h" 00003 00004 osMutexId stdio_mutex; 00005 osMutexDef(stdio_mutex); 00006 00007 void notify(const char* name, int state) { 00008 osMutexWait(stdio_mutex, osWaitForever); 00009 printf("%s: %d\n\r", name, state); 00010 osMutexRelease(stdio_mutex); 00011 } 00012 00013 void test_thread(void const *args) { 00014 while (true) { 00015 notify((const char*)args, 0); osDelay(1000); 00016 notify((const char*)args, 1); osDelay(1000); 00017 } 00018 } 00019 00020 void t2(void const *argument) {test_thread("Th 2");} 00021 osThreadDef(t2, osPriorityNormal, 1, DEFAULT_STACK_SIZE); 00022 00023 void t3(void const *argument) {test_thread("Th 3");} 00024 osThreadDef(t3, osPriorityNormal, 1, DEFAULT_STACK_SIZE); 00025 00026 int main() { 00027 stdio_mutex = osMutexCreate(osMutex(stdio_mutex)); 00028 00029 osThreadCreate(osThread(t2), NULL); 00030 osThreadCreate(osThread(t3), NULL); 00031 00032 test_thread((void *)"Th 1"); 00033 }
The ARM C standard library has already mutexes in place to protect the access to stdio, therefore on the M3 mbed the above example is not necessary. On the contrary, ARM microlib (used on the M0 mbed) does not provide default stdio mutexes making the above example a necessity.
Because of the mutexes in the ARM C standard library you can not use printf in ISR!
The Semaphore Management function group is used to manage and protect access to shared resources. For example, with a Semaphore the access to a group of identical peripherals can be managed. The number of available resources is specified as parameter of the osSemaphoreCreate function.

00001 #include "mbed.h" 00002 #include "cmsis_os.h" 00003 00004 osSemaphoreId two_slots; 00005 osSemaphoreDef(two_slots); 00006 00007 void test_thread(void const *name) { 00008 while (true) { 00009 osSemaphoreWait(two_slots, osWaitForever); 00010 printf("%s\n\r", (const char*)name); 00011 osDelay(1000); 00012 osSemaphoreRelease(two_slots); 00013 } 00014 } 00015 00016 void t2(void const *argument) {test_thread("Th 2");} 00017 osThreadDef(t2, osPriorityNormal, 1, DEFAULT_STACK_SIZE); 00018 00019 void t3(void const *argument) {test_thread("Th 3");} 00020 osThreadDef(t3, osPriorityNormal, 1, DEFAULT_STACK_SIZE); 00021 00022 int main (void) { 00023 two_slots = osSemaphoreCreate(osSemaphore(two_slots), 2); 00024 00025 osThreadCreate(osThread(t2), NULL); 00026 osThreadCreate(osThread(t3), NULL); 00027 00028 test_thread((void *)"Th 1"); 00029 }
The Signal Management function group allow to control or wait signal flags. Each thread has assigned signal flags.
00001 #include "mbed.h" 00002 #include "cmsis_os.h" 00003 00004 DigitalOut led(LED1); 00005 00006 void led_thread(void const *argument) { 00007 while (true) { 00008 // Signal flags that are reported as event are automatically cleared. 00009 osSignalWait(0x1, osWaitForever); 00010 led = !led; 00011 } 00012 } 00013 00014 osThreadDef(led_thread, osPriorityNormal, 1, DEFAULT_STACK_SIZE); 00015 00016 int main (void) { 00017 osThreadId tid = osThreadCreate(osThread(led_thread), NULL); 00018 00019 while (true) { 00020 osDelay(1000); 00021 osSignalSet(tid, 0x1); 00022 } 00023 }
The Message Queue Management function group allow to control, send, receive, or wait for messages. A message can be a integer or pointer value that is send to a thread or interrupt service routine.
00001 #include "mbed.h" 00002 #include "cmsis_os.h" 00003 00004 typedef struct { 00005 float voltage; /* AD result of measured voltage */ 00006 float current; /* AD result of measured current */ 00007 uint32_t counter; /* A counter value */ 00008 } message_t; 00009 00010 osPoolDef(mpool, 16, message_t); 00011 osPoolId mpool; 00012 00013 osMessageQDef(queue, 16, message_t); 00014 osMessageQId queue; 00015 00016 void send_thread (void const *argument) { 00017 uint32_t i = 0; 00018 while (true) { 00019 i++; // fake data update 00020 message_t *message = (message_t*)osPoolAlloc(mpool); 00021 message->voltage = (i * 0.1) * 33; 00022 message->current = (i * 0.1) * 11; 00023 message->counter = i; 00024 osMessagePut(queue, (uint32_t)message, osWaitForever); 00025 osDelay(1000); 00026 } 00027 } 00028 00029 osThreadDef(send_thread, osPriorityNormal, 1, DEFAULT_STACK_SIZE); 00030 00031 int main (void) { 00032 mpool = osPoolCreate(osPool(mpool)); 00033 queue = osMessageCreate(osMessageQ(queue), NULL); 00034 00035 osThreadCreate(osThread(send_thread), NULL); 00036 00037 while (true) { 00038 osEvent evt = osMessageGet(queue, osWaitForever); 00039 if (evt.status == osEventMessage) { 00040 message_t *message = (message_t*)evt.value.p; 00041 printf("\nVoltage: %.2f V\n\r" , message->voltage); 00042 printf("Current: %.2f A\n\r" , message->current); 00043 printf("Number of cycles: %u\n\r", message->counter); 00044 00045 osPoolFree(mpool, message); 00046 } 00047 } 00048 }
The Mail Queue Management function group allow to control, send, receive, or wait for mail. A mail is a memory block that is send to a thread or interrupt service routine.

00001 #include "mbed.h" 00002 #include "cmsis_os.h" 00003 00004 typedef struct { 00005 float voltage; /* AD result of measured voltage */ 00006 float current; /* AD result of measured current */ 00007 uint32_t counter; /* A counter value */ 00008 } mail_t; 00009 00010 osMailQDef(mail_box, 16, mail_t); 00011 osMailQId mail_box; 00012 00013 void send_thread (void const *argument) { 00014 uint32_t i = 0; 00015 while (true) { 00016 i++; // fake data update 00017 mail_t *mail = (mail_t*)osMailAlloc(mail_box, osWaitForever); 00018 mail->voltage = (i * 0.1) * 33; 00019 mail->current = (i * 0.1) * 11; 00020 mail->counter = i; 00021 osMailPut(mail_box, mail); 00022 osDelay(1000); 00023 } 00024 } 00025 00026 osThreadDef(send_thread, osPriorityNormal, 1, DEFAULT_STACK_SIZE); 00027 00028 int main (void) { 00029 mail_box = osMailCreate(osMailQ(mail_box), NULL); 00030 osThreadCreate(osThread(send_thread), NULL); 00031 00032 while (true) { 00033 osEvent evt = osMailGet(mail_box, osWaitForever); 00034 if (evt.status == osEventMail) { 00035 mail_t *mail = (mail_t*)evt.value.p; 00036 printf("\nVoltage: %.2f V\n\r" , mail->voltage); 00037 printf("Current: %.2f A\n\r" , mail->current); 00038 printf("Number of cycles: %u\n\r", mail->counter); 00039 00040 osMailFree(mail_box, mail); 00041 } 00042 } 00043 }
The Timer Management function group allow creating and and controlling of timer functions in the system. A timer function is called when a time period expires whereby both on-shot and periodic timers are possible. A timer can be started, restarted, or stopped.
Timers are handled in the thread osTimerThread. Callback functions run under control of this thread and may use CMSIS-RTOS API calls.
00001 #include "mbed.h" 00002 #include "cmsis_os.h" 00003 00004 DigitalOut LEDs[4] = { 00005 DigitalOut(LED1), DigitalOut(LED2), DigitalOut(LED3), DigitalOut(LED4) 00006 }; 00007 00008 void blink(void const *n) { 00009 LEDs[(int)n] = !LEDs[(int)n]; 00010 } 00011 00012 osTimerDef(blink_0, blink); 00013 osTimerDef(blink_1, blink); 00014 osTimerDef(blink_2, blink); 00015 osTimerDef(blink_3, blink); 00016 00017 int main(void) { 00018 osTimerId timer_0 = osTimerCreate(osTimer(blink_0), osTimerPeriodic, (void *)0); 00019 osTimerId timer_1 = osTimerCreate(osTimer(blink_1), osTimerPeriodic, (void *)1); 00020 osTimerId timer_2 = osTimerCreate(osTimer(blink_2), osTimerPeriodic, (void *)2); 00021 osTimerId timer_3 = osTimerCreate(osTimer(blink_3), osTimerPeriodic, (void *)3); 00022 00023 osTimerStart(timer_0, 2000); 00024 osTimerStart(timer_1, 1000); 00025 osTimerStart(timer_2, 500); 00026 osTimerStart(timer_3, 250); 00027 00028 osDelay(osWaitForever); 00029 }
The same CMSIS-RTOS can be used in ISR. The only two warnings are:
00001 #include "mbed.h" 00002 #include "cmsis_os.h" 00003 00004 osMessageQDef(queue, 5, message_t); 00005 osMessageQId queue; 00006 00007 DigitalOut myled(LED1); 00008 00009 void queue_isr() { 00010 osMessagePut(queue, (uint32_t)2, 0); 00011 00012 myled = !myled; 00013 } 00014 00015 void queue_thread(void const *argument) { 00016 while (true) { 00017 osMessagePut(queue, 1, 0); 00018 osDelay(1000); 00019 } 00020 } 00021 00022 osThreadDef(queue_thread, osPriorityNormal, 1, DEFAULT_STACK_SIZE); 00023 00024 int main (void) { 00025 queue = osMessageCreate(osMessageQ(queue), NULL); 00026 00027 osThreadCreate(osThread(queue_thread), NULL); 00028 00029 Ticker ticker; 00030 ticker.attach(queue_isr, 1.0); 00031 00032 while (true) { 00033 osEvent evt = osMessageGet(queue, osWaitForever); 00034 if (evt.status != osEventMessage) { 00035 printf("queue->get() returned %02x status\n\r", evt.status); 00036 } else { 00037 printf("queue->get() returned %d\n\r", evt.value.v); 00038 } 00039 } 00040 }
When calling an rtos object method in an ISR all the timeout parameters have to be set to 0 (no wait): waiting in ISR is not allowed.
The stack configuration is very dependent from the underling CMSIS-RTOS implementation.
As first reference implementation we are using RTX. One of the limitation of the RTX implementation is that it requires to statically configure the maximum number of threads and the size of the memory pool for their stacks.
This is our default configuration:
| mbed NXP LPC11U24 | mbed NXP LPC1768 | |
|---|---|---|
| Max number of user threads + (timer) | 3 + (1) | 7 + (1) |
| Default stack size in bytes | 0.5 Kb | 1 Kb |
To edit the configuration:
rtos library and select "Edit library...".
RTX_Conf_CM.c as needed.
The Status and Error Codes section lists all the return values that the CMSIS-RTOS functions will return:
osOK: function completed; no event occurred.
osEventSignal: function completed; signal event occurred.
osEventMessage: function completed; message event occurred.
osEventMail: function completed; mail event occurred.
osEventTimeout: function completed; timeout occurred.
osErrorParameter: parameter error: a mandatory parameter was missing or specified an incorrect object.
osErrorResource: resource not available: a specified resource was not available.
osErrorTimeoutResource: resource not available within given time: a specified resource was not available within the timeout period.
osErrorISR: not allowed in ISR context: the function cannot be called from interrupt service routines.
osErrorISRRecursive: function called multiple times from ISR with same object.
osErrorPriority: system cannot determine priority or thread has illegal priority.
osErrorNoMemory: system is out of memory: it was impossible to allocate or reserve memory for the operation.
osErrorValue: value of a parameter is out of range.
osErrorOS: unspecified RTOS error: run-time error but no other error message fits.
The osEvent data structure is returned by get methods of Queue and Mail objects.
This data structure contains both an error code and a pointer to the actual data:
Data Fields |
|
| osStatus | status |
|
status code: event or error information
|
|
| union { | |
| uint32_t v | |
|
message as 32-bit value
|
|
| void * p | |
|
message or mail as void pointer
|
|
| int32_t signals | |
|
signal flags
|
|
| } | value |
|
event value
|
|
| union { | |
| osMailQId mail_id | |
|
mail id obtained by osMailCreate
|
|
| osMessageQId message_id | |
|
message id obtained by osMessageCreate
|
|
| } | def |
|
event definition
|
|
No tags
|
6 comments
Please login to post comments.
I was trying out the cmsis_rtos_queue example code and I think there might be a typo on the osPoolDef size of 16. Instead of just osDelay(1000) on the producer, I added a random time delay, osDelay(rand()>>23); to both the consumer and producer thread to mix it up and desync it all a bit more so that the queue will be empty and full at times. It seems to lock up whenever the queue fills up. I think this happens since it allocates another item before waiting on a full queue. 17 made it run a bit longer, but only 18 seems to fix it all. It probably has an error code on osPoolAlloc() that is not being checked when it locks. A function to printf OS status and error code messages might be handy. Do RTOS errors ever blink the LEDs like the old runtime errors? - did not see anything on LEDs.
It looks like the mbed RTOS rtos_queue example has a similar issue, but perhaps a bit more since 18 helped but did not fix it.