任务挂起与恢复API

挂起任务和删除任务有什么不同?
挂起任务类似暂停,可恢复; 删除任务,无法恢复,类似“人死两清。
有很多API函数都带有FromISR后缀,带FromISR后缀是在中断函数中专用的API函数 。
任务挂起函数
void vTaskSuspend(TaskHandle_t xTaskToSuspend) ;
此函数用于挂起任务,使用时需将宏 INCLUDE_vTaskSuspend 配置为 1。
无论优先级如何,被挂起的任务都将不再被执行,直到任务被恢复。
注意:当传入的参数为NULL,则代表挂起任务自身(当前正在运行的任务)。
任务恢复函数
void vTaskResume(TaskHandle_t xTaskToResume);

使用该函数注意宏:INCLUDE_vTaskSuspend必须定义为 1 。
注意:任务无论被 vTaskSuspend() 挂起多少次,只需在任务中调用 vTakResume() 恢复一次,就可以继续运行。且被恢复的任务会进入就绪态!
我们的任务挂起,是不支持嵌套的,不管挂起多少次,其意义上和挂起一次是一样的,只需恢复一次即可。
任务恢复函数(中断)
BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume);
函数返回值xTaskResumeFromISR:

任务恢复后需要进行任务切换,大家想一下,什么情况下需要任务切换?
就是你正在恢复的这个任务,优先级大于我们当前正在执行的任务。所以,优先级高需要任务切换,就返回TRUE;反之,就会返回FALSE。注意要手动切换,当然这也是API函数,不需要太麻烦。任务切换的API函数时portYIELD_FROM_ISR( x )使用该函数注意宏:INCLUDE_vTaskSuspend 和 INCLUDE_xTaskResumeFromISR 必须定义为 1。
该函数专用于中断服务函数中,用于解挂被挂起任务。
注意:中断服务程序中要调用FreeRTOS的API函数则中断优先级不能高于FreeRTOS所管理的最高优先级,FreeRTOS所管理的中断优先级是5~15。
任务挂起与恢复
学习目的:学会 使用FreeRTOS中的任务挂起与恢复相关API函数:vTaskSuspend( )、 vTaskResume( )、xTaskResumeFromISR( )。
目标设计:将设计四个任务:start_task、task1、task2、task3。

其实,前三个任务我们前面的课程都已经完成,我们只需要写task3即可:
我们按照要求,先把宏给配置成1:
#define INCLUDE_vTaskSuspend 1 /* 挂起任务 */
然后写task3任务:
void task3(void *pvParameters)
{
uint8_t key = 0;
while(1)
{
key = Key_Scan();
if(key == 1)//KEY0按下
{
vTaskSuspend(task1_handler);
}
else if(key == 2)//KEY1按下
{
vTaskResume(task1_handler);
}
vTaskDelay(10);//按键延时
}
}
我们还需要完成剩下的一个功能,就是按下KEY2,在中断中恢复task1:
首先,按键是外部中断,所以我们需要初始化EXTI,下面是初始化EXTI的代码:
注意优先级的设置,FreeRTOS是看抢占式中断优先级,必须是5~15之间,即NVIC的分组方式要选择NVIC_PriorityGroup_4,代表都是抢占式,没有子优先级,而且抢占式要选择在5~15之间。
void Key2_EXTI_Init(void)
{
//开启GPIOE时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
//配置GPIOE
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//KEY2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入模式
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; // 上拉
GPIO_Init(GPIOE, &GPIO_InitStructure);
//创建出一个EXTI结构体。
EXTI_InitTypeDef EXTI_InitStructure;
//指定要使能或禁用的EXTI线路。此参数可以是 @ref EXTI_Lines 的任意组合。
EXTI_InitStructure.EXTI_Line=EXTI_Line2;
//指定所选EXTI线路的新状态。此参数可以设置为 ENABLE 或 DISABLE 。
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
// 指定EXTI线路的模式。此参数可以是 @ref EXTIMod_TypeDef 的一个值 。
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
//指定EXTI线路的触发信号有效边沿。此参数可以是 @ref EXTITrigger_TypeDef 的一个值 。
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
//根据 EXTI_InitStruct 中指定的参数初始化 EXTI 外设。
EXTI_Init(&EXTI_InitStructure);
//设置本程序的分组方式
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=EXTI2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=5;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_Init(&NVIC_InitStructure);
}
然后我们需要把FreeRTOSConfig中的宏配置一下:
#define INCLUDE_xTaskResumeFromISR 1 /* 恢复在中断中挂起的任务 */
然后写我们的中断函数EXTI2_IRQHandler:
EXTI2_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line2) != RESET) { // 检查指定的 EXTI 线路中断是否发生
if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_2) == 0)
{
BaseType_t xYieldRequired;
xYieldRequired = xTaskResumeFromISR(task1_handler);
if (xYieldRequired == pdTRUE) {
portYIELD_FROM_ISR(xYieldRequired);
}
}
EXTI_ClearITPendingBit(EXTI_Line2); // 清除 EXTI 线路挂起位
}
}
这样,就完美实现了我们的目标功能,不过记得在主逻辑函数中初始化EXTI哦。