文章目录
一、任务的挂起与恢复的API函数
1.1.介绍
API函数 | 描述 |
---|---|
vTaskSuspend( ) | 挂起任务 |
vTaskResume( ) | 恢复被挂起的任务 |
xTaskResumeFromISR( ) | 在中断中恢复被挂起的任务 |
- 挂起:挂起任务类似暂停,可恢复;删除任务,无法恢复
- 恢复:恢复被挂起的任务
- “ FromISR ”:带 FromISR 后缀是在中断函数中专用的API函数
1.2.任务挂起函数
该函数的完整写法是void vTaskSuspend(TaskHandle_t xTaskToSuspend);
,它的形参是将被挂起任务的任务句柄,此函数是用来挂起任务,使用时需将宏INCLUDE_vTaskSuspend
配置为1;无论优先级如何,被挂起的任务都将不再被执行,直到任务被恢复;当传入的参数为NULL
时,代表把自己给挂起来,相当于删除任务函数。
1.3.任务恢复函数(任务中恢复)
该函数的完整写法是void vTaskResume(TaskHandle_t xTaskToResume);
,它的形参是待恢复任务的任务句柄,任务无论被vTaskSuspend( );
挂起多少次,只需要在任务中调用vTaskResume( );
恢复一次,即可继续运行,而且被恢复的任务会进入就绪态。
1.4.任务恢复函数(中断中恢复)
该函数的完整写法是BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume);
,它的形参是待恢复任务的任务句柄,它的返回值有两种:
返回值 | 描述 |
---|---|
pdTRUE | 任务恢复后需要进行任务切换(当恢复的任务比正在执行的任务的优先级高,就要进行任务切换) |
pdFALSE | 任务恢复后不需要进行任务切换 |
使用该函数需要将INCLUDE_vTaskSuspend
和INCLUDE_xTaskResumeFromISR
必须定义为1;该函数专用于中断服务函数中,用于解挂被挂起任务;注意:中断服务程序中要调用 FreeRTOS 的 API 函数则中断优先级不能高于 FreeRTOS 所管理的最高优先级。
二、实验部分
2.1.实验设计(任务中恢复)
设计四个任务:
- start_task:创建其他三个任务
- task1:实现 LED0 每500ms闪烁一次
- task2:实现 LED0 每500ms闪烁一次
- task3:判断按键,按下 KEY0,挂起 task1;按下 KEY1 在任务中恢复 task1
将宏INCLUDE_vTaskSuspend
和配置为1;使用动态创建任务,除了 task3 的任务内容需要做出更改,其他三个任务与《动态创建与删除任务实战》内容相似。以下代码是 task1、2、3 的内容,定义一个变量来证明任务被挂起再恢复,该任务能否继续进行原来的任务:
//task1
void task1(void *pvParameters)
{
uint32_t num1 = 0;
while(1)
{
printf("task1_num = %d\r\n",++num1);
LED0_TOGGLE();
vTaskDelay(500);
}
}
//task2
void task2(void *pvParameters)
{
uint32_t num2 = 0;
while(1)
{
printf("task2_num = %d\r\n",++num2);
LED1_TOGGLE();
vTaskDelay(500);
}
}
通过串口助手,确实证明了任务挂起再恢复之后,还能进行挂起之前的操作,下图是 task1 计数到25之后被挂起,恢复之后从26开始计数。
2.2.实验设计(中断中恢复)
按下 KEY2 在中断中恢复 task1,外部中断线实现,在 exti.c 文件里配置 KEY_UP 为外部中断按钮:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
vTaskDelay(20);
switch(GPIO_Pin)
{
BaseType_t xYieldRequired; //定义一个变量
case WKUP_INT_GPIO_PIN:
if (WK_UP == 1)
{
xYieldRequired = xTaskResumeFromISR(task1_handler); //承接在中断恢复函数返出来的值
printf("在中断恢复task1\r\n");
}
if(xYieldRequired == pdTRUE)
{
portYIELD_FROM_ISR(xYieldRequired); //任务恢复后,进行任务切换
}
break;
default : break;
}
}
如果把中断优先级设置为抢占优先级为2,响应优先级为2,虽然可以成功恢复,但是会出现如下图提示的错误:
所以建议将所有优先级位指定为抢占优先级位,不留下任何优先级位作为子优先级位。任何其他配置都会使configMAX_SYSCALL_INTERRUPT_PRIORITY
设置与分配给各个外设中断的优先级之间的直接关系复杂化;如果将 STM32 与 STM32 驱动库一起使用,则在启动 RTOS 之前,通过调用NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
来确保将所有优先级位分配为抢占优先级位。下面两个图为解决该错误的方法:
以“FromISR”结尾的 FreeRTOS 函数是中断安全的,但即使是这些函数也不能从逻辑优先级高于ConfigMAX SYSCALL INTERRUPT PRIORITY
定义的优先级的中断中调用(confgMAX SYSCALL INTERRUPT PRIORITY在FreeRTOSConig.h头文件中定义)。因此,任何使用 RTOSAPI 函数的中断服务例程都必须将其优先级手动设置为数值上等于或大于 configMAX SYSCALL INTERRUPT PRIORITY
设置的值。这可确保中断的逻辑优先级等于或小于configMAX SYSCALL INTERRUPT PRIORITY
设置。Cortex-M 中断默认为优先级值为零。零是可能的最高优先级值。因此,切勿将使用中断安全 RTOS API 的中断的优先级保留为其默认值。
下图是中断优先级和任务优先级的对比:
- 中断优先级数值越小越优先
- 任务优先级数值越大越优先