当前位置:首页 > 公众号精选 > 雷达通信电子战
[导读]关注、星标公众号,直达精彩内容ID:技术让梦想更伟大整理:李肖遥回顾任务的创建删除在FreeRTOS基础系列《FreeRTOS系列第10篇---FreeRTOS任务创建和删除》中介绍了任务创建API函数xTaskCreate(),我们这里先回顾一下这个函数的声明:BaseType...


关注、星标公众号,直达精彩内容

ID:技术让梦想更伟大

整理:李肖遥


回顾任务的创建删除

在FreeRTOS基础系列《FreeRTOS系列第10篇---FreeRTOS任务创建和删除》中介绍了任务创建API函数xTaskCreate(),我们这里先回顾一下这个函数的声明:

BaseType_t xTaskCreate(
    TaskFunction_tp vTaskCode,
    const char * constpcName,
    unsigned short usStackDepth,
    void *pvParameters,
    UBaseType_t uxPriority,
    TaskHandle_t *pvCreatedTask
);
这个API函数的作用是创建新的任务并将它加入到任务就绪列表,函数参数含义为:

  • 「pvTaskCode」:函数指针,指向任务函数的入口。任务永远不会返回(位于死循环内)。该参数类型TaskFunction_t定义在文件projdefs.h中,定义为:typedef void(*TaskFunction_t)( void * ),即参数为空指针类型并返回空类型。

  • 「pcName」:任务描述。主要用于调试。字符串的最大长度(包括字符串结束字符)由宏configMAX_TASK_NAME_LEN指定,该宏位于FreeRTOSConfig.h文件中。

  • 「usStackDepth」:指定任务堆栈大小,能够支持的堆栈变量数量(堆栈深度),而不是字节数。比如,在16位宽度的堆栈下,usStackDepth定义为100,则实际使用200字节堆栈存储空间。堆栈的宽度乘以深度必须不超过size_t类型所能表示的最大值。比如,size_t为16位,则可以表示堆栈的最大值是65535字节。这是因为堆栈在申请时是以字节为单位的,申请的字节数就是堆栈宽度乘以深度,如果这个乘积超出size_t所表示的范围,就会溢出,分配的堆栈空间也不是我们想要的。

  • 「pvParameters」:指针,当任务创建时,作为一个参数传递给任务。

  • 「uxPriority」:任务的优先级。具有MPU支持的系统,可以通过置位优先级参数的portPRIVILEGE_BIT位,随意的在特权(系统)模式下创建任务。比如,创建一个优先级为2的特权任务,参数uxPriority可以设置为 ( 2 | portPRIVILEGE_BIT )

  • 「pvCreatedTask」:用于回传一个句柄(ID),创建任务后可以使用这个句柄引用任务。

虽然xTaskCreate()看上去很像函数,但其实是一个宏,真正被调用的函数是xTaskGenericCreate()xTaskCreate()宏定义如下所示:

#define xTaskCreate( pvTaskCode, pcName, usStackDepth,pvParameters, uxPriority, pxCreatedTask )    \
      xTaskGenericCreate( ( pvTaskCode ),( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask), ( NULL ), ( NULL ), ( NULL ) )
可以看到,xTaskCreatexTaskGenericCreate少了三个参数,在宏定义中,这三个参数被设置为NULL。

这三个参数用于使用静态变量的方法分配堆栈、任务TCB空间以及设置MPU相关的参数。

一般情况下,这三个参数是不使用的,所以任务创建宏xTaskCreate定义的时候,将这三个参数对用户隐藏了。

接下来的章节中,为了方便,我们还是称xTaskCreate()为函数,虽然它是一个宏定义。

上面我们提到了任务TCB(任务控制块),这是一个需要重点介绍的关键点。

它用于存储任务的状态信息,包括任务运行时的环境。每个任务都有自己的任务TCB。

任务TCB是一个相对比较大的数据结构,这也是情理之中的,因为与任务相关的代码占到整个FreeRTOS代码量的一半左右,这些代码大都与任务TCB相关。

「我们先来介绍一下任务TCB数据结构的定义」

typedef struct tskTaskControlBlock
{
    volatile StackType_t    *pxTopOfStack; /*当前堆栈的栈顶,必须位于结构体的第一项*/
 
    #if ( portUSING_MPU_WRAPPERS == 1 )
        xMPU_SETTINGS   xMPUSettings;      /*MPU设置,必须位于结构体的第二项*/
    #endif
 
    ListItem_t          xStateListItem; /*任务的状态列表项,以引用的方式表示任务的状态*/
    ListItem_t          xEventListItem;    /*事件列表项,用于将任务以引用的方式挂接到事件列表*/
    UBaseType_t         uxPriority;        /*保存任务优先级,0表示最低优先级*/
    StackType_t         *pxStack;           /*指向堆栈的起始位置*/
    char               pcTaskName[ configMAX_TASK_NAME_LEN ];/*任务名字*/
 
    #if ( portSTACK_GROWTH > 0 )
        StackType_t     *pxEndOfStack;     /*指向堆栈的尾部*/
    #endif
 
    #if ( portCRITICAL_NESTING_IN_TCB == 1 )
        UBaseType_t     uxCriticalNesting; /*保存临界区嵌套深度*/
    #endif
 
    #if ( configUSE_TRACE_FACILITY == 1 )
        UBaseType_t     uxTCBNumber;       /*保存一个数值,每个任务都有唯一的值*/
        UBaseType_t     uxTaskNumber;      /*存储一个特定数值*/
    #endif
 
    #if ( configUSE_MUTEXES == 1 )
        UBaseType_t     uxBasePriority;    /*保存任务的基础优先级*/
        UBaseType_t     uxMutexesHeld;
    #endif
 
    #if ( configUSE_APPLICATION_TASK_TAG == 1 )
        TaskHookFunction_t pxTaskTag;
    #endif
 
    #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
        void *pvThreadLocalStoragePointers[configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
    #endif
 
    #if( configGENERATE_RUN_TIME_STATS == 1 )
        uint32_t        ulRunTimeCounter;  /*记录任务在运行状态下执行的总时间*/
    #endif
 
    #if ( configUSE_NEWLIB_REENTRANT == 1 )
        /* 为任务分配一个Newlibreent结构体变量。Newlib是一个C库函数,并非FreeRTOS维护,FreeRTOS也不对使用结果负责。如果用户使用Newlib,必须熟知Newlib的细节*/
        struct _reent xNewLib_reent;
    #endif
 
    #if( configUSE_TASK_NOTIFICATIONS == 1 )
        volatile uint32_t ulNotifiedValue; /*与任务通知相关*/
        volatile uint8_t ucNotifyState;
    #endif
 
    #if( configSUPPORT_STATIC_ALLOCATION == 1 )
        uint8_t ucStaticAllocationFlags; /* 如果堆栈由静态数组分配,则设置为pdTRUE,如果堆栈是动态分配的,则设置为pdFALSE*/
    #endif
 
    #if( INCLUDE_xTaskAbortDelay == 1 )
        uint8_t ucDelayAborted;
    #endif
 
} tskTCB;
 
typedef tskTCB TCB_t;
「下面我们详细的介绍这个数据结构的主要成员:」

指针pxTopOfStack必须位于结构体的第一项,指向当前堆栈的栈顶,对于向下增长的堆栈,pxTopOfStack总是指向最后一个入栈的项目。

如果使用MPU,xMPUSettings必须位于结构体的第二项,用于MPU设置。

接下来是状态列表项xStateListItem和事件列表项xEventListItem,我们在上一章介绍列表和列表项的文章中提到过:列表被FreeRTOS调度器使用,用于跟踪任务,处于就绪、挂起、延时的任务,都会被挂接到各自的列表中。

调度器就是通过把任务TCB中的状态列表项xStateListItem和事件列表项xEventListItem挂接到不同的列表中来实现上述过程的。

在task.c中,定义了一些静态列表变量,其中有就绪、阻塞、挂起列表,例如当某个任务处于就绪态时,调度器就将这个任务TCB的xStateListItem列表项挂接到就绪列表。

事件列表项也与之类似,当队列满的情况下,任务因入队操作而阻塞时,就会将事件列表项挂接到队列的等待入队列表上。

uxPriority用于保存任务的优先级,0为最低优先级。任务创建时,指定的任务优先级就被保存到该变量中。

指针pxStack指向堆栈的起始位置,任务创建时会分配指定数目的任务堆栈,申请堆栈内存函数返回的指针就被赋给该变量。

很多刚接触FreeRTOS的人会分不清指针pxTopOfStackpxStack的区别,「这里简单说一下:」

pxTopOfStack指向当前堆栈栈顶,随着进栈出栈,pxTopOfStack指向的位置是会变化的;

pxStack指向当前堆栈的起始位置,一经分配后,堆栈起始位置就固定了,不会被改变了。

「那么为什么需要pxStack变量呢?」

这是因为随着任务的运行,堆栈可能会溢出,在堆栈向下增长的系统中,这个变量可用于检查堆栈是否溢出;

如果在堆栈向上增长的系统中,要想确定堆栈是否溢出,还需要另外一个变量pxEndOfStack来辅助诊断是否堆栈溢出,后面会讲到这个变量。

字符数组pcTaskName用于保存任务的描述或名字,在任务创建时,由参数指定。

名字的长度由宏configMAX_TASK_NAME_LEN(位于FreeRTOSConfig.h中)指定,包含字符串结束标志。

如果堆栈向上生长(portSTACK_GROWTH > 0),指针pxEndOfStack指向堆栈尾部,用于检验堆栈是否溢出。

变量uxCriticalNesting用于保存临界区嵌套深度,初始值为0。

接下来两个变量用于可视化追踪,仅当宏configUSE_TRACE_FACILITY(位于FreeRTOSConfig.h中)为1时有效。

变量uxTCBNumber存储一个数值,在创建任务时由内核自动分配数值(通常每创建一个任务,值增加1),每个任务的uxTCBNumber值都不同,主要用于调试。

变量uxTaskNumber用于存储一个特定值,与变量uxTCBNumber不同,uxTaskNumber的数值不是由内核分配的,而是通过API函数vTaskSetTaskNumber()来设置的,数值由函数参数指定。

如果使用互斥量(configUSE_MUTEXES == 1),任务优先级被临时提高时,变量uxBasePriority用来保存任务原来的优先级。

变量ucStaticAllocationFlags也需要说明一下,我们前面说过任务创建API函数xTaskCreate()只能使用动态内存分配的方式创建任务堆栈和任务TCB,如果要使用静态变量实现任务堆栈和任务TCB就需要使用函数xTaskGenericCreate()来实现。

如果任务堆栈或任务TCB由静态数组和静态变量实现,则将该变量设置为pdTRUE(任务堆栈空间由静态数组变量实现时为0x01,任务TCB由静态变量实现时为0x02,任务堆栈和任务TCB都由静态变量实现时为0x03),如果堆栈是动态分配的,则将该变量设置为pdFALSE。

到这里任务TCB的数据结构就讲完了,下面我们用一个例子「来讲述任务创建的过程」,为方便起见,假设被创建的任务叫“任务A”,任务函数为vTask_A():

TaskHandle_t xHandle;
xTaskCreate(vTask_A,”Task A”,120,NULL,1,
本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

(全球TMT2023年8月1日讯)7月27日,黑芝麻智能RTOS Microkernel产品获得DEKRA德凯颁发的ASIL D功能安全产品认证。本次认证通过标志着黑芝麻智能可以为客户提供一款高实时性、高安全性的本土操...

关键字: KERNEL MICRO RTOS 智能汽车

(全球TMT2022年11月9日讯)近日,DEKRA德凯为黑芝麻智能科技颁发ISO 26262:2018 ASIL D功能安全流程认证证书,标志着黑芝麻智能科技建立起了符合功能安全最高等级ASIL D级别的车规芯片开发...

关键字: 智能科技 ASIL ISO RTOS

有深入理解RTOS原理,或阅读过RTOS源码的同学应该知道:RTOS实现任务间通信通常是由一系列指针进行操作实现的。

关键字: RTOS 指针 数组

这次我成功将妹子约到了公司附近的咖啡馆,继续探讨RTOS的heap的技术特点。当我把准备好的数据和动图展示在她面前,她立马激动起来了。

关键字: RTOS heap FreeRTOS

很多RTOS之所以可以用于资源很少的单片机,是因为它们可以配置,一般可以使用宏定义来选择需要的功能,而裁剪掉不必要的功能,以减少对硬件的资源占用。

关键字: RTOS 单片机

处理特定任务的单片机可减轻主单片机或微处理器的任务和工作负荷,从而有助于简化各种应用的设计流程。

关键字: Microchip 单片机 RTOS

关注星标公众号,不错过精彩内容作者|strongerHuang微信公众号 | 嵌入式专栏不知道大家有没有做过低功耗产品?低功耗产品看似很简单,其实,要做好一款低功耗产品,特别是做到超低功耗,难度相对更高。今天就来讲讲在R...

关键字: RTOS 低功耗设计 单片机 CPU

关注星标公众号,不错过精彩内容作者|strongerHuang微信公众号 | 嵌入式专栏不知道大家有没有做过低功耗产品?低功耗产品看似很简单,其实,要做好一款低功耗产品,特别是做到超低功耗,难度相对更高。今天就来讲讲在R...

关键字: RTOS 低功耗设计

星标「嵌入式大杂烩」,一起进步!作者|strongerHuang微信公众号|嵌入式专栏经常在交流群都会看到有些小伙伴在问:**资料、**文档、**源码在哪里下载?资料、文档、源码在哪里找?很多初学小白,找资料、文档、源码...

关键字: RTOS 开源

AzureRTOS使资源受限的设备能够连接到微软的AzureIoT,这是全球领先的IoT生态系统之一。AzureRTOS为企业级应用程序(如航空电子设备、医疗设备、运输和工业控制)提供理想的软件开发平台而闻名,然而,其设...

关键字: RTOS 硬件 物联网设备
关闭
关闭