Your first step into the RTOS world. One task. One LED. One console message. But an entirely new paradigm.
Create a single FreeRTOS task on STM32F446RE using CMSIS-RTOS v2 that toggles the onboard LED every 500 ms, and observe real-time execution via the SWV ITM Data Console in STM32CubeIDE.
| Component | Qty |
|---|---|
| STM32 Nucleo-F446RE | 1 |
| USB Type-A to Mini-B cable | 1 |
main() → while(1) { toggle; HAL_Delay(500); toggle; ... }
CPU is STUCK during HAL_Delay — can't do anything else
Scheduler starts
│
└─► Task_1 (RUNNING)
│
├── HAL_GPIO_TogglePin()
├── printf("Task_1 Executing...")
└── osDelay(500) ◄── Task enters BLOCKED state
CPU is FREE for other tasks
│
500ms later...
│
Task_1 → READY → RUNNING again
osThreadNew()
│
▼
┌─────────┐
│ READY │◄──────────────────────┐
└────┬────┘ │
│ Scheduler selects it │
▼ │
┌─────────┐ │
│ RUNNING │──── osDelay(500) ───► │
└─────────┘ ┌────┴────┐
│ BLOCKED │
└─────────┘
- PA5 → GPIO Output (LED LD2)
| Setting | Value |
|---|---|
| RCC | BYPASS Clock Source |
| HCLK | 84 MHz |
| SYS → Debug | Trace Asynchronous Sw |
| SYS → Timebase Source | TIM6 (not SysTick — FreeRTOS uses it) |
- Middleware & Software Packs → FreeRTOS → Interface: CMSIS_V2
- Tasks & Queues tab → configure the default task:
| Field | Value |
|---|---|
| Task Name | Task_1 |
| Priority | osPriorityNormal |
| Entry Function | Task1_function |
| Stack Size | 128 words (default) |
- Project Properties → C/C++ Build → Settings → check
printfandscanffloat support
- Project name → STM32CubeIDE → Generate Code → Open Project
Debug icon → Edit Debug Configurations
│
├── Debugger tab:
│ └── ST-LINK → click SCAN → board S/N auto-detected
│
└── Serial Wire Viewer (SWV):
├── ☑ Enable
├── Core Clock: 84.0 MHz ← must match your clock config!
└── Apply → Close
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes *//* USER CODE BEGIN 0 */
int _write(int file, char *ptr, int len) {
for (int i = 0; i < len; i++) {
ITM_SendChar(*ptr++);
}
return len;
}
/* USER CODE END 0 */
ITM_SendChar()sends one character at a time through the Instrumentation Trace Macrocell — a built-in Cortex-M4 debug channel that requires zero UART pins.
/* USER CODE END Header_Task1_function */
void Task1_function(void *argument) {
/* USER CODE BEGIN 5 */
for (;;) {
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
printf("Task_1 Executing for LED Toggle \n");
osDelay(500); // Blocks task for 500ms; CPU is free
}
/* USER CODE END 5 */
}Step 1: Window → Show View → Other → SWV → SWV ITM Data Console
Step 2: Click Debug button → Switch to Debug perspective
Step 3: In SWV ITM Data Console:
☑ Enable PC Sampling
☑ Port 0 ← this is where ITM_SendChar(0) outputs
Step 4: Click ● Start Trace (red dot)
Step 5: Click ▶ Resume
Expected output on Port 0:
Task_1 Executing for LED Toggle
Task_1 Executing for LED Toggle
Task_1 Executing for LED Toggle
... (every 500 ms)
| Feature | HAL_Delay(500) |
osDelay(500) |
|---|---|---|
| CPU during delay | Spinning (busy) | Released to scheduler |
| Other tasks can run | ❌ No | ✅ Yes |
| Timing accuracy | Depends on SysTick | Managed by RTOS kernel |
| Use in ISR | Allowed | ❌ Not allowed |
A single FreeRTOS task (Task_1) was created using the CMSIS-RTOS v2 interface. The LED toggled at a precise 500 ms interval driven by osDelay(), and real-time execution was verified non-intrusively via the SWV ITM Data Console on Port 0.