这是一个自我学习FreeRTOS的过程,目的是总结学习中所学到的操作和分享,最重要的是为自己DIY一个stm32平衡小车做铺垫。第一次写博客如果有错误求指出啊哈哈哈。
FreeRTOS是一个免费开源的实时操作系统,我们很容易的对其进行移植并且运用在我们项目之中。话不多说,我们一起先创建一个FreeRTOS的工程吧。
一、我们先要创建一个基本的FreeRTOS的框架—创建FreeRTOS的文件夹
首先我们创建一个总FreeRTOS的文件夹在任意位置中(名字随便起哈哈哈别数字或者中文就可以了,因为keil识别不了中文或数字开头,否则后期会一直报错的哈哈哈,文件名切记要!!!)。
接下来我们在其内创建4个子文件夹分别如下:
Doc为说明文档,freertos中包含着include和protabel,project用来放置keil的工程,User为用户文件夹,主要用于放置main.c。
用哪个芯片就拷贝哪个芯片的RVDS进去即可(M3、M4),此处用ARM-CM3
这样我们的FreeRTOS的基本框架就创建好了。
二、打开keil,新建工程并且保存在freertos的project中并且选着芯片M3
新建工程并且保存在freertos的project中并且选着芯片M3
勾选上这两个选项哟!!!
然后我们在Keil上创建工程分组
然后打开文件夹在User中新建main.c文件夹(一定要把后缀改为.c!!!!!!)
并且把main.c添加到工程中,右键分组user,Add File to Group 。
并且先给 main函数写个空循环吧,防止后续检查问题时一直报错!!
keil需要配置芯片的时钟!!! 不同的芯片时钟的要求不一致,打开ARMCM3.c文件就可以看到其对时钟的要求!!!
系统时钟SYSTEM—CLOCK=XTAL/2=25MHZ(M3),顺便配置一下keil吧
然后我们需要添加一下头文件的路径
这样我们一个FreeRTOS的keil才算是设置好,接下来可以开始我们最重要的构建链表的操作啦!!
三、链表的创建与实现
这就是单向链表的原理图,在此不多解释了,大伙们可以去看看数据结构这本书或者是网课!!(没有数据结构的基础是学不好操作系统的哟!!后续如果有时间我会再写一篇有关于这方面数据结构的文章)
FreeRTOS中与链表相关的操作均在list.h和list.c这两个文件中实现
首先我们要创建list.h和list.c。
新建list.c在freeRTOS中
同理创建list.h在include下并将其添加进去工程分组的FreeRTOS/Source中。
移植并且添加进去FreeRTOS/Source
具体的文件可以去野火官网或者是FreeRTOS官网下载哟!!FreeRTOS - Market leading RTOS (Real Time Operating System) for embedded systems with Internet of Things extensions
实现节点的初始化(原理)
链表节点的数据结构在list.h中定义
struct xLIST_ITEM
{
TickType_t xItemValue;
struct xLIST_ITEM * pxNext; /* 辅助值,用于帮助节点做顺序排列 */
struct xLIST_ITEM * pxPrevious; /* 指向链表下一个节点 */
void * pvOwner; /* 指向链表前一个节点 */
void * pvContainer; /* 指向拥有该节点的内核对象,通常是TCB */
};
typedef struct xLIST_ITEM ListItem_t; /* 节点数据类型重定义 */
链表根节点的数据结构在list.h中定义。
typedef struct xLIST
{
UBaseType_t uxNumberOfItems; /*链表节点计数器*/
ListItem_t * pxIndex; /*链表节点索引指针*/
MiniListItem_t xListEnd; /*链表最后一个索引*/
}List_t
链表节点的初始化在list.c中实现:vListInitialiseItem()
void vListInitialiseItem( ListItem_t * const pxItem )
{
/* 初始化该节点所在的链表为空,表示节点还没有插入任何链表 */
pxItem->pvContainer = NULL ;
}
链表根节点的初始化在list.c中实现:vListInitialise()。
void vListInitialise( List_t *const pxList)
{
/* 将链表索引指针指向最后一个节点 */
pxList->pxIndex = (ListItem_t *)&pxList->xListEnd;
/* 将链表最后一个节点的辅助排序的值设置为最大,确保该节点就是链表的最后节点 */
pxList ->xListEnd.xItemValue = portMAX_DELAY;
/* 将最后一个节点的pxNext和pxPrevious指针均指向节点自身,表示链表为空 */
pxList ->xListEnd.pxPrevious= (ListItem_t *)&( pxList ->xListEnd);
pxList ->xListEnd.pxNext=(ListItem_t *)&( pxList ->xListEnd);
/* 初始化链表节点计数器的值为0,表示链表为空 */
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
}
为了便于分别双向链表的结尾定义多一个结构体mini节点,作为双向链表的结尾。(双向链表是首尾相连的,头即是尾,尾即是头)
struct xMINI_LIST_ITEM
{
TickType_t xItemValue; /* 辅助值,用于帮助节点做升序排列 */
struct xLIST_ITEM * pxNext; /* 指向链表下一个节点 */
struct xLIST_ITEM * pxPrevious; /* 指向链表前一个节点 */
};
typedef struct xMINI_LIST_ITEM MiniListItem_t; /* 最小节点数据类型重定义 */
然后我们需要有插入节点的操作均在list.c中实现:分别为:将节点插入到链表的尾部、将节点按照升序排列插入到链表、将节点从链表中删除 这三个操作。
将节点插入到链表的尾部:
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ) //两个参数(插入的链表,新的节点)
{
ListItem_t * const pxIndex = pxList->pxIndex; //定义一个索引指向链表的End
pxNewListItem->pxNext = pxIndex; //第一步;将新节点的Next指向End
pxNewListItem->pxPrevious = pxIndex->pxPrevious; //第二部;将新节点的Previous指向原来End的上一个
pxIndex->pxPrevious->pxNext = pxNewListItem; //第三步;将原本End上一个的Next指向新节点
pxIndex->pxPrevious = pxNewListItem; //第四步;将End的Previous指向新节点
/* 记住该节点所在的链表 */
pxNewListItem->pvContainer =( void * ) pxList;
/* 链表节点计数器++ */
( pxList->uxNumberOfItems )++;
}
将节点按照升序排列插入到链表:
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) //两个参数(插入的链表,新的节点)
{
ListItem_t *pxIterator;
/* 获取节点的排序辅助值 */
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
/* 寻找节点要插入的位置 */
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->pvContainer = (void*) pxList;
/* 链表节点计数器++ */
( pxList->uxNumberOfItems )++;
}
将节点从链表中删除:
/* 将节点从链表中删除 */
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* 获取节点所在的链表 */
List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
/* Make sure the index is left pointing to a valid item. */
if( pxList->pxIndex == pxItemToRemove )
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
/* 初始化该节点所在的链表为空,表示节点还没有插入任何链表 */
pxItemToRemove->pvContainer = NULL;
/* 链表节点计数器-- */
( pxList->uxNumberOfItems )--;
/* 返回链表中剩余节点的个数 */
return pxList->uxNumberOfItems;
}
这样我们就可以实现链表的创建和操作啦,注意list.c中需要引用头文件: "FreeRTOS.h"
和 <stdlib.h>和"list.h"!!!!!list.h需要引用头文件"FreeRTOS.h"。
那接下来我们就在main()创建一个有三个节点的链表吧。
首先先定义一个根节点、然后再定义三个节点。
再分别对根节点和三个节点进行初始化
最后再对其进行排序插入节点。
#include "list.h"
/*1.定义一个根节点*/
struct xLIST List_Text;
/*2.定义3个节点*/
struct xLIST_ITEM List_Item1;
struct xLIST_ITEM List_Item2;
struct xLIST_ITEM List_Item3;
int main(void)
{ /*3.根节点初始化*/
vListInitialise(&List_Text);
/*节点初始化*/
vListInitialiseItem(&List_Item1);
vListInitialiseItem(&List_Item2);
vListInitialiseItem(&List_Item3);
/*插入节点*/
vListInsert(&List_Text,&List_Item1);
vListInsert(&List_Text,&List_Item2);
vListInsert(&List_Text,&List_Item3);
for(;;)
{
//空
}
}
最后debug看看是否符合预期,要run一下!!!
最后结构正确,各节点指针指向符合双向链表预期!!本章就到这啦,如果有错请指出哟!!!如果我的学习过程可以帮到你就最好啦!!别忘记点赞哟!!