提示:好记性不如烂笔头。本博客作为学习笔记,有错误的地方希望指正


文章目录

  • 前言:
  • 一、任务的创建
  • 1.1、使用FreeRTOS标准的创建任务API
  • 1.1.1、TaskHandle_t 任务句柄
  • 1.1.2、xTaskCreate创建任务
  • 1.1.3、xTaskCreateStatic 创建任务
  • 1.1.4、xTaskCreateRestrictedStatic创建任务
  • 1.2、使用ESP32的 xTaskCreatePinnedToCore API创建任务
  • 二、任务的删除
  • 三、实验源码


前言:

  参考资料:FreeRTOS API参考   现在开始对FreeRTOS全面的学习,之前也看过许多的文档,使用的正点的文档学习的,但是都是没有记录的学习,不会就去找文档查看资料,但是没有系统体系的对FreeRTOS全面的学习。
  第一篇我们先开始从创建任务开始,对于ESP32而言,本身Demo就已经只带了FreeRTOS操作系统,省去繁琐复杂的移植,FreeRTOS是一个开源的操作系统,可以方便我们移植到许多的单片机上,之前学习stm32的时候,哪个时候st还没有出cubemax,需要手动去移植FreeRTOS,哪个对刚开始学习操作系统确实比较头大,还有许多汇编的知识,对刚开始学习不太友好,所以想要学习操作系统的话,最起码是先能跑起来先玩起来,至于最后深层的移植工作也是看需求和个人爱好是否继续深入研究。
  首先我们先从创建任务开始学习,ESP32的话创建任务有两套方法,一个是使用FreeRTOS默认是那套API来创建任务,另外可以使用ESP32它本身的API来创建,使用ESP32自己的API来创建任务的话可以指定在哪个核中创建任务,其中ESP32的双核使用的是SMP构架,SMP构架的系统中所有CPU共享系统内存和外设资源,由操作系统负责处理器间协作,并保持数据结构的一致性.两个核使用互斥信号量的保护,使得任务运行一些外设例如串口之类的不会发生冲突。
  在原码中我找到xTaskCreate的函数定义,可以看出xTaskCreate创建的时候是调用xTaskCreatePinnedToCoreAPI来实现的。

static inline IRAM_ATTR BaseType_t xTaskCreate(
                            TaskFunction_t pvTaskCode,
                            const char * const pcName,     /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                            const uint32_t usStackDepth,
                            void * const pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t * const pxCreatedTask) PRIVILEGED_FUNCTION
    {
        return xTaskCreatePinnedToCore( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask, tskNO_AFFINITY );
    }

一、任务的创建

  创建任务这里分为使用FreeRTOS中的API来创建和使用ESP32带指定核API来创建。

1.1、使用FreeRTOS标准的创建任务API

1.1.1、TaskHandle_t 任务句柄

  引用任务的类型。例如,对xTaskCreate的调用(通过指针参数)返回一个TaskHandle_t变量,然后可以将它用作vTaskDelete的参数来删除任务。其中TaskHandle_t 是一个 void* 类型的数据,使用TaskHandle_t定义的变量是空指针类型。
  TaskHandle_t的定义:

#ifdef ESP_PLATFORM // IDF-3769
typedef void* TaskHandle_t;
#else
typedef struct tskTaskControlBlock* TaskHandle_t;
#endif // ESP_PLATFORM

1.1.2、xTaskCreate创建任务

xTaskCreate API原型:

BaseType_t xTaskCreate(    TaskFunction_t pvTaskCode,
                            const char * const pcName,
                            configSTACK_DEPTH_TYPE usStackDepth,
                            void *pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t *pxCreatedTask
                          );

  创建一个新任务,并将其添加到准备运行的任务列表中。configSUPPORT_DYNAMIC_ALLOCATION必须在FreeRTOSConfig.h中设置为1,或者不设置为undefined(在这种情况下,它将默认为1),以便这个RTOS API函数可用。
  每个任务都需要用于保存任务状态的RAM,并被任务用作其堆栈。如果使用xTaskCreate()创建任务,则从FreeRTOS堆自动分配所需的RAM。如果使用xTaskCreateStatic()创建任务,那么RAM是由应用程序编写器提供的,因此可以在编译时静态分配它。有关更多信息,请参阅静态与动态分配页面。
  如果你正在使用FreeRTOS-MPU,那么我们建议你使用xTaskCreateRestricted()而不是xTaskCreate()。
xTaskCreate 函数参数:

  • pvTaskCode:指向任务入口函数的指针(只是实现任务的函数的名称,参见下面的示例)。任务通常以无限循环的形式实现;实现任务的函数决不能尝试返回或退出。但是,任务可以删除自己。
  • pcName:任务的描述性名称。这主要用于方便调试,但也可用于获取任务句柄。任务名称的最大长度由FreeRTOSConfig.h中的configMAX_TASK_NAME_LEN定义。
  • usStackDepth:分配的用作任务堆栈的字数(不是字节!)例如,如果堆栈是16位宽的,usStackDepth是100,那么将分配200字节作为任务的堆栈。另一个例子是,如果堆栈是32位宽的,usStackDepth是400,那么将分配1600字节作为任务的堆栈。堆栈深度乘以堆栈宽度不能超过size_t类型变量所能包含的最大值。参见常见问题解答堆栈应该有多大?
  • pvParameters:作为参数传递给创建的任务的值。如果pvParameters被设置为一个变量的地址,那么当创建的任务执行时,该变量必须仍然存在,所以传递堆栈变量的地址是无效的。
  • uxPriority:创建的任务执行的优先级。包含MPU支持的系统可以通过在uxPriority中设置位portPRIVILEGE_BIT来选择以特权(系统)模式创建任务。例如,要创建优先级为2的特权任务,将uxPriority设置为(2 | portPRIVILEGE_BIT)。优先级被断言小于configMAX_PRIORITIES。如果configASSERT未定义,优先级将静默地封顶为(configMAX_PRIORITIES - 1)。
    -pxCreatedTask:用于从xTaskCreate()函数向创建的任务传递句柄。pxCreatedTask是可选的,可以设置为NULL。
    返回:
      如果任务被成功创建,则返回pdPASS。否则返回errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY。
    示例code:
/* Task to be created. */
void vTaskCode( void * pvParameters )
{
    /* The parameter value is expected to be 1 as 1 is passed in the
    pvParameters value in the call to xTaskCreate() below. 
    configASSERT( ( ( uint32_t ) pvParameters ) == 1 );*/

    for( ;; )
    {
        /* Task code goes here. */
    }
}

/* Function that creates a task. */
void vOtherFunction( void )
{
BaseType_t xReturned;
TaskHandle_t xHandle = NULL;

    /* Create the task, storing the handle. */
    xReturned = xTaskCreate(
                    vTaskCode,       /* Function that implements the task. */
                    "NAME",          /* Text name for the task. */
                    STACK_SIZE,      /* Stack size in words, not bytes. */
                    ( void * ) 1,    /* Parameter passed into the task. */
                    tskIDLE_PRIORITY,/* Priority at which the task is created. */
                    &xHandle );      /* Used to pass out the created task's handle. */

    if( xReturned == pdPASS )
    {
        /* The task was created.  Use the task's handle to delete the task. */
        vTaskDelete( xHandle );
    }
}

1.1.3、xTaskCreateStatic 创建任务

xTaskCreateStatic API原型:

TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
                                 const char * const pcName,
                                 const uint32_t ulStackDepth,
                                 void * const pvParameters,
                                 UBaseType_t uxPriority,
                                 StackType_t * const puxStackBuffer,
                                 StaticTask_t * const pxTaskBuffer );

  创建一个新的任务,并将其添加到准备运行的任务列表中。 configSUPPORT_STATIC_ALLOCATION必须在FreeRTOSConfig.h中被设置为1,这个RTOS API函数才可用。
  每个任务都需要RAM,用来保存任务状态,并由任务作为其堆栈使用。如果一个任务是用xTaskCreate()创建的,那么所需的RAM会自动从FreeRTOS的堆中分配。如果一个任务是用xTaskCreateStatic()创建的,那么RAM是由应用程序编写者提供的,这将导致更多的参数,但允许RAM在编译时被静态分配。更多信息请参见静态与动态分配页面。
  如果你使用FreeRTOS-MPU,那么我们建议你使用xTaskCreateRestricted()而不是xTaskCreateStatic()。
xTaskCreateStatic 函数参数:

  • pxTaskCode :指向任务入口函数的指针(只是实现任务的函数名称,见下面的例子)。任务通常被实现为一个无限循环;实现任务的函数决不能试图返回或退出。然而,任务可以自我删除。
  • pcName :任务的描述性名称。这主要是为了方便调试,但也可以用来获得一个任务句柄。一个任务名称的最大长度由FreeRTOSConfig.h中的configMAX_TASK_NAME_LEN定义。
  • ulStackDepth: puxStackBuffer参数用于向xTaskCreateStatic()传递一个StackType_t变量的数组,ulStackDepth必须被设置为数组中的索引数。参见FAQ 堆栈应该有多大?
  • pvParameters: 作为参数传递给创建的任务的值。如果pvParameters被设置为一个变量的地址,那么当创建的任务执行时,该变量必须仍然存在,所以传递堆栈变量的地址是无效的。
  • uxPriority: 创建的任务将以何种优先级执行。包含MPU支持的系统可以通过设置uxPriority中的portPRIVILEGE_BIT位,选择性地在特权(系统)模式下创建任务。例如,为了创建一个优先级为2的特权任务,将uxPriority设置为( 2 |portPRIVILEGE_BIT )。优先级被断言为小于 configMAX_PRIORITIES。如果configASSERT是未定义的,优先级就会默默地以(configMAX_PRIORITIES - 1)为上限。
  • puxStackBuffer: 必须指向一个StackType_t数组,该数组至少有ulStackDepth索引(见上面的ulStackDepth参数)–该数组将被用作任务的堆栈,所以它必须是持久的(不是在一个函数的堆栈中声明)。
  • pxTaskBuffer: 必须指向一个StaticTask_t类型的变量。这个变量将被用来保存新任务的数据结构(TCB),所以它必须是持久的(不是在函数的堆栈中声明)。
    返回:
      如果puxStackBuffer和pxTaskBuffer都是NULL,那么任务将被创建,并返回任务的句柄。如果puxStackBuffer或pxTaskBuffer是NULL,那么任务将不会被创建,并返回NULL。
    示例code:
/* Dimensions of the buffer that the task being created will use as its stack.
    NOTE:  This is the number of words the stack will hold, not the number of
    bytes.  For example, if each stack item is 32-bits, and this is set to 100,
    then 400 bytes (100 * 32-bits) will be allocated. */
    #define STACK_SIZE 200

    /* Structure that will hold the TCB of the task being created. */
    StaticTask_t xTaskBuffer;

    /* Buffer that the task being created will use as its stack.  Note this is
    an array of StackType_t variables.  The size of StackType_t is dependent on
    the RTOS port. */
    StackType_t xStack[ STACK_SIZE ];

    /* Function that implements the task being created. */
    void vTaskCode( void * pvParameters )
    {
        /* The parameter value is expected to be 1 as 1 is passed in the
        pvParameters value in the call to xTaskCreateStatic(). */
        configASSERT( ( uint32_t ) pvParameters == 1UL );

        for( ;; )
        {
            /* Task code goes here. */
        }
    }

    /* Function that creates a task. */
    void vOtherFunction( void )
    {
        TaskHandle_t xHandle = NULL;

        /* Create the task without using any dynamic memory allocation. */
        xHandle = xTaskCreateStatic(
                      vTaskCode,       /* Function that implements the task. */
                      "NAME",          /* Text name for the task. */
                      STACK_SIZE,      /* Number of indexes in the xStack array. */
                      ( void * ) 1,    /* Parameter passed into the task. */
                      tskIDLE_PRIORITY,/* Priority at which the task is created. */
                      xStack,          /* Array to use as the task's stack. */
                      &xTaskBuffer );  /* Variable to hold the task's data structure. */

        /* puxStackBuffer and pxTaskBuffer were not NULL, so the task will have
        been created, and xHandle will be the task's handle.  Use the handle
        to suspend the task. */
        vTaskSuspend( xHandle );
    }

1.1.4、xTaskCreateRestrictedStatic创建任务

xTaskCreateRestrictedStatic API原型:

BaseType_t xTaskCreateRestrictedStatic( TaskParameters_t *pxTaskDefinition,
                                        TaskHandle_t *pxCreatedTask );

  创建一个新的内存保护单元(MPU)限制的任务,并将其添加到准备运行的任务列表中。 configSUPPORT_STATIC_ALLOCATION必须在FreeRTOSConfig.h中被设置为1,这个RTOS API函数才可用。
  在FreeRTOS的内部,每个任务需要两个内存块。第一个区块用来存放任务的数据结构。第二个区块被用作任务的堆栈。如果一个任务是用xTaskCreateRestricted()创建的,那么任务的堆栈的内存是由应用程序编写者提供的,而任务的数据结构的内存是自动从FreeRTOS堆中分配的。如果一个任务是用xTaskCreateRestrictedStatic()创建的,那么应用程序编写者也必须为任务的数据结构提供内存。因此,xTaskCreateRestrictedStatic()允许在不使用任何动态内存分配的情况下创建一个内存保护任务。
  xTaskCreateRestrictedStatic()是为了与FreeRTOS-MPU一起使用,该演示程序包含了如何使用类似函数xTaskCreateRestricted()的全面和记录的例子。
xTaskCreateRestrictedStatic 函数参数:

  • pxTaskDefinition: 指向定义任务的结构的指针。该结构在本页面有描述。
  • pxCreatedTask: 用于传回一个句柄,通过它可以引用所创建的任务。
    返回:
      如果任务被成功创建并添加到准备好的列表中,则返回pdPASS,否则返回projdefs.h文件中定义的错误代码。
      包含MPU支持的任务比不包含MPU的任务需要更多的参数来创建。将每个参数单独传递给xTaskCreateRestrictedStatic()会很不方便,因此使用了TaskParameters_t结构来允许在编译时静态配置参数。该结构在task.h中定义如下。
typedef struct xTASK_PARAMETERS
{
    TaskFunction_t pvTaskCode;
    const signed char * const pcName;
    unsigned short usStackDepth;
    void *pvParameters;
    UBaseType_t uxPriority;
    portSTACK_TYPE *puxStackBuffer;
    MemoryRegion_t xRegions[ portNUM_CONFIGURABLE_REGIONS ];
    #if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )
        StaticTask_t * const pxTaskBuffer;
    #endif
} TaskParameters_t;
// 其中MemoryRegion_t定义为:
typedef struct xMEMORY_REGION
{
    void *pvBaseAddress;
    unsigned long ulLengthInBytes;
    unsigned long ulParameters;
} MemoryRegion_t;

以下是每个结构成员的描述:

  • pvTaskCode 到 uxPriority:这些成员与发送给xTaskCreate()的同名参数完全相同。特别是uxPriority用于设置任务的优先级和任务的执行模式。例如,要创建一个优先级为2的用户模式任务,只需将uxPriority设置为2,要创建一个优先级为2的特权模式任务,则将uxPriority设置为( 2 | portPRIVILEGE_BIT )。
  • puxStackBuffer:每次任务被切换进来时,MPU都会被动态地重新配置,以定义一个区域,为任务提供对其自身堆栈的读写权限。MPU区域必须满足一些约束条件–特别是,所有这些区域的大小和对齐方式必须等于2的同次方值。标准的FreeRTOS端口在每次创建任务时使用pvPortMalloc()来分配一个新的堆栈。提供一个pvPortMalloc()的实现来照顾MPU的数据对齐要求是可能的,但在RAM的使用上也会很复杂和低效。为了消除这种复杂性,FreeRTOS-MPU允许在编译时静态地声明堆栈。这允许使用编译器扩展来管理对齐,并由链接器来管理RAM的使用效率。例如,如果使用GCC,可以用下面的代码声明一个堆栈并正确对齐。 char cTaskStack[ 1024 ] attribute((aligned(1024))。puxStackBuffer通常会被设置为静态声明的堆栈的地址。作为一种选择,puxStackBuffer可以被设置为NULL–在这种情况下,pvPortMallocAligned()将被调用以分配任务栈,应用程序编写者有责任提供一个满足MPU对齐要求的pvPortMallocAligned()的实现。
  • xRegions:xRegions是一个MemoryRegion_t结构的数组,每个结构都定义了一个用户可定义的内存区域,供正在创建的任务使用。ARM Cortex-M3 FreeRTOS-MPU端口定义了portNUM_CONFIGURABLE_REGIONS为3。pvBaseAddress和ulLengthInBytes成员分别是内存区域的起始点和内存区域的长度,这是不言自明的。 ulParameters定义了允许任务访问内存区域的方式,可以采用以下值的位数OR。
portMPU_REGION_READ_WRITE
	portMPU_REGION_PRIVILEGED_READ_ONLY
	portMPU_REGION_READ_ONLY
	portMPU_REGION_PRIVILEGED_READ_WRITE
	portMPU_REGION_CACHEABLE_BUFFERABLE
	portMPU_REGION_EXECUTE_NEVER
  • pxTaskBuffer:必须指向一个 StaticTask_t 类型的变量。这个变量将被用来保存新任务的数据结构,所以它必须是持久的(不是在函数的堆栈中声明)。
    示例code
static PRIVILEGED_DATA StaticTask_t xTaskBuffer;
static const TaskParameters_t xCheckTaskParameters =
{
  vATask,     /* pvTaskCode - the function that implements the task. */
  "ATask",    /* pcName - just a text name for the task to assist debugging. */
  100,        /* usStackDepth - the stack size DEFINED IN WORDS. */
  NULL,       /* pvParameters - passed into the task function as the function parameters. */
  ( 1UL | portPRIVILEGE_BIT ),/* uxPriority - task priority, set the portPRIVILEGE_BIT 
                                   if the task should run in a privileged state. */
  cStackBuffer,/* puxStackBuffer - the buffer to be used as the task stack. */

  /* xRegions - Allocate up to three separate memory regions for access by
   * the task, with appropriate access permissions.  Different processors have
   * different memory alignment requirements - refer to the FreeRTOS documentation
   * for full information. */
  {
      /* Base address                 Length  Parameters */
      { cReadWriteArray,              32,     portMPU_REGION_READ_WRITE },
      { cReadOnlyArray,               32,     portMPU_REGION_READ_ONLY },
      { cPrivilegedOnlyAccessArray,   128,    portMPU_REGION_PRIVILEGED_READ_WRITE }
  }

  &xTaskBuffer; /* Holds the task's data structure. */
 };

 int main( void )
 {
 TaskHandle_t xHandle;

  /* Create a task from the const structure defined above.  The task handle
   * is requested (the second parameter is not NULL) but in this case just for
   * demonstration purposes as its not actually used. */
  xTaskCreateRestricted( &xRegTest1Parameters, &xHandle );

  /* Start the scheduler. */
  vTaskStartScheduler();

  /* Will only get here if there was insufficient memory to create the idle
   * and/or timer task. */
  for( ;; );
 }

1.2、使用ESP32的 xTaskCreatePinnedToCore API创建任务

xTaskCreatePinnedToCore API原型:

BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pvTaskCode,           	 //任务名 这里直接是函数名
                                        const char * const pcName,           //任务名字 字符串 最大长度在FreeRTOSConfig.h中有限制,ESP32中最大16个字符
                                        const uint32_t usStackDepth,         //任务堆栈大小,总体的大小是要乘以 4 单位 byte usStackDepth 单位是字word 
                                        void * const pvParameters,           //给任务传入参数,这个参数可以是任意类型的   
                                        UBaseType_t uxPriority,              //任务优先级别 空闲优先级是0 0最低,往上就越高 最大值在FreeRTOSConfig.h 中设置   
                                        TaskHandle_t * const pvCreatedTask,  //任务句柄 方便对任务的一些操作,例如删除、挂起传入任务句柄作为参数的
                                        const BaseType_t xCoreID);           //任务创建在哪个核

  创建一个具有指定亲和力的新任务。这个函数类似于xTaskCreate,但允许在SMP系统中设置任务亲和力。在SMP系统中。
xTaskCreatePinnedToCore 函数参数:

  • pvTaskCode: 指向任务输入函数的指针。 任务必须实现永不返回(即连续循环),或者应该使用vTaskDelete函数终止。
  • pcName 任务的描述性名称。 这主要是为了方便调试。 最大长度由configMAX_TASK_NAME_LEN定义 - 默认是16。
  • usStackDepth: 任务栈的大小,以字节数表示。字节数。注意,这与vanilla FreeRTOS不同。
  • pvParameters: 指针将被用作任务的参数。正在创建。
  • uxPriority: 任务运行的优先级。 系统包括MPU支持的系统可以选择在特权(系统)下创建任务。通过设置优先级参数的位portPRIVILEGE_BIT来创建任务。 例如例如,要创建一个优先级为2的特权任务,uxPriority参数应该被设置为 ( 2 | portPRIVILEGE_BIT )。
  • pvCreatedTask: 用于传回一个句柄,创建的任务可以通过它来引用。可以被引用。
  • xCoreID: 如果该值为tskNO_AFFINITY,则创建的任务不被钉在任何CPU上。钉在任何CPU上,调度器可以在任何可用的核心上运行它。值为0或1时,表示该任务应该被钉在CPU上的索引号。被钉住。指定大于(portNUM_PROCESSORS - 1)的值将导致函数失败。导致该函数失败。
      如果任务被成功创建并添加到准备好的列表中,返回pdPASS。列表中,否则会有一个错误代码,该代码在文件projdefs.h中定义。

二、任务的删除

vTaskDelete API原型:

void vTaskDelete( TaskHandle_t xTask );

  INCLUDE_vTaskDelete必须被定义为1,这个函数才可用。更多信息请参见RTOS配置文档。
  从RTOS内核管理中删除一个任务。被删除的任务将从所有就绪、阻塞、暂停和事件列表中删除。
  注意:空闲任务负责释放RTOS内核分配给被删除的任务的内存。因此,如果你的应用程序对vTaskDelete()进行了任何调用,空闲任务不会被剥夺微控制器的处理时间,这一点很重要。任务代码分配的内存不会被自动释放,应该在任务被删除前释放。
  请参阅演示程序文件death.c中利用vTaskDelete()的示例代码。
参数
  xTask 要删除的任务的句柄。传递NULL将导致调用的任务被删除。
  对于删除任务可以使用两种方法,一种是在任务外删除另一个任务,另外就是在本任务中删除自己。
示例code

void vOtherFunction( void )
 {
 TaskHandle_t xHandle = NULL;

     // Create the task, storing the handle.
     xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );

     // Use the handle to delete the task.
     if( xHandle != NULL )
     {
         vTaskDelete( xHandle );
     }
 }

三、实验源码

  这里我主要测试使用xTaskCreate、xTaskCreateStatic、xTaskCreatePinnedToCore、vTaskDelete来作为实验测试,没有使用xTaskCreateRestrictedStatic来创建任务,xTaskCreateRestrictedStatic创建任务需要开启MPU,这里不做深入了解,需要了解的自行查阅相关资料。

/**
 * @file 1_CreateTask.c
 * @author WSP
 * @brief  任务创建
 * @version 0.1
 * @date 2022-10-05
 * 
 * @copyright Copyright (c) 2022
 * 
 */

#include "System_Include.h"

const static char * TAG = "CreateTask";
//  Create_common_task 任务的一些变量
TaskHandle_t Create_common_task_handle = NULL;;

// Create_common_task 任务的一些变量
#define CREATE_STATIC_TASK_STACK_SIZE 2048
TaskHandle_t CreateStaticTask_Handle = NULL;
StaticTask_t CreateStaticTask_Buffer;
StackType_t  CreateStaticTask_xStack[ CREATE_STATIC_TASK_STACK_SIZE ];

// Create_with_core_task 任务的一些变量
TaskHandle_t Create_with_core_task_handle = NULL;

/**
 * @brief Create_common_task
 * @param arg 任务传入的参数
 * @return NULL
 */
void Create_common_task(void * arg)
{
    while (1){
        vTaskDelay(1000/portTICK_PERIOD_MS);
        ESP_LOGI(TAG,"Create_common_task");
    }
}
/**
 * @brief CreateStatic_Task
 * @param arg 任务传入的参数
 * @return NULL
 */
void CreateStatic_Task(void *arg)
{
    while (1){
        vTaskDelay(1000/portTICK_PERIOD_MS);
        ESP_LOGI(TAG,"CreateStatic_Task");
    }
}
/**
 * @brief CreateRestricted_Task
 * @param arg 任务传入的参数
 * @return NULL
 */
void CreateRestricted_Task(void * arg)
{
    while (1){
        vTaskDelay(1000/portTICK_PERIOD_MS);
        ESP_LOGI(TAG,"CreateRestricted_Task");
    } 
}
/**
 * @brief Create_with_core_task
 * @param arg 任务传入的参数
 * @return NULL
 */
void Create_with_core_task(void *arg)
{
    // char loop_value = 5;
    while (1){
        vTaskDelay(1000/portTICK_PERIOD_MS);
        ESP_LOGI(TAG,"Create_with_core_task");
        // 第二种删除函数打方法: 在自己的任务中删除自己 
        // if(!loop_value)
        //     vTaskDelete(NULL); 
        // loop_value --;
    }
}
/**
 * @brief   创建函数初始化
 * @param   NULL
 * @return  NULL
 */
void CreateTask_Init(void)
{
    BaseType_t Create_with_core_task_return;

    // 使用 freeREOS的标准API创建任务
    xTaskCreate(Create_common_task,             // 创建任务
                "Create_common_task",           // 创建任务名
                2048,                           // 任务堆栈
                NULL,                           // 传入任务参数
                2,                              // 任务优先级  
                &Create_common_task_handle);    // 任务句柄
    // 使用xTaskCreateStatic 创建任务
    CreateStaticTask_Handle = xTaskCreateStatic(CreateStatic_Task,                          // 创建任务     
                                                "CreateStatic_Task",                        // 创建任务名        
                                                CREATE_STATIC_TASK_STACK_SIZE,              // 任务堆栈         
                                                NULL,                                       // 传入任务参数     
                                                2,                                          // 任务优先级
                                                CreateStaticTask_xStack,                    // 任务的堆栈数组     
                                                &CreateStaticTask_Buffer);                  // 新任务的数据结构       
    // 指定在哪个核创建任务
    Create_with_core_task_return = xTaskCreatePinnedToCore( Create_with_core_task,          // 创建任务
                                                            "CreateCore_Task",              // 创建任务名
                                                            2048,                           // 任务堆栈
                                                            NULL,                           // 传入任务参数
                                                            2,                              // 任务优先级
                                                            &Create_with_core_task_handle,  // 任务句柄
                                                            1);                             // 创建在哪个核

    vTaskDelay(3000/portTICK_PERIOD_MS);
    // 第一种删除函数打方法: 在其他函数中删除Create_with_core_task 任务 Create_with_core_task需要在死循环中,不然会一直复位提示
    // FreeRTOS: FreeRTOS Task "Create_with_cor" should not return, Aborting now! 就是说我们的Create_with_core_task 函数没有死循环 
    // 值的注意的是复位提示的数据Create_with_cor字符串是我们创建任务中const char * const pcName 这个参数,但是我们创建的时候传入的是Create_with_core_task
    // 怎么他打印就不全了呢,这个字符串的长度在FreeRTOSConfig.h中configMAX_TASK_NAME_LEN参数设定了 ESP32默认16个长度,所以就是输出Create_with_cor,其中字符串结束\0站一个byte
    if(Create_with_core_task_return == pdPASS)  // pdPASS = 1
        vTaskDelete(Create_with_core_task_handle);  

}