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 librarymbed-rtos

No documentation found.

/media/uploads/JimCarver/thread_priority.png

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 librarymbed-rtos

No documentation found.

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 librarymbed-rtos

No documentation found.

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 librarymbed-rtos

No documentation found.

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 librarymbed-rtos

No documentation found.

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 librarymbed-rtos

No documentation found.

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 librarymbed-rtos

No documentation found.

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.


Discussion topics

Nothing here yet!

Questions