基于STM32的FreeRTOS学习之列表和列表项实验(十)

记录一下,方便以后翻阅~

上一章介绍了FreeRTOS列表和列表项的相关概念和函数说明,本章进行相关实验。

1. 实验目的

列表和列表项实验演练。

2. 实验设计

本实验设计3个任务:
start_task:用来创建其他2个任务;
task1_task:应用任务1,控制LED灯闪烁,提示系统正常运行;
list_task:列表和列表项操作任务,调用列表和列表项相关API函数,通过串口输出相应信息观察这些API函数的运行过程。
本实验需要用到按键,用于控制任务的运行。

3. 硬件

1) 正点原子战舰v3开发板(其他板子应该也可以,主要涉及USART,LED,KEY);
2) JLINK仿真器。

4. 代码解读

如果不考虑usart、led、key的函数(不是本章的重点),那么主要的函数都在main.c文件里:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
#include "timer.h"
#include "key.h"
#include "exti.h"
#include "FreeRTOS.h"
#include "task.h"
// 开始任务
#define START_TASK_PRIO  1           // 任务优先级
#define START_STK_SIZE   128         // 任务堆栈大小 
TaskHandle_t StartTask_Handler;      // 任务句柄
void start_task(void *pvParameters); // 任务函数
// 应用任务1
#define TASK1_TASK_PRIO  2           // 任务优先级
#define TASK1_STK_SIZE   128         // 任务堆栈大小 
TaskHandle_t Task1Task_Handler;      // 任务句柄
void task1_task(void *pvParameters); // 任务函数
// 列表任务
#define LIST_TASK_PRIO  3            // 任务优先级
#define LIST_STK_SIZE   128          // 任务堆栈大小 
TaskHandle_t ListTask_Handler;       // 任务句柄
void list_task(void *pvParameters);  // 任务函数
// 定义一个测试用的列表和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();                                    // 延时函数初始化  
 uart_init(115200);                               // 初始化串口
 LED_Init();                                      // 初始化LED
 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();                                 // 开启任务调度
}
// 开始任务任务函数
void start_task(void *pvParameters)
{
    
    
  taskENTER_CRITICAL();           // 进入临界区
  // 创建TASK1任务
  xTaskCreate(  (TaskFunction_t )task1_task,             
                (const char*    )"task1_task",           
                (uint16_t       )TASK1_STK_SIZE,        
                (void*          )NULL,                  
                (UBaseType_t    )TASK1_TASK_PRIO,        
                (TaskHandle_t*  )&Task1Task_Handler);   
  // 创建LIST任务
  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();            // 退出临界区
}
// task1任务函数
void task1_task(void *pvParameters)
{
    
    
 while(1)
 {
    
    
  LED0=!LED0;
  vTaskDelay(500);  // 延时500ms,也就是500个时钟节拍 
 }
}
// list任务函数
void list_task(void *pvParameters)
{
    
    
 // 第一步:初始化列表和列表项
 vListInitialise(&TestList);
 vListInitialiseItem(&ListItem1);
 vListInitialiseItem(&ListItem2);
 vListInitialiseItem(&ListItem3);
 ListItem1.xItemValue=40;   //ListItem1列表项值为40
 ListItem2.xItemValue=60;   //ListItem2列表项值为60
 ListItem3.xItemValue=50;   //ListItem3列表项值为50
 // 第二步:打印列表和其他列表项的地址
 printf("/*******************列表和列表项地址*******************/\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("按下KEY_UP键继续!\r\n\r\n\r\n");
 while(KEY_Scan(0)!=WKUP_PRES) delay_ms(10);  // 等待KEY_UP键按下
 // 第三步:向列表TestList添加列表项ListItem1,并通过串口打印所有列表项中成员变量pxNext和pxPrevious的值,
 // 通过这两个值观察列表项在列表中的连接情况。
 vListInsert(&TestList,&ListItem1);          // 插入列表项ListItem1
 printf("/******************添加列表项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("按下KEY_UP键继续!\r\n\r\n\r\n");
 while(KEY_Scan(0)!=WKUP_PRES) delay_ms(10);     // 等待KEY_UP键按下
 // 第四步:向列表TestList添加列表项ListItem2,并通过串口打印所有列表项中成员变量pxNext和pxPrevious的值,
 // 通过这两个值观察列表项在列表中的连接情况。
 vListInsert(&TestList,&ListItem2); // 插入列表项ListItem2
 printf("/******************添加列表项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("按下KEY_UP键继续!\r\n\r\n\r\n");
 while(KEY_Scan(0)!=WKUP_PRES) delay_ms(10);     // 等待KEY_UP键按下
 // 第五步:向列表TestList添加列表项ListItem3,并通过串口打印所有列表项中成员变量pxNext和pxPrevious的值,
 // 通过这两个值观察列表项在列表中的连接情况。
 vListInsert(&TestList,&ListItem3);                 // 插入列表项ListItem3
 printf("/******************添加列表项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("按下KEY_UP键继续!\r\n\r\n\r\n");
 while(KEY_Scan(0)!=WKUP_PRES) delay_ms(10);     // 等待KEY_UP键按下 
 // 第六步:删除ListItem2,并通过串口打印所有列表项中成员变量pxNext和pxPrevious的值,
 // 通过这两个值观察列表项在列表中的连接情况。
 uxListRemove(&ListItem2);      //删除ListItem2
 printf("/******************删除列表项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("按下KEY_UP键继续!\r\n\r\n\r\n");
 while(KEY_Scan(0)!=WKUP_PRES) delay_ms(10);     // 等待KEY_UP键按下 
 // 第七步:删除ListItem2,并通过串口打印所有列表项中成员变量pxNext和pxPrevious的值,
 // 通过这两个值观察列表项在列表中的连接情况。
 TestList.pxIndex=TestList.pxIndex->pxNext;       // pxIndex向后移一项,这样pxIndex就会指向ListItem1。
 vListInsertEnd(&TestList,&ListItem2);            // 列表末尾添加列表项ListItem2
 printf("/***************在末尾添加列表项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)
 {
    
    
  LED1=!LED1;
  vTaskDelay(1000);       // 延时1s,也就是1000个时钟节拍 
 }
}

5. 实验结果

系统运行后,观察串口调试助手,初始化后如下图所示:
在这里插入图片描述
初始化后:
pxIndex指向xListEnd,为ox200000c0;
xListEnd的地址为ox200000c0;
ListItem1的地址为ox200000cc;
ListItem2的地址为ox200000e0;
ListItem3的地址为ox200000f4;
单击按键,添加列表项ListItem1,完成后如下图所示:
在这里插入图片描述
xListEnd的下一个项的地址为ox200000cc,xListEnd的上一个项的地址为ox200000cc;
ListItem1下一个项的地址为0x200000c0,ListItem1上一个项的地址为0x200000c0。
因为:

xListEnd ————>ListItem1————>xListEnd

继续单击按键,添加列表项ListItem2,完成后如下图所示:
在这里插入图片描述
xListEnd的下一个项的地址为ox200000cc,xListEnd的上一个项的地址为ox200000e0;
ListItem1下一个项的地址为0x200000e0,ListItem1上一个项的地址为0x200000c0;
ListItem2下一个项的地址为0x200000c0,ListItem2上一个项的地址为0x200000cc;
因为:

xListEnd ————>ListItem1————>ListItem2————>xListEnd

继续单击按键,添加列表项ListItem3,完成后如下图所示:
在这里插入图片描述
xListEnd的下一个项的地址为ox200000cc,xListEnd的上一个项的地址为ox200000f4;
ListItem1下一个项的地址为0x200000f4,ListItem1上一个项的地址为0x200000c0;
ListItem3下一个项的地址为0x200000c0,ListItem3上一个项的地址为0x200000cc;
因为:

xListEnd ————>ListItem1————>ListItem3————>xListEnd

继续单击按键是,删除列表项ListItem2,完成后如下图所示:
在这里插入图片描述
xListEnd的下一个项的地址为ox200000cc,xListEnd的上一个项的地址为ox200000e0;
ListItem1下一个项的地址为0x200000f4,ListItem1上一个项的地址为0x200000c0;
ListItem3下一个项的地址为0x200000c0,ListItem3上一个项的地址为0x200000cc;
ListItem2下一个项的地址为0x200000c0,ListItem2上一个项的地址为0x200000f4;
因为:

xListEnd ————>ListItem1————>ListItem3————>ListItem2————>xListEnd

继续单击按键,末尾添加列表项ListItem2,完成后如下图所示:
在这里插入图片描述
xListEnd的下一个项的地址为ox200000e0,xListEnd的上一个项的地址为ox200000f4;
ListItem2下一个项的地址为0x200000cc,ListItem2上一个项的地址为0x200000c0;
ListItem1下一个项的地址为0x200000f4,ListItem1上一个项的地址为0x200000e0;
ListItem3下一个项的地址为0x200000c0,ListItem3上一个项的地址为0x200000cc;
因为:

xListEnd ————>ListItem2————>ListItem1————>ListItem3————>xListEnd

还要注意一个:
此时,pxIndex指向ListItem1,及其地址为0x200000cc!
你可以理解为,末尾添加列表项后,pxIndex指向添加该列表项的后一项地址。

猜你喜欢

转载自blog.csdn.net/Leisure_ksj/article/details/115075477