本文是FreeRTOS复习笔记的第四节,列表项的插入和删除。
上一篇文章: 【复习笔记】FreeRTOS(三)任务挂起和恢复
列表和列表项是FreeRTOS的一个数据结构,FreeRTOS大量使用到了列表和列表项,它是FreeRTOS的基石。
列表是FreeRTOS中的一个数据结构,与链表类似,列表被用来跟踪FreeRTOS中的任务。
其结构体 List_t 在 list.h 文件中被定义:
typedef struct xLIST
{
/* 列表内有效列表项个数 */
configLIST_VOLATILE UBaseType_t uxNumberOfItems;
/* 记录当前列表项索引号,用于遍历列表 */
ListItem_t * configLIST_VOLATILE pxIndex;
/* 列表中最后一个列表项,表示列表结束 */
MiniListItem_t xListEnd;
} List_t;
列表项就是存放在列表中的项目,FreeRTOS提供两种类型的列表项:列表项和迷你列表项。
列表项的结构体 ListItem_t 在 list.h 文件中被定义:
静态创建任务函数包含了7个参数:
struct xLIST_ITEM
{
/* 列表项值 */
configLIST_VOLATILE TickType_t xItemValue;
/* 指向下一个列表项值 */
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
/* 指向上一个列表项值 */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
/* 当前列表项的拥有者 */
void * pvOwner;
/* 当前列表项归属的列表 */
void * configLIST_VOLATILE pvContainer;
};
typedef struct xLIST_ITEM ListItem_t;
有些情况下不需要列表项这么全的功能,为了避免造成内存浪费,定义了迷你列表项。
迷你列表项的结构体 MiniListItem_t 在 list.h 文件中被定义:
struct xMINI_LIST_ITEM
{
/* 列表项值 */
configLIST_VOLATILE TickType_t xItemValue;
/* 指向下一个列表项值 */
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
/* 指向上一个列表项值 */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
学习列表项的插入和删除。
主函数 main.c代码如下:
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
#include "key.h"
#include "FreeRTOS.h"
#include "task.h"
#include "Lcd_Driver.h"
#include "LCD_Config.h"
#include "TFT_demo.h"
#define START_TASK_PRIO 1 //任务优先级
#define START_STK_SIZE 128 //任务堆栈大小
TaskHandle_t StartTask_Handler; //任务句柄
void start_task(void *pvParameters); //任务函数
#define LED_TASK_PRIO 2 //任务优先级
#define LED_STK_SIZE 128 //任务堆栈大小
TaskHandle_t LEDTask_Handler; //任务句柄
void LED_task(void *p_arg); //任务函数
#define List_TASK_PRIO 3 //任务优先级
#define List_STK_SIZE 128 //任务堆栈大小
TaskHandle_t ListTask_Handler; //任务句柄
void List_task(void *p_arg); //任务函数
//定义一个测试用的列表和3个列表项
List_t TestList; //测试用列表
ListItem_t ListItem1; //测试用列表项1
ListItem_t ListItem2; //测试用列表项2
ListItem_t ListItem3; //测试用列表项3
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口
Lcd_Init();
LED_Init(); //初始化LED端口
Lcd_Clear(GRAY0);
KEY_Init();
// 创建开始任务
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); //任务句柄
vTaskStartScheduler(); //开启任务调度
}
/**
* @brief 开始任务任务函数
* @param None
* @retval None
*/
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
xTaskCreate((TaskFunction_t )LED_task,
(const char* )"LED_task",
(uint16_t )LED_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED_TASK_PRIO,
(TaskHandle_t* )&LEDTask_Handler);
xTaskCreate((TaskFunction_t )List_task,
(const char* )"List_task",
(uint16_t )List_STK_SIZE,
(void* )NULL,
(UBaseType_t )List_TASK_PRIO,
(TaskHandle_t* )&ListTask_Handler);
vTaskDelete(StartTask_Handler); //删除自身开始任务
taskEXIT_CRITICAL(); //退出临界区
}
/**
* @brief 任务1,LED 0.5s闪烁
* @param None
* @retval None
*/
void LED_task(void *pvParameters)
{
while(1)
{
LED0=~LED0;
vTaskDelay(500);
}
}
/**
* @brief 列表项控制函数
* @param None
* @retval None
*/
void List_task(void *pvParameters)
{
/*第1步:初始化列表和列表项*/
vListInitialise(&TestList);
vListInitialiseItem(&ListItem1);
vListInitialiseItem(&ListItem2);
vListInitialiseItem(&ListItem3);
ListItem1.xItemValue=10; //ListItem1列表项值为10
ListItem2.xItemValue=20; //ListItem2列表项值为20
ListItem3.xItemValue=15; //ListItem3列表项值为15
/*第2步:打印列表和其他列表项的地址*/
printf("/*******************2.列表和列表项地址*******************/\r\n");
printf("项目 地址 \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);
printf("/************************结束**************************/\r\n");
printf("按下K1键继续\r\n\r\n\r\n");
while(KEY_Scan(0)!=WKUP_PRES) delay_ms(10);
/*第3步:向列表TestList添加列表项ListItem1,并通过串口打印所有列表项中成员变量pxNext和pxPrevious的值,
通过这两个值观察列表项在列表中的连接情况。*/
vListInsert(&TestList,&ListItem1); //插入列表项ListItem1
printf("/******************3.添加列表项ListItem1*****************/\r\n");
printf("项目 地址 \r\n");
printf("TestList->xListEnd->pxNext %#x \r\n",(int)(TestList.xListEnd.pxNext));
printf("ListItem1->pxNext %#x \r\n",(int)(ListItem1.pxNext));
printf("/*******************前后向连接分割线********************/\r\n");
printf("TestList->xListEnd->pxPrevious %#x \r\n",(int)(TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious %#x \r\n",(int)(ListItem1.pxPrevious));
printf("/************************结束**************************/\r\n");
printf("按下K1键继续\r\n\r\n\r\n");
while(KEY_Scan(0)!=WKUP_PRES) delay_ms(10);
/*第4步:向列表TestList添加列表项ListItem2,并通过串口打印所有列表项中成员变量pxNext和pxPrevious的值,
通过这两个值观察列表项在列表中的连接情况。*/
vListInsert(&TestList,&ListItem2); //插入列表项ListItem2
printf("/******************4.添加列表项ListItem2*****************/\r\n");
printf("项目 地址 \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("/*******************前后向连接分割线********************/\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("/************************结束**************************/\r\n");
printf("按下K1键继续\r\n\r\n\r\n");
while(KEY_Scan(0)!=WKUP_PRES) delay_ms(10);
/*第5步:向列表TestList添加列表项ListItem3,并通过串口打印所有列表项中成员变量pxNext和pxPrevious的值,
通过这两个值观察列表项在列表中的连接情况。*/
vListInsert(&TestList,&ListItem3); //插入列表项ListItem3
printf("/******************5.添加列表项ListItem3*****************/\r\n");
printf("项目 地址 \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("/*******************前后向连接分割线********************/\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("/************************结束**************************/\r\n");
printf("按下K1键继续\r\n\r\n\r\n");
while(KEY_Scan(0)!=WKUP_PRES) delay_ms(10);
/*第6步:删除ListItem2,并通过串口打印所有列表项中成员变量pxNext和pxPrevious的值,
通过这两个值观察列表项在列表中的连接情况。*/
uxListRemove(&ListItem2); //删除ListItem2
printf("/******************6.删除列表项ListItem2*****************/\r\n");
printf("项目 地址 \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("/*******************前后向连接分割线********************/\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("/************************结束**************************/\r\n");
printf("按下K1键继续\r\n\r\n\r\n");
while(KEY_Scan(0)!=WKUP_PRES) delay_ms(10); //等待KEY_UP键按下
/*第7步:删除ListItem2,并通过串口打印所有列表项中成员变量pxNext和pxPrevious的值,
通过这两个值观察列表项在列表中的连接情况。*/
TestList.pxIndex=TestList.pxIndex->pxNext; //pxIndex向后移一项,这样pxIndex就会指向ListItem1。
vListInsertEnd(&TestList,&ListItem2); //列表末尾添加列表项ListItem2
printf("/***************7.在末尾添加列表项ListItem2***************/\r\n");
printf("项目 地址 \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("/*******************前后向连接分割线********************/\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("/************************结束**************************/\r\n\r\n\r\n");
while(1)
{
Fullscreen_showimage(gImage_XHR128);
vTaskDelay(1500);
}
}
实验效果如下:
首先是可以看到LED引脚电平500ms改变一次,一闪一闪的。然后按一下,再按一下,显示屏显示一个图案。
接上串口,按一下复位键,会打印任务里第2步的列表和列表项地址,按一下按键k1,会打印任务里第3步到第5步的添加列表项ListItem1/2/3信息,接着按一下按键k1,会打印删除列表项ListItem2,在末尾添加列表项ListItem2等信息。
当按下按键时,LED停止闪烁,显示屏显示“task1:stopping”下行显示“task2 Delete task1”。如果接上串口,就能看到提示内容。
好了,本节主要是学习和掌握任务创建以及 vTaskDelete() 任务删除函数的使用。
完整程序放在gitee上:程序下载地址。