记录一下,方便以后翻阅~
上一章介绍了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。
因为:
继续单击按键,添加列表项ListItem2,完成后如下图所示:
xListEnd的下一个项的地址为ox200000cc,xListEnd的上一个项的地址为ox200000e0;
ListItem1下一个项的地址为0x200000e0,ListItem1上一个项的地址为0x200000c0;
ListItem2下一个项的地址为0x200000c0,ListItem2上一个项的地址为0x200000cc;
因为:
继续单击按键,添加列表项ListItem3,完成后如下图所示:
xListEnd的下一个项的地址为ox200000cc,xListEnd的上一个项的地址为ox200000f4;
ListItem1下一个项的地址为0x200000f4,ListItem1上一个项的地址为0x200000c0;
ListItem3下一个项的地址为0x200000c0,ListItem3上一个项的地址为0x200000cc;
因为:
继续单击按键是,删除列表项ListItem2,完成后如下图所示:
xListEnd的下一个项的地址为ox200000cc,xListEnd的上一个项的地址为ox200000e0;
ListItem1下一个项的地址为0x200000f4,ListItem1上一个项的地址为0x200000c0;
ListItem3下一个项的地址为0x200000c0,ListItem3上一个项的地址为0x200000cc;
ListItem2下一个项的地址为0x200000c0,ListItem2上一个项的地址为0x200000f4;
因为:
继续单击按键,末尾添加列表项ListItem2,完成后如下图所示:
xListEnd的下一个项的地址为ox200000e0,xListEnd的上一个项的地址为ox200000f4;
ListItem2下一个项的地址为0x200000cc,ListItem2上一个项的地址为0x200000c0;
ListItem1下一个项的地址为0x200000f4,ListItem1上一个项的地址为0x200000e0;
ListItem3下一个项的地址为0x200000c0,ListItem3上一个项的地址为0x200000cc;
因为:
还要注意一个:
此时,pxIndex指向ListItem1,及其地址为0x200000cc!
你可以理解为,末尾添加列表项后,pxIndex指向添加该列表项的后一项地址。