任务挂起与恢复的API函数
vTaskSuspend() 任务挂起函数。
vTaskResume() 任务恢复函数。
xTaskResumeFromISR() 在中断中恢复被挂起的任务函数。
任务挂起和删除任务的区别是任务挂起后可以恢复,任务删除后无法恢复。
任务恢复即恢复被挂起的任务。
带有"FromISR"后缀表示专门用在中断函数中的API函数。
vTaskSuspend()函数具体形式如下。
使用此函数用于挂起任务时,需要将宏INCLUDE_vTAaskSuspend配置为1。
无论任务优先级如何,只要任务被挂起,就不会执行任务。
当传入的参数是NULL时,则代表挂起任务自身。
vTaskResume()函数具体形式如下。
使用此函数用于挂起任务时,需要将宏INCLUDE_vTaskSuspend配置为1。
任务无论使用vTaskSuspend()挂起多少次,只需要使用vTaskResume()恢复一次即可,且被恢复的任务会立即进入就绪态。
vTaskResumeFromISR()函数具体形式如下。
当恢复的任务的优先级高于当前执行的任务的优先级时,则任务恢复后需要进行任务切换,反之则不需要任务切换。
xTaskResumeFromISR()要将宏INCLUDE_vTaskSuspend和INCLUDE_xTaskResumeFromISR定义为1。
该函数专门用在中断服务函数中,用于恢复被挂起任务。
中断服务程序中要调用FreeRTOS的API函数则中断优先级不能高于FreeRTOS所管理的最高优先级,FreeRTOS中的中断优先级一般是5-15。
任务挂起和任务恢复
#define START_TASK_STACK_SIZE 128
#define START_TASK_PRIORITY 1
#define START_TASK1_STACK_SIZE 128
#define START_TASK1_PRIORITY 2
#define START_TASK2_STACK_SIZE 128
#define START_TASK2_PRIORITY 3
#define START_TASK3_STACK_SIZE 128
#define START_TASK3_PRIORITY 4
void start_task(void* pvParameters);
void task1(void* pvParameters);
void task2(void* pvParameters);
void task3(void* pvParameters);
TaskHandle_t start_task_handler;
TaskHandle_t task1_handler;
TaskHandle_t task2_handler;
TaskHandle_t task3_handler;
void freertos_demo(void)
{
/*创建任务start_task*/
xTaskCreate(start_task,
"start_task",
START_TASK_STACK_SIZE,
NULL, START_TASK_PRIORITY,
&start_task_handler);
/*开启任务调度器*/
vTaskStartScheduler();
}
void start_task(void* pvParameters)
{
/*创建任务task1*/
xTaskCreate(task1,
"task1",
START_TASK1_STACK_SIZE,
NULL, START_TASK1_PRIORITY,
&task1_handler);
/*创建任务task2*/
xTaskCreate(task2,
"task2",
START_TASK2_STACK_SIZE,
NULL, START_TASK2_PRIORITY,
&task2_handler);
/*创建任务task3*/
xTaskCreate(task3,
"task3",
START_TASK3_STACK_SIZE,
NULL, START_TASK3_PRIORITY,
&task3_handler);
/*删除任务 防止start_task多次创建Task1、Task2、Task3使得超出FreeRTOS提供的堆栈大小*/
vTaskDelete(NULL);
//vTaskDelete(start_task_handler);
}
/*LED0每500ms翻转一次*/
void task1(void* pvParameters)
{
while(1)
{
LED0_TOGGLE();
vTaskDelay(500); //系统延时函数
}
}
/*LED1每500ms翻转一次*/
void task2(void* pvParameters)
{
while(1)
{
LED1_TOGGLE();
vTaskDelay(500); //系统延时函数
}
}
void task3(void* pvParameters)
{
uint8_t key = 0;
while(1)
{
key = key_scan(0);
if(key == KEY0_PRES)
{
vTaskSuspend(task1_handler);
}
else if(key == KEY1_PRES)
{
vTaskResume(task1_handler);
}
vTaskDelay(10);
}
}
中断函数中使用xTaskResumeFromISR()恢复任务。
void HAL_GPIO_EXTI_CallBack(uint16_t GPIO_Pin)
{
delay_ms(20);
switch(GPIO_Pin)
{
BaseType_t xYieldRequired;
case KEY_INT_GPIO_PIN;
if(KEY2 == 0)
{
xYieldRequired = xTaskResumeFromISR(task1_handler);
}
if(xYieldRequired == pdTRUE)
{
/*进行任务切换*/
portYIELD_FROM_ISR(xYieldRequired);
}
break;
default:break;
}
}
有两点需要关注,一是中断的抢占优先级的大小应该5-15之间。二是建议将所有的中断优先级指定为抢占优先级,也就是说,不留任何优先级位作为子优先级位,否则会使得FreeRTOS管理复杂化,可以通过NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)来确保所有优先级位分配为抢占优先级位。
挂起函数和恢复函数的内部实现
1.根据任务句柄获取TCB,若句柄为NULL,则挂起任务自身。
pxTCB = prvGetTCBFromHandle( xTaskToSuspend );
通过函数prvGetTCBFromHandle() 从句柄xTaskToSuspend中获取TCB。
#define prvGetTCBFromHandle(pxHandle) (((pxHandle) == NULL) ? pxCurrentTCB : (pxHandle))
(((pxHandle) == NULL) ? pxCurrentTCB : (pxHandle))是一个三目运算符,意思是如果pxHandle == NULL则使用pxCurrentTCB即当前任务控制块,否则则通过指针pxHandle指向其他TCB。
2.将要挂起的任务移除状态列表项和事件列表项。
/* Remove task from the ready/delayed list and place in thesuspended list. */
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Is the task waiting on an event also? */
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
{
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
3.将待挂起任务的任务状态列表插入到挂起态任务列表的末尾。
vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
4.判断调度器是否运行,若运行则更新阻塞时间。
if( xSchedulerRunning != pdFALSE )
{
taskENTER_CRITICAL();
{
prvResetNextTaskUnblockTime();
}
taskEXIT_CRITICAL();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
5.若挂起任务自身,继续判断调度器是否运行。若调度器正在运行,则进行一次强制任务切换。若调度器未运行,判断挂起任务数是否等于任务总数。若等于则表示所有任务均被挂起,则当前任务块赋值NULL。若不等于则通过函数vTaskSwitchContext寻找下一个最高优先级任务。
if( pxTCB == pxCurrentTCB )
{
if( xSchedulerRunning != pdFALSE )
{
/* The current task has just been suspended. */
configASSERT( uxSchedulerSuspended == 0 );
portYIELD_WITHIN_API();
}
else
{
/* The scheduler is not running, but the task that was pointed
* to by pxCurrentTCB has just been suspended and pxCurrentTCB
* must be adjusted to point to a different task. */
if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks )
/*lint !e931 Right has no side effect, just volatile. */
{
/* No other tasks are ready, so set pxCurrentTCB back to
* NULL so when the next task is created pxCurrentTCB will
* be set to point to it no matter what its relative priority
* is. */
pxCurrentTCB = NULL;
}
else
{
vTaskSwitchContext();
}
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}