Recent changes
Order
tag order
CMSIS RTOS
RTOS
mbed Website Releases
Help
Firmware
Homepage
From the mbed microcontroller Handbook.  

RTOS

RTOS

  1. mbed RTOS
  2. CMSIS RTOS

The mbed RTOS API is a simple C++ encapsulation of the CMSIS RTOS C API.

» Import this library into a programrtos

mbed RTOS library

Thread

The Thread class allows 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 "rtos.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         Thread::wait(1000);
00011     }
00012 }
00013 
00014 int main() {
00015     Thread thread(led2_thread);
00016     
00017     while (true) {
00018         led1 = !led1;
00019         Thread::wait(500);
00020     }
00021 }

main

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

» Import this library into a program

Public Member Functions

  Thread (void(*task)(void const *argument), void *argument=NULL, osPriority priority=osPriorityNormal, uint32_t stacksize=DEFAULT_STACK_SIZE)
osStatus  terminate ()
osStatus  set_priority (osPriority priority)
osPriority  get_priority ()
int32_t  signal_set (int32_t signals)

Static Public Member Functions

static osEvent   signal_wait (int32_t signals, uint32_t millisec=osWaitForever)
static osStatus  wait (uint32_t millisec)
static osStatus  yield ()
static osThreadId  gettid ()

Mutex

A Mutex is used to synchronize the execution of threads: for example to protect the access to a shared resource.

ISR

The Mutex methods cannot be called from interrupt service routines (ISR).

/media/uploads/emilmont/mutex.png

» Import this program

00001 #include "mbed.h"
00002 #include "rtos.h"
00003 
00004 Mutex stdio_mutex; 
00005 
00006 void notify(const char* name, int state) {
00007     stdio_mutex.lock();
00008     printf("%s: %d\n\r", name, state);
00009     stdio_mutex.unlock();
00010 }
00011 
00012 void test_thread(void const *args) {
00013     while (true) {
00014         notify((const char*)args, 0); Thread::wait(1000);
00015         notify((const char*)args, 1); Thread::wait(1000);
00016     }
00017 }
00018 
00019 int main() {
00020     Thread t2(test_thread, (void *)"Th 2");
00021     Thread t3(test_thread, (void *)"Th 3");
00022     
00023     test_thread((void *)"Th 1");
00024 }

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, malloc & new in ISR

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

» Import this library into a program

Public Member Functions

  Mutex ()
osStatus  lock (uint32_t millisec=osWaitForever)
bool  trylock ()
osStatus  unlock ()

Semaphore

A Semaphore is particularly useful to manage thread access to a pool of shared resources of a certain type.

/media/uploads/emilmont/semaphore.png

» Import this program

00001 #include "mbed.h"
00002 #include "rtos.h"
00003 
00004 Semaphore two_slots(2);
00005 
00006 void test_thread(void const *name) {
00007     while (true) {
00008         two_slots.wait();
00009         printf("%s\n\r", (const char*)name);
00010         Thread::wait(1000);
00011         two_slots.release();
00012     }
00013 }
00014 
00015 int main (void) {
00016     Thread t2(test_thread, (void *)"Th 2");
00017     Thread t3(test_thread, (void *)"Th 3");
00018     
00019     test_thread((void *)"Th 1");
00020 }

» Import this library into a program

Public Member Functions

  Semaphore (int32_t count)
int32_t  wait (uint32_t millisec=osWaitForever)
osStatus  release (void)

Signals

Each Thread can be notified and wait for signals:

» Import this program

00001 #include "mbed.h"
00002 #include "rtos.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         Thread::signal_wait(0x1);
00010         led = !led;
00011     }
00012 }
00013 
00014 int main (void) {
00015     Thread thread(led_thread);
00016     
00017     while (true) {
00018         Thread::wait(1000);
00019         thread.signal_set(0x1);
00020     }
00021 }

Queue

A Queue allows you to queue pointers to data from producers threads to consumers threads:

/media/uploads/emilmont/messagequeue.png

Code

Queue<message_t, 16> queue;

message_t *message;

queue.put(message);

osEvent evt = queue.get();
if (evt.status == osEventMessage) {
    message_t *message = (message_t*)evt.value.p;

» Import this library into a program

Public Member Functions

  Queue ()
osStatus  put (T *data, uint32_t millisec=0)
osEvent   get (uint32_t millisec=osWaitForever)

MemoryPool

The MemoryPool class is used to define and manage fixed-size memory pools:

Code

MemoryPool<message_t, 16> mpool;

message_t *message = mpool.alloc();

mpool.free(message);

» Import this program

00001 #include "mbed.h"
00002 #include "rtos.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 MemoryPool<message_t, 16> mpool;
00011 Queue<message_t, 16> queue;
00012 
00013 /* Send Thread */
00014 void send_thread (void const *argument) {
00015     uint32_t i = 0;
00016     while (true) {
00017         i++; // fake data update
00018         message_t *message = mpool.alloc();
00019         message->voltage = (i * 0.1) * 33; 
00020         message->current = (i * 0.1) * 11;
00021         message->counter = i;
00022         queue.put(message);
00023         Thread::wait(1000);
00024     }
00025 }
00026 
00027 int main (void) {
00028     Thread thread(send_thread);
00029     
00030     while (true) {
00031         osEvent evt = queue.get();
00032         if (evt.status == osEventMessage) {
00033             message_t *message = (message_t*)evt.value.p;
00034             printf("\nVoltage: %.2f V\n\r"   , message->voltage);
00035             printf("Current: %.2f A\n\r"     , message->current);
00036             printf("Number of cycles: %u\n\r", message->counter);
00037             
00038             mpool.free(message);
00039         }
00040     }
00041 }

Mail

A Mail works like a queue with the added benefit of providing a memory pool for allocating messages (not only pointers).

/media/uploads/emilmont/mailqueue.png

» Import this program

00001 #include "mbed.h"
00002 #include "rtos.h"
00003 
00004 /* Mail */
00005 typedef struct {
00006   float    voltage; /* AD result of measured voltage */
00007   float    current; /* AD result of measured current */
00008   uint32_t counter; /* A counter value               */
00009 } mail_t;
00010 
00011 Mail<mail_t, 16> 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_box.alloc();
00018         mail->voltage = (i * 0.1) * 33; 
00019         mail->current = (i * 0.1) * 11;
00020         mail->counter = i;
00021         mail_box.put(mail);
00022         Thread::wait(1000);
00023     }
00024 }
00025 
00026 int main (void) {
00027     Thread thread(send_thread);
00028     
00029     while (true) {
00030         osEvent evt = mail_box.get();
00031         if (evt.status == osEventMail) {
00032             mail_t *mail = (mail_t*)evt.value.p;
00033             printf("\nVoltage: %.2f V\n\r"   , mail->voltage);
00034             printf("Current: %.2f A\n\r"     , mail->current);
00035             printf("Number of cycles: %u\n\r", mail->counter);
00036             
00037             mail_box.free(mail);
00038         }
00039     }
00040 }

» Import this library into a program

Public Member Functions

  Mail ()
T *  alloc (uint32_t millisec=0)
T *  calloc (uint32_t millisec=0)
osStatus  put (T *mptr)
osEvent   get (uint32_t millisec=osWaitForever)
osStatus  free (T *mptr)

RTOS Timer

The RtosTimer class allows 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/rtostimer.png

» Import this program

00001 #include "mbed.h"
00002 #include "rtos.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 int main(void) {
00013     RtosTimer led_1_timer(blink, osTimerPeriodic, (void *)0);
00014     RtosTimer led_2_timer(blink, osTimerPeriodic, (void *)1);
00015     RtosTimer led_3_timer(blink, osTimerPeriodic, (void *)2);
00016     RtosTimer led_4_timer(blink, osTimerPeriodic, (void *)3);
00017     
00018     led_1_timer.start(2000);
00019     led_2_timer.start(1000);
00020     led_3_timer.start(500);
00021     led_4_timer.start(250);
00022     
00023     Thread::wait(osWaitForever);
00024 }

» Import this library into a program

Public Member Functions

  RtosTimer (void(*task)(void const *argument), os_timer_type type=osTimerPeriodic, void *argument=NULL)
osStatus  stop (void)
osStatus  start (uint32_t millisec)

Interrupt Service Routines

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

» Import this program

00001 #include "mbed.h"
00002 #include "rtos.h"
00003 
00004 Queue<uint32_t, 5> queue;
00005 
00006 DigitalOut myled(LED1);
00007 
00008 void queue_isr() {
00009     queue.put((uint32_t*)2);
00010     myled = !myled;
00011 }
00012 
00013 void queue_thread(void const *argument) {
00014     while (true) {
00015         queue.put((uint32_t*)1);
00016         Thread::wait(1000);
00017     }
00018 }
00019 
00020 int main (void) {
00021     Thread thread(queue_thread);
00022     
00023     Ticker ticker;
00024     ticker.attach(queue_isr, 1.0);
00025     
00026     while (true) {
00027         osEvent evt = queue.get();
00028         if (evt.status != osEventMessage) {
00029             printf("queue->get() returned %02x status\n\r", evt.status);
00030         } else {
00031             printf("queue->get() returned %d\n\r", evt.value.v);
00032         }
00033     }
00034 }
00035 

Default Timeouts

The mbed rtos API has made the choice of defaulting to 0 timeout (no wait) for the producer methods, and osWaitForever (infinitive wait) for the consumer methods.

A typical scenario for a producer could be a peripheral triggering an interrupt to notify an event: in the corresponding interrupt service routine you cannot wait (this would deadlock the entire system). On the other side, the consumer could be a background thread waiting for events: in this case the desired default behaviour is not using CPU cycles until this event is produced, hence the osWaitForever.

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.

Stack Configuration

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 LPC11U24mbed NXP LPC1768
Max number of user threads + (timer)3 + (1)7 + (1)
Default stack size in bytes0.5 Kb1 Kb

configuration

To edit the configuration:

  • Right-click the rtos library and select "Edit library...".
  • Modify RTX_Conf_CM.c as needed.

Status and Error Codes

The Status and Error Codes section lists all the return values that the CMSIS-RTOS functions will return:

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



calendar Page history
Last modified 6 days, 22 hours ago, by   user Emilio Monti   tag No tags | 32 comments  

32 comments on RTOS:

14 Feb 2012

Great work, was waiting since the beginnig of mbed,if anyone could achieve this! Really cool. Thanks a lot!

14 Feb 2012

Well done Emilio!

The 25% of RAM is a bit scary though ... it's RAM that's fighting me at the moment. Have you looked into using some of the other banks of RAM for say the stack etc to give more room for application code?

14 Feb 2012

user Anthony Buckton wrote:

The 25% of RAM is a bit scary though

Yes, you are right. This is mainly due to the need to specify statically at compile time the maximum number of tasks and the stack size.

At the moment, the rtos library cannot be compiled by the online build system, otherwise we could expose this configuration to the user. (although in the future we will also look for alternative CMSIS-RTOS implementations without this constraint).

My next task is going to be a "revamp" of the online build system to be able to compile libraries like the rtos and mbed itself.

We are aiming at dogfooding for every mbed software development.

Cheers, Emilio

14 Feb 2012

That is great with dedicated and support from mbed team.

14 Feb 2012

Well done Emilio, this looks really cool!

15 Feb 2012

Just tried the first demo and it works!

What about the networking code examples being used with the RTOS?

Has anyone tried it and/or is a RTOS version of the networking code in the works?

15 Feb 2012

Hi Jim, All,

FYI, we're now also starting an "official" networking code stack too; we really wanted to have an RTOS in place to allow networking to use a sockets-like interface without the need for polling. Now that is in place, we're on it! More soon...

Simon

15 Feb 2012

user Simon Ford wrote:

we're now also starting an "official" networking code stack too; we really wanted to have an RTOS in place to allow networking to use a sockets-like interface without the need for polling. Now that is in place, we're on it!

Regarding our forthcoming TCP/IP stack, I would like to emphasize that its use will not require the use of our RTOS library, but it will require the use of our RTOS API.

In other words, with our RTOS library release, we would like to get the attention of the mbed community on the proposed RTOS API.

During the years many RTOS solutions for mbed have been proposed and we are not assuming that our RTOS library implementation is going to be better than the ones proposed by other mbed developers.

What we care to establish instead is a higher level API that we can rely on to provide useful middle-ware (the TCP/IP stack being an example of that).

Cheers, Emilio

15 Feb 2012

So you mean to use your new stack without your RTOS, one would have to implement the same RTOS API? Will the full API be necessary, or you will be relying only on some subset of it? Also, do you mean the CMSIS-RTOS API or the high-level C++ API (rtos::Thread etc)?

15 Feb 2012

user Igor Skochinsky wrote:

So you mean to use your new stack without your RTOS, one would have to implement the same RTOS API?

Yes, correct.

user Igor Skochinsky wrote:

Will the full API be necessary, or you will be relying only on some subset of it?

Very likely, it will rely only on a small subset (ie: Thread, Mutex and Queue), but we are still experimenting with it and we cannot yet specify it.

user Igor Skochinsky wrote:

Also, do you mean the CMSIS-RTOS API or the high-level C++ API (rtos::Thread etc)?

We intend to support CMSIS-RTOS API on mbed. Every middle-ware based on it will be supported.

Although, for our internal libraries we will be using the higher level API that you see documented on this page and implemented in this library. On the mbed platform we will encourage to base the development of new middleware on this mbed RTOS API.

Cheers, Emilio

16 Feb 2012

This is awesome! I'm looking at the .h files and your constructor looks reminiscent of uC/OS-II of the function for creating a task. Am I able to adjust that last parameter and use custom stack sizes?

When I use the sprintf function to format a string my program stops functioning, why might this be?

16 Feb 2012

user Tyler Weaver wrote:

Am I able to adjust that last parameter and use custom stack sizes?

Yes you are.

In our implementation, we had to configure statically the size of the memory pool allocated for the stacks (MAX_THREADS_NUM * DEFAULT_STACK_SIZE), although you can rearrange the consumption of this pool among your threads: each thread can use a custom stack size.

user Tyler Weaver wrote:

When I use the sprintf function to format a string my program stops functioning, why might this be?

Do you have a published program presenting the issue?

The simpler explanation would be the use of sprintf in a ISR.

The C standard library is protecting the access to stdio with mutexes. This is why the use of printf/sprintf in ISR can deadlock the system.

16 Feb 2012

It'd be nice if the RtosTimer api had a templated form like for example the InterruptIn one:

Code

template<typename T> void fall(T *tptr, void (T::*mptr)(void))

for member method callbacks.

16 Feb 2012

user Emilio Monti wrote:

The C standard library is protecting the access to stdio with mutexes. This is why the use of printf/sprintf in ISR can deadlock the system.

Why should sprintf deadlock? It doesn't access stdio and shouldn't need protecting with a mutex.

16 Feb 2012

user Emilio Monti wrote:

Do you have a published program presenting the issue?

» Import this programrtos_sprintf

Demonstration of the use of sprintf() causing a run time problem with RTOS.

If you un-comment out one of the sprintf() function calls, the application fails at that point. The way I published it, the first led comes on and then it stops and does nothing more. I'm using the cortex-m3 based mbed board.

16 Feb 2012

user Hugo Vincent wrote:

It'd be nice if the RtosTimer api had a templated form like for example the InterruptIn one for member method callbacks.

+1. Ticket opened.

user Hugo Vincent wrote:

Why should sprintf deadlock? It doesn't access stdio and shouldn't need protecting with a mutex.

Good point. My second guess would be a stack overflow, but of course I should see the program to know what is going wrong...

edit: Great, in the mean time the program has been published... :-)

16 Feb 2012

user Tyler Weaver wrote:

» Import this programrtos_sprintf

Demonstration of the use of sprintf() causing a run time problem with RTOS.

If you un-comment out one of the sprintf() function calls, the application fails at that point. The way I published it, the first led comes on and then it stops and does nothing more. I'm using the cortex-m3 based mbed board.

OK, that was simpler than expected. You just need to specify a buffer for your sprintf to work.

Change that:

Code

char* teststring = "";

With that:

Code

char teststring[40];
16 Feb 2012

Thankyou for helping with my problem with sprintf. That was a dumb question.

It'd be nice if there was an option to set MAX_THREADS_NUM to something greater than 7. That number seems arbitrarily low.

I'd like it if there was an option to work with smaller stacks and max treads of something larger like 64.

16 Feb 2012

user Tyler Weaver wrote:

It'd be nice if there was an option to set MAX_THREADS_NUM to something greater than 7. That number seems arbitrarily low.

I'd like it if there was an option to work with smaller stacks and max treads of something larger like 64.

RTX assumes that you are in control of your configuration file, where you can edit any maximum number of threads.

We will soon release the sources and you will be able to edit the configuration as you like.

Additionally, in the future, for the mbed RTOS, we will favour implementations that do not require the editing of a configuration file to specify the maximum number of threads.

Summarizing, let me empathise that this is a temporary implementation detail and it is not part of the mbed RTOS API.

16 Feb 2012

user Emilio Monti wrote:

We intend to support CMSIS-RTOS API on mbed. Every middle-ware based on it will be supported.

By the way, today we have released a set of example programs running on mbed based on the CMSIS-RTOS API: /handbook/CMSIS-RTOS

16 Feb 2012

Is there a way to pause a thread to keep it from executing?

16 Feb 2012

user Tyler Weaver wrote:

Is there a way to pause a thread to keep it from executing?

You can terminate it, you can yeld in it, you can wait for a certain time in it, or you can wait for a certain event in it (mutex or semaphore availability, signal, message or mail, timeout).

This is a diagram of the thread states:

/media/uploads/emilmont/threadstatus.png

23 Feb 2012

Does this require re-enterable libraries? Which C libraries are you using and are they all re-enterable?

23 Feb 2012

user Joey Ye wrote:

Does this require re-enterable libraries?

It is not a requirement, but it is a design goal. Making non re-entrant functions thread-safe has a cost.

There are different ways to make non reentrant function thread-safe:

  • Use mutexes
  • Keep a separate area of memory for each thread

user Joey Ye wrote:

Which C libraries are you using and are they all re-enterable?

We are using two separate C standard libraries: ARM C standard library (on the Cortex-M3) and microlib (on the Cortex-M0).

Both libraries are not completely re-entrant, the ARM C standard library is thread safe, microlib is not.

At system initialization we provide to the ARM C standard library a set of mutexes and a separate area of memory for each thread:

Code

 static uint32_t std_libspace[OS_TASK_CNT][96/4];
 static OS_MUT   std_libmutex[OS_MUTEXCNT];

When a non-reentrant function has to store some state in memory it can point to a thread specific area of memory calling __user_perthread_libspace:

Code

void *__user_perthread_libspace (void) {
  /* Provide a separate libspace for each task. */
  uint32_t idx;

  idx = runtask_id ();
  if (idx == 0) {
    /* RTX not running yet. */
    return (&__libspace_start);
  }
  return ((void *)&std_libspace[idx-1]);
}

When a shared resource (ie stdio) has to be accessed a mutex is acquired.

24 Feb 2012

user Emilio Monti wrote:

When a non-reentrant function has to store some state in memory it can point to a thread specific area of memory calling __user_perthread_libspace:

How does lib function do this without modifying library code?

user Emilio Monti wrote:

When a shared resource (ie stdio) has to be accessed a mutex is acquired.

Generally how did you know which lib function is reenterable and which is not? Do you protect every library function call with mutex?

24 Feb 2012

user Joey Ye wrote:

How does lib function do this without modifying library code?

This is a feature provided by the ARM C standard library: http://www.keil.com/support/man/docs/armlib/armlib_Chdhgdid.htm

user Joey Ye wrote:

Generally how did you know which lib function is reenterable and which is not? Do you protect every library function call with mutex?

This is well documented in the library documentation.

For the ARM C standard library the user can use any library call in the RTOS threads, all the non-reentrant functions do use a separate area of memory.

For microlib, at the moment, the user needs to check in the documentation if the function is reentrant, or not, and need to code accordingly. There are plans to make microlib thread safe.

Cheers, Emilio

14 Mar 2012

can you estimate a date that you will be able to release the configurable RTOS? I'm looking at using the cortex m0 but need a few more threads...

Many thanks,

Joe.

20 Mar 2012

Hi guys, what is the OS tick rate? 1000 Hz?

- Brian

21 Mar 2012

user joe holdsworth wrote:

can you estimate a date that you will be able to release the configurable RTOS?

I could release the source code of the RTOS today, the problem is that it does not yet compile with the online build system.

Our plan is to unify the offline and online build system, but this has been scheduled after another major release of "mbed.org", therefore there are two separate release cycles before that happen. It could take up to a couple of months.

user Brian Ramos wrote:

what is the OS tick rate? 1000 Hz?

Yes: 1000Hz (Period: 1ms).

Code

//   <o>Timer tick value [us] <1-1000000>
//   <i> Defines the timer tick value.
//   <i> Default: 1000  (1ms)
#ifndef OS_TICK
 #define OS_TICK        1000
#endif

Emilio

11 Apr 2012

Hi all

When I include the rtos.h, the serial rx isr block the program when an interrupt is asserted. If I remove the "#include..." line and the rtos.h from the project, the programm works...

Code

#include "mbed.h"
#include "rtos.h"

DigitalOut led1(LED1);
DigitalOut led2(LED2);

const char test_string[]="pc";
char c;
Serial pc(USBTX, USBRX);

void callback() {
    
    led2 = !led2;
   return;   
}

int main() {
    pc.attach(callback);
    pc.printf("%s\r\n",test_string);
    while (1) {
      wait(1.0);
    }
}
13 Apr 2012

Hi Emilio Monti,

Is there any way to get the state of a thread? I'd like to monitor a bunch of threads and see when they complete. I don't want to just use a flag at the end of the function because of a race condition.

- Brian

13 Apr 2012

user Brian Ramos wrote:

Is there any way to get the state of a thread?

At the moment, there is not an mbed RTOS API to get the state of a thread. It is possible to retrieve it using the RTX implementation internals.

Of course, it is a sensible request and I will open a ticket to add it in the next release.

Emilio

Please login to post comments.