CMSIS RTOS

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.

CMSIS-RTOS is the fundation of the offical mbed RTOS:

» Import this library into a programmbed-rtos

Official mbed Real Time Operating System based on the RTX implementation of the CMSIS-RTOS API open standard.

Thread

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.

» Import this program

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 *args) {
00008     while (true) {
00009         led2 = !led2;
00010         osDelay(1000);
00011     }
00012 }
00013 osThreadDef(led2_thread, osPriorityNormal, 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 }

main

The main function is already the first thread scheduled by the rtos.

» Import this library into a program

Data Fields

os_pthread  pthread
  start address of thread function
osPriority  tpriority
  initial thread priority
uint32_t  stacksize
  stack size requirements in bytes
unsigned char *  stack_pointer
  pointer to the stack memory block

A Thread can be in the following states:

  • RUNNING: The thread that is currently running is in the RUNNING state. Only one thread at a time can be in this state.
  • READY: Threads which are ready to run are in the READY state. Once the RUNNING thread has terminated or is WAITING the next READY thread with the highest priority becomes the RUNNING thread.
  • WAITING: Threads that are waiting for an event to occur are in the WAITING state.
  • INACTIVE: Threads that are not created or terminated are in the INACTIVE state. These threads typically consume no system resources.

/media/uploads/emilmont/threadstatus.png

osDelay

A call to osDelay will put the calling thread in the WAITING state for the specified amount of milliseconds. During this time, the RTOS scheduler will run other threads in the READY state.

Mutex

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.

ISR

Mutex Management functions cannot be called from interrupt service routines (ISR).

/media/uploads/emilmont/mutex.png

» Import this program

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, DEFAULT_STACK_SIZE);
00022 
00023 void t3(void const *argument) {test_thread("Th 3");}
00024 osThreadDef(t3, osPriorityNormal, 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 }

C standard library mutexes

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.

printf in ISR

Because of the mutexes in the ARM C standard library you can not use printf in ISR!

Semaphore

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.

/media/uploads/emilmont/semaphore.png

» Import this program

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, DEFAULT_STACK_SIZE);
00018 
00019 void t3(void const *argument) {test_thread("Th 3");}
00020 osThreadDef(t3, osPriorityNormal, 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 }

Signals

The Signal Management function group allow to control or wait signal flags. Each thread has assigned signal flags.

» Import this program

00001 #include "mbed.h"
00002 #include "cmsis_os.h"
00003 
00004 DigitalOut led(LED1);
00005 
00006 void led_thread(void const *args) {
00007     while (true) {
00008         // Signal flags that are reported as event are automatically cleared.
00009         osSignalWait(0x1, osWaitForever);
00010         led = !led;
00011     }
00012 }
00013 osThreadDef(led_thread, osPriorityNormal, DEFAULT_STACK_SIZE);
00014 
00015 int main (void) {
00016     osThreadId tid = osThreadCreate(osThread(led_thread), NULL);
00017     
00018     while (true) {
00019         osDelay(1000);
00020         osSignalSet(tid, 0x1);
00021     }
00022 }

Message Queue

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.

/media/uploads/emilmont/messagequeue.png

» Import this library into a program

Data Fields

uint32_t  queue_sz
  number of elements in the queue
void *  pool
  memory array for messages

Memory Pool

» Import this program

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 *args) {
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, 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 }

» Import this library into a program

Data Fields

uint32_t  pool_sz
  number of items (elements) in the pool
uint32_t  item_sz
  size of an item
void *  pool
  pointer to memory for pool

Mail Queue

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.

/media/uploads/emilmont/mailqueue.png

» Import this program

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 *args) {
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, 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 }

» Import this library into a program

Data Fields

uint32_t  queue_sz
  number of elements in the queue
uint32_t  item_sz
  size of an item
void *  pool
  memory array for mail

Timer

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.

/media/uploads/emilmont/timer.png

» Import this program

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 }

Interrupt Service Routines

The same CMSIS-RTOS can be used in ISR. The only two warnings are:

  • Mutexes can not be used.
  • Wait in ISR is not allowed: all the timeouts in method parameters have to be set to 0 (no wait).

» Import this program

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 *args) {
00016     while (true) {
00017        osMessagePut(queue, 1, 0);
00018        osDelay(1000);
00019     }
00020 }
00021 
00022 osThreadDef(queue_thread, osPriorityNormal, 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 }

No wait in ISR

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.

Status and Error Codes

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.

osEvent

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:

» Import this library into a program

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



22 comments:

23 Feb 2012

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.

24 Feb 2012

user jim hamblen wrote:

I added a random time delay, osDelay(rand()>>23); to both the consumer and producer thread to mix it up and desync it all

[...]

It probably has an error code on osPoolAlloc() that is not being checked

Yes, the "right" depth of the queue is very dependent on the "burst" of messages from the producer, therefore if you change that you have to expect needing to change the depth of the queue.

user jim hamblen wrote:

It probably has an error code on osPoolAlloc() that is not being checked when it locks

Yes, checking all the return codes is the right thing to do. I was tempted to write the examples adding all the error checking, but that was obfuscating the API. At the end, the requirements for these little "RTOS API Hello Worlds" were:

  • run correctly (cheating with producer and consumer synchronization, as you noticed)
  • being short and terse to showcase the core of the API.

In the long run, I think we will split the API documentation in one page for each class and we will have two examples. A small "Hello World" like the current one and a more sophisticated one, with a "tougher" use case and proper error checking and handling.

user jim hamblen wrote:

A function to printf OS status and error code messages might be handy.

Yes, good idea. In the mbed RTOS API we should add this function. I'll open a ticket for it.

user jim hamblen wrote:

Do RTOS errors ever blink the LEDs like the old runtime errors? - did not see anything on LEDs.

Yes, the RTX implementation have two internal error notification functions:

  • os_error
  • sysThreadError

We reimplemented both of them calling mbed_die.

Mind that these functions are called only in situations that cannot be safely recovered like a stack overflow, not for a simple failure allocating an object in a memory pool.

Thanks for the feedback.

Cheers, Emilio

» Show archived comment by hapex73
14 May 2012

Hi!

I'd like to present a question whether an offline compilation shall be supported in RTOS API? For example, the examples presented in this webpage are not compiling offline (I have replaced the mbed library by 'export' library as explained in http://mbed.org/handbook/Exporting-to-offline-toolchains ).

For example, for first example in this page (cmsis_rtos_basic), the following errors are produced when exporting to offline toolchain (GCC Arm Embedded):

main.o: In function `led2_thread(void const*)': main.cpp:(.text._Z11led2_threadPKv+0x14): undefined reference to `osDelay' main.o: In function `main': main.cpp:(.text.startup.main+0x6): undefined reference to `osThreadCreate' main.cpp:(.text.startup.main+0x1c): undefined reference to `osDelay' rtos/RTX_Conf_CM.o: In function `software_init_hook': RTX_Conf_CM.c:(.text.software_init_hook+0x1a): undefined reference to `osKernelS tart' collect2: ld returned 1 exit status

So, it seems that compiling itself succeeds but the linker cannot find the os functions.

Is there any intend on making this offline-compiled or some quick fix I can do manually to enable it?

Thanks for awesome work! Yours, Hannu

» Show archived comment by emilmont
14 May 2012

user Hannu Hirvi wrote:

I'd like to present a question whether an offline compilation shall be supported in RTOS API? Is there any intend on making this offline-compiled or some quick fix I can do manually to enable it?

Yes, the sources are ready for GCC toolchain support.

We should be able to distribute a build in few days.

Cheers, Emilio

» Show archived comment by emilmont
16 May 2012

user Hannu Hirvi wrote:

I'd like to present a question whether an offline compilation shall be supported in RTOS API? Is there any intend on making this offline-compiled or some quick fix I can do manually to enable it?

OK, I pushed live a build of the RTOS library that can be used with the GCC toolchains (ARM, CodeSourcery and CodeRed).

Note: You'll need the "export" version of both the mbed and rtos libraries.

To simplify this process I created a basic example project containing the above export libraries:

» Import this programcmsis_rtos_basic_export

A CMSIS RTOS basic project ready to be exported

The sources are going to be open, we are just waiting to deploy our online collaboration system to release them.

Cheers, Emilio

» Show archived comment by hapex73
21 May 2012

Thanks!

Now, the export is working fine on CMSIS-RTOS. Thanks for your kind effort!

28 May 2012

Any documentation for CMSIS RTOS, please?

thanks, Aung

28 May 2012

Hi Aung,

user Aung KL wrote:

Any documentation for CMSIS RTOS, please?

I would argue that the above 1500 words, 5 pictures and 8 example projects are some sort of documentation. :-)

Is there any aspect of CMSIS-RTOS in particular that you would like to see more documented?

Cheers, Emilio

29 May 2012

I agree that. It would be more understanding if some document are provided because of I'm new to RTOS, thanks, Aung

29 May 2012

Aung, maybe you could checkout this Free RTOS Tutorial which seems like it gives some general background on RTOS usage in embedded systems.

29 May 2012

Yes, the FreeRTOS tutorial is taking an interesting approach, exemplifying not only how to use an RTOS, but also why to use an RTOS.

user Chris Styles was suggesting me the same approach, perhaps we should provide a similar example based on the mbed RTOS API.

Cheers, Emilio

29 May 2012

Thanks for your info, Adam and Emilio. I'll try on that FreeRTOS book. Any reference of compatibility between CMSIS and FreeRTOS? Thanks, Aung

29 May 2012

user Aung KL wrote:

Any reference of compatibility between CMSIS and FreeRTOS?

The core RTOS concepts are very similar. The API is quite different.

Although, in the future, FreeRTOS could provide a compatibility layer for CMSIS-RTOS.

29 Jul 2012

Does it work for the LPC11U24? I am getting the following error :

undefined symbol use_two_region_memory

regards.

30 Jul 2012

Hi Hermann,

user Hermann Helmholz wrote:

Does it work for the LPC11U24? I am getting the following error: undefined symbol use_two_region_memory

Yes, sorry, the implementation of the new RTOS memory model was half backed for the LPC11U24.

If you update to the latest mbed-rtos revision it should work fine.

Cheers, Emilio

31 Jul 2012

Great! Thanks, it works fine. However, when I use it in my program, it does not work because, there is not enough place. Is there a way I can decrease the size of the RTOS? I actualy only need the thread functionality...

thanks

31 Jul 2012

Hi Herman,

user Hermann Helmholz wrote:

Is there a way I can decrease the size of the RTOS? I actualy only need the thread functionality...

Yes, you can customize the memory usage of the RTOS editing mbed-rtos/RTX_Conf_CM.c.

For example, if you do not need the Timer thread, you can change the OS_TIMERS define to 0. This will save half a kilobyte of RAM:

#ifndef OS_TIMERS
 #define OS_TIMERS      0
#endif

HTH, Emilio

31 Jul 2012

Thanks, it indeed works. Last question, my program is already 30.3kB (max = 32kB I think). The RTOS is 5.96kB (- 0.5kb = 5.46kB). So there is no way I can put an RTOS in my app. So, I am looking fro another solution... I though of decreasing the mbed.h?

However, the code of the mbed.h is not accesible... any hint what I can do?

thanks.

31 Jul 2012

user Hermann Helmholz wrote:

my program is already 30.3kB (max = 32kB I think). The RTOS is 5.96kB (- 0.5kb = 5.46kB). So there is no way I can put an RTOS in my app.

Are you using an LPC1768, or an LPC11U24?

Before you was mentioning the LPC11U24, but it has only 8KB of RAM. Take a look at the Memory Model pages:

user Hermann Helmholz wrote:

I though of decreasing the mbed.h? However, the code of the mbed.h is not accesible... any hint what I can do?

We are not yet releasing the sources of the mbed library, although there is very little room for shrinking its size.

Emilio

02 Aug 2012

oups. Well, the size of my binarie is 30.3kB... (I am using the LPC11u24)

02 Aug 2012

user Hermann Helmholz wrote:

Well, the size of my binarie is 30.3kB... (I am using the LPC11u24)

OK, my suggestion in this case is: upgrade to the mbed NXP LPC1768.

Cheers, Emilio

05 Apr 2013

Hello.

Can anyone help me? I'm trying to compile and run any of these examples from eclipse/terminal in Linux. I tried the Code Sourcery compiler and the GNU ARM compiler. I managed to compile with both compilers but when i upload the program and run in i get the LEDs blinking in pairs meaning an internal error.

Posting comments for this page has been disabled