
小白理解 抛砖引玉 欢迎加v共同学习!!!加V请备注"Freertos "。备注:本系列均基于stm32cubeIDE,并非keilV5哦!!!

FreeRTOS基本文件生成和说明
在Middleware下勾选Freertos,接口选择CMSIS_V1或CMSIS_V1,点击上方小齿轮重生成代码后,将在“project explorer->Middlewares->Third_Party->FreeRTOS->Source"目标下生成若干文件和文件夹。
(1):include文件夹、portable文件夹、CMSIS_RTOS或CMSIS_RTOS_V2文件夹(这两个文件加2选1,如果Freertos的接口选择为CMSIS_V1则为CMSIS_RTOS;否则为接口选择为CMSIS_V2则为CMSIS_RTOS_V2);
- include文件放置若干头文件,这里我们关心list.h就可以了。其他头文件说明将在之后进行更新。
- portable文件夹包括GCC和MemMang文件夹;GCC下包括port.c和portmacro.h两个文件。MemMang包括heap_4.c,这是内存分配方案4,FreeRTOS总共提供5中内存分配方案,文件分别为heap_x.c,x=1,2,3,4,5,一般默认x=4。之后会更新这些文件的说明。
(2):croutine.c、event_groups.c、list.c、queue.c、stream_buffer.c、task.c、timers.c.这里只需要关心list.c就可以了,其他头文件说明将在之后进行更新。
基本概念:
- freertos中变量名以c、s、l、p、u、x开头,分别代表该变量为char,short/16位,long/32位,pointer(指针)、unsigned和其他类型。
- freertos中Tab键等于4个空格,不是8个,因此编程时不推荐使用Tab键。
什么是list和list_item?
list:实际上是C语言中的链表,而list_item即C语言中链表的节点。
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现。链表的节点包括数据项和指针项;其中数据项用来存储数据,指针项指向下一个/上一个节点的物理地址!链表的节点/结点实际上是一种特殊的结构体。ps:(链表的【头节点/头结点】只存在指针项不存在数据项,指针项用来指出链表物理存储区域的开始地址,链表可以不需要【头节点/头结点】)。在list.h中定义FreeRTOS使用的xLIST_ITEM、xMINI_LIST_ITEM、xLIST三种结构体。首元节点:链表种第一个含有数据项的节点。

(1)首先分析configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES==0时,即忽略用于检查链表数据的完整性的相关参数。
list.h初探
list.h定义了3种链表节点,分别是普通节点xLIST_ITEM、精简节点xMINI_LIST_ITEM、头节点xLIST。因为头节点和普通节点的数据结构和含义并不完全相同,因此尽管头节点xLIST包含数据项,我在这里任然称其为头节点,而不是首元节点。三种结构的定义依次如下:
struct
- 首先定义了结构体xLIST_ITEM/ListItem_t,两个名字都代表同一个结构体;
- configLIST_VOLATILE 是修饰符,视为VOLATILE修饰符即可;
- TickType_t 是类型uint32_t或uint16_t,由portmacro.h中的configUSE_16_BIT_TICKS定义;参数xItemValue,用来做排序,一般降序;后面的lists.c文件中的函数void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )会用到。
- pxNext和pxPrevious为xLIST_ITEM指针变量,分别指向下一个xLIST_ITEM节点和上一个xLIST_ITEM节点;
- pvOwner指针变量,指向拥有该节点的内核对象,一般是TCB;(不是很好理解,之后会完善)
- pvContainer指针变量,指向节点所在的列表
- void* 表示的是任意类型的指针;
- listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE/listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE两个变量用于检查链表数据的完整性,当configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES==1是需要自己设置为已知值。
struct
- 首先定义了结构体xMINI_LIST_ITEM/MiniListItem_t,两个名字都代表同一个结构体;
- TickType_t 是类型uint32_t或uint16_t,由portmacro.h中的configUSE_16_BIT_TICKS定义;参数xItemValue用来做排序,一般降序;
- pxNext和pxPrevious为xLIST_ITEM指针变量,分别指向下一个xLIST_ITEM节点和上一个xLIST_ITEM节点;
- listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE用于检查链表数据的完整性,当configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES==1是需要自己设置为已知值。
- xMINI_LIST_ITEM比xLIST_ITEM少2个用于指向节点所属链表和所属内核对象的指针参数,也没有listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE。
typedef
- 首先定义了结构体xLIST/List_t,两个名字都代表同一个结构体;
- configLIST_VOLATILE 是修饰符,视为VOLATILE修饰符即可;
- TickType_t 是类型uint32_t或uint16_t,由portmacro.h中的configUSE_16_BIT_TICKS定义;参数uxNumberOfItems链表节点计数器,用来表示该链表下有多少个链表节点的;
- pxIndex指针变量,用于遍历链表的所有节点,指向头节点内xListEnd的地址;
- xListEnd是一个MiniListItem_t节点,是链表的最后一个节点。因为FreeRTOS定义的链表是循环双向链表,因此XListEnd也是链表的第一节点。
- listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE/listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE两个变量用于检查链表数据的完整性,当configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES==1是需要自己设置为已知值。
list.c初探
其中list.c中定义了以下5个函数。
- void vListInitialiseItem( ListItem_t * const pxItem )负责普通链表节点的初始化;
- void vListInitialise( List_t * const pxList );负责头节点的初始化,即链表初始化;
- void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )负责将pxNewListItem指向的普通节点插入pxList指向的链表的末尾,由于链表为循环链表,因此这里所说的末尾是把头节点去除之后的双链链表的末尾;
- void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )负责在指定List插入list_item;
- UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )负责删除链表中的普通链表节点;
1.0普通链表节点的初始化 void vListInitialiseItem( ListItem_t * const pxItem )
void
链表节点(将list_item翻译为“列表项”,我认为不合适。链表和列表的数据结构是不同的)初始化。
pxItem
上句将节点所属的链表指向为空,此时链表节点不属于任何链表。此链表节点包括5个参数项!!使用的是ListItem_t 不是xMINI_ListItem_t 。
2.0链表头节点(最后一个节点)的初始化void vListInitialise( List_t * const pxList );链表头节点初始化完成后,链表已经存在,只是链表的节点数量只有头节点内的xListEnd节点,没有其他节点,因此节点数量等于0。头节点初始化即是链表初始化。
void
uxNumberOfItems记录本链表种普通节点(ListItem_t节点)的总数量;
- pxIndex指针向量指向头节点内部的精简版节点xListEnd的物理地址,用来遍历整个链表。
- 精简版节点xListEnd的xItemValue等于portMAX_DELAY(0xffffffffUL或0xffffUL,取决于configUSE_16_BIT_TICKSd的值);表示xListEnd节点为链表的最后一个节点;
pxList
- 以上两句分别分别将xListEnd节点的上一个节点和下一个节点指向自己,表示该链表除此xListEnd节点外,没有其他节点。
pxList
- 上句设置链表的节点数量uxNumberOfItems初始化为0;/*UBaseType_t 类型是usigned long*/
3.0链表新增普通链表节点。void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
void

PS:图中1号节点为链表的首元节点。
pxIndex = pxList->pxIndex; 链表的头节点的xLIST_ITEM节点地址,图中0号节点。
pxNewListItem->pxNext = pxIndex; 新增链表节点的下一个节点是头节点内的xLIST_ITEM节点。即图中的0号节点。
pxNewListItem->pxPrevious = pxIndex->pxPrevious;新增链表节点的上一个节点是原来头节点内的xLIST_ITEM节点的上一个节点,即节点4.
pxIndex->pxPrevious->pxNext = pxNewListItem;原来的头节点内的xLIST_ITEM节点的上一个节点(图中节点4)的下一个节点应该为新增加的链表节点5。
pxIndex->pxPrevious = pxNewListItem; 头节点内的xLIST_ITEM节点的上一个节点是新增链表节点5。
PS:头节点内xLIST_ITEM节点的上一个节点为除xLIST_ITEM节点外的最后一个节点,即图中节点4(pxIndex->pxPrevious);头节点的xLIST_ITEM节点的下一个节点为除xLIST_ITEM节点外的第一节点(pxIndex->pxNext),即图中节点1。
pxNewListItem->pvContainer = ( void * ) pxList;
新增的普通链表节点属于pxList指向的链表;
( pxList->uxNumberOfItems )++;
上句设置头节点的链表计数器uxNumberOfItems加1
4.0链表删除普通链表节点。UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )负责删除链表中的普通链表节点;
UBaseType_t
这个返回有返回值,返回的是删除节点后,链表的数量。UBaseType_t 类型是usigned long
List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;找到要删除普通链表节点pxItemToRemove所属的 节点的;并将地址存储在指针变量pxList上。
pxItemToRemove
将要删除的链表节点的下一个节点(图中3号节点)的上一个节点指向要删除的节点的上一个节点(图中1号节点);
将要删除的链表节点的上一个节点(图中1号节点)的下一个节点指向要删除节点的下一个节点(图中3号节点)。

PS:图中1号节点为链表的首元节点。
if( pxList->pxIndex == pxItemToRemove )
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
xList->pxIndex == pxItemToRemove,如果要删除的节点是头节点内的xListEnd,即链表的最后一个节点。则将除头节点内的xListEnd节点外的最后一个节点作为头节点的xListEnd。
pxItemToRemove->pvContainer = NULL;
( pxList->uxNumberOfItems )--;
return pxList->uxNumberOfItems;
将删除的链表做初始化处理,即不属于任何链表;链表的计数器uxNumberOfItems减1;返回链表删除后的普通链表节点的数量uxNumberOfItems。
5.0将普通节点按照升序排序增加到链表中void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
负责将pxNewListItem指向的普通节点插入pxList指向的首元节点(链表)的末尾;
void
TickType_t xValueOfInsertion = pxNewListItem->xItemValue;首先是获取待插入的链表节点的赋值值xItemValue;TickType_t 是类型uint32_t或uint16_t,由portmacro.h中的configUSE_16_BIT_TICKS定义;首先判定该值是否为最大值portMAX_DELAY。如果是,直接将待插入的普通链表节点插入到链表的末尾即可。否则进入for循环,找到待插入的位置。比如现在有5个节点,他们的xItemValue分别是11,13,15,17,19。如果待插入的链表节点的xItemValue=XX,XX大于等于19或等于portMAX_DELAY。则插入后的顺序为头节点,11,13,15,17,19,XX。
for
假设XX=13则for循环以后pxIterator等于xItemValue=13对应节点的物理地址。
假设XX=14则for循环以后pxIterator等于xItemValue=15对应节点的物理地址。
假设XX=15则for循环以后pxIterator等于xItemValue=15对应节点的物理地址。
以XX=15为例:
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
- 待插入的链表节点的下一个节点指向pxIterator的下一个节点(即17号节点);
- 待插入的链表节点的下一个节点(17号)的上一个节点指向指向带插入节点;
- 待插入的链表节点的上一个节点指向之前的old15号节点;(也就是xItemValue相同的情况下,待加入的节点加载原先的节点的后面,比如原来已经有10个xItemValue=15的,则待加入的链表节点应该在添加在之前10个链表节点的后面);
- 待插入节点的上一个节点是pxIterator(old之前的15号)的下一个节点指向带插入节点(15号);
- PS:图中11号节点为链表的首元节点。

list.h文件还定义了若干的带宏参数:
#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )
#define listGET_LIST_ITEM_OWNER( pxListItem ) ( ( pxListItem )->pvOwner )
初始化/获取链表节点的内核对象拥有者
#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( ( pxListItem )->xItemValue = ( xValue ) )
#define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue )
初始化/获取链表节点的排序辅助值
#define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext )
#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext->xItemValue )
获取头节点内xListEnd节点的下一个链表节点地址的,即实际的链表的首元节点地址
获取首元节点的xItemValue值。
#define listGET_NEXT( pxListItem ) ( ( pxListItem )->pxNext )
获取链表节点的下一个节点的地址
#define listGET_END_MARKER( pxList ) ( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) )
获取链表的头节点内xListEnd的地址,即最后一个节点的物理地址。
#define listLIST_IS_EMPTY( pxList ) ( ( BaseType_t ) ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) )
#define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems )
判断链表是否为空及链表拥有普通链表节点的数量uxNumberOfItems,uxNumberOfItems=0时为链表为空链表。
#define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( (&( ( pxList )->xListEnd ))->pxNext->pvOwner )
获取链表首元节点的内核对象拥有者
#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pvContainer )
获取链表节点所属的链表
#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( BaseType_t ) ( ( pxListItem )->pvContainer == ( void * ) ( pxList ) ) )
判断链表节点是否被指定的链表所包含
#define listLIST_IS_INITIALISED( pxList ) ( ( pxList )->xListEnd.xItemValue == portMAX_DELAY )
判断链表是否完成初始化
void
这一段没看懂,求大神赐教
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )
{
List_t * const pxConstList = ( pxList );
/* Increment the index to the next item and return the item, ensuring */
/* we don't return the marker used at the end of the list. */
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) )
{
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;
}
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;
}
获取首元节点所属的内核对象拥有者
#if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 0 )
/* Define the macros to do nothing. */
#define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE
#define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE
#define listFIRST_LIST_INTEGRITY_CHECK_VALUE
#define listSECOND_LIST_INTEGRITY_CHECK_VALUE
#define listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem )
#define listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem )
#define listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList )
#define listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList )
#define listTEST_LIST_ITEM_INTEGRITY( pxItem )
#define listTEST_LIST_INTEGRITY( pxList )
#else
/* Define macros that add new members into the list structures. */
#define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue1;
#define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue2;
#define listFIRST_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue1;
#define listSECOND_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue2;
/* Define macros that set the new structure members to known values. */
#define listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) ( pxItem )->xListItemIntegrityValue1 = pdINTEGRITY_CHECK_VALUE
#define listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) ( pxItem )->xListItemIntegrityValue2 = pdINTEGRITY_CHECK_VALUE
#define listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList ) ( pxList )->xListIntegrityValue1 = pdINTEGRITY_CHECK_VALUE
#define listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList ) ( pxList )->xListIntegrityValue2 = pdINTEGRITY_CHECK_VALUE
/* Define macros that will assert if one of the structure members does not
contain its expected value. */
#define listTEST_LIST_ITEM_INTEGRITY( pxItem ) configASSERT( ( ( pxItem )->xListItemIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxItem )->xListItemIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )
#define listTEST_LIST_INTEGRITY( pxList ) configASSERT( ( ( pxList )->xListIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxList )->xListIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )
#endif /* configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES */
以上代码用于检查链表节点数据的完整性,目前还没有接触到。等研究明白了更新。或者指导的大神,请不吝赐教。谢谢
c语言知识链表参考:https://blog.youkuaiyun.com/baidu_41813368/article/details/82776527