FreeRTOSFreeRTOS学习笔记— 列表和列表项(链表和节点)
Posted 果果小师弟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FreeRTOSFreeRTOS学习笔记— 列表和列表项(链表和节点)相关的知识,希望对你有一定的参考价值。
1、什么是列表和列表项?
在 FreeRTOS中存在着大量的基础数据结构列表和列表项的操作,要想读懂 FreeRTOS的源码或者从0到1开始实现FreeRTOS,就必须弄懂列表和列表项的操作,其实也没那么难列表和列表项是直接从FreeRTos源码的注释中的list和list_item翻译过来的,其实就是对应我们C语言当中的链表和节点。所以在FreeRTOS我们也可以认为链表就是列表,节点就是列表项。
1.1、C语言链表简介
链表作为 C 语言中一种基础的数据结构,在平时写程序的时候用的并不多,但在操作系统里面使用的非常多。链表就好比一个圆形的晾衣架,晾衣架上面有很多钩子,钩子首尾相连。链表也是,链表由节点组成,节点与节点之间首尾相连。
晾衣架的钩子本身不能代表很多东西,但是钩子本身却可以挂很多东西。同样,链表也类似,链表的节点本身不能存储太多东西,或者说链表的节点本来就不是用来存储大量
数据的,但是节点跟晾衣架的钩子一样,可以挂很多数据。
1.2、单向链表
单向链链表中共有 n个节点,前一个节点都有一个箭头指向后一个节点,首尾相连,组成一个圈。
节点本身必须包含一个节点指针,用于指向后一个节点,除了这个节点指针是必须有
的之外,节点本身还可以携带一些私有信息,怎么携带?
节点都是一个自定义类型的数据结构,在这个数据结构里面可以有单个的数据、数组、
指针数据和自定义的结构体数据类型等等信息,
struct node
{
struct node *next; /* 指向链表的下一个节点 */
char data1; /* 单个的数据 */
unsigned char array[]; /* 数组 */
unsigned long *prt; /* 指针数据 */
struct userstruct data2; /* 自定义结构体类型数据 */
/* ...... */
}
除了 struct node *next 这个节点指针之外,剩下的成员都可以理解为节点携带的数据,但是这种方法很少用。通常的做法是节点里面只包含一个用于指向下一个节点的指针。要通过链表存储的数据内嵌一个节点即可,这些要存储的数据通过这个内嵌的节点即可挂接到链表中,就好像晾衣架的钩子一样,把衣服挂接到晾衣架中。具体的伪代码实现见代码清单
/* 节点定义 */
struct node
{
struct node *next; /* 指向链表的下一个节点 */
}
struct userstruct
{
/* 在结构体中,内嵌一个节点指针,通过这个节点将数据挂接到链表 */
struct node *next;
/* 各种各样......,要存储的数据 */
}
1.3、列表和列表项
列表是FreeRTOS
中的一个数据结构,概念上和链表差不多,列表被用来跟踪 FreeRTOS中的任务。与列表相关的全部东西都在文件list.c
和list.h
中。在FreeRTOS认为链表就是列表,节点就是列表项,这一点要注意。
2、列表和列表项结构体介绍
在FreeRTOS中关与列表和列表项,也就是链表和节点的结构体不多,只有三个。链表节点 结构体定义xLIST_ITEM
、链表精简节点结构体定义xMINI_LIST_ITEM
、链表根节点数据结构体定义List_t
。
/*
* 链表节点 数据结构定义
*/
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< 如果listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE设置为1,则设置为已知值. */
configLIST_VOLATILE TickType_t xItemValue; /*< 辅助值,用于帮助节点做顺序排列.uint32_t类型 */
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< 用于指向链表下一个节点. */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< 用于指向链表前一个节点. */
void * pvOwner; /*< 用于指向该节点的拥有者,即该节点内嵌在哪个数据结构中,属于哪个数据结构的一个成员,通常是 TCB . */
struct xLIST * configLIST_VOLATILE pxContainer; /*< 用于指向该节点所在的链表,通常指向链表的根节点. */
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< 如果listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE设置为1,则设置为已知值. */
};
typedef struct xLIST_ITEM ListItem_t; /* 节点数据类型重定义. */
/*
* 链表精简节点 结构体定义
*/
struct xMINI_LIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< 如果listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE设置为1,则设置为已知值. */
configLIST_VOLATILE TickType_t xItemValue; /* 辅助值,用于帮助节点做升序排列. */
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /* 指向链表下一个节点. */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /* 指向链表前一个节点. */
};
typedef struct xMINI_LIST_ITEM MiniListItem_t; /* 精简节点数据类型重定义 */
/*
* 链表根节点 数据结构定义
*/
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< 如果listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE设置为1,则设置为已知值. */
volatile UBaseType_t uxNumberOfItems; /*< 链表节点计数器,用于表示该链表下有多少个节点,根节点除外. */
ListItem_t * configLIST_VOLATILE pxIndex; /*< 链表节点索引指针,用于遍历节点. */
MiniListItem_t xListEnd; /*< 链表最后一个节点,链表是首尾相连的,是一个圈,首就是尾,尾就是首,
这里从字面上理解就是链表的最后一个节点,实际也就是链表的第一个节点,我们称之为生产者. */
listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< 如果listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE设置为1,则设置为已知值. */
} List_t;
1、节点数据结构体
链表节点的数据结构在list.h
中定义。结构体名就是xLIST_ITEM
,根据这个拼音就是知道LIST
代表列表,ITEM
表示项目,合起来就是列表项,也就是节点。所以根据这个结构体名就知道这是一个关于节点的结构体。所以不要觉得链表好难,结构体看不懂。其实你看名字就知道这是结构体的功能了,既然是关于节点的结构体,那么这个结构体肯定包含了一个节点的必要信息对吧。节点是干嘛的?节点是用来存储数据的,就是用来挂衣服的。然后再来看这个结构体的成员变量:
/*
* 节点——数据结构体
*/
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< 如果listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE设置为1,则设置为已知值. */
configLIST_VOLATILE TickType_t xItemValue; /*< 辅助值,用于帮助节点做顺序排列.uint32_t类型 */
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< 用于指向链表下一个节点. */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< 用于指向链表前一个节点. */
void * pvOwner; /*< 用于指向该节点的拥有者,即该节点内嵌在哪个数据结构中,属于哪个数据结构的一个成员,通常是 TCB . */
struct xLIST * configLIST_VOLATILE pxContainer; /*< 用于指向该节点所在的链表,通常指向链表的根节点. */
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< 如果listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE设置为1,则设置为已知值. */
};
typedef struct xLIST_ITEM ListItem_t; /* 节点数据类型重定义. */
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE
:忽略不管- xItemValue:一个辅助值,用于帮助节点做顺序排列。该辅助值的数据类型为
TickType_t
,也就是uint32_t
- pxNext:用于指向链表下一个节点。
- pxPrevious:用于指向链表前一个节点。
- pvOwner:用于指向该节点的拥有者,即该节点内嵌在哪个数据结构中,属于哪个数据结构的一个成员,通常是
TCB
,也就是任务控制块。 - pxContainer:用于指向该节点所在的链表,通常指向链表的根节点。
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE
:忽略不管。
这个结构体乍一看有7个成员变量,其实只有5个成员。第一个和对后一个这两个都是用来检查节点完整性的,需要将宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES
设置为 1,开启以后会向这两个地方分别添加一个变量xListIntegrityValue1
和xListIntegrityValue2
,在初始化链表的时候会这两个变量中写入一个特殊的值,默认不开启这个功能。在学习链表的时候不讨论这个功能。
2、链表数据结构体
链表的数据结构体在list.h
中定义。结构体名就是xLIST
,根据这个拼音就是知道LIST
代表列表,也就是链表。所以根据这个结构体名就知道这是一个关于链表的结构体。既然是关于链表的结构体,那么这个结构体肯定包肯定就没有节点结构体那么复杂,链表只是一个圆环。链表是干嘛的?链表是用来挂节点的,就是用来挂挂钩的的。然后再来看这个结构体的成员变量:
/*
* 链表——数据结构定义.
*/
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< 如果listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE设置为1,则设置为已知值. */
volatile UBaseType_t uxNumberOfItems; /*< 链表节点计数器,用于表示该链表下有多少个节点,根节点除外. */
ListItem_t * configLIST_VOLATILE pxIndex; /*< 链表节点索引指针,用于遍历节点. */
MiniListItem_t xListEnd; /*< 链表最后一个节点,链表是首尾相连的,是一个圈,首就是尾,尾就是首,
这里从字面上理解就是链表的最后一个节点,实际也就是链表的第一个节点,我们称之为生产者. */
listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< 如果listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE设置为1,则设置为已知值. */
} List_t;
listFIRST_LIST_INTEGRITY_CHECK_VALUE
:忽略不管- uxNumberOfItems:链表节点计数器,用于表示该链表下有多少个节点,根节点除外。
- pxIndex:链表节点索引指针,用于遍历节点。
- xListEnd:链表最后一个节点,链表是首尾相连的,是一个圈,首就是尾,尾就是首。
listSECOND_LIST_INTEGRITY_CHECK_VALUE
:忽略不管。
这个结构体乍一看有5个成员变量,其实只有3个成员。第一个和对后一个这两个跟节点用法一样,都是用来检查链表完整性的,需要将宏listFIRST_LIST_INTEGRITY_CHECK_VALUE
设置为 1,开启以后会向这两个地方分别添加一个变量xListIntegrityValue1
和xListIntegrityValue2
,在初始化列表的时候会这两个变量中写入一个特殊的值,默认不开启这个功能。在学习列表的时候不讨论这个功能。
3、精简节点结构体
在链表结构体中xListEnd
表示链表最后一个节点。我们知道,链表是首尾相连的,是一个圈,首就是尾,尾就是首,这里从字面上理解就是链表的最后一个节点,实际也就是链表的第一个节点,我们称之为生产者。该生产者的数据类型是一个精简的节点,这个精简的节点也在list.h
中定义
/*
* 精简节点——结构体
*/
struct xMINI_LIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< 如果listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE设置为1,则设置为已知值. */
configLIST_VOLATILE TickType_t xItemValue; /* 辅助值,用于帮助节点做升序排列. */
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /* 指向链表下一个节点. */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /* 指向链表前一个节点. */
};
typedef struct xMINI_LIST_ITEM MiniListItem_t; /* 精简节点数据类型重定义 */
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE
:忽略不管- xItemValue:记录链表节点值。
- pxNext:指向下一个节点。
- pxPrevious:指向上一个节点。
可以看出迷你列表项只是比列表项少了几个成员变量,迷你列表项有的成员变量列表项都有的,没感觉有什么本质区别啊?那为什么要弄个迷你列表项出来呢?那是因为有些情况下我们不需要列表项这么全的功能,可能只需要其中的某几个成员变量,如果此时用列表项的话会造成内存浪费!比如上面列表结构体List_t
中表示最后一个列表项的成员变量xListEnd
就是MiniListItem_t
类型的。
3、列表和列表项结函数介绍
3.1、链表初始化函数
/**
* @brief 列表(链表)根节点初始化函数
* @details 新创建或者定义的列表需要对其做初始化处理,列表的初始化其实就是初始化列表结构体
* List_t 中的各个成员变量,列表的初始化通过使函数 vListInitialise()来完成,
*
* @param[in] pxList List_t结构体句柄,初始化时请定义该句柄,并用其地址来传参。
* List_t structure handle. When initializing,
* please define the handle and use its address
* to pass parameters.
*
* @return 无.
**/
void vListInitialise( List_t * const pxList )
{
/* 将链表索引指针指向最后一个节点,即第一个节点*/
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. */
/* 将链表最后一个节点(也可以理解为第一个节点)的辅助排序的值设置为最大,确保该节点就是链表的最后节点 */
pxList->xListEnd.xItemValue = portMAX_DELAY;
/* 将最后一个节点(也可以理解为第一个节点)的 pxNext 和 pxPrevious 指针均指向节点自身,表示链表为空 */
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. */
/* 初始化链表节点计数器的值为 0,表示链表为空 */
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 );
}
3.2、节点初始化函数
表节点ListItem_t
总共有5个成员,但是初始化的时候只需将pvContainer
初始化为空即可,表示该节点还没有插入到任何链表。
/*-----------------------------------------------------------*/
/**
* @brief 列表项(节点)初始化函数。
* @details 列表项(链表)的初始化很简单,只是将列表项成员变量 pvContainer 初始化为 NULL,并且给用于完整性检查的变量赋值。
* @param[in] pxItem ListItem_t结构体句柄,初始化时请定义该句柄,并用其地址来传参。
* ListItem_t structure handle. When initializing,
* please define the handle and use its address
* to pass parameters.
* @return 无.
**/
void vListInitialiseItem( ListItem_t * const pxItem )
{
/* 初始化该节点所在的链表为空,表示节点还没有插入任何链表. */
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 );
}
3.3、将节点插入到链表的尾部函数
将节点插入到链表的尾部(可以理解为头部)就是将一个新的节点插入到一个空的链表。
/**
* @brief 列表项(节点)末尾插入函数,将节点插入到链表的尾部
* @details 将节点插入到链表的尾部(可以理解为头部)就是将一个新的节点插入到一个空的链表
*
* @param[in] pxList 列表项要插入的列表(节点要插入的链表)
* pxNewListItem 要插入的列表项 (要插入的节点)
*
* @return 无.
**/
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;
/* 记住该节点所在的链表. */
pxNewListItem->pxContainer = pxList;
( pxList->uxNumberOfItems )++;
}
3.4、将节点插入到链表
将节点按照升序排列插入到链表,如果有两个节点的值相同,则新节点在旧节点的后面插入
/**
* @brief 列表项(节点)插入函数。将节点按照升序排列插入到链表
* @details 将节点按照升序排列插入到链表,如果有两个节点的值相同,则新节点在旧节点的后面插入
*
* @param[in] pxList 列表项要插入的列表(节点要插入的链表)
* pxNewListItem 要插入的列表项 (要插入的节点)
*
* @return 无.
**/
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 ) /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. *//*lint !e440 The iterator moves to a different value, not xValueOfInsertion. */
{
/* 没有事情可做,不断迭代只为了找到节点要插入的位置 */
}
}
/* 根据升序排列,将节点插入 */
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
/* 记住该节点所在的链表 */
pxNewListItem->pxContainer = pxList;
/* 链表节点计数器++ */
( pxList->uxNumberOfItems )++;
}
3.5、将节点从链表删除
将节点从链表删除。假设将一个有三个节点的链表中的中间节点节点删除。
/**
* @brief 列表项(节点)删除函数。
* @details 将节点从链表删除.
*
* @param[in] pxItemToRemove 要删除的列表项(节点)
* pxNewListItem 要插入的列表项(节点)
*
* @return UBaseType_t 返回删除列表项(节点)以后的列表剩余列表项(节点)数目.
**/
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();
}
/* 初始化该节点所在的链表为空,表示节点还没有插入任何链表 */
pxItemToRemove->pxContainer = NULL;
/* 链表节点计数器-- */
( pxList->uxNumberOfItems )--;
/* 返回链表中剩余节点的个数 */
return pxList->uxNumberOfItems;
}
3.6、节点带参宏小函数
/* 初始化节点的拥有者 */
#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_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext->xItemValue )
/* 获取链表的入口节点 */
#define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext )
/* 获取节点的下一个节点 */
#define listGET_NEXT( pxListItem ) ( ( pxListItem )->pxNext )
/* 获取链表的最后一个节点 */
#define listGET_END_MARKER( pxList ) ( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) )
/* 判断链表是否为空 */
#define listLIST_IS_EMPTY( pxList ) ( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE )
/* 获取链表的节点数 */
#define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems )
4、链表节点插入试验
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "lcd.h"
#include "SEGGER_RTT.h"
#include "math.h"
#include "FreeRTOS.h"
#include "task.h"
#define START_TASK_PRIO 1 //任务优先级
#define TASK1_TASK_PRIO 2 //任务优先级
#define TASK2_TASK_PRIO 3 //任务优先级
#define LIST_TASK_PRIO 3 //任务优先级
#define START_STK_SIZE 128 //任务堆栈大小
#define TASK1_STK_SIZE 128 //任务堆栈大小
#define TASK2_STK_SIZE 128 //任务堆栈大小
#define LIST_STK_SIZE 128 //任务堆栈大小
TaskHandle_t StartTask_Handler; //任务句柄
TaskHandle_t Task1Task_Handler; //任务句柄
TaskHandle_t Task2Task_Handler; //任务句柄
TaskHandle_t ListTask_Handler; //任务句柄
void start_task(void *pvParameters); //任务函数声明
void task1_task(void *pvParameters); //任务函数声明
void task2_task(void *pvParameters); //任务函数声明
void list_task(void *pvParameters); //任务函数声明
//定义一个测试用的列表(链表)和3个列表项(节点)
List_t TestList; //测试用列表(链表)
ListItem_t ListItem1; //测试用列表项(节点)1
ListItem_t ListItem2; //测试用列表项(节点)2
ListItem_t ListItem3; //测试用列表项(节点)3
int main(void)
{
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(8, 336, 2, 7); //设置时钟,168Mhz
delay_init(168); //初始化延时函数
KEY_Init();
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); //任务句柄
//开启任务调度
/* 启动RTOS,其实就是启动“任务管理器”,启动之后任务管理器就开始调度线程,
* 此时pc(程序计数器)就会指向某线程的指令,开始多线程并发运行。
* 如果没有创建多线程的话,那就只有一个线程。*/
vTaskStartScheduler();
/* 由于调用了vTaskStartScheduler之后,PC就指向了线程中的指令,因此vTaskStartScheduler后面代码
* 并不会被CPU执行,所以vTaskStartScheduler后的代码没有意义。 */
while(1)
{
//这里的代码不会被执行,写了也没用
}
}
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建TASK1任务
xTaskCreate((TaskFunction_t )task1_task,
(const char* )"task1_task",
(uint16_t )TASK1_STK_SIZE,
以上是关于FreeRTOSFreeRTOS学习笔记— 列表和列表项(链表和节点)的主要内容,如果未能解决你的问题,请参考以下文章
FreeRTOSFreeRTOS学习笔记— 任务创建删除挂起和恢复
FreeRTOSFreeRTOS学习笔记— 开始创建任务并测试任务代码
FreeRTOSFreeRTOS学习笔记(13)— FreeRTOS创建任务和任务管理(原生API)
FreeRTOSFreeRTOS学习笔记(14)— FreeRTOS的消息队列(原生API)