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
- Creating and starting NitroOS tasks
- Using delays and yielding between tasks
- Protecting shared resources with mutexes
- Basic GPIO control for LED blinking
Prerequisites
- NitroCortex SDK installed (Getting Started Guide)
- STM32F4-Discovery board (or use NitroSIM emulator)
- ARM GCC toolchain
Start by creating a new NitroOS project:
nitro new hello-rtos --target stm32f407vg --components nitroos
cd hello-rtos
Open src/main.c and replace its contents with the following code:
/**
* 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;
}
Task Creation
Each task is created with NT_TaskCreate() specifying:
- Task handle: Reference to the task for later control
- Name: Human-readable identifier for debugging
- Entry function: The function that runs as the task
- Stack: Dedicated memory for the task's stack
- Priority: Higher number = higher priority (0-31)
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.
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.
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
If running on hardware, you should see:
- Green LED (PD12): Blinking at 1 Hz
- Orange LED (PD13): Blinking at 2 Hz
- UART output: Status messages every 5 seconds
Expected UART output:
Exercises
Try these modifications to deepen your understanding:
- Add a third LED: Create another task to blink the red LED (PD14) at 0.5 Hz
- Change priorities: Set the status task to priority 3 and observe any changes
- Add a semaphore: Use
NT_SemCreate()to signal between tasks - Measure timing: Use
NT_GetTickCount()to measure actual blink intervals
Next Steps
- API Overview - Explore all NitroOS functions
- NitroOS Product Page - Full feature list
- Contact Us - Get early access to more tutorials