FreeRTOS【3-1】创建第一个多任务程序复习笔记
一、整体知识框架与学习目标
本次课程聚焦 FreeRTOS 多任务程序创建,核心是掌握利用 FreeRTOS 接口创建多任务,理解多任务意义、任务句柄概念,通过实操搭建基础多任务应用。后续基于此,可拓展更复杂任务调度、通信等 FreeRTOS 开发。
二、核心概念与需求拆解
(一)多任务的意义
- 背景:在嵌入式系统中,若仅单任务执行,程序需按顺序依次完成功能,比如先执行 LED 控制,再执行 LCD 显示,无法同时响应多个功能需求 。
- 价值:多任务能让系统 “并行”(实际是 FreeRTOS 调度的伪并行,基于时间片或优先级切换 )处理不同功能,像同时采集传感器数据、控制设备外设(LED、LCD 等)、处理通信数据,提升系统响应速度与效率,让嵌入式设备功能更丰富、交互更流畅。
(二)任务句柄(TaskHandle_t )
- 定义:任务句柄是 FreeRTOS 中用于标识和操作任务的一种数据类型 。可把它理解成任务的 “身份证”“把手”,通过句柄能在系统中找到对应任务,进而对任务进行挂起、恢复、删除、查询状态等操作 。
- 作用:创建任务时,若提供句柄(
xTaskCreate
的pxCreatedTask
参数 ),后续可通过该句柄精准控制任务;即便创建时未主动使用,FreeRTOS 内部也会用它管理任务调度等流程 。
三、分步解析(结合图片与代码 )
(一)目标
- 内容:需创建两个任务,任务 A 运行
Led_Test
函数,负责 LED 相关功能(比如 LED 闪烁、亮灭控制 );任务 B 运行LCD_Test
函数,处理 LCD 显示(字符、画面刷新等 ),让 LED 控制和 LCD 显示 “并行” 开展 。
(二)接口函数
- 函数原型:
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, const char * const pcName, const configSTACK_DEPTH_TYPE usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask );
- 参数解析:
TaskFunction_t pxTaskCode
:任务函数入口,填要运行的任务函数名(如Led_Test
、LCD_Test
),告诉系统任务具体执行啥逻辑 。const char * const pcName
:任务名称,字符串形式,主要用于调试、查看任务信息,方便识别不同任务 。const configSTACK_DEPTH_TYPE usStackDepth
:任务栈深度,为任务运行分配栈空间,存储局部变量、函数调用上下文等,需根据任务复杂度合理设置(如128
,表示栈空间大小 ) 。void * const pvParameters
:传给任务函数的参数,若任务函数无需参数,填NULL
;若需传数据(如给 LCD 任务传显示内容 ),可通过此参数传递 。UBaseType_t uxPriority
:任务优先级,数值越大优先级越高,FreeRTOS 依据优先级调度任务,高优先级任务满足条件时会抢占低优先级任务运行 。TaskHandle_t * const pxCreatedTask
:任务句柄存储地址,创建任务成功后,句柄值会填充到该指针指向的变量,后续用句柄操作任务 。
- 位置:在明确目标后,解析创建任务的 “工具”——
xTaskCreate
函数,是代码编写的关键依据 。插入第二张图,清晰展示函数原型与参数,辅助理解每个参数干啥用 。
(三)代码示例与关联(对应第三、四、五、六张图 )
1. 任务函数示例(对应第四张图 OLED_Test
、第三张图 MyTask
等 )
OLED_Test
函数:- 代码逻辑:先初始化 OLED(
OLED_Init
)、清屏(OLED_Clear
),然后进入死循环while(1)
,循环里不断执行字符打印(OLED_PutChar
打印'A'
、'Y'
)、字符串显示(OLED_PrintString
显示Hello World!
)、数值打印(OLED_PrintSignedVal
显示递增的cnt
),模拟 LCD/OLED 持续显示刷新的任务逻辑 。可把它类比成LCD_Test
这类显示任务的实现参考,体现任务函数的一般写法:初始化 + 循环执行功能 。。
- 代码逻辑:先初始化 OLED(
MyTask
函数:- 代码逻辑:
void MyTask(void *argument)
是任务函数模板,argument
用于接收xTaskCreate
传入的参数(pvParameters
),函数内死循环while(1)
不断调用Led_Test
,实现 LED 相关功能持续执行,是任务 A(运行Led_Test
)的一种封装方式 。
- 代码逻辑:
2. 任务创建代码
- 标准创建示例:
- 代码:
xTaskCreate(MyTask, "myfirsttask", 128, NULL, osPriorityNormal, NULL);
- 参数解析:
MyTask
:任务函数,即任务要执行的代码逻辑入口 。"myfirsttask"
:任务名称,调试时便于识别 。128
:栈深度,给任务分配 128 单位栈空间(需根据实际任务复杂度调整,太小可能栈溢出,太大会浪费内存 ) 。NULL
:传给任务函数的参数,这里无参数传递 。osPriorityNormal
:任务优先级,osPriorityNormal
是系统定义的优先级宏(不同系统可能有差异,FreeRTOS 里对应特定数值 ) 。NULL
:任务句柄,这里未使用,传NULL
,若后续要控制任务,需替换成有效的句柄变量 。
- 代码:
xTaskCreate
与xTaskCreateStatic
对比:- 代码逻辑:根据
mem
变量值选择创建任务的方式,mem == 1
时用xTaskCreateStatic
(静态创建任务,需提前分配栈和任务控制块内存 );mem == 0
时用xTaskCreate
(动态创建,由 FreeRTOS 管理内存分配 ) 。体现 FreeRTOS 灵活的任务创建方式,动态创建更便捷(无需手动管理内存 ),静态创建在对内存分配有严格控制需求(如内存资源紧张、需确定内存布局 )时使用 。
- 代码逻辑:根据
3. 多任务框架关联
- 内容:图中展示
osThreadNew
作为中间层,对接不同 RTOS(FreeRTOS、RT-Thread ),调用osThreadNew
实际会中转到xTaskCreate
(FreeRTOS 场景 )或rt_thread_create
(RT-Thread 场景 ) 。说明在不同 RTOS 封装、中间件环境下,任务创建的 “中转” 逻辑,帮助理解上层接口与 FreeRTOS 底层创建函数的关联,也体现 RTOS 生态里接口封装、适配的思路 。
(四)源码工程
- 路径:
DShanMCU-F103开发板资料 > 5_程序源码 > 02_FreeRTOS程序 > 02_视频配套的源码 > 01_freertos_my.7z
,解压后01_freertos_my
是工程文件夹 。 - 作用:这是实操的基础,所有代码修改、编译、调试都基于该工程。需把前面解析的任务创建代码(如创建任务 A、任务 B 的
xTaskCreate
调用 )添加到工程合适位置(一般在初始化代码或主任务创建区域 ),编译工程(不同开发环境操作有差异,如 MDK-ARM、GCC 等 ),下载到开发板运行,验证多任务是否按预期执行(LED 功能、LCD 功能同时运行 ) 。
四、完整实操流程梳理(把知识串起来 )
- 准备工程:解压
01_freertos_my.7z
,找到工程文件,用对应开发环境(如 Keil MDK 打开.uvprojx
文件 )加载工程 。 - 编写任务函数:定义
Led_Test
和LCD_Test
函数(参考OLED_Test
、MyTask
写法 ),Led_Test
实现 LED 控制逻辑(如循环翻转 LED 引脚电平实现闪烁 ),LCD_Test
实现 LCD 显示逻辑(字符、画面更新 ) 。 - 创建任务:在工程初始化代码(如
main
函数、任务创建初始化函数 )里,调用xTaskCreate
两次,分别创建任务 A(关联Led_Test
)、任务 B(关联LCD_Test
),设置好任务名称、栈深度、优先级、句柄(按需使用 )等参数 。 - 编译下载:编译工程,解决语法错误(如函数未定义、参数不匹配等 ),编译通过后,用调试器或下载工具把程序烧录到 DShanMCU-103 开发板 。
- 验证功能:观察开发板运行情况,LED 按
Led_Test
逻辑工作(如规律闪烁 ),LCD 按LCD_Test
逻辑显示(如字符、画面正常 ),说明多任务创建成功、调度正常 。
五、易错点与补充说明
(一)易错点
- 栈深度设置:若栈深度过小,任务运行中局部变量多、函数调用层级深时,会栈溢出,导致程序崩溃、行为异常(如任务莫名停止、系统重启 );设置过大,会浪费内存,可能使系统内存不足无法创建其他任务 。需根据任务实际需求,逐步调试、确定合理栈深度(可从经验值如 128、256 等尝试 ) 。
- 任务函数死循环:任务函数必须是死循环(如
while(1)
),若函数执行完(无循环 ),任务会进入删除状态,无法持续执行功能。编写Led_Test
、LCD_Test
时,要确保逻辑在循环里 。 - 优先级冲突:若多个高优先级任务持续占用 CPU(如死循环里无阻塞操作 ),低优先级任务可能长期无法得到调度,需合理设置优先级,且在任务中适当加入阻塞(如
vTaskDelay
,让 CPU 有机会调度其他任务 ) 。
(二)补充说明
- FreeRTOS 配置:工程需正确配置 FreeRTOS(如
FreeRTOSConfig.h
里使能任务创建相关宏、设置系统时钟节拍等 ),否则xTaskCreate
可能无法正常工作 。一般开发板配套 FreeRTOS 工程已做好基础配置,若自定义工程,要仔细核对 。 - 调试技巧:若任务未按预期运行,可借助开发环境调试功能(如断点调试,查看任务函数是否执行、变量值是否正确 ),或在任务里加入串口打印(如打印调试信息到串口,查看任务执行流程 ),辅助定位问题 。