【FreeRTOS】创建一个任务的详细流程
目录
这三个函数的作用:
1. osKernelInitialize()
2. MX_FREERTOS_Init()
3. osKernelStart()
为什么这是必须的?
在 FreeRTOS 中创建新任务的详细流程
完整创建新任务的步骤
步骤 1: 在 freertos.c 中定义任务函数
步骤 2: 定义任务句柄和属性
步骤 3: 在 MX_FREERTOS_Init 中创建任务
步骤 4: 实现任务函数
关键概念解释
调试建议
必须在 main 函数中加入调度器的初始化、FreeRTOS 对象初始化和调度器启动代码,FreeRTOS 才能对任务进行调度。
/* Init scheduler */
osKernelInitialize(); // 初始化 FreeRTOS 内核/* Call init function for freertos objects (in cmsis_os2.c) */
MX_FREERTOS_Init(); // 创建任务、队列、信号量等 FreeRTOS 对象/* Start scheduler */
osKernelStart(); // 启动调度器,开始任务调度
这三个函数的作用:
1. osKernelInitialize()
-
初始化 FreeRTOS 内核
-
设置内核所需的数据结构和变量
-
不启动任务调度,只是为调度做准备
2. MX_FREERTOS_Init()
-
创建任务(如您的
defaultTask
) -
创建队列、信号量、互斥量等同步对象
-
设置定时器等
-
这个函数是 STM32CubeMX 自动生成的,包含了您通过图形界面配置的所有 FreeRTOS 对象
3. osKernelStart()
-
启动任务调度器
-
开始按照优先级调度任务
-
从此控制权交给 FreeRTOS,main 函数中此后的代码不会执行
为什么这是必须的?
-
没有调度器,任务不会运行:即使创建了任务,如果没有启动调度器,这些任务只是存在于内存中,不会被执行。
-
执行顺序很重要:
-
必须先初始化内核 (
osKernelInitialize
) -
然后创建任务和其他对象 (
MX_FREERTOS_Init
) -
最后启动调度器 (
osKernelStart
)
-
-
控制权转移:
osKernelStart()
是一个不会返回的函数(除非发生错误),它将 CPU 的控制权完全交给 FreeRTOS 调度器。
在 FreeRTOS 中创建新任务的详细流程
作为刚接触 RTOS 的小白,我来为您详细讲解创建新任务的完整流程。以您现有的代码为基础,我会用最简单的方式说明。
完整创建新任务的步骤
步骤 1: 在 freertos.c
中定义任务函数
首先创建一个新的任务函数,比如叫 TaskA
:
/* USER CODE BEGIN FunctionPrototypes */
// 声明任务函数
void StartTaskA(void *argument);
/* USER CODE END FunctionPrototypes */
步骤 2: 定义任务句柄和属性
在变量定义区域添加:
/* USER CODE BEGIN Variables */
/* Definitions for TaskA */
osThreadId_t taskAHandle; // 任务句柄
const osThreadAttr_t taskA_attributes = {.name = "TaskA", // 任务名称.stack_size = 128 * 4, // 栈大小(字节).priority = (osPriority_t) osPriorityNormal, // 优先级
};
/* USER CODE END Variables */
步骤 3: 在 MX_FREERTOS_Init 中创建任务
在初始化函数中找到创建任务的地方,添加新任务的创建代码:
void MX_FREERTOS_Init(void) {/* USER CODE BEGIN Init *//* USER CODE END Init */// ... 其他初始化代码 .../* Create the thread(s) *//* creation of defaultTask */defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);/* creation of TaskA */taskAHandle = osThreadNew(StartTaskA, NULL, &taskA_attributes); // 添加这行/* USER CODE BEGIN RTOS_THREADS *//* add threads, ... *//* USER CODE END RTOS_THREADS */
}
步骤 4: 实现任务函数
在文件末尾实现任务函数的具体逻辑:
/* USER CODE BEGIN Application */
// 实现 TaskA 任务函数
void StartTaskA(void *argument)
{/* 任务初始化代码 */log_i("TaskA started");/* 无限循环 - 这是RTOS任务的标准模式 */for(;;){log_i("TaskA is running");HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0); // 示例:切换LED状态osDelay(500); // 延迟500ms}
}
/* USER CODE END Application */
关键概念解释
-
任务句柄 (osThreadId_t):就像任务的身份证,用来标识和管理任务
-
任务属性 (osThreadAttr_t):
-
name
:任务名称,调试时有用 -
stack_size
:任务栈大小,根据任务需求调整 -
priority
:任务优先级,数字越大优先级越高
-
-
osThreadNew():创建新任务的函数
-
参数1:任务函数指针
-
参数2:传递给任务的参数
-
参数3:任务属性
-
-
任务函数格式:必须包含无限循环,否则任务执行一次就会结束
调试建议
-
使用
log_i()
输出信息查看任务运行情况 -
如果任务没有运行,检查优先级设置
-
确保栈大小足够,避免栈溢出
现在已经成功创建了两个任务:defaultTask
和 TaskA
。FreeRTOS 调度器会自动根据优先级调度这两个任务。