理论
任务的挂起与恢复的API函数
| API函数 | 描述 |
|---|---|
| vTaskSuspend() | 挂起任务 |
| vTaskResume() | 恢复被挂起的任务 |
| xTaskResumeFromISR() | 在中断中恢复被挂起的任务 |
- 挂起:挂起任务类似暂停,可恢复; 删除任务,无法恢复,类似“人死两清”
- 恢复:恢复被挂起的任务
- "FromISR":带FromISR后缀是在中断函数中专用的API函数
任务挂起函数
void vTaskSuspend(TaskHandle_t xTaskToSuspend)
| 形参 | 描述 |
|---|---|
| xTaskToSuspend | 待挂起任务的任务句柄 |
此函数用于挂起任务,使用时需将宏 ** INCLUDE_vTaskSuspend** 配置为 1。
无论优先级如何,被挂起的任务都将不再被执行,直到任务被恢复 。
注意:当传入的参数为NULL,则代表挂起任务自身(当前正在运行的任务)
任务恢复函数(任务中恢复)
void vTaskResume(TaskHandle_t xTaskToResume)
| 形参 | 描述 |
|---|---|
| xTaskToResume | 待恢复任务的任务句柄 |
使用该函数注意宏:INCLUDE_vTaskSuspend必须定义为 1
注意:任务无论被 vTaskSuspend() 挂起多少次,只需在任务中调用 vTakResume() 恢复一次,就可以继续运行。且被恢复的任务会进入就绪态!
任务恢复函数(中断中恢复)
BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume)
| 形参 | 描述 |
|---|---|
| xTaskToResume | 待恢复任务的任务句柄 |
函数:xTaskResumeFromISR返回值描述如下:
| 返回值 | 描述 |
|---|---|
| pdTRUE | 任务恢复后需要进行任务切换 |
| pdFALSE | 任务恢复后不需要进行任务切换 |
使用该函数注意宏:INCLUDE_vTaskSuspend 和 INCLUDE_xTaskResumeFromISR 必须定义为 1
该函数专用于中断服务函数中,用于解挂被挂起任务
注意:中断服务程序中要调用freeRTOS的API函数则中断优先级不能高于FreeRTOS所管理的最高优先级
- 不能高于FreeRTOS所管理的最高优先级
- FreeRTOS建议优先级分组为全抢占优先级,不分配响应优先级(子优先级)
- 中断中使用FreeRTOS的delay会报错
- 不符合1、2会报错但不影响使用(task.c port.c 报错)
内部实现
任务挂起函数内部实现
-
获取所要挂起任务的控制块:通过传入的任务句柄,判断所需要挂起哪个任务,NULL代表挂起自身
-
移除所在列表:将要挂起的任务从相应的状态列表和事件列表中移除(就绪或阻塞列表)
-
插入挂起任务列表:将待挂起任务的任务状态列表向插入到挂起态任务列表末尾
-
判断任务调度器是否运行:在运行,更新下一次阻塞时间,防止被挂起任务为下一次阻塞超时任务
-
判断待挂起任务是否为当前任务:如果挂起的是任务自身,且调度器正在运行,需要进行一次任务切换
调度器没有运行,判断挂起任务数是否等于任务总数,是:当前控制块赋值为NULL,否:寻找下一个最高优先级任务
任务恢复函数内部实现
- 恢复任务不能是正在运行任务
- 判断任务是否在挂起列表中:若是 就会将该任务在挂起列表中移除, 将该任务添加到就绪列表中
- 判断恢复任务优先级:判断恢复的任务优先级是否大于当前正在运行的 是的话执行任务切换
实践
任务挂起与恢复实验

freeRTOS_demo()
-
初始化变量 作用是储存触发按键
-
执行按键扫描
-
判断触发按键
-
挂起
vTaskSuspend(TASK1_Handler); -
恢复
vTaskResume(TASK1_Handler);任务中恢复
-
void TASK3(void *pvParameters)
{
uint8_t key=0;
while(1)
{
key=key_scan(0);
if(key==KEY0_PRES)
{
printf("挂起task1\n");
vTaskSuspend(TASK1_Handler);
}
else if(key==KEY1_PRES)
{
printf("在任务中恢复task1\r\n");
vTaskResume(TASK1_Handler);
}
vTaskDelay(50);
}
}
exti.c
- 调用中断处理公用函数(函数中会判断触发引脚、清除中断标志位和执行回调函数
HAL_GPIO_EXTI_Callback(GPIO_Pin);入口参数附带触发引脚) - 再次清除中断标志
//WK_UP 外部中断服务程序
void WKUP_INT_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); /* 调用中断处理公用函数 清除KEY_UP所在中断线 的中断标志位,中断下半部在HAL_GPIO_EXTI_Callback执行 */
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0); /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */
}
exit回调函数 在HAL库中所有的外部中断服务函数都会调用此函数
-
延时消抖(使用vtaskdelay 会导致任务切换报错?)
-
判断外部中断触发引脚
-
执行对应处理函数
-
引用全局变量任务句柄
-
定义 从中断中恢复任务 函数 的 返回值
-
执行从中断中恢复任务并接受返回值
-
判断返回值
- pdTRUE 则执行 任务切换
portYIELD_FROM_ISR(xYieldRequired);
- pdTRUE 则执行 任务切换
-
extern TaskHandle_t TASK1_Handler;
BaseType_t xYieldRequired;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(20);
switch(GPIO_Pin)
{
case GPIO_PIN_0:
if (WK_UP == 1)
{
xYieldRequired = xTaskResumeFromISR(TASK1_Handler);
printf("在中断中恢复task1\r\n");
}
if(xYieldRequired == pdTRUE)
{
portYIELD_FROM_ISR(xYieldRequired);
}
break;
}
}
外部中断初始化程序
- 使能中断引脚的时钟
- 配置引脚初始化参数
- 引脚初始化
- 设置优先级,以符合RTOS的要求(注意修改中断优先级分组,无需响应优先级)
- 使能中断
void extix_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
WKUP_GPIO_CLK_ENABLE(); /* WKUP时钟使能 */
gpio_init_struct.Pin = GPIO_PIN_0;
gpio_init_struct.Mode = GPIO_MODE_IT_RISING; /* 上升沿触发 */
gpio_init_struct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(GPIOA, &gpio_init_struct); /* WKUP配置为下降沿触发中断 */
HAL_NVIC_SetPriority(WKUP_INT_IRQn, 5, 0); /* 抢占2,子优先级2 */
HAL_NVIC_EnableIRQ(WKUP_INT_IRQn); /* 使能中断线0 */
}
```