5.7 FreeRTOS 使用说明
本章说明 N32G033 开发包中 FreeRTOS 的中间件组成、内核接口来源,以及 LED_Blink 1 个示例。任务调度、移植层与中断优先级请配合 FreeRTOS 内核说明与用户手册的“中断和事件”章节阅读,芯片已知问题以勘误手册为准。
5.7.1 相关文件
| 相对路径 | 作用 |
|---|---|
Middlewares/freertos/include/FreeRTOSConfig.h |
配置内核行为:节拍频率、最大优先级数、最小栈与堆大小、抢占开关,并映射移植层的异常处理函数 |
Middlewares/freertos/include/task.h |
声明任务创建、调度启动、延时、删除与临界区等任务管理接口 |
Middlewares/freertos/tasks.c |
实现任务管理与调度器等内核功能 |
Middlewares/freertos/portable/RVDS/ARM_CM0/port.c |
提供 Cortex-M0 的移植层实现,含启动调度、任务切换与节拍所需函数 |
projects/n32g033_EVAL/examples/FreeRTOS/ |
FreeRTOS 示例根目录,含 LED_Blink 1 个示例 |
projects/n32g033_EVAL/examples/FreeRTOS/LED_Blink/ |
该示例的工程目录组成:MDK-ARM/、EWARM/、inc/、src/、readme.txt |
projects/n32g033_EVAL/bsp/ |
示例共用的板级支持文件:log.h 的串口日志接口 |
5.7.2 API 参考
本模块不提供独立的驱动库,接口来自开发包内的 FreeRTOS 内核与配置。任务管理接口声明于 Middlewares/freertos/include/task.h,由 Middlewares/freertos/tasks.c 实现;内核行为由 Middlewares/freertos/include/FreeRTOSConfig.h 配置,节拍频率、最大优先级数与堆大小是其中最常用的几项;Cortex-M0 的移植实现分别位于 Middlewares/freertos/portable/RVDS/ARM_CM0/port.c 与 Middlewares/freertos/portable/IAR/ARM_CM0/port.c。示例用到的接口有 xTaskCreate、vTaskStartScheduler、vTaskDelay、vTaskDelete、xTaskGetSchedulerState 以及临界区宏 taskENTER_CRITICAL、taskEXIT_CRITICAL;节拍与上下文切换由移植层提供的 xPortSysTickHandler、vPortSVCHandler、xPortPendSVHandler 完成。
5.7.3 Demo 示例说明
本节说明 FreeRTOS 的 1 个示例:LED_Blink 用三个任务分别负责翻转 LED、打印计数与控制时序,并由 TIM3 更新中断额外翻转一个 LED。
5.7.3.1 LED_Blink 任务调度闪灯
工程目录: projects/n32g033_EVAL/examples/FreeRTOS/LED_Blink/
初始化串口、LED 引脚、TIM3 与系统节拍→创建起始任务并启动调度器→起始任务以临界区创建 LED 与日志任务后删除自身→LED 任务周期翻转 LED 并延时、日志任务周期打印并延时
| 项目 | 源码解读 |
|---|---|
| 功能 | 用 FreeRTOS 调度 LED 翻转任务与串口打印任务,并由 TIM3 更新中断额外翻转一个 LED,展示任务与中断并存。 |
| 源码组成 | src/main.c 含任务优先级与栈大小宏以及 main、bsp_initialized、start_task、led_task、log_task;src/led.c 的 LED_GPIO_Config 配置两个 LED 引脚;src/timer.c 的 TIM3_Config 与中断处理函数翻转一个 LED;src/n32g033_it.c 的 SysTick_Handler 把节拍转发给内核;工程文件在 MDK-ARM/ 与 EWARM/,readme.txt 给出配置与测试步骤。 |
| 关键配置 | 起始任务、日志任务、LED 任务优先级依次为 0、1、2,栈大小均为 100; 节拍频率 1000 Hz(1 ms 一个节拍),最大优先级数 5; LED2 接 PA7、LED3 接 PB1 均推挽输出,TIM3 预分频 1000-1、周期 64000-1;串口 115200,TX PA9、RX PA10。 |
| 程序与数据路径 | main → bsp_initialized 初始化串口、LED 引脚、TIM3 并配置 1 ms 系统节拍 → xTaskCreate 创建起始任务 → vTaskStartScheduler 启动调度 → 起始任务在临界区内创建 LED 任务与日志任务后删除自身 → LED 任务每次翻转 PA7 后延时 500 个节拍,日志任务每次打印计数值后延时 1000 个节拍。 |
| 主要 API | 本模块无独立驱动库,示例使用 Middlewares/freertos/ 的内核接口,见 API 参考节。 |
| 验证方法 | 编译烧录并复位,LED2 每 500 ms 闪烁,LED3 每 1 s 闪烁,串口每秒打印一个累加数。 |
| 使用提示 | 开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1,硬件基于评估板 N32G033K8Q7-1_STB V1.0; 系统时钟 64 MHz。 |
核心代码解读与移植
示例把 LED 翻转与日志打印拆成两个任务,用起始任务完成创建后再删除自身,并用 TIM3 中断演示中断与任务并存。
数据路径: main → bsp_initialized 初始化串口、LED 与 TIM3 并设 1 ms 节拍 → xTaskCreate 建起始任务 → vTaskStartScheduler 启动调度 → 起始任务建 LED 与日志任务后 vTaskDelete 自删 → 两个任务各用 vTaskDelay 让出 CPU
1. 硬件与节拍在内核启动前一次性初始化。 bsp_initialized 备好串口、LED、TIM3 与系统节拍后,main 才创建任务并启动调度。
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");
}
节拍由 SysTick_Config(SystemCoreClock / configTICK_RATE_HZ) 定成 1 ms,移植时要保证重装载值与内核节拍一致,否则任务延时的时间会失准。
2. 起始任务在临界区内创建其余任务后删除自身。 进入临界区可避免建任务过程中被调度打断,两个任务建好后再删除起始任务。
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();
}
删除起始任务后它的栈与内存由内核回收;若把两个任务改到调度启动前创建,则不需要起始任务这一层。
3. 两个业务任务各自用延时让出 CPU。 LED 任务翻转 PA7 后延时 500 个节拍,日志任务打印后延时 1000 个节拍。
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);
}
}
任务是死循环且必须以 vTaskDelay 之类的阻塞调用收尾,否则同优先级的其他任务得不到运行。
4. 节拍中断转发给内核,TIM3 中断独立工作。 系统节拍处理函数在调度器启动后把节拍交给内核,TIM3 更新中断则单独翻转 PB1。
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);
}
}
移植时要把这几个异常处理函数名与内核配置对应,并把调用内核接口的中断优先级设到内核允许的范围内。
对应源码: src/main.c、src/led.c、src/timer.c、src/n32g033_it.c、Middlewares/freertos/include/FreeRTOSConfig.h
5.7.4 注意与排障
| 场景 | 检查与建议 | 来源 |
|---|---|---|
| 中断优先级级数 | 用户手册中断和事件章写明 N32G033 有 4 个可编程优先等级(使用 2 位中断优先级); 配置内核与外设中断优先级时不要超过该级数。 |
用户手册 |
| 调用内核接口的中断优先级 | 用户手册中断和事件章给出各异常的向量优先级,如 SVCall 为 3、PendSV 为 5、SysTick 为 6; 调用内核接口的中断需按内核要求设置在允许范围内。 |
用户手册 |
| 节拍与时间基准 | 用户手册中断和事件章写明系统嘀嗒校准值固定为 8000,系统嘀嗒时钟设为 8 MHz 时产生 1 ms 时间基准; 节拍频率要与系统嘀嗒配置一致,否则任务延时失准。 |
用户手册 |
| 任务切换依赖的异常 | 用户手册中断和事件章写明 SVC 与 PendSV 由内核用于启动调度与任务切换,SysTick 用于产生节拍; 不要把这些异常的处理函数改为空实现。 |
用户手册 |
勘误手册 V1.0.0(2025.9.25)针对 A 版芯片,未列出 FreeRTOS 相关条目。