学习:
1.任务状态:Running / Ready / Blocked / Suspended
Running:运行态,表示当前任务正在占用 CPU 执行。
Ready:就绪态,表示任务已经具备运行条件,但是还未被调度器选中执行。(由于有更高优先级任务正在运行,或者同优先级任务还没轮到它,所以暂时没有执行)
Blocked:阻塞态,表示任务正在等待某个时间到达,或者等待某个事件发生。在阻塞期间,任务不占用 CPU,也不会被调度器选中运行。等时间到了,或者事件发生了,任务会重新进入 Ready 状态。(等待延时结束,等待队列数据,等待信号量,等待事件组,等待任务通知,等待互斥量)
例如
例如:
vTaskDelay(100);任务会阻塞 100 个 tick。注意:100 tick 不一定等于 100ms,具体取决于系统 tick 周期
再比如:
xQueueReceive(queue, &data, 100);如果队列没数据,任务会阻塞等待;如果 100 个 tick 内还没等到数据,就超时退出阻塞。
Suspended:挂起态,表示任务被手动挂起后,不会参与调度,也不会因为超时自动恢复。只有调用 vTaskResume() 或 xTaskResumeFromISR() 才能恢复到 Ready 状态。
2.任务为什么会阻塞
因为任务调用了会等待的 FreeRTOS API,主动让出 CPU,去等待时间、资源或者事件。vTaskDelay();
xQueueReceive();
xSemaphoreTake();
xEventGroupWaitBits();
ulTaskNotifyTake();这些函数都可能进入阻塞态
总结:FreeRTOS任务不是一直占用CPU运行的。当任务需要等待延时、队列、信号量、事件组或任务通知时,就会进入阻塞态。阻塞态任务不会消耗CPU,调度器会去运行其他Ready任务。等事件发生或者等待超时后,该任务再回到Ready态,等待调度器重新调度。
3. BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
const char * const pcName,
const configSTACK_DEPTH_TYPE uxStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pxCreatedTask
);个人理解
pvTaskCode任务函数入口地址,任务函数通常是一个无限循环,不能直接 return。如果任务不再需要运行,应该调用 vTaskDelete() 删除自己。 例如:
void LED_Task(void *argument) { while(1) { HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); vTaskDelay(pdMS_TO_TICKS(500)); } }如果任务想删除自己:
vTaskDelete(NULL);vTaskDelete(NULL)不是删除空任务,而是删除当前正在运行的任务自己
pcName是任务的名称,主要用于
1. 调试 2. 任务列表显示 3. FreeRTOS Trace 工具显示uxStackDepth为任务分配的栈深度,单位通常是 StackType_t,不是字节。在 STM32 上 128 通常约等于 512 字节。注意:这是 FreeRTOS 原生 xTaskCreate() 的规则;如果用 CMSIS-RTOS 或 CubeMX 配置任务,界面里的 stack size 可能按字节理解,要看具体接口。
pvParameters是传递给任务函数的参数指针。例如:
uint8_t led_id = 1; xTaskCreate(LED_Task, "LED", 128, &led_id, 2, NULL);任务里接收:
void LED_Task(void *argument) { uint8_t id = *(uint8_t *)argument; while(1) { // 使用 id } }注意,如果传局部变量地址,要小心变量生命周期。 led_id 是局部变量,创建任务的函数退出后,这个地址可能失效;所以传给任务的参数最好是全局变量、static变量,或者长期存在的结构体。
uxPriority是任务的优先级,数字越大优先级越高
pxCreatedTask是一个输出参数,用来保存创建成功后的任务句柄。
xTaskCreate() 还有一个返回值
BaseType_t常见返回值:
pdPASS:任务创建成功 errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY:内存不足,创建失败4.vTaskDelay() 作用
vTaskDelay() 用于让当前任务进入阻塞态,延时指定的 tick 数。延时时间到后,任务从 Blocked 状态回到 Ready 状态,延时时间到后,任务从 Blocked 状态回到 Ready 状态,等待调度器选中后才会重新进入 Running。。它是相对延时,适合普通延时;如果要实现严格周期任务,应使用 vTaskDelayUntil()。
5.Delay后任务去哪了
任务在 Running 状态调用 vTaskDelay() 后会主动让出 CPU,让其他 Ready 任务得到执行机会。本任务会进入 Blocked(阻塞态),在延时未结束前一直处于阻塞态,不参与调度,也不占用 CPU。当延时时间到了以后,任务会从 Blocked 状态转为 Ready(就绪态),然后等待调度器调度,只有被调度器选中后,才会重新进入 Running(运行态)。
总结
一句话:Running 是正在跑,Ready 是能跑但没轮到,Blocked 是等时间或事件,Suspended 是被手动暂停。