5.任务的挂起与恢复

一、简介

在这里插入图片描述

二、相关的API函数

在这里插入图片描述

1.任务挂起函数

在这里插入图片描述
在这里插入图片描述

(1)函数 vTaskSuspend()

函数 vTaskSuspend()在 task.c 文件中有定义,具体的代码如下所示:

void vTaskSuspend(
 TaskHandle_t xTaskToSuspend) /* 待挂起任务的任务句柄 */
{
 TCB_t * pxTCB;
 
 /* 进入临界区 */
 taskENTER_CRITICAL();
 {
 /* 如果传入的任务句柄为空(NULL)
 * 此函数会将待挂起的任务设置为调用该函数的任务本身
 * 因此,如果要在任务中挂起任务本身,
 * 那么可以调用函数 vTaskSuspend(),并传入任务句柄,
 * 或传入 NULL
 */
 pxTCB = prvGetTCBFromHandle( xTaskToSuspend );
 
 /* 调试使用,不用理会 */
 traceTASK_SUSPEND( pxTCB );
 
 /* 将任务从任务所在任务状态列表(就绪态任务列表或阻塞态任务列表)中移除
 * 如果移除后列表中的列表项数量为 0
 * 那么就需要更新任务优先级记录
 * 因为此时系统中可能已经没有和被挂起任务相同优先级的任务了
 */
 if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
 {
 /* 更新任务优先级记录 */
 taskRESET_READY_PRIORITY( pxTCB->uxPriority );
 }
 else
 {
 mtCOVERAGE_TEST_MARKER();
 }
 
 /* 判断被挂起的任务是否还有等待的事件 */
 if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
 {
 /* 将被挂起任务的事件列表项,从所在事件列表中移除 */
 ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
 }
 else
 {
 mtCOVERAGE_TEST_MARKER();
 }
 
 /* 将待挂起任务的任务状态列表向插入到挂起态任务列表末尾 */
 vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
 
 /* 此宏用于启用任务通知功能 */
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
{
 BaseType_t x;
 
 /* 遍历待挂起任务的所有任务通知状态 */
 for( x = 0; x < configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ )
 {
 /* 如果有正在等待的任务通知,则取消等待
 * 因为此时,任务已经被挂起
 */
 if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION )
 {
 pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION;
 }
 }
}
#endif
 }
 /* 退出临界区 */
 taskEXIT_CRITICAL();
 
 /* 判断任务调度器是否正在运行
 * 如果任务调度器正在运行,
 * 则需要更新下一个任务的阻塞超时时间,
 * 以防被挂起的任务就是下一个阻塞超时的任务
 */
 if( xSchedulerRunning != pdFALSE )
 {
 taskENTER_CRITICAL();
 {
 prvResetNextTaskUnblockTime();
 }
 taskEXIT_CRITICAL();
 }
 else
 {
 mtCOVERAGE_TEST_MARKER();
 }
 
 /* 如果待挂起任务就是任务本身 */
 if( pxTCB == pxCurrentTCB )
 {
 /* 如果任务调度器正在运行,则需要切换任务 */
 if( xSchedulerRunning != pdFALSE )
 {
 /* 此时任务调度器不能处于挂起状态 */
 configASSERT( uxSchedulerSuspended == 0 );
 /* 进行任务切换 */
 portYIELD_WITHIN_API();
 }
 else
 {
 /* 如果任务调度器没有运行,并且 pxCurrentTCB 又指向了待挂起的任务,
 * 那么就需要将 pxCurrentTCB 指向其他任务
 */
 if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) ==
 uxCurrentNumberOfTasks )
 {
 /* 没有就绪的任务,则将 pxCurrentTCB 指向空(NULL) */
 pxCurrentTCB = NULL;
 }
 else
 {
 /* 更新 pxCurrentTCB 为优先级最高的就绪态任务 */
 vTaskSwitchContext();
 }
 }
 }
 else
 {
 mtCOVERAGE_TEST_MARKER();
 }
}
 

使用函数 vTaskSuspend()挂起任务时,如果任务调度器没有运行,并且待挂起的任务又是调用函数 vTaskSuspend()的任务,那么 pxCurrentTCB 需要指向其他优先级最高的就绪态任务,更新 pxCurrentTCB 的操作,时通过调用函数 vTaskSwitchContext()实现的。

(2)函数 vTaskSwitchContext()

该函数用于更新 pxCurrentTCB 指向就绪态任务列表中优先级最高的任务,该函数在 task.c
文件中有定义,具体代码如下所示:

void vTaskSwitchContext( void )
{
 if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
 {
 /* 任务调度器没有运行,不允许切换上下文,直接退出函数 */
 xYieldPending = pdTRUE;
 }
 else
 {
 xYieldPending = pdFALSE;
 /* 用于调试,不用理会 */
 traceTASK_SWITCHED_OUT();
 /* 此宏用于启用任务运行时间统计功能 */
#if ( configGENERATE_RUN_TIME_STATS == 1 )
{
#ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
 portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime );
#else
 ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE();
#endif
 
 if( ulTotalRunTime > ulTaskSwitchedInTime )
 {
 pxCurrentTCB->ulRunTimeCounter +=
 ( ulTotalRunTime - ulTaskSwitchedInTime );
 }
 else
 {
 mtCOVERAGE_TEST_MARKER();
 }
 
 ulTaskSwitchedInTime = ulTotalRunTime;
}
#endif
 
 /* 未定义,不用理会 */
 taskCHECK_FOR_STACK_OVERFLOW();
 
 /* 与 POSIX 相关配置,不用理会 */
#if ( configUSE_POSIX_ERRNO == 1 )
{
 pxCurrentTCB->iTaskErrno = FreeRTOS_errno;
}
#endif
 
 /* 此函数用于将 pxCurrentTCB 更新为指向优先级最高的就绪态任务 */
 taskSELECT_HIGHEST_PRIORITY_TASK();
 /* 用于调试,不用理会 */
 traceTASK_SWITCHED_IN();
 
 /* 与 POSIX 相关配置,不用理会 */
#if ( configUSE_POSIX_ERRNO == 1 )
{
 FreeRTOS_errno = pxCurrentTCB->iTaskErrno;
 }
#endif
 
 /* 与 Newlib 相关配置,不用理会 */
#if ( configUSE_NEWLIB_REENTRANT == 1 )
{
 _impure_ptr = &( pxCurrentTCB->xNewLib_reent );
}
#endif
 }
}
 

此 函 数 的 重 点 在 于 调 用 了 函 数 taskSELETE_HIGHEST_PRIORITY_TASK() 更 新
pxCurrentTCB 指向优先级最高的就绪态任务,函数 taskSELETE_HIGHEST_PRIORITY_TASK()实际上是一个宏定义,task.c 文件中有定义,具体的代码如下所示:

#define taskSELECT_HIGHEST_PRIORITY_TASK() \
{ \
 UBaseType_t uxTopPriority; \
 \
 /* 查找就绪态任务列表中最高的任务优先级 */ \
 portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority ); \
 /* 此任务优先级不能时最低的任务优先级 */ \
 configASSERT( \
 listCURRENT_LIST_LENGTH(&( pxReadyTasksLists[ uxTopPriority ] ) ) > \
 0 \
 ); \
 /* 让 pxCurrentTCB 指向该任务优先级就绪态任务列表中的任务 */ \
 listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, \
 &( pxReadyTasksLists[ uxTopPriority ] ) ); \
}

2.任务恢复函数

在这里插入图片描述
在这里插入图片描述

(1) 函数 vTaskResume()

函数 vTaskResume()在 task.c 文件中有定义,具体的代码如下所示:

void vTaskResume(
 TaskHandle_t xTaskToResume) /* 待恢复的任务句柄 */
{
 TCB_t * const pxTCB = xTaskToResume;
 
 /* 确保有指定的待恢复任务 */
 configASSERT( xTaskToResume );
 /* 待恢复的任务不能时当前正在运行的任务,并且也不能为空(NULL) */
 if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) )
 {
 /* 进入临界区 */
 taskENTER_CRITICAL();
 {
 /* 判断任务是否被挂起
 * 只有被挂起的任务才需要恢复
 */
 if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE )
 {
 /* 用于调试,不用理会 */
 traceTASK_RESUME( pxTCB );
 
 /* 将待恢复任务的任务状态列表项
 * 从所在任务状态列表(挂起态任务列表)中移除
 */
 ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
 /* 将待恢复任务的任务状态列表项
 * 添加到就绪态任务列表中
 */
 prvAddTaskToReadyList( pxTCB );
 
 /* 如果待恢复任务的优先级比当前正在运行的任务的任务优先级高
 * 则需要进行任务切换
 */
 if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
 {
 taskYIELD_IF_USING_PREEMPTION();
 }
 else
 {
 mtCOVERAGE_TEST_MARKER();
 }
 }
 else
 {
 mtCOVERAGE_TEST_MARKER();
 }
 }
 /* 退出临界区 */
 taskEXIT_CRITICAL();
 }
 else
 {
 mtCOVERAGE_TEST_MARKER();
 }
}

3.任务恢复函数(中断中恢复)

在这里插入图片描述
在这里插入图片描述

三、相关实验

在这里插入图片描述


#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );

/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );

/* TASK3 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK3_PRIO         4
#define TASK3_STACK_SIZE   128
TaskHandle_t    task3_handler;
void task3( void * pvParameters );
/******************************************************************************************************/


/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}


void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
                
    xTaskCreate((TaskFunction_t         )   task3,
                (char *                 )   "task3",
                (configSTACK_DEPTH_TYPE )   TASK3_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK3_PRIO,
                (TaskHandle_t *         )   &task3_handler );
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
}

/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
    uint32_t task1_num = 0;
    while(1)
    {
        printf("task1_num:%d\r\n",++task1_num);
        LED0_TOGGLE();
        vTaskDelay(500);
    }
}

/* 任务二,实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{
    uint32_t task2_num = 0;
    while(1)
    {
        printf("task2_num:%d\r\n",++task2_num);
        LED1_TOGGLE();
        vTaskDelay(500);
    }
}

/* 任务三,判断按键KEY0,按下KEY0删除task1 */
void task3( void * pvParameters )
{
    uint8_t key = 0;
    while(1)
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            printf("挂起task1\r\n");
            vTaskSuspend(task1_handler);
        }else if(key == KEY1_PRES)
        {
            printf("在任务中恢复task1\r\n");
            vTaskResume(task1_handler);
        }
        vTaskDelay(10);
    }
}


#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/KEY/key.h"
#include "./BSP/EXTI/exti.h"
#include "FreeRTOS.h"
#include "task.h"

extern TaskHandle_t    task1_handler;
/**
 * @brief       KEY2 外部中断服务程序
 * @param       无
 * @retval      无
 */
void KEY2_INT_IRQHandler(void)
{ 
    HAL_GPIO_EXTI_IRQHandler(KEY2_INT_GPIO_PIN);        /* 调用中断处理公用函数 清除KEY2所在中断线 的中断标志位,中断下半部在HAL_GPIO_EXTI_Callback执行 */
    __HAL_GPIO_EXTI_CLEAR_IT(KEY2_INT_GPIO_PIN);        /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */
}

/**
 * @brief       中断服务程序中需要做的事情
                在HAL库中所有的外部中断服务函数都会调用此函数
 * @param       GPIO_Pin:中断引脚号
 * @retval      无
 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    delay_ms(20);      /* 消抖 */
    switch(GPIO_Pin)
    {
        BaseType_t xYieldRequired;
        case KEY2_INT_GPIO_PIN:
            if (KEY2 == 0)
            {
                xYieldRequired = xTaskResumeFromISR(task1_handler);
                printf("在中断中恢复task1\r\n");
            }
            if(xYieldRequired == pdTRUE)
            {
                portYIELD_FROM_ISR(xYieldRequired);
            }
            break;
        default : break;
    }
}

/**
 * @brief       外部中断初始化程序
 * @param       无
 * @retval      无
 */
void extix_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    
    key_init();    
    gpio_init_struct.Pin = KEY2_INT_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_IT_FALLING;            /* 下降沿触发 */
    gpio_init_struct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(KEY2_INT_GPIO_PORT, &gpio_init_struct);    /* KEY2配置为下降沿触发中断 */
    
    HAL_NVIC_SetPriority(KEY2_INT_IRQn, 5, 0);               /* 抢占5,子优先级0 */
    HAL_NVIC_EnableIRQ(KEY2_INT_IRQn);                       /* 使能中断线2 */

}
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值