NitroOS Hello World

Difficulty: Beginner Time: ~30 minutes Target: STM32F4

In this tutorial, you'll create your first NitroOS application with multiple tasks, LED control, and mutex synchronization. By the end, you'll understand the fundamentals of RTOS programming with NitroCortex.

What You'll Learn

Prerequisites

1 Create the Project

Start by creating a new NitroOS project:

nitro new hello-rtos --target stm32f407vg --components nitroos
cd hello-rtos
2 Write the Main Application

Open src/main.c and replace its contents with the following code:

src/main.c C
/**
 * NitroOS Hello World - Multi-task LED Blink
 * Demonstrates task creation, delays, and mutex usage
 */

#include <nitroos.h>
#include <stm32f4xx.h>

// Task handles
static NT_TASK task_led_green;
static NT_TASK task_led_orange;
static NT_TASK task_status;

// Mutex for UART output
static NT_MUTEX uart_mutex;

// Task stacks
static uint32_t stack_green[256];
static uint32_t stack_orange[256];
static uint32_t stack_status[512];

/**
 * Green LED task - blinks at 1 Hz
 */
void task_led_green_entry(void* arg)
{
    (void)arg;

    while (1) {
        // Toggle green LED (PD12)
        GPIOD->ODR ^= GPIO_ODR_OD12;

        // Delay 500ms (1 Hz blink)
        NT_TaskDelay(500);
    }
}

/**
 * Orange LED task - blinks at 2 Hz
 */
void task_led_orange_entry(void* arg)
{
    (void)arg;

    while (1) {
        // Toggle orange LED (PD13)
        GPIOD->ODR ^= GPIO_ODR_OD13;

        // Delay 250ms (2 Hz blink)
        NT_TaskDelay(250);
    }
}

/**
 * Status task - prints system info every 5 seconds
 */
void task_status_entry(void* arg)
{
    (void)arg;
    uint32_t counter = 0;

    while (1) {
        // Acquire mutex before UART access
        NT_MutexLock(&uart_mutex, NT_WAIT_FOREVER);

        // Print status (thread-safe)
        printf("[%lu] NitroOS running - Tasks: 3, Uptime: %lu sec\n",
               counter++, NT_GetTickCount() / 1000);

        // Release mutex
        NT_MutexUnlock(&uart_mutex);

        // Wait 5 seconds
        NT_TaskDelay(5000);
    }
}

/**
 * Initialize GPIO for LEDs
 */
void gpio_init(void)
{
    // Enable GPIOD clock
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;

    // Configure PD12, PD13 as outputs
    GPIOD->MODER |= GPIO_MODER_MODE12_0 | GPIO_MODER_MODE13_0;
}

/**
 * Application entry point
 */
int main(void)
{
    // Initialize hardware
    gpio_init();

    // Initialize NitroOS
    NT_Init();

    // Create mutex for UART
    NT_MutexCreate(&uart_mutex, "uart_mtx");

    // Create tasks with different priorities
    NT_TaskCreate(&task_led_green, "LED_Green",
                   task_led_green_entry, NULL,
                   stack_green, sizeof(stack_green), 2);

    NT_TaskCreate(&task_led_orange, "LED_Orange",
                   task_led_orange_entry, NULL,
                   stack_orange, sizeof(stack_orange), 2);

    NT_TaskCreate(&task_status, "Status",
                   task_status_entry, NULL,
                   stack_status, sizeof(stack_status), 1);

    // Start the scheduler - never returns
    NT_Start();

    return 0;
}
3 Understanding the Code

Task Creation

Each task is created with NT_TaskCreate() specifying:

Task Delays

NT_TaskDelay(ms) suspends the current task for the specified milliseconds, allowing other tasks to run. This is non-blocking - the CPU executes other ready tasks.

Mutex Synchronization

The uart_mutex protects the UART output from concurrent access. Without it, output from multiple tasks could interleave and become garbled.

Priority Inheritance

NitroOS mutexes support priority inheritance - if a high-priority task waits on a mutex held by a low-priority task, the low-priority task temporarily inherits the higher priority to prevent priority inversion.

4 Build and Flash

Build the project and flash to your board:

# Build
cmake -B build -G Ninja
cmake --build build

# Flash to STM32F4-Discovery
nitro flash build/hello-rtos.elf

# Or run in NitroSIM emulator
nitrosim --elf build/hello-rtos.elf --target stm32f407vg
5 Observe the Results

If running on hardware, you should see:

Expected UART output:

[0] NitroOS running - Tasks: 3, Uptime: 0 sec [1] NitroOS running - Tasks: 3, Uptime: 5 sec [2] NitroOS running - Tasks: 3, Uptime: 10 sec ...

Exercises

Try these modifications to deepen your understanding:

  1. Add a third LED: Create another task to blink the red LED (PD14) at 0.5 Hz
  2. Change priorities: Set the status task to priority 3 and observe any changes
  3. Add a semaphore: Use NT_SemCreate() to signal between tasks
  4. Measure timing: Use NT_GetTickCount() to measure actual blink intervals

Next Steps