RTOS

Import librarymbed-rtos

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

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 program

#include "mbed.h"
#include "rtos.h"
 
DigitalOut led1(LED1);
DigitalOut led2(LED2);
 
void led2_thread(void const *args) {
    while (true) {
        led2 = !led2;
        Thread::wait(1000);
    }
}
 
int main() {
    Thread thread(led2_thread);
    
    while (true) {
        led1 = !led1;
        Thread::wait(500);
    }
}

main

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

Import library

Public Types

enum   State {
   Inactive , Ready , Running , WaitingDelay ,
   WaitingInterval , WaitingOr , WaitingAnd , WaitingSemaphore ,
   WaitingMailbox , WaitingMutex
}
 

State of the Thread .

More...

Public Member Functions

  Thread (void(*task)(void const *argument), void *argument=NULL, osPriority priority=osPriorityNormal, uint32_t stack_size=DEFAULT_STACK_SIZE, unsigned char *stack_pointer=NULL)
  Create a new thread, and start it executing the specified function.
osStatus  terminate ()
  Terminate execution of a thread and remove it from Active Threads.
osStatus  set_priority (osPriority priority)
  Set priority of an active thread.
osPriority  get_priority ()
  Get priority of an active thread.
int32_t  signal_set (int32_t signals)
  Set the specified Signal Flags of an active thread.
State   get_state ()
  State of this Thread .

Static Public Member Functions

static osEvent   signal_wait (int32_t signals, uint32_t millisec=osWaitForever)
  Wait for one or more Signal Flags to become signaled for the current RUNNING thread.
static osStatus  wait (uint32_t millisec)
  Wait for a specified time period in millisec:
static osStatus  yield ()
  Pass control to next thread that is in state READY.
static osThreadId  gettid ()
  Get the thread id of the current running thread.

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

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 program

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

Mutex stdio_mutex; 

void notify(const char* name, int state) {
    stdio_mutex.lock();
    printf("%s: %d\n\r", name, state);
    stdio_mutex.unlock();
}

void test_thread(void const *args) {
    while (true) {
        notify((const char*)args, 0); Thread::wait(1000);
        notify((const char*)args, 1); Thread::wait(1000);
    }
}

int main() {
    Thread t2(test_thread, (void *)"Th 2");
    Thread t3(test_thread, (void *)"Th 3");
    
    test_thread((void *)"Th 1");
}

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.

stdio (printf, putc, getc, etc), malloc & new in ISR

Because of the mutexes in the ARM C standard library you cannot use stdio (printf, putc, getc, etc), malloc and new in ISR!

Import library

Public Member Functions

  Mutex ()
  Create and Initialize a Mutex object.
osStatus  lock (uint32_t millisec=osWaitForever)
  Wait until a Mutex becomes available.
bool  trylock ()
  Try to lock the mutex, and return immediately.
osStatus  unlock ()
  Unlock the mutex that has previously been locked by the same thread.

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 program

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

Semaphore two_slots(2);

void test_thread(void const *name) {
    while (true) {
        two_slots.wait();
        printf("%s\n\r", (const char*)name);
        Thread::wait(1000);
        two_slots.release();
    }
}

int main (void) {
    Thread t2(test_thread, (void *)"Th 2");
    Thread t3(test_thread, (void *)"Th 3");
    
    test_thread((void *)"Th 1");
}

Import library

Public Member Functions

  Semaphore (int32_t count)
  Create and Initialize a Semaphore object used for managing resources.
int32_t  wait (uint32_t millisec=osWaitForever)
  Wait until a Semaphore resource becomes available.
osStatus  release (void)
  Release a Semaphore resource that was obtain with Semaphore::wait .

Signals

Each Thread can be notified and wait for signals:

Import program

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

DigitalOut led(LED1);

void led_thread(void const *argument) {
    while (true) {
        // Signal flags that are reported as event are automatically cleared.
        Thread::signal_wait(0x1);
        led = !led;
    }
}

int main (void) {
    Thread thread(led_thread);
    
    while (true) {
        Thread::wait(1000);
        thread.signal_set(0x1);
    }
}

Queue

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

/media/uploads/emilmont/messagequeue.png

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 programrtos_queue

Basic example showing the Queue and MemoryPool API

Import library

Public Member Functions

  Queue ()
  Create and initialise a message Queue .
osStatus  put (T *data, uint32_t millisec=0)
  Put a message in a Queue .
osEvent   get (uint32_t millisec=osWaitForever)
  Get a message or Wait for a message from a Queue .

MemoryPool

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

MemoryPool<message_t, 16> mpool;

message_t *message = mpool.alloc();

mpool.free(message);

Import program

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

typedef struct {
    float    voltage;   /* AD result of measured voltage */
    float    current;   /* AD result of measured current */
    uint32_t counter;   /* A counter value               */
} message_t;

MemoryPool<message_t, 16> mpool;
Queue<message_t, 16> queue;

/* Send Thread */
void send_thread (void const *args) {
    uint32_t i = 0;
    while (true) {
        i++; // fake data update
        message_t *message = mpool.alloc();
        message->voltage = (i * 0.1) * 33; 
        message->current = (i * 0.1) * 11;
        message->counter = i;
        queue.put(message);
        Thread::wait(1000);
    }
}

int main (void) {
    Thread thread(send_thread);
    
    while (true) {
        osEvent evt = queue.get();
        if (evt.status == osEventMessage) {
            message_t *message = (message_t*)evt.value.p;
            printf("\nVoltage: %.2f V\n\r"   , message->voltage);
            printf("Current: %.2f A\n\r"     , message->current);
            printf("Number of cycles: %u\n\r", message->counter);
            
            mpool.free(message);
        }
    }
}

Import library

Public Member Functions

  MemoryPool ()
  Create and Initialize a memory pool.
T *  alloc (void)
  Allocate a memory block of type T from a memory pool.
T *  calloc (void)
  Allocate a memory block of type T from a memory pool and set memory block to zero.
osStatus  free (T *block)
  Return an allocated memory block back to a specific memory pool.

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 program

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

/* Mail */
typedef struct {
  float    voltage; /* AD result of measured voltage */
  float    current; /* AD result of measured current */
  uint32_t counter; /* A counter value               */
} mail_t;

Mail<mail_t, 16> mail_box;

void send_thread (void const *args) {
    uint32_t i = 0;
    while (true) {
        i++; // fake data update
        mail_t *mail = mail_box.alloc();
        mail->voltage = (i * 0.1) * 33; 
        mail->current = (i * 0.1) * 11;
        mail->counter = i;
        mail_box.put(mail);
        Thread::wait(1000);
    }
}

int main (void) {
    Thread thread(send_thread);
    
    while (true) {
        osEvent evt = mail_box.get();
        if (evt.status == osEventMail) {
            mail_t *mail = (mail_t*)evt.value.p;
            printf("\nVoltage: %.2f V\n\r"   , mail->voltage);
            printf("Current: %.2f A\n\r"     , mail->current);
            printf("Number of cycles: %u\n\r", mail->counter);
            
            mail_box.free(mail);
        }
    }
}

Import library

Public Member Functions

  Mail ()
  Create and Initialise Mail queue.
T *  alloc (uint32_t millisec=0)
  Allocate a memory block of type T.
T *  calloc (uint32_t millisec=0)
  Allocate a memory block of type T and set memory block to zero.
osStatus  put (T *mptr)
  Put a mail in the queue.
osEvent   get (uint32_t millisec=osWaitForever)
  Get a mail from a queue.
osStatus  free (T *mptr)
  Free a memory block from a mail.

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 program

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

DigitalOut LEDs[4] = {
    DigitalOut(LED1), DigitalOut(LED2), DigitalOut(LED3), DigitalOut(LED4)
};

void blink(void const *n) {
    LEDs[(int)n] = !LEDs[(int)n];
}

int main(void) {
    RtosTimer led_1_timer(blink, osTimerPeriodic, (void *)0);
    RtosTimer led_2_timer(blink, osTimerPeriodic, (void *)1);
    RtosTimer led_3_timer(blink, osTimerPeriodic, (void *)2);
    RtosTimer led_4_timer(blink, osTimerPeriodic, (void *)3);
    
    led_1_timer.start(2000);
    led_2_timer.start(1000);
    led_3_timer.start(500);
    led_4_timer.start(250);
    
    Thread::wait(osWaitForever);
}

Import library

Public Member Functions

  RtosTimer (void(*task)(void const *argument), os_timer_type type=osTimerPeriodic, void *argument=NULL)
  Create and Start timer.
osStatus  stop (void)
  Stop the timer.
osStatus  start (uint32_t millisec)
  start a timer.

Interrupt Service Routines

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

  • Mutex 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 program

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

Queue<uint32_t, 5> queue;

DigitalOut myled(LED1);

void queue_isr() {
    queue.put((uint32_t*)2);
    myled = !myled;
}

void queue_thread(void const *args) {
    while (true) {
        queue.put((uint32_t*)1);
        Thread::wait(1000);
    }
}

int main (void) {
    Thread thread(queue_thread);
    
    Ticker ticker;
    ticker.attach(queue_isr, 1.0);
    
    while (true) {
        osEvent evt = queue.get();
        if (evt.status != osEventMessage) {
            printf("queue->get() returned %02x status\n\r", evt.status);
        } else {
            printf("queue->get() returned %d\n\r", evt.value.v);
        }
    }
}

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.

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:

[Not found]

Implementation

The mbed RTOS is based on the CMSIS RTOS.




6 related questions:


105 comments:

14 Feb 2012

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

» Show archived comment by abuckton
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?

» Show archived comment by emilmont
14 Feb 2012

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

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

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.

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.

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

» Show archived comment by tylerjw
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?

» Show archived comment by emilmont
16 Feb 2012

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.

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:

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

for member method callbacks.

» Show archived comment by hugov
16 Feb 2012

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.

» Show archived comment by tylerjw
16 Feb 2012

Emilio Monti wrote:

Do you have a published program presenting the issue?

Import 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.

» Show archived comment by emilmont
16 Feb 2012

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.

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... :-)

» Show archived comment by emilmont
16 Feb 2012

Tyler Weaver wrote:

Import 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:

char* teststring = "";

With that:

char teststring[40];
» Show archived comment by tylerjw
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.

» Show archived comment by emilmont
16 Feb 2012

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

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

Posting new comments for this page has been disabled