FreeRtos列表与列表项
文章目录
前言
本次分享的是FreeRtos中的列表和列表项的知识点,对于FreeRtos的学习来说这是很重要的一部分。
一、什么是列表和列表项
1.1 列表
列表结构示意图
列表是 FreeRTOS 中的一个数据结构,概念上和链表有点类似,列表被用来跟踪 FreeRTOS中的任务。与列表相关的全部东西都在文件 list.c 和list.h 中。在 list.h 中定义了一个叫 List_t 的结构体。
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
volatile UBaseType_t uxNumberOfItems;
ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
} List_t;
(1)
listFIRST_LIST_INTEGRITY_CHECK_VALUE listSECOND_LIST_INTEGRITY_CHECK_VALUE
这两个都是用来检测列表的完整性的,在使用的使用我们还需要将configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 设置为 1,开启后会向这两个地方添加一个变量xListIntegrityValue1 和 xListIntegrityValue2在初始化列表的时候会这两个变量中写入一个特殊的值,默认不开启这个功能。
(2)、uxNumberOfItems 用来记录列表中列表项的数量。
(3)、pxIndex 用来记录当前列表项索引号,用于遍历列表。
(4)、列表中最后一个列表项,用来表示列表结束,此变量类型为 MiniListItem_t,这是一个迷你列表项
1.2 列表项
列表项结构示意图
列表和列表项都放在List.c 和list.h这两个文件中,列表项中的成员比列表的成员要多一些。
列表项结构体成员如下:
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_VOLATILE TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */
void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */
struct xLIST * configLIST_VOLATILE pxContainer; /*< Pointer to the list in which this list item is placed (if any). */
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
};
(1)、listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE 这两个成员和列表中第一个与最后一个功能相同都是检测列表项的完整性。
(2)、xItemValue 为列表项值。
(3)、pxNext 指向下一个列表项。
(4)、pxPrevious 指向前一个列表项,和 pxNext 配合起来实现类似双向链表的功能。
(5)、pvOwner 记录此链表项归谁拥有,通常是任务控制块。
(6)、pvContainer 用来记录此列表项归哪个列表
1.3 mini列表项
mini列表项结构示意图
typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */
struct xMINI_LIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_VOLATILE TickType_t xItemValue;
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
(1)listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE 用于检测列表项的完整性
2)、xItemValue 记录列表列表项值。
(3)、pxNext 指向下一个列表项。
(4)、pxPrevious 指向上一个列表项
二、列表和列表项的初始化
2.1 列表初始化
新创建或者定义的列表需要对其做初始化处理,列表的初始化其实就是初始化列表结构体List_t 中的各个成员变量,列表的初始化通过使函数 vListInitialise()来完成,此函数在 list.c 中有定义,函数如下:
void vListInitialise( List_t * const pxList )
{
/* The list structure contains a list item which is used to mark the
* end of the list. To initialise the list the list end is inserted
* as the only list entry. */
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */
/* The list end value is the highest possible value in the list to
* ensure it remains at the end of the list. */
pxList->xListEnd.xItemValue = portMAX_DELAY;
/* The list end next and previous pointers point to itself so we know
* when the list is empty. */
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
/* Write known values into the list if
* configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}
(1)、xListEnd 用来表示列表的末尾,而 pxIndex 表示列表项的索引号,此时列表只有一个列表项,那就是 xListEnd,所以 pxIndex 指向 xListEnd。
(2)、xListEnd 的列表项值初始化为 portMAX_DELAY, portMAX_DELAY 是个宏,在文件portmacro.h 中有定义。根据所使用的 MCU 的不同,portMAX_DELAY 值也不相同,可以为 0xffff或者 0xffffffffUL,本教程中为 0xffffffffUL。
(3)、初始化列表项 xListEnd 的 pxNext 变量,因为此时列表只有一个列表项 xListEnd,因此 pxNext 只能指向自身.
(4)、最后两个用于检测列表的完整性。
2.2 列表项的初始化
列表项和列表一样在使用的时候也需要先将其进行初始化,列表项初始化由函数 vListInitialiseItem()。
void vListInitialiseItem( ListItem_t * const pxItem )
{
/* Make sure the list item is not recorded as being on a list. */
pxItem->pxContainer = NULL;
/* Write known values into the list item if
* configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
下面从插入列表项值分别为40 、60 、50实现过程进行演示
1、在一个空的列表 List 中插入一个列表值为 40 的列表项 ListItem1
列表是一个环形的
2、接着再插入一个值为 60 的列表项 ListItem2
3、再插入一个值为 50 的列表项 ListItem3
三、demo演示列表项的插入、删除、遍历
3.1 列表项的插入
列表末尾插入列表项的操作通过函数 vListInsertEnd ()来完成,函数原型如下:
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
void vListInsertEnd( List_t * const pxList,
ListItem_t * const pxNewListItem )
{
ListItem_t * const pxIndex = pxList->pxIndex;
/* Only effective when configASSERT() is also defined, these tests may catch
* the list data structures being overwritten in memory. They will not catch
* data errors caused by incorrect configuration or use of FreeRTOS. */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
/* Insert a new list item into pxList, but rather than sort the list,
* makes the new list item the last item to be removed by a call to
* listGET_OWNER_OF_NEXT_ENTRY(). */
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
/* Only used during decision coverage testing. */
mtCOVERAGE_TEST_DELAY();
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
/* Remember which list the item is in. */
pxNewListItem->pxContainer = pxList;
( pxList->uxNumberOfItems )++;
}
3.2 列表项的删除
当我们需要将列表中的列表项进行删除的时候,可以通过专门的删除函数
uxListRemove()来完成,函数原型如下:
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* The list item knows which list it is in. Obtain the list from the list
* item. */
List_t * const pxList = pxItemToRemove->pxContainer;
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
/* Only used during decision coverage testing. */
mtCOVERAGE_TEST_DELAY();
/* Make sure the index is left pointing to a valid item. */
if( pxList->pxIndex == pxItemToRemove )
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
pxItemToRemove->pxContainer = NULL;
( pxList->uxNumberOfItems )--;
return pxList->uxNumberOfItems;
}
3.3 列表项的遍历
FreeRTOS提供了一个函数来完成列表的遍历
这个函数是 listGET_OWNER_OF_NEXT_ENTRY()。每调用一次这个函数列表的 pxIndex 变量就会指向下一个列表项,并且返回这个列表项的 pxOwner变量值。这个函数本质上是一个宏,这个宏在文件 list.h 中如下定义
3.4 demo演示
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define LED0_TASK_PRIO 2
//任务堆栈大小
#define LED0_STK_SIZE 50
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);
//任务优先级
#define LIST_TASK_PRIO 3
//任务堆栈大小
#define LIST_STK_SIZE 50
//任务句柄
TaskHandle_t LISTTask_Handler;
//任务函数
void list_task(void *pvParameters);
List_t TestList;
ListItem_t ListItem1;
ListItem_t ListItem2;
ListItem_t ListItem3;
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
delay_init(); //延时函数初始化
uart_init(115200); //初始化串口
LED_Init(); //初始化LED
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
vTaskStartScheduler(); //开启任务调度
}
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建LED0任务
xTaskCreate((TaskFunction_t )led0_task,
(const char* )"led0_task",
(uint16_t )LED0_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED0_TASK_PRIO,
(TaskHandle_t* )&LED0Task_Handler);
//创建LED1任务
xTaskCreate((TaskFunction_t )list_task,
(const char* )"list_task",
(uint16_t )LIST_TASK_PRIO,
(void* )NULL,
(UBaseType_t )LIST_TASK_PRIO,
(TaskHandle_t* )&LISTTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//LED1任务函数
void led0_task(void *pvParameters)
{
while(1)
{
LED1=0;
vTaskDelay(200);
LED1=1;
vTaskDelay(800);
printf("led2 is running\r\n");
}
}
void list_task(void *pvParameters)
{
vListInitialise(&TestList);
vListInitialiseItem(&ListItem1);
vListInitialiseItem(&ListItem2);
vListInitialiseItem(&ListItem3);
ListItem1.xItemValue = 40;
ListItem2.xItemValue = 60;
ListItem3.xItemValue = 50;
printf("------------------------列表与列表项地址------------------------\r\n");
printf("项目 地址 \r\n");
printf("TestList %#x \r\n",(int)&TestList);
printf("TestList->pxIndex %#x \r\n",(int)TestList.pxIndex);
printf("TestList->xListEnd %#x \r\n",(int)&(TestList.xListEnd));
printf("ListItem1 %#x \r\n",(int)&ListItem1);
printf("ListItem2 %#x \r\n",(int)&ListItem2);
printf("ListItem3 %#x \r\n",(int)&ListItem3);
printf("------------------------------END--------------------------------\r\n");
//TestList 插入列表项ListItem1
vListInsert(&TestList,&ListItem1);
printf("--------------------------添加列表项ListItem1-----------------\r\n");
printf("TestList->xListEnd->pxNext %#x \r\n",(int)TestList.xListEnd.pxNext);
printf("ListItem1->pxNext %#x \r\n",(int)ListItem1.pxNext);
printf("TestList->xListEnd %#x \r\n",(int)&(TestList.xListEnd));
printf("---------------------------前后连接分割线-----------------------\r\n");
printf("TestList->xListEnd->pxPrevious %#x \r\n",(int)TestList.xListEnd.pxPrevious);
printf("ListItem1->pxPrevious %#x \r\n",(int)ListItem1.pxPrevious);
printf("-------------------------------END---------------------------------\r\n");
//TestList 插入列表项ListItem2
vListInsert(&TestList,&ListItem2);
printf("--------------------------添加列表项ListItem1-----------------\r\n");
printf("TestList->xListEnd->pxNext %#x \r\n",(int)TestList.xListEnd.pxNext);
printf("ListItem1->pxNext %#x \r\n",(int)ListItem1.pxNext);
printf("ListItem2->pxNext %#x \r\n",(int)ListItem2.pxNext);
printf("---------------------------前后连接分割线----------------------\r\n");
printf("TestList->xListEnd->pxPrevious %#x \r\n",(int)TestList.xListEnd.pxPrevious);
printf("ListItem1->pxPrevious %#x \r\n",(int)ListItem1.pxPrevious);
printf("ListItem2->pxPrevious %#x \r\n",(int)ListItem2.pxPrevious);
printf("--------------------------------NED----------------------------\r\n");
//TestList 插入列表项ListItem3
vListInsert(&TestList,&ListItem3);
printf("--------------------------添加列表项ListItem1------------------\r\n");
printf("TestList->xListEnd->pxNext %#x \r\n",(int)TestList.xListEnd.pxNext);
printf("ListItem1->pxNext %#x \r\n",(int)ListItem1.pxNext);
printf("ListItem2->pxNext %#x \r\n",(int)ListItem2.pxNext);
printf("ListItem3->pxNext %#x \r\n",(int)ListItem3.pxNext);
printf("---------------------------前后连接分割线-----------------------\r\n");
printf("TestList->xListEnd->pxPrevious %#x \r\n",(int)TestList.xListEnd.pxPrevious);
printf("ListItem1->pxPrevious %#x \r\n",(int)ListItem1.pxPrevious);
printf("ListItem2->pxPrevious %#x \r\n",(int)ListItem2.pxPrevious);
printf("ListItem3->pxPrevious %#x \r\n",(int)ListItem3.pxPrevious);
printf("--------------------------------NED-----------------------------\r\n");
//TestList 删除ListItem2
uxListRemove(&ListItem2);
printf("--------------------------列表删除项ListItem2------------------\r\n");
printf("TestList->xListEnd->pxNext %#x \r\n",(int)TestList.xListEnd.pxNext);
printf("ListItem1->pxNext %#x \r\n",(int)ListItem1.pxNext);
printf("ListItem3->pxNext %#x \r\n",(int)ListItem3.pxNext);
printf("---------------------------前后连接分割线-----------------------\r\n");
printf("TestList->xListEnd->pxPrevious %#x \r\n",(int)TestList.xListEnd.pxPrevious);
printf("ListItem1->pxPrevious %#x \r\n",(int)ListItem1.pxPrevious);
printf("ListItem3->pxPrevious %#x \r\n",(int)ListItem3.pxPrevious);
printf("--------------------------------NED-----------------------------\r\n");
//TestList末尾添加ListItem2
TestList.pxIndex = TestList.pxIndex->pxNext;
vListInsertEnd(&TestList,&ListItem2);
printf("--------------------------列表删除项ListItem2------------------\r\n");
printf("TestList %#x \r\n",(int)&TestList);
printf("TestList->xListEnd->pxNext %#x \r\n",(int)TestList.xListEnd.pxNext);
printf("ListItem1->pxNext %#x \r\n",(int)ListItem1.pxNext);
printf("ListItem2->pxNext %#x \r\n",(int)ListItem2.pxNext);
printf("ListItem3->pxNext %#x \r\n",(int)ListItem3.pxNext);
printf("---------------------------前后连接分割线-----------------------\r\n");
printf("TestList->xListEnd->pxPrevious %#x \r\n",(int)TestList.xListEnd.pxPrevious);
printf("ListItem1->pxPrevious %#x \r\n",(int)ListItem1.pxPrevious);
printf("ListItem2->pxPrevious %#x \r\n",(int)ListItem2.pxPrevious);
printf("ListItem3->pxPrevious %#x \r\n",(int)ListItem3.pxPrevious);
printf("--------------------------------NED-----------------------------\r\n");
}