1、列表就是一个双向循环链表,列表项就是其中的节点
2、其用途就是在三大链表(挂起、阻塞、就绪)中将任务(列表项)进行排序管理。
列表是 FreeRTOS 中的一个数据结构,概念上和链表有点类似,列表被用来跟踪 FreeRTOS中的任务。
列表的特点:列表项间的地址非连续的,是人为的连接到一起的。列表项的数目是由后期添加的个数决定的,随时可以改变
数组的特点:数组成员地址是连续的,数组在最初确定了成员数量后期无法改变
在OS中任务的数量是不确定的,并且任务状态是会发生改变的,所以非常适用列表(链表)这种数据结构
有关于列表的东西均在文件 list.c 和 list.h 中,首先我们先看下在list.h中的,
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE /* 校验值 */
volatile UBaseType_t uxNumberOfItems; /* 列表中的列表项数量 */
ListItem_t * configLIST_VOLATILE pxIndex /* 用于遍历列表项的指针 */
MiniListItem_t xListEnd /* 末尾列表项 */
listSECOND_LIST_INTEGRITY_CHECK_VALUE /* 校验值 */
} List_t;
1、在该结构体中, 包含了两个宏(校验值),这两个宏是确定的已知常量, FreeRTOS通过检查这两个常量的值,来判断列表的数据在程序运行过程中,是否遭到破坏 ,该功能一般用于调试, 默认是不开启的。
2、成员uxNumberOfItems,用于记录列表中列表项的个数(不包含 xListEnd 不算尾列表项)
3、成员 pxIndex 用于指向列表中的某个列表项,一般用于遍历列表中的所有列表项
4、成员变量 xListEnd 是一个迷你列表项,排在最末尾
**也就是说空列表中注意三点列表项数、指向某个列表项的指针,末尾列表项(是一个迷你列表项) **
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /* 用于检测列表项的数据完整性 /
configLIST_VOLATILE TickType_t xItemValue / 列表项的值 /
struct xLIST_ITEM * configLIST_VOLATILE pxNext / 下一个列表项 /
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious / 上一个列表项 /
void * pvOwner / 列表项的拥有者 /
struct xLIST * configLIST_VOLATILE pxContainer; / 列表项所在列表 /
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE / 用于检测列表项的数据完整性 */
};
typedef struct xLIST_ITEM ListItem_t;
两个宏同上不用
1、成员变量 xItemValue 为列表项的值,这个值多用于按升序对列表中的列表项进行排序
(当这个列表项插入某个列表时排序使用,确定插在哪)
比如一个任务是10一个任务20(不是任务名这个值不同人物到不同状态时可能不一样)按升序排序20排在10后。
2、成员变量 pxNext 和 pxPrevious 分别用于指向列表中列表项的下一个列表项和上一个列表项、
3、成员变量 pxOwner 用于指向包含列表项的对象(通常是任务控制块) 这个列表项代表哪个任务
4、成员变量 pxContainer 用于指向列表项所在列表。 (当前任务的列表项在哪个列表 ,该任务处于什么状态。)
就是用于标记我是最后一个列表了,你最大也得在我前面插入因为我是ffffffff最大
迷你列表项也是列表项,但迷你列表项仅用于标记列表的末尾和挂载其他插入列表中的列表项
struct xMINI_LIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /* 用于检测数据完整性 */
configLIST_VOLATILE TickType_t xItemValue; /* 列表项的值 */
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /* 上一个列表项 */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /* 下一个列表项 */
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
1、成员变量 xItemValue 为列表项的值,这个值多用于按升序对列表中的列表项进行排序
2、成员变量 pxNext 和 pxPrevious 分别用于指向列表中列表项的下一个列表项和上一个列表项
3、迷你列表项只用于标记列表的末尾和挂载其他插入列表中的列表项,因此不需要成员变量 pxOwner 和 pxContainer,以节省内存开销
解释一下这幅图
空列表时,末尾列表上一个下一个都指向自己
1插入时 1的上一个下一个指向末尾 末尾的上一个下一个也指向1
2插入时 比较xItemValue的大小2比1大在1后面 1的下一个指向2上一个指向尾 2的上一个指向1下一个指向尾,尾的上一个指向2下一个指向1。
初始化列表
vListInitialise()
无返回值参数为初始化的列表项
void vListInitialise( List_t * const pxList )
{
//列表中的便利指针指向末尾列表项
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
//末尾列表项的排序值设置为最大
pxList->xListEnd.xItemValue = portMAX_DELAY;
//末尾列表项指向下个列表的指针指向自身
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
//末尾列表项指向上个列表的指针指向自身
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
//列表中列表项数目设置为0
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
/* 初始化用于检测列表数据完整性的校验值 */
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}
初始化列表项
vListInitialiseItem()
无返回值 参数待初始化的列表项
void vListInitialiseItem( ListItem_t * const pxItem )
{
//所属列表为NULL
pxItem->pxContainer = NULL;
/* 初始化用于检测列表项数据完整性的校验值 */
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
vListInsert()
此函数用于将待插入列表的列表项按照列表项值升序进行排序,有序地插入到列表中
pxList 列表
pxNewListItem 待插入列表项
void vListInsert( List_t * const pxList,
ListItem_t * const pxNewListItem )
{
//声明一个临时列表项
ListItem_t * pxIterator;
//获取列表项升序排列数值
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
/* 检查参数是否正确 */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
/* 如果待插入列表项的值为最大值 */
if( xValueOfInsertion == portMAX_DELAY )
{
//将列表项的末尾列表的上一个列表指向这个临时列表
pxIterator = pxList->xListEnd.pxPrevious;
}
else
{
//否则将列表当前指针指向的列表项赋值给临时列表项 ,比较当前列表的下个列表的排序值是否比待插入的列表项大,如果不大将下一个列表项赋值给给当前临时列表项
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext )
//便利排序
}
//排序完成后将待插入列表插入到找到的列表后面
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
//待插入列表项属于当前列表
pxNewListItem->pxContainer = pxList;
//列表项数目加1
( pxList->uxNumberOfItems )++;
}
vListInsertEnd()
此函数用于将待插入列表的列表项插入到列表 pxIndex 指针指向的列表项前面,是一种无序的插入方法
void vListInsertEnd( List_t * const pxList,
ListItem_t * const pxNewListItem )
{
//声明一个列表项并将当前列表指向的列表项赋值给它
ListItem_t * const pxIndex = pxList->pxIndex;
//检查待插入列表和列表项的完整性
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
//待插入列表项插入到当前列表指针所指列表项前面
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
/* Only used during decision coverage testing. */
mtCOVERAGE_TEST_DELAY();
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
/* 待插入列表项属于当前列表 */
pxNewListItem->pxContainer = pxList;
//列表数目++
( pxList->uxNumberOfItems )++;
}
uxListRemove()
此函数用于将列表项从列表项所在列表中移除
参数 :待移除的列表项
返回值:移除后当前列表所剩列表项数目
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
//声明一个列表赋值当前列表项所在列表值
List_t * const pxList = pxItemToRemove->pxContainer;
//移除当前列表项
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
mtCOVERAGE_TEST_DELAY();
//如果当前指针指向该列表项
if( pxList->pxIndex == pxItemToRemove )
{
//让他指向它的前一个
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//所属列表为NULL
pxItemToRemove->pxContainer = NULL;
//列表项数目-1并返回
( pxList->uxNumberOfItems )--;
return pxList->uxNumberOfItems;
}
1、实验目的:学会对FreeRTOS 列表和列表项的操作函数使用,并观察运行结果和理论分析是否一致
2、实验设计:将设计三个任务:start_task、task1、task2
三个任务的功能如下:
start_task:用来创建其他的2个任务
task1:实现LED0每500ms闪烁一次,用来提示系统正在运行
task2:调用列表和列表项相关API函数,并且通过串口输出相应的信息,进行观察
/**
****************************************************************************************************
* @file freertos.c
* @author 正点原子团队(ALIENTEK)
* @version V1.4
* @date 2022-01-04
* @brief FreeRTOS 移植实验
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 F407电机开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
****************************************************************************************************
*/
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.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 );
#define TASK1_PRIO 2
#define TASK1_STACK_SIZE 128
TaskHandle_t task1_handler;
void task1( void * pvParameters );
#define TASK2_PRIO 3
#define TASK2_STACK_SIZE 128
TaskHandle_t task2_handler;
void task2( void * pvParameters );
/******************************************************************************************************/
List_t TestList; /* 定义测试列表 */
ListItem_t ListItem1; /* 定义测试列表项1 */
ListItem_t ListItem2; /* 定义测试列表项2 */
ListItem_t ListItem3; /* 定义测试列表项3 */
/******************************************************************************************************/
/**
* @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 );
vTaskDelete(NULL);//删除当前任务也就是开始任务
taskEXIT_CRITICAL();
}
void task1( void * pvParameters )
{
while(1)
{
LED0_TOGGLE();
vTaskDelay(500);
}
}
void task2( void * pvParameters )
{
vListInitialise(&TestList);
vListInitialiseItem(&ListItem1);
vListInitialiseItem(&ListItem2);
vListInitialiseItem(&ListItem3);
ListItem1.xItemValue= 40;
ListItem1.xItemValue= 50;
ListItem1.xItemValue= 60;
UBaseType_t i;
printf("/**************第二步:打印列表和列表项的地址**************/\r\n");
printf("项目\t\t\t地址\r\n");
printf("TestList\t\t0x%p\t\r\n", &TestList);
printf("TestList->pxIndex\t0x%p\t\r\n", TestList.pxIndex);
printf("TestList->xListEnd\t0x%p\t\r\n", (&TestList.xListEnd));
printf("ListItem1\t\t0x%p\t\r\n", &ListItem1);
printf("ListItem2\t\t0x%p\t\r\n", &ListItem2);
printf("ListItem3\t\t0x%p\t\r\n", &ListItem3);
printf("/**************************结束***************************/\r\n");
printf("\r\n/*****************第三步:列表项1插入列表******************/\r\n");
vListInsert( (List_t *) &TestList,
(ListItem_t *) &ListItem1 );
printf("项目\t\t\t\t地址\r\n");
printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
printf("/**************************结束***************************/\r\n");
/* 第四步:列表项2插入列表 */
printf("\r\n/*****************第四步:列表项2插入列表******************/\r\n");
vListInsert((List_t* )&TestList, /* 列表 */
(ListItem_t*)&ListItem2); /* 列表项 */
printf("项目\t\t\t\t地址\r\n");
printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
printf("/**************************结束***************************/\r\n");
/* 第五步:列表项3插入列表 */
printf("\r\n/*****************第五步:列表项3插入列表******************/\r\n");
vListInsert((List_t* )&TestList, /* 列表 */
(ListItem_t*)&ListItem3); /* 列表项 */
printf("项目\t\t\t\t地址\r\n");
printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
printf("/**************************结束***************************/\r\n");
/* 第六步:移除列表项2 */
printf("\r\n/*******************第六步:移除列表项2********************/\r\n");
i= uxListRemove( (ListItem_t * ) &ListItem2 );
printf("当前列表剩余列表项数目%u\r\n",i);
printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
printf("/**************************结束***************************/\r\n");
/* 第七步:列表末尾添加列表项2 */
printf("\r\n/****************第七步:列表末尾添加列表项2****************/\r\n");
TestList.pxIndex = &ListItem1;
vListInsertEnd( (List_t * ) &TestList,
( ListItem_t * ) &ListItem2 );
printf("项目\t\t\t\t地址\r\n");
printf("TestList->pxIndex\t\t0x%p\r\n", TestList.pxIndex);
printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
printf("/************************实验结束***************************/\r\n");
while(1)
{
vTaskDelay(1000);
}
}