5.7 FreeRTOS Guide
This chapter describes the FreeRTOS middleware layout, the source of the kernel interfaces, and the single LED_Blink example in the N32G033 development package. Read the FreeRTOS kernel documentation and the “Interrupts and events” chapter of the user manual for task scheduling, the port layer, and interrupt priorities, and the errata sheet for known chip issues.
5.7.1 Related files
| Relative path | Purpose |
|---|---|
Middlewares/freertos/include/FreeRTOSConfig.h |
Configures the kernel: tick rate, maximum priority count, minimum stack and heap size, and preemption, and maps the port exception handlers |
Middlewares/freertos/include/task.h |
Declares the task-management interfaces for task creation, scheduler start, delay, deletion, and the critical section |
Middlewares/freertos/tasks.c |
Implements the kernel functions for task management and the scheduler |
Middlewares/freertos/portable/RVDS/ARM_CM0/port.c |
Provides the Cortex-M0 port layer, including the functions needed to start the scheduler, switch tasks, and produce the tick |
projects/n32g033_EVAL/examples/FreeRTOS/ |
FreeRTOS example root directory, with 1 example: LED_Blink |
projects/n32g033_EVAL/examples/FreeRTOS/LED_Blink/ |
Project layout of this example: MDK-ARM/, EWARM/, inc/, src/, and readme.txt |
projects/n32g033_EVAL/bsp/ |
Board support files shared by the examples: the UART log interface in log.h |
5.7.2 API reference
This module provides no separate driver library; its interfaces come from the FreeRTOS kernel and configuration inside the development package. The task-management interfaces are declared in Middlewares/freertos/include/task.h and implemented in Middlewares/freertos/tasks.c; the kernel behavior is configured by Middlewares/freertos/include/FreeRTOSConfig.h, where the tick rate, the maximum priority count, and the heap size are the most used entries; the Cortex-M0 port implementations are in Middlewares/freertos/portable/RVDS/ARM_CM0/port.c and Middlewares/freertos/portable/IAR/ARM_CM0/port.c. The interfaces used by the example are xTaskCreate, vTaskStartScheduler, vTaskDelay, vTaskDelete, and xTaskGetSchedulerState plus the critical-section macros taskENTER_CRITICAL and taskEXIT_CRITICAL; the tick and context switching are handled by xPortSysTickHandler, vPortSVCHandler, and xPortPendSVHandler from the port layer.
5.7.3 Demo descriptions
This section describes the single FreeRTOS example: LED_Blink uses three tasks to toggle an LED, print a counter, and control the timing, and uses the TIM3 update interrupt to toggle one more LED.
5.7.3.1 LED_Blink Task-scheduled blinking
Example directory: projects/n32g033_EVAL/examples/FreeRTOS/LED_Blink/
Initialize the UART, the LED pins, TIM3, and the system tick→Create the start task and start the scheduler→The start task creates the LED and log tasks in a critical section and then deletes itself→The LED task toggles an LED and waits, and the log task prints and waits
| Item | Source analysis |
|---|---|
| Function | Uses FreeRTOS to schedule an LED toggle task and a UART print task, and uses the TIM3 update interrupt to toggle one more LED, showing a task and an interrupt side by side. |
| Source files | src/main.c holds the task priority and stack-size macros plus main, bsp_initialized, start_task, led_task, and log_task;LED_GPIO_Config in src/led.c configures the two LED pins;TIM3_Config and the interrupt handler in src/timer.c toggle one LED;SysTick_Handler in src/n32g033_it.c forwards the tick to the kernel;the project files are in MDK-ARM/ and EWARM/, and readme.txt gives the configuration and test steps. |
| Key configuration | The start, log, and LED tasks have priorities 0, 1, and 2 with a stack size of 100 each; the tick rate is 1000 Hz (one tick every 1 ms) and the maximum priority count is 5; LED2 on PA7 and LED3 on PB1 are push-pull outputs, and TIM3 uses a prescaler of 1000-1 and a period of 64000-1;UART: 115200 baud, TX PA9 and RX PA10. |
| Program and data path | main -> bsp_initialized sets up the UART, the LED pins, TIM3, and a 1 ms system tick -> xTaskCreate creates the start task -> vTaskStartScheduler starts the scheduler -> the start task creates the LED and log tasks in a critical section and then deletes itself -> the LED task toggles PA7 and waits 500 ticks, and the log task prints the counter and waits 1000 ticks. |
| Main APIs | This module has no separate driver library; the example uses the kernel interfaces in Middlewares/freertos/, see the API reference section. |
| Verification | Compile, download, and reset; LED2 blinks every 500 ms, LED3 blinks every 1 s, and the UART prints one cumulative count per second. |
| Use note | The development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board; the system clock is 64 MHz. |
Core-code walkthrough and porting
The example splits the LED toggle and the log print into two tasks, uses a start task to create them and then delete itself, and uses a TIM3 interrupt to show an interrupt and a task side by side.
Data path: main → bsp_initialized sets up the UART, the LEDs, and TIM3 and sets a 1 ms tick → xTaskCreate creates the start task → vTaskStartScheduler starts the scheduler → the start task creates the LED and log tasks and then deletes itself with vTaskDelete → both tasks yield the CPU with vTaskDelay
1. The hardware and the tick are initialized once before the kernel starts. bsp_initialized sets up the UART, the LEDs, TIM3, and the system tick, and only then does main create the task and start the scheduler.
int main(void)
{
/* hardware initialization */
bsp_initialized();
/* Create Start Task */
xTaskCreate((TaskFunction_t )start_task, //Task function
(const char* )"start_task", //Task name
(uint16_t )START_STK_SIZE, //Task stack size
(void* )NULL, //Parameters passed to the task function
(UBaseType_t )START_TASK_PRIO, //Task priority
(TaskHandle_t* )&StartTask_Handler); //task handle
/* Start task scheduling */
vTaskStartScheduler();
while (1)
{
}
}
void bsp_initialized(void)
{
log_init();
LED_GPIO_Config();
TIM3_Config();
SysTick_Config(SystemCoreClock / configTICK_RATE_HZ);
printf("bsp_initialized \r\n");
}
The tick is set to 1 ms by SysTick_Config(SystemCoreClock / configTICK_RATE_HZ), so keep the reload value consistent with the kernel tick when porting, or the task delays will drift.
2. The start task creates the other tasks in a critical section and then deletes itself. Entering the critical section keeps the creation from being preempted, and the start task is deleted once the two tasks exist.
void start_task(void *pvParameters)
{
/* Entering the critical zone */
taskENTER_CRITICAL();
/* Create LED Test task */
xTaskCreate((TaskFunction_t )led_task,
(const char* )"led_task",
(uint16_t )LED_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED_TASK_PRIO,
(TaskHandle_t* )&LEDTask_Handler);
/* Create Uart Test task */
xTaskCreate((TaskFunction_t )log_task,
(const char* )"log_task",
(uint16_t )LOG_STK_SIZE,
(void* )NULL,
(UBaseType_t )LOG_TASK_PRIO,
(TaskHandle_t* )&LOGTask_Handler);
/* Delete Start Task */
vTaskDelete(StartTask_Handler);
/* Exit the critical zone */
taskEXIT_CRITICAL();
}
After the start task is deleted, the kernel reclaims its stack and memory; if you create the two tasks before the scheduler starts instead, you do not need this start-task layer.
3. Each application task yields the CPU with a delay. The LED task toggles PA7 and waits 500 ticks, and the log task prints and waits 1000 ticks.
void led_task(void *pvParameters)
{
while(1)
{
GPIO_TogglePin(GPIOA, GPIO_PIN_7);
vTaskDelay(500);
}
}
void log_task(void *pvParameters)
{
uint32_t num = 0;
while(1)
{
num += 1;
printf("num: %d \r\n",num);
vTaskDelay(1000);
}
}
A task is an infinite loop and must end in a blocking call such as vTaskDelay, or tasks of the same priority never run.
4. The tick interrupt is forwarded to the kernel, and the TIM3 interrupt works on its own. The system tick handler hands the tick to the kernel once the scheduler is running, and the TIM3 update interrupt toggles PB1 separately.
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED) //The system is already running
{
xPortSysTickHandler();
}
}
void TIM3_IRQHandler(void)
{
if (TIM_GetIntStatus(TIM3, TIM_INT_UPDATE) != RESET)
{
TIM_ClrIntPendingBit(TIM3, TIM_INT_UPDATE);
GPIO_TogglePin(GPIOB, GPIO_PIN_1);
}
}
When porting, map these exception handler names to the kernel configuration and set the priority of any interrupt that calls a kernel interface within the range the kernel allows.
Corresponding source files: src/main.c, src/led.c, src/timer.c, src/n32g033_it.c, Middlewares/freertos/include/FreeRTOSConfig.h
5.7.4 Notes and troubleshooting
| Scenario | Check & action | Source |
|---|---|---|
| Interrupt priority levels | The Interrupts and events chapter of the user manual states that the N32G033 has 4 programmable priority levels (using 2 priority bits); do not exceed that many levels when you set a kernel or peripheral interrupt priority. |
User manual |
| Priority of interrupts that call kernel interfaces | The Interrupts and events chapter of the user manual gives the vector priority of each exception, such as SVCall 3, PendSV 5, and SysTick 6; an interrupt that calls a kernel interface must be set within the range the kernel allows. |
User manual |
| Tick and time base | The Interrupts and events chapter of the user manual states that the SysTick calibration value is fixed at 8000 and that a SysTick clock of 8 MHz produces a 1 ms time base; keep the tick rate consistent with the SysTick setup, or the task delays drift. |
User manual |
| Exceptions used for task switching | The Interrupts and events chapter of the user manual states that the kernel uses SVC and PendSV to start the scheduler and switch tasks and uses SysTick to produce the tick; do not turn these exception handlers into empty implementations. |
User manual |
Errata sheet V1.0.0 (2025.9.25) covers revision A silicon and lists no FreeRTOS entries.