Mistake on this page?
Report an issue in GitHub or email us

Execution

Threads

Your application (main function) starts execution in the main thread, but it's not the only thread running in Mbed OS. There are many threads running system services, such as:

  • Main - The default thread that executes the application's main function. The main thread has 4kB of stack space by default. The application can configure it in mbed_app.json by defining the MAIN_STACK_SIZE parameter.
  • Idle - The thread that's run by the scheduler when there's no other activity in the system (e.g. all other threads are waiting for some event). It's used to make sure the board is not burning empty processor cycles, but is put to sleep for as long as possible.
  • Timer - The thread that handles system and user timer objects. Note: The user timer class RtosTimer is deprecated. You should not use it for new designs. Use EventQueue instead.

On top of the standard system threads, some drivers may use additional threads. Users can create threads using the Thread class.

Modes

Mbed OS executes in two modes:

  • Thread mode - Default application mode. All user threads execute in this mode. It uses dedicated thread specific stack memory.
  • Handler mode - Interrupt mode, system code and interrupt handlers execute in this mode. It uses static system ISR stack memory.

Handler mode

All the ISR handlers execute in this mode. You can use the same RTOS API in ISR handlers. The important difference between the modes is that code written for the handler mode can't wait; it executes as fast as possible and returns to the thread mode. Therefore:

  • You cannot use Mutex.
  • Wait in ISR is not allowed; all the timeouts in method parameters have to be set to 0.

ISR example

This example uses a message from the queue to trigger an interrupt.

#include "mbed.h"

Ticker ticker;
Thread thread;
Queue<const char*, 5> trail;

// Since we're printing from multiple threads, we need a mutex
Mutex print_lock;

enum ExecutionTypes {
    IDLE,
    USER,
    ISR
};

const char* ExecutionMessages[] = {
    "the idle thread",
    "a user thread",
    "interrupt context"
};

void handler() {
    // Check to see if we're in interrupt context
    if (core_util_is_isr_active()) {
        // Do not print since we're in interrupt context
        trail.put(&(ExecutionMessages[ISR]));
    } else {
        // Safe to print since we're in a user thread
        print_lock.lock();
        printf("Starting user thread\r\n");
        print_lock.unlock();
        while(true) {
            trail.put(&(ExecutionMessages[USER]));
            wait(5);
        }
    }
}

void custom_idle_function() {
    // Custom idle behavior would go here
    // We won't print here since the default idle thread's stack is too small
    trail.put(&(ExecutionMessages[IDLE]));

    // Switch back to the default idle behavior
    Kernel::attach_idle_hook(NULL);
}

int main() {
    printf("Starting execution example\r\n");

    // Attach the custom idle thread function
    Kernel::attach_idle_hook(custom_idle_function);

    // Trigger the interrupt every 3 seconds
    ticker.attach(handler, 3);

    // Start the user thread
    thread.start(handler);

    // Get the past exectuion trail
    while (true) {
        osEvent evt = trail.get();
        if (evt.status != osEventMessage) {
            print_lock.lock();
            printf("Failed to retrieve the execution trail (returned %02x)\r\n", evt.status);
            print_lock.unlock();
        } else {
            print_lock.lock();
            printf("Execution was in %s\r\n", *(const char**)evt.value.v);
            print_lock.unlock();
        }
    }
}

Important Information for this Arm website

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies. If you are not happy with the use of these cookies, please review our Cookie Policy to learn how they can be disabled. By disabling cookies, some features of the site will not work.