【复习笔记】FreeRTOS(四) 列表项的插入和删除

本文是FreeRTOS复习笔记的第四节,列表项的插入和删除。

上一篇文章: 【复习笔记】FreeRTOS(三)任务挂起和恢复

文章目录

一、列表和列表项
1.1. 列表
1.2. 列表项
1.3. 迷你列表项
二、实验目的
三、测试例程
四、实验效果

一、列表和列表项

列表和列表项是FreeRTOS的一个数据结构,FreeRTOS大量使用到了列表和列表项,它是FreeRTOS的基石。

1.1. 列表

列表是FreeRTOS中的一个数据结构,与链表类似,列表被用来跟踪FreeRTOS中的任务。
其结构体 List_t 在 list.h 文件中被定义:

typedef struct xLIST
{
    /* 列表内有效列表项个数 */
    configLIST_VOLATILE UBaseType_t uxNumberOfItems;
    /* 记录当前列表项索引号,用于遍历列表 */
    ListItem_t * configLIST_VOLATILE pxIndex;           
    /* 列表中最后一个列表项,表示列表结束 */
    MiniListItem_t xListEnd;                            
} List_t;

1.2. 列表项

列表项就是存放在列表中的项目,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;

1.3. 迷你列表项

有些情况下不需要列表项这么全的功能,为了避免造成内存浪费,定义了迷你列表项。
迷你列表项的结构体 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上:程序下载地址