1.写在前面:

本文章为《STM32MP157开发教程之FreeRTOS操作系统篇》系列中的一篇,笔者使用的开发平台为华清远见FS-MP1A开发板(STM32MP157开发板)。stm32mp157是ARM双核,2个A7核,1个M4核,A7核上可以跑Linux操作系统,M4核上可以跑FreeRTOS、RT-Thread等实时操作系统,STM32MP157开发板所以既可以学嵌入式linux,也可以学stm32单片机。

针对FS-MP1A开发板,除了FreeRTOS操作系统篇外,还包括其他多系列教程,包括Cortex-A7开发篇、Cortex-M4开发篇、扩展板驱动移植篇、Linux应用开发篇、Linux系统移植篇、Linux驱动开发篇、硬件设计篇、人工智能机器视觉篇、Qt应用编程篇、Qt综合项目实战篇等。

2.FreeRTOS列表和列表项

2.1列表和列表项简介

学习FreeRTOS,肯定少不了列表和列表项,列表和列表项是FreeRTOS的一个数据结构,它是FreeRTOS的基石。

列表被用来跟踪FreeRTOS中的任务,在list.h文件中定义了列表结构体List_t如下

typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE
configLIST_VOLATILE UBaseType_t uxNumberOfItems;
ListItem_t * configLIST_VOLATILE pxIndex;
MiniListItem_t xListEnd;
listSECOND_LIST_INTEGRITY_CHECK_VALUE
} List_t;
第一行和第五行都是用来检查列表完整性,需要将宏
 configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1。uxNumberOfItems用来记录列表中列表项的数量。
pxIndex用来记录当前列表项索引号,用于遍历列表。
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;
void * configLIST_VOLATILE pvContainer;
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE
};
typedef struct xLIST_ITEM ListItem_t;
第一行和第七行同列表一样,都是用来检查列表项完整性。
xItemValue为列表项值。
pxNext指向下一个列表项。
pxPrevious指向前一个列表项,与pxNext相配合。
pvOwner记录此列表项归谁拥有,通常是任务控制块。
pvContainer用来记录此列表项归哪个列表。
另外还有一种迷你列表项,其定义如下
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;
第一行同样用于检查迷你列表项的完整性。
xItemValue记录列表项值。
pxNext指向下一个列表项。
pxPrevious指向前一个列表项。
在这里之所以弄个迷你列表项,是因为有些情况下,我们不需要列表项那么全的功能,可能只需要其中的某几个成员变量,此时用列表项的话可能会造成内存浪费。
2.2列表与列表项函数
2.2.1列表与列表项初始化
新创建或者定义的列表都需要对其做初始化处理,对列表的初始化函数为vListInitialise(),其函数原型如下:
void vListInitialise ( List* const pxList)
参数:
pxList: 要进行初始化的列表。
返回值:无
对列表项的初始化函数为vListInitialiseItem(),其函数原型如下:
void vListInitialiseItem ( ListItem_t * const pxItem)
参数:
pxItem: 要进行初始化的列表。
返回值:无
在此不对初始化函数的具体代码做描述了,有兴趣的可以去list.c文件中查看。
2.2.2 列表项插入
列表项的插入函数为vListInsert(),其函数原型如下:
void vListInsert( List* const pxList, ListItem_t * const pxNewListItem)
参数:
pxList: 列表项要插入的列表。
pxNewListItem: 要插入的列表项。
返回值:无
2.2.3列表项末尾插入
列表在末尾插入列表项的函数为vListInsertEnd(),其函数原型如下:
void vListInsertEnd( List * const pxList, ListItem_t * const pxNewListItem)
参数:
pxList: 列表项要插入的列表。
pxNewListItem: 要插入的列表项。
返回值:无
2.2.4 列表项删除
有插入那么必然有删除,列表项的删除函数为uxListRemove(),其函数原型如下:
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
参数:
pxItemToRemove: 要删除的列表项。
返回值:
返回删除列表项以后的列表剩余列表项数目。
列表项的删除只是将指定的列表项从列表中删除掉,并不会将列表项的内存给释放掉。
2.3操作实验
2.3.1实验设计
本次设计中创建了一个列表以及三个列表项,通过中断采集按键状态,当KEY1按下时,依次将三个列表项添加到列表中,当KEY2按下时,删除第2条列表项,当KEY3按下时,再将第2条列表项从末尾插入。
可参考12.3.2章节进行导入已有工程,工程存放路径【华清远见-FS-MP1A开发资料\02-程序源码\ARM体系结构与接口技术\FreeRTOS\7_MP1A-FreeRTOS-List】
任务及其功能如下:
StartTask02(): 进行列表与列表项的初始化并打印,然后采集按键状态,根据不同的按键结果进行不同处理。
StartDefaultTask(): 让LED3循环闪烁,提示系统正常运行。
2.3.2实验过程与分析
首先,根据之前几章内容配置好CubeMX,按照上一节配置“FREERTOS”,完成后生成代码。 在StartDefaultTask() 与StartTask02()中添加代码如下。
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
LED_3_TOG();
osDelay(1000);
}
/* USER CODE END 5 */
}
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
//初始化列表和列表项
vListInitialise(&TestList);
vListInitialiseItem(&ListItem1);
vListInitialiseItem(&ListItem2);
vListInitialiseItem(&ListItem3);
ListItem1.xItemValue=40; //ListItem1列表项值为40
ListItem2.xItemValue=60; //ListItem2列表项值为60
ListItem3.xItemValue=50; //ListItem3列表项值为50
//打印列表和其他列表项的地址
printf("project address \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);
for(;;)
{
if(key == EVENTBIT_1)
{
//向列表TestList添加列表项ListItem1,并通过串口打印所有
//列表项中成员变量pxNext和pxPrevious的值,通过这两个值观察列表
//项在列表中的连接情况。
vListInsert(&TestList,&ListItem1); //插入列表项ListItem1
printf("project address \r\n");
printf("TestList->xListEnd->pxNext %#x \r\n",(int)(TestList.xListEnd.pxNext));
printf("ListItem1->pxNext %#x \r\n",(int)(ListItem1.pxNext));
printf("/***********Connect the divider front and back 1************/\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,并通过串口打印所有
//列表项中成员变量pxNext和pxPrevious的值,通过这两个值观察列表
//项在列表中的连接情况。
vListInsert(&TestList,&ListItem2); //插入列表项ListItem2
printf("project address \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("/***********Connect the divider front and back 2************/\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("/***************************end*****************************/\r\n");
//向列表TestList添加列表项ListItem3,并通过串口打印所有
//列表项中成员变量pxNext和pxPrevious的值,通过这两个值观察列表
//项在列表中的连接情况。
vListInsert(&TestList,&ListItem3); //插入列表项ListItem3
printf("project address \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("ListItem2->pxNext %#x \r\n",(int)(ListItem2.pxNext));
printf("/************Connect the divider front and back 3************/\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("ListItem2->pxPrevious %#x \r\n",(int)(ListItem2.pxPrevious));
printf("/***************************end*****************************/\r\n");
}
if(key == EVENTBIT_2)
{
//删除ListItem2,并通过串口打印所有列表项中成员变量pxNext和Previous的值,通过这两个值观察列表项在列表中的连接情况。
uxListRemove(&ListItem2); //删除ListItem2
printf("/**************Delete list items ListItem2*************/\r\n");
printf("project address \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("/***********Connect the divider front and back 2************/\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("/*************************end****************************/\r\n");
}
if(key == EVENTBIT_3)
{
//末尾添加ListItem2,并通过串口打印所有列表项中成员变量pxNext和Previous的值,
//通过这两个值观察列表项在列表中的连接情况。
TestList.pxIndex=TestList.pxIndex->pxNext; //Index向后移一项,这样pxIndex就会指向ListItem1。
vListInsertEnd(&TestList,&ListItem2); //列表末尾添加列表项ListItem2
printf("/***********Add the list at the end of ListItem2***********/\r\n");
printf("project address \r\n");
printf("TestList->pxIndex %#x \r\n",(int)TestList.pxIndex);
printf("TestList->xListEnd->pxNext %#x
\r\n",(int)(TestList.xListEnd.pxNext));
printf("ListItem2->pxNext %#x \r\n",(int)(ListItem2.pxNext));
printf("ListItem1->pxNext %#x \r\n",(int)(ListItem1.pxNext));
printf("ListItem3->pxNext %#x \r\n",(int)(ListItem3.pxNext));
printf("/************Connect the divider front and back 2*************/\r\n");
printf("TestList->xListEnd->pxPrevious %#x
\r\n",(int)(TestList.xListEnd.pxPrevious));
printf("ListItem2->pxPrevious %#x \r\n",(int)(ListItem2.pxPrevious));
printf("ListItem1->pxPrevious %#x \r\n",(int)(ListItem1.pxPrevious));
printf("ListItem3->pxPrevious %#x \r\n",(int)(ListItem3.pxPrevious));
printf("/***************************end*****************************/\r\n");
}
key = 0;
osDelay(100);
}
/* USER CODE END StartTask02 */
}
编写按键中断回调函数如下
void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
switch(GPIO_Pin)
{
case GPIO_PIN_8:
if(HAL_GPIO_ReadPin(GPIOF,GPIO_PIN_8) == GPIO_PIN_SET) /* read KEY3 PF8 state */
key = EVENTBIT_3;
break;
case GPIO_PIN_7:
if(HAL_GPIO_ReadPin(GPIOF,GPIO_PIN_7) == GPIO_PIN_SET) /* read KEY2 PF7 state */
key = EVENTBIT_2;
break;
case GPIO_PIN_9:
if(HAL_GPIO_ReadPin(GPIOF,GPIO_PIN_9) == GPIO_PIN_SET) /* read KEY1 PF9 state */
key = EVENTBIT_1;
break;
}
}