Notas de estudio de FreeRTOS (2) -gestión de tareas

Uno, el archivo de encabezado

#include "FreeRTOS.h"
#include "task.h"

2. Creación e inicio de tareas

2.1 Descripción de la API relacionada

2.1.1 TaskHandle_t

Identificador de tareas. Por ejemplo, xTaskCreatela llamada a regresa. Se puede utilizar como parámetro para vTaskDeleteeliminar tareas.

/**
 * task. h
 *
 * Type by which tasks are referenced.  For example, a call to xTaskCreate
 * returns (via a pointer parameter) an TaskHandle_t variable that can then
 * be used as a parameter to vTaskDelete to delete the task.
 *
 * \defgroup TaskHandle_t TaskHandle_t
 * \ingroup Tasks
 */
typedef void * TaskHandle_t;

2.1.2 xTaskCreate

Utilice la memoria dinámica para crear una tarea.

función BaseType_t xTaskCreate
(TaskFunction_t pxTaskCode,
const char * const pcName,
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask)
parámetro pxTaskCode: función de entrada de tarea, el nombre de la función de tarea, necesita ser personalizado y realizado

pcName: nombre de tarea, forma de cadena, nombre de tarea debe ser consistente con el nombre de la función de entrada de tarea, fácil de depurar

usStackDepth: tamaño de pila de tarea, unidad es palabra, en el procesador de 32 bits (STM32), una palabra es igual a 4 bytes, si se pasa 512, el tamaño de la tarea es 512 * 4 bytes

pvParameters: función de entrada de tarea parámetros formales, cuando no esté en uso, configúrelo en 0 o NULL

uxPriority: prioridad de la tarea. En FreeRTOS, cuanto mayor es el valor, mayor es la prioridad. 0 representa la prioridad más baja.

pxCreatedTask: se usa para devolver un identificador (ID), que se puede usar para hacer referencia a la tarea después de que la tarea sea creado
valor de retorno pdPASS: la tarea se creó correctamente

errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY: debido a la falta de espacio en el montón de memoria, FreeRTOS no puede asignar suficiente espacio para guardar los datos de la estructura de la tarea y la pila de tareas, por lo que la tarea no se puede crear

2.1.3 vTaskDelete

Elimínese o elimine otras tareas. Una vez eliminada la tarea, dejará de existir y no entrará en estado de ejecución.

función vTaskDelete vacío (TaskHandle_t xTaskToDelete)
parámetro xTaskToDelete: el identificador de la tarea eliminada (tarea de destino). Si se va a borrar a sí mismo, el parámetro formal es NULL
valor de retorno No

Para utilizar esta función debe estar FreeRTOSConfig.hen el INCLUDE_vTaskDeletedefinido para estar habilitada.

2.1.4 vTaskStartScheduler

Cuando se crea la tarea, debemos encender el programador, porque la creación es solo para agregar la tarea al sistema, no se ha programado realmente, no se ha implementado la tarea inactiva y no se ha implementado la tarea del temporizador. Implementado Todos estos están abriendo la función de programación Implementado en vTaskStartScheduler (). ¿Por qué tareas inactivas? Porque una vez que se inicia FreeRTOS, es necesario asegurarse de que haya una tarea en el estado de ejecución (En ejecución) en todo momento en el sistema, y ​​las tareas inactivas no se pueden suspender ni eliminar. La prioridad de las tareas inactivas es la más baja para que otras Tareas en el sistema Tasks puede apoderarse de los derechos de uso de CPU de las tareas inactivas en cualquier momento.

función void vTaskStartScheduler (void)
parámetro No
valor de retorno No

2.2 Ejemplo

#include "FreeRTOS.h"
#include "task.h"

#include "bsp_led.h"
#include "bsp_usart.h"

/****************** 任务句柄 ******************/
/* 
 * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
 * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
 * 这个句柄可以为NULL。
 */
 /* 创建任务句柄 */
static TaskHandle_t AppTaskCreate_Handle = NULL;
/* LED1 任务句柄 */ 
static TaskHandle_t LED1_Task_Handle = NULL; 
/* LED2 任务句柄 */ 
static TaskHandle_t LED2_Task_Handle = NULL;

/*
*************************************************************************
* 函数声明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用于创建任务 */
 
static void LED1_Task(void* pvParameters);/* LED1_Task 任务实现 */ 
static void LED2_Task(void* pvParameters);/* LED2_Task 任务实现 */

/*****************************************************************
* @brief 主函数
* @param 无
* @retval 无
* @note 第一步:开发板硬件初始化
        第二步:创建 APP 应用任务
        第三步:启动 FreeRTOS,开始多任务调度
****************************************************************/
int main(void)
{
    
    
    BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为 pdPASS */

    /* 开发板硬件初始化 */
    BSP_Init();
    /* 创建 AppTaskCreate 任务 */
    xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任务入口函数 */
                            (const char* )"AppTaskCreate",/* 任务名字 */
                            (uint16_t )512, /* 任务栈大小 */
                            (void* )NULL,/* 任务入口函数参数 */
                            (UBaseType_t )1, /* 任务的优先级 */
                            (TaskHandle_t* )&AppTaskCreate_Handle);/* 任务控制块指针 */
    /* 启动任务调度 */
    if (pdPASS == xReturn)
    {
    
    
        vTaskStartScheduler(); /* 启动任务,开启调度 */
    }
    else
    {
    
    
        return -1;
    }

    while (1); /* 正常不会执行到这里 */
}

/***********************************************************************
* @ 函数名 : AppTaskCreate
* @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
* @ 参数 : 无
* @ 返回值 : 无
**********************************************************************/
static void AppTaskCreate(void)
{
    
    
    BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为 pdPASS */

    taskENTER_CRITICAL(); //进入临界区

    /* 创建 LED_Task 任务 */ 
    xReturn = xTaskCreate((TaskFunction_t )LED1_Task, /* 任务入口函数 */ 
                            (const char* )"LED1_Task",/* 任务名字 */ 
                            (uint16_t )512, /* 任务栈大小 */ 
                            (void* )NULL, /* 任务入口函数参数 */ 
                            (UBaseType_t )2, /* 任务的优先级 */ 
                            (TaskHandle_t* )&LED1_Task_Handle);/* 任务控制块指针 */ 
    if (pdPASS == xReturn) 
    {
    
    
        printf("创建 LED1_Task 任务成功!\r\n"); 
    }

    /* 创建 LED_Task 任务 */ 
    xReturn = xTaskCreate((TaskFunction_t )LED2_Task, /* 任务入口函数 */ 
                            (const char* )"LED2_Task",/* 任务名字 */ 
                            (uint16_t )512, /* 任务栈大小 */ 
                            (void* )NULL, /* 任务入口函数参数 */ 
                            (UBaseType_t )3, /* 任务的优先级 */ 
                            (TaskHandle_t* )&LED2_Task_Handle);/* 任务控制块指针 */ 
    if (pdPASS == xReturn) 
    {
    
    
        printf("创建 LED2_Task 任务成功!\r\n"); 
    }

    vTaskDelete(AppTaskCreate_Handle); //删除 AppTaskCreate 任务

    taskEXIT_CRITICAL(); //退出临界区
}

/**********************************************************************
* @ 函数名 : LED1_Task
* @ 功能说明: LED1_Task 任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void LED1_Task(void* parameter) 
{
    
     
    while (1) 
    {
    
     
        LED1_ON; 
        vTaskDelay(500); /* 延时 500 个 tick */ 
        printf("led1_task running,LED1_ON\r\n"); 

        LED1_OFF; 
        vTaskDelay(500); /* 延时 500 个 tick */ 
        printf("led1_task running,LED1_OFF\r\n"); 
    } 
} 

/**********************************************************************
* @ 函数名 : LED2_Task
* @ 功能说明: LED2_Task 任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void LED2_Task(void* parameter) 
{
    
     
    while (1) 
    {
    
     
        LED2_ON; 
        vTaskDelay(1000); /* 延时 500 个 tick */ 
        printf("led1_task running,LED2_ON\r\n"); 
 
        LED2_OFF; 
        vTaskDelay(1000); /* 延时 500 个 tick */ 
        printf("led1_task running,LED2_OFF\r\n"); 
    } 
}

/***********************************************************************
* @ 函数名 : BSP_Init
* @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
* @ 参数 :
* @ 返回值 : 无
*********************************************************************/
static void BSP_Init(void)
{
    
    
    /*
    * STM32 中断优先级分组为 4,即 4bit 都用来表示抢占优先级,范围为:0~15
    * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
    * 都统一用这个优先级分组,千万不要再分组,切忌。
    */
    NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );

    /* LED 初始化 */
    LED_GPIO_Config();

    /* 串口初始化*/
    USART_Config();
}

Tres, retraso de la tarea

3.1 Descripción de la API relacionada

3.1.1 vTaskDelay

Función de retardo relativo. Se utiliza para bloquear el retraso. Después de llamar a esta función, la tarea entrará en el estado de bloqueo y la tarea que entra en el estado de bloqueo cederá los recursos de la CPU

función void vTaskDelay (const TickType_t xTicksToDelay)
parámetro xTicksToDelay: la unidad es el período de tic del sistema. Por ejemplo, el período de tic del reloj del sistema es de 1 ms, luego el tiempo de retraso de la llamada a vTaskDelay (1) es de 1 ms
valor de retorno No

Para utilizar esta función debe estar FreeRTOSConfig.hen el INCLUDE_vTaskDelaydefinido para estar habilitada.

3.1.2 vTaskDelayUntil

Función de retardo absoluto. A menudo se usa para tareas de ejecución periódicas más precisas. Por ejemplo, tengo una tarea y espero que se ejecute regularmente a una frecuencia fija sin influencia externa. El intervalo de tiempo desde la última ejecución hasta la siguiente ejecución es absoluto, no relativamente

función void vTaskDelayUntil (TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement)
parámetro pxPreviousWakeTime: La última vez que la tarea salió del estado de bloqueo (se despertó). Este momento se utiliza como punto de referencia para calcular la próxima vez que la tarea sale del estado bloqueado

xTimeIncrement: se utiliza para implementar una determinada tarea periódicamente a una frecuencia fija; esta frecuencia la especifica xTimeIncrement. La unidad es el período de latido, puede usar el puerto constanteTICK_RATE_MS para convertir milisegundos al período de latido
valor de retorno No

Para utilizar esta función debe estar FreeRTOSConfig.hen el INCLUDE_vTaskDelayUntildefinido para estar habilitada.

3.2 Ejemplo

3.2.1 Retraso relativo

void vTaskA( void * pvParameters )
{
    
    
    while (1) 
    {
    
    
        // ...
        // 这里为任务主体代码
        // ...

        /* 调用相对延时函数,阻塞 1000 个 tick */ 
        vTaskDelay( 1000 ); 
    }
}

3.2.2 Retraso absoluto

Nota: Cuando se usa, el tiempo de demora debe convertirse en latidos del sistema y la función de demora debe llamarse antes que el cuerpo de la tarea.

void vTaskA( void * pvParameters ) 
{
    
    
    /* 用于保存上次时间。调用后系统自动更新 */ 
    static portTickType PreviousWakeTime; 
    /* 设置延时时间,将时间转为节拍数 */ 
    const portTickType TimeIncrement = pdMS_TO_TICKS(1000); 

    /* 获取当前系统时间 */ 
    PreviousWakeTime = xTaskGetTickCount(); 

    while (1)
    {
    
    
        /* 调用绝对延时函数,任务时间间隔为 1000 个 tick */ 
        vTaskDelayUntil( &PreviousWakeTime,TimeIncrement ); 
 
        // ...
        // 这里为任务主体代码 
        // ...
 
    }
}

Cuarto, suspensión de tareas y reanudación.

4.1 Descripción de la API relacionada

4.1.1 vTaskSuspend

Suspenda la tarea especificada. Una tarea suspendida nunca tendrá derecho a usar la CPU, sin importar la prioridad que tenga la tarea.

función void vTaskSuspend (TaskHandle_t xTaskToSuspend)
parámetro xTaskToSuspend: Suspende el identificador de la tarea especificada. La tarea debe ser una tarea creada. Puede suspender la tarea en sí pasando NULL
valor de retorno No

Para utilizar esta función debe estar FreeRTOSConfig.hen el INCLUDE_vTaskSuspenddefinido para estar habilitada.

4.1.2 vTaskSuspendAll

Suspenda todas las tareas.

función void vTaskSuspendAll (void)
parámetro No
valor de retorno No

4.1.3 vTaskResume

Deje que la tarea suspendida vuelva a entrar en el estado listo, y la tarea restaurada retendrá la información del estado antes de la suspensión y continuará ejecutándose de acuerdo con el estado suspendido cuando se reanude. Si la tarea restaurada es la primera en la lista de mayor prioridad entre todas las tareas listas, el sistema cambiará el contexto de la tarea.

función vTaskResume vacío (TaskHandle_t xTaskToResume)
parámetro xTaskToResume: restaura el identificador de tarea de la tarea especificada
valor de retorno No

4.1.4 xTaskResumeFromISR

xTaskResumeFromISR() 与 vTaskResume() 一样都是用于恢复被挂起的任务,不一样的是 xTaskResumeFromISR() 专门用在中断服务程序中 。

函数 void vTaskResume( TaskHandle_t xTaskToResume )
参数 xTaskToResume: 恢复指定任务的任务句柄
返回值

要想使用该函数必须在 FreeRTOSConfig.h 中把 INCLUDE_vTaskSuspendINCLUDE_vTaskResumeFromISR 定义为 1 来使能。

4.1.5 xTaskResumeAll

将所有的任务都挂起。

函数 void xTaskResumeAll( void )
参数
返回值

4.2 示例

4.2.1 挂起

/**************************** 任务句柄 ********************************/
/*
* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
* 这个句柄可以为 NULL。
*/
static TaskHandle_t LED_Task_Handle = NULL;/* LED 任务句柄 */ 

static void KEY_Task(void* parameter)
{
    
    
    while (1) 
    {
    
    
        if ( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON ) 
        {
    
    
            /* K1 被按下 */
            printf("挂起 LED 任务!\n");
            vTaskSuspend(LED_Task_Handle);/* 挂起 LED 任务 */ 
        }
        vTaskDelay(20);/* 延时 20 个 tick */
    }
}

4.2.2 恢复

/**************************** 任务句柄 ********************************/
/*
* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
* 这个句柄可以为 NULL。
*/
static TaskHandle_t LED_Task_Handle = NULL;/* LED 任务句柄 */ 

static void KEY_Task(void* parameter)
{
    
    
    while (1) 
    {
    
    
        if ( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON ) 
        {
    
    
            /* K2 被按下 */
            printf("恢复 LED 任务!\n");
            vTaskResume(LED_Task_Handle);/* 恢复 LED 任务! */ 
        }
        vTaskDelay(20);/* 延时 20 个 tick */
    }
}

4.2.3 中断服务中恢复

使用 xTaskResumeFromISR()的时候有几个需要注意的地方:

  1. 当函数的返回值为 pdTRUE 时:恢复运行的任务的优先级等于或高于正在运行的任务,表明在中断服务函数退出后必 须进行一次上下文切换 , 使用 portYIELD_FROM_ISR() 进行上下文切换。当函数的返回值为 pdFALSE 时:恢复运行的任务的优先级低于当前正在运行的任务,表明在中断服务函数退出后不需 要进行上下文切换。
  2. xTaskResumeFromISR() 通常被认为是一个危险的函数,因为它的调用并非是固定的,中断可能随时来来临。所以 xTaskResumeFromISR()不能用于任务和中断间的同步,如果中断恰巧在任务被挂起之前到达,这就会导致一次中断丢失(任务还没有挂起,调用 xTaskResumeFromISR()函数是没有意义的,只能等下一次中断)。这种情况下,可以使用信号量或者任务通知来同步就可以避免这种情况。
void vAnExampleISR( void )
{
    
    
    BaseType_t xYieldRequired;

    /* 恢复被挂起的任务 */ 
    xYieldRequired = xTaskResumeFromISR( xHandle ); 

    if ( xYieldRequired == pdTRUE ) 
    {
    
     
        /* 执行上下文切换, ISR 返回的时候将运行另外一个任务 */ 
        portYIELD_FROM_ISR(); 
    } 
}

4.2.4 全部恢复

xTaskResumeAll 函数的使用方法很简单,但是要注意,调用了多少次 vTaskSuspendAll() 函数就必须同样调用多少次 xTaskResumeAll() 函数。

void vDemoFunction( void )
{
    
    
    vTaskSuspendAll();
    /* 处理 xxx 代码 */
    vTaskSuspendAll();
    /* 处理 xxx 代码 */
    vTaskSuspendAll();
    /* 处理 xxx 代码 */
 
    xTaskResumeAll(); 
    xTaskResumeAll(); 
    xTaskResumeAll(); 
}

五、获取任务状态

5.1 相关API说明

5.1.1 eTaskGetState

获取任务当前状态。

函数 eTaskState eTaskGetState( TaskHandle_t xTask )
参数 xTask: 任务句柄
返回值 以下值
/* Task states returned by eTaskGetState. */
typedef enum
{
    
    
    eRunning = 0,     /* A task is querying the state of itself, so must be running. */
    eReady,           /* The task being queried is in a read or pending ready list. */
    eBlocked,         /* The task being queried is in the Blocked state. */
    eSuspended,       /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */
    eDeleted,         /* The task being queried has been deleted, but its TCB has not yet been freed. */
    eInvalid          /* Used as an 'invalid state' value. */
} eTaskState;

• 由 Leung 写于 2020 年 10 月 30 日

• 参考:野火FreeRTOS视频与PDF教程

Supongo que te gusta

Origin blog.csdn.net/qq_36347513/article/details/109381405
Recomendado
Clasificación