原创 五、freeRTOS 列表与列表项

2020-8-23 16:28 1137 18 10 分类: 汽车电子 文集: FreeRTOS

FreeRTOS 列表

列表的数据结构:

  1. /*
  2. * Definition of the type of queue used by the scheduler.
  3. */
  4. typedef struct xLIST
  5. {
  6. listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
  7. configLIST_VOLATILE UBaseType_t uxNumberOfItems;
  8. ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
  9. MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
  10. listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
  11. } List_t;
  12. /* Macros used for basic data corruption checks. */
  13. #ifndef configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES
  14. #define configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 0
  15. #endif
  16. // 如果宏定义为1,则这两个链表检查项定义成变量
  17. #if (configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 0)
  18. /* Define the macros to do nothing. */
  19. #define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE // 链表节点检查项
  20. #define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE
  21. #define listFIRST_LIST_INTEGRITY_CHECK_VALUE // 链表检查项
  22. #define listSECOND_LIST_INTEGRITY_CHECK_VALUE
  23. #else
  24. /* Define macros that add new members into the list structures. */
  25. #define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue1;
  26. #define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue2;
  27. // 链表检查项定义成变量
  28. #define listFIRST_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue1;
  29. #define listSECOND_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue2;
  30. #endif

列表的元素分析:

列表的首尾两个元素,都是用来检查列表完整性的,需要将宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 设置为 1,开启以后会向这两个地方分别添加一个变量 xListIntegrityValue1 和 xListIntegrityValue2,在初始化列表的时候会这两个变量中写入一个特殊的值,默认不开启这个功能;

uxNumberOfItems 用来记录列表中列表项的数量;

pxIndex 用来记录当前列表项索引号,用于遍历列表;

列表中最后一个列表项,用来表示列表结束,此变量类型为 MiniListItem_t,这是一个迷你列表项;

列表的结构图:

列表项:

列表项就是存放在列表中的项目,FreeRTOS 提供了两种列表项:列表项和迷你列表项;

  1. /*
  2. * Definition of the only type of object that a list can contain.
  3. */
  4. struct xLIST_ITEM
  5. {
  6. listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
  7. configLIST_VOLATILE TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */
  8. struct xLIST_ITEM *configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */
  9. struct xLIST_ITEM *configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */
  10. void *pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */
  11. void *configLIST_VOLATILE pvContainer; /*< Pointer to the list in which this list item is placed (if any). */
  12. listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
  13. };
  14. typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */

首尾两项与列表类似,为完整性检查;

xItemValue 为列表项值;

pxNext 指向下一个列表项,pxPrevious 指向前一个列表项,和 pxNext 配合起来实现类似双向链表的功能;

pvOwner 记录此链表项归谁拥有,通常是任务控制块;

pvContainer 用来记录此列表项归哪个列表;

列表项的结构示意图:

任务控制块的数据结构:

迷你列表项:

  1. struct xMINI_LIST_ITEM
  2. {
  3. listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
  4. configLIST_VOLATILE TickType_t xItemValue;
  5. struct xLIST_ITEM *configLIST_VOLATILE pxNext;
  6. struct xLIST_ITEM *configLIST_VOLATILE pxPrevious;
  7. };
  8. typedef struct xMINI_LIST_ITEM MiniListItem_t;

参数说明:

列表和列表项相关的操作:

1 列表初始化示意图:

2 列表的升序插入示意图:

插入单个列表项

插入多个列表项

列表的初始化 排序插入等操作,都不需要操作遍历指针 pxList->pxIndex 这个指针。

链表的尾部插入:

由于是环形链表,所以根本就没有头部和尾部之说。那么,如何确定链表开始的地方?这就要靠遍历指针 pxList->pxIndex

pxIndex这个指针指向的列表项,就是链表的开始,那么很明显,pxIndex->Previous指向的列表项就是链表的结尾;

我们所说的链表尾部插入,就是要插入到pxIndex->Previous处。

 

重点分析:

  1. void vListInsertEnd(List_t *const pxList, ListItem_t *const pxNewListItem)
  2. {
  3. ListItem_t *const pxIndex = pxList->pxIndex;
  4. /* Insert a new list item into pxList, but rather than sort the list,
  5. makes the new list item the last item to be removed by a call to
  6. listGET_OWNER_OF_NEXT_ENTRY(). */
  7. // 在pxList中插入一个新的列表项,而不是对列表进行排序
  8. // 使新的列表项成为调用listGET_OWNER_OF_NEXT_ENTRY()删除的最后一个项
  9. // 这里所谓的末尾要根据列表的成员变量pxIndex来确定,pxIndex所指向的列表项就是要遍历的开始列表项
  10. // 即pxIndex所指向的列表项就代表列表头,由于是环形列表,新列表项就应该插入到pxIndex所指向的列表项的前面
  11. // 只有这样,才能保证调用listGET_OWNER_OF_NEXT_ENTRY()时被最后删除
  12. pxNewListItem->pxNext = pxIndex;
  13. pxNewListItem->pxPrevious = pxIndex->pxPrevious;
  14. pxIndex->pxPrevious->pxNext = pxNewListItem;
  15. pxIndex->pxPrevious = pxNewListItem;
  16. /* Remember which list the item is in. */
  17. pxNewListItem->pvContainer = (void *)pxList;
  18. (pxList->uxNumberOfItems)++;
  19. }

插入位置分析:

尾部插入的图示:

插入前:

插入后:

列表的遍历:

 

列表项的插入和删除实验

代码如下:

  1. // 定义一个列表和三个列表项
  2. List_t testList;
  3. ListItem_t ListItem1;
  4. ListItem_t ListItem2;
  5. ListItem_t ListItem3;
  6. void list_task(void *pvParameters)
  7. {
  8. vListInitialise(&testList); // 初始化列表
  9. vListInitialiseItem(&ListItem1); // 初始化列表项1
  10. vListInitialiseItem(&ListItem2); // 初始化列表项2
  11. vListInitialiseItem(&ListItem3); // 初始化列表项3
  12. ListItem1.xItemValue = 40;
  13. ListItem2.xItemValue = 60;
  14. ListItem3.xItemValue = 50;
  15. // 打印列表和其他列表项的地址
  16. printf("\n/*********************列表和列表项地址************************/\n");
  17. printf("项目 地址 \n");
  18. printf("testList %#X \n", (int)&testList);
  19. printf("testList->pxIndex %#X \n", (int)testList.pxIndex);
  20. printf("testList->xListEnd %#X \n", (int)&testList.xListEnd);
  21. printf("ListItem1 %#X \n", (int)&ListItem1);
  22. printf("ListItem2 %#X \n", (int)&ListItem2);
  23. printf("ListItem3 %#X \n", (int)&ListItem3);
  24. printf("/***************************结束*****************************/\n");
  25. printf("按下K1按键继续!\r\n\r\n");
  26. while (key_scan(KEY1_GPIO_Port, KEY1_Pin) != KEY_ON);
  27. // testList插入ListItem1
  28. vListInsert(&testList, &ListItem1); // 插入列表项1
  29. printf("\n/*******************添加列表项ListItem1**********************/\n");
  30. printf("项目 地址 \n");
  31. printf("testList->xListEnd->pxNext %#X \n", (int)testList.xListEnd.pxNext);
  32. printf("ListItem1->pxNext %#X \n", (int)ListItem1.pxNext);
  33. printf("/**********************前后向连接分割线***********************/\n");
  34. printf("testList->xListEnd->pxPrevious %#X \n", (int)testList.xListEnd.pxPrevious);
  35. printf("ListItem1->pxPrevious %#X \n", (int)ListItem1.pxPrevious);
  36. printf("/***************************结束*****************************/\n");
  37. printf("按下K1按键继续!\r\n\r\n");
  38. while (key_scan(KEY1_GPIO_Port, KEY1_Pin) != KEY_ON);
  39. // testList插入ListItem2
  40. vListInsert(&testList, &ListItem2); // 插入列表项2
  41. printf("\n/*******************添加列表项ListItem2**********************/\n");
  42. printf("项目 地址 \n");
  43. printf("testList->xListEnd->pxNext %#X \n", (int)testList.xListEnd.pxNext);
  44. printf("ListItem1->pxNext %#X \n", (int)ListItem1.pxNext);
  45. printf("ListItem2->pxNext %#X \n", (int)ListItem2.pxNext);
  46. printf("/**********************前后向连接分割线***********************/\n");
  47. printf("testList->xListEnd->pxPrevious %#X \n", (int)testList.xListEnd.pxPrevious);
  48. printf("ListItem1->pxPrevious %#X \n", (int)ListItem1.pxPrevious);
  49. printf("ListItem2->pxPrevious %#X \n", (int)ListItem2.pxPrevious);
  50. printf("/***************************结束*****************************/\n");
  51. printf("按下K1按键继续!\r\n\r\n");
  52. while (key_scan(KEY1_GPIO_Port, KEY1_Pin) != KEY_ON);
  53. // testList插入ListItem3
  54. vListInsert(&testList, &ListItem3); // 插入列表项3
  55. printf("\n/*******************添加列表项ListItem3**********************/\n");
  56. printf("项目 地址 \n");
  57. printf("testList->xListEnd->pxNext %#X \n", (int)testList.xListEnd.pxNext);
  58. printf("ListItem1->pxNext %#X \n", (int)ListItem1.pxNext);
  59. printf("ListItem3->pxNext %#X \n", (int)ListItem3.pxNext);
  60. printf("ListItem2->pxNext %#X \n", (int)ListItem2.pxNext);
  61. printf("/**********************前后向连接分割线***********************/\n");
  62. printf("testList->xListEnd->pxPrevious %#X \n", (int)testList.xListEnd.pxPrevious);
  63. printf("ListItem1->pxPrevious %#X \n", (int)ListItem1.pxPrevious);
  64. printf("ListItem3->pxPrevious %#X \n", (int)ListItem3.pxPrevious);
  65. printf("ListItem2->pxPrevious %#X \n", (int)ListItem2.pxPrevious);
  66. printf("/***************************结束*****************************/\n");
  67. printf("按下K1按键继续!\r\n\r\n");
  68. while (key_scan(KEY1_GPIO_Port, KEY1_Pin) != KEY_ON);
  69. // testList删除ListItem2
  70. uxListRemove(&ListItem2);
  71. printf("\n/*******************删除列表项ListItem2**********************/\n");
  72. printf("项目 地址 \n");
  73. printf("testList->xListEnd->pxNext %#X \n", (int)testList.xListEnd.pxNext);
  74. printf("ListItem1->pxNext %#X \n", (int)ListItem1.pxNext);
  75. printf("ListItem3->pxNext %#X \n", (int)ListItem3.pxNext);
  76. printf("/**********************前后向连接分割线***********************/\n");
  77. printf("testList->xListEnd->pxPrevious %#X \n", (int)testList.xListEnd.pxPrevious);
  78. printf("ListItem1->pxPrevious %#X \n", (int)ListItem1.pxPrevious);
  79. printf("ListItem3->pxPrevious %#X \n", (int)ListItem3.pxPrevious);
  80. printf("/***************************结束*****************************/\n");
  81. printf("按下K1按键继续!\r\n\r\n");
  82. while (key_scan(KEY1_GPIO_Port, KEY1_Pin) != KEY_ON);
  83. // pxIndex向后移一项,这样pxIndex就会指向ListItem1
  84. testList.pxIndex = testList.pxIndex->pxNext;
  85. vListInsertEnd(&testList, &ListItem2); // 列表末尾添加列表项ListItem2
  86. printf("\n/*****************在末尾添加列表项ListItem2******************/\n");
  87. printf("项目 地址 \n");
  88. printf("testList.pxIndex %#X \n", (int)testList.pxIndex);
  89. printf("testList->xListEnd->pxNext %#X \n", (int)testList.xListEnd.pxNext);
  90. printf("ListItem2->pxNext %#X \n", (int)ListItem2.pxNext);
  91. printf("ListItem1->pxNext %#X \n", (int)ListItem1.pxNext);
  92. printf("ListItem3->pxNext %#X \n", (int)ListItem3.pxNext);
  93. printf("/**********************前后向连接分割线***********************/\n");
  94. printf("testList->xListEnd->pxPrevious %#X \n", (int)testList.xListEnd.pxPrevious);
  95. printf("ListItem2->pxPrevious %#X \n", (int)ListItem2.pxPrevious);
  96. printf("ListItem1->pxPrevious %#X \n", (int)ListItem1.pxPrevious);
  97. printf("ListItem3->pxPrevious %#X \n", (int)ListItem3.pxPrevious);
  98. printf("/***************************结束*****************************/\n");
  99. printf("按下K1按键继续!\r\n\r\n");
  100. while (key_scan(KEY1_GPIO_Port, KEY1_Pin) != KEY_ON);
  101. printf("list_task开始任务调度\n");
  102. for (;;)
  103. {
  104. printf("list_task run!\n");
  105. vTaskDelay(500);
  106. }
  107. }

程序执行结果如下:

/*********************列表和列表项地址************************/

项目                            地址                          

testList                        0X20000080                          

testList->pxIndex               0X20000088                          

testList->xListEnd              0X20000088                          

ListItem1                       0X20000094                          

ListItem2                       0X200000A8                          

ListItem3                       0X200000BC                          

/***************************结束*****************************/

按下K1按键继续!


 

/*******************添加列表项ListItem1**********************/

项目                            地址                          

testList->xListEnd->pxNext      0X20000094                          

ListItem1->pxNext               0X20000088                          

/**********************前后向连接分割线***********************/

testList->xListEnd->pxPrevious  0X20000094                          

ListItem1->pxPrevious           0X20000088                          

/***************************结束*****************************/

按下K1按键继续!


 

/*******************添加列表项ListItem2**********************/

项目                            地址                          

testList->xListEnd->pxNext      0X20000094                          

ListItem1->pxNext               0X200000A8                          

ListItem2->pxNext               0X20000088                          

/**********************前后向连接分割线***********************/

testList->xListEnd->pxPrevious  0X200000A8                          

ListItem1->pxPrevious           0X20000088                          

ListItem2->pxPrevious           0X20000094                          

/***************************结束*****************************/

按下K1按键继续!


 

/*******************添加列表项ListItem3**********************/

项目                            地址                          

testList->xListEnd->pxNext      0X20000094                          

ListItem1->pxNext               0X200000BC                          

ListItem3->pxNext               0X200000A8                          

ListItem2->pxNext               0X20000088                          

/**********************前后向连接分割线***********************/

testList->xListEnd->pxPrevious  0X200000A8                          

ListItem1->pxPrevious           0X20000088                          

ListItem3->pxPrevious           0X20000094                          

ListItem2->pxPrevious           0X200000BC                          

/***************************结束*****************************/

按下K1按键继续!


 

/*******************删除列表项ListItem2**********************/

项目                            地址                          

testList->xListEnd->pxNext      0X20000094                          

ListItem1->pxNext               0X200000BC                          

ListItem3->pxNext               0X20000088                          

/**********************前后向连接分割线***********************/

testList->xListEnd->pxPrevious  0X200000BC                          

ListItem1->pxPrevious           0X20000088                          

ListItem3->pxPrevious           0X20000094                          

/***************************结束*****************************/

按下K1按键继续!


 

/*****************在末尾添加列表项ListItem2******************/

项目                            地址                          

testList.pxIndex                0X20000094                          

testList->xListEnd->pxNext      0X200000A8                          

ListItem2->pxNext               0X20000094                          

ListItem1->pxNext               0X200000BC                          

ListItem3->pxNext               0X20000088                          

/**********************前后向连接分割线***********************/

testList->xListEnd->pxPrevious  0X200000BC                          

ListItem2->pxPrevious           0X20000088                          

ListItem1->pxPrevious           0X200000A8                          

ListItem3->pxPrevious           0X20000094                          

/***************************结束*****************************/

按下K1按键继续!

 

list_task开始任务调度

list_task run!

list_task run!

list_task run!

list_task run!

list_task run!

转载于:https://blog.csdn.net/dingyc_ee/article/details/104071570

PARTNER CONTENT

文章评论1条评论)

登录后参与讨论

商业资讯 2021-7-29 14:59

不错
相关推荐阅读
jinsheng 2020-08-23 17:10
十二、freeRTOS 软件定时器
定时器硬件定时器:CPU内部自带的定时器模块,通过初始化、配置可以实现定时,定时时间到以后就会执行相应的定时器中断处理函数。硬件定时器一般都带有其他功能,比如PWM输出、输入捕获等功能。但缺点是硬件定...
jinsheng 2020-08-23 17:07
十一、freeRTOS 优先级翻转与互斥信号量
优先级翻转优先级翻转简介:就是高优先级的任务运行起来的效果好像成了低优先级,而低优先级比高优先级先运行。优先级翻转如下所示:优先级翻转过程:为什么会发生优先级翻转?因为两个任务(L和H)使用了同一个二...
jinsheng 2020-08-23 17:05
十、freeRTOS 计数型信号量
计数型信号量简介:计数型信号量的创建:计数型信号量动态创建函数:释放和获取信号量(与二值信号量相同)释放信号量:获取信号量: 测试实验用按键来模拟事件,按键按下后表示有事件发生,则释放计数型...
jinsheng 2020-08-23 16:54
九、freeRTOS 二值信号量
信号量1 信号量用于共享资源的访问:2 信号量用于任务同步:为什么一直说在中断服务函数中,不能够做太多的事情?在进入中断服务函数时,低优先级的中断就不能响应,同类型的中断也无法响应,所以就要求ISR一...
jinsheng 2020-08-23 16:49
八、freeRTOS 队列
队列简介:注意,队列长度并不是每个队列的字节数,而是能存多少条数据。如我们创建一个队列,每条消息最大10字节,队列可以保存4条消息,那么队列的长度为4。队列的功能:1 数据拷贝为什么一定要使用数据拷贝...
jinsheng 2020-08-23 16:45
七、FreeRTOS时间管理
FreeRTOS时间管理FreeRTOS的两个延时函数:vTaskDelay()          相对延时vTaskDelayUntil()&nbs...
EE直播间
更多
我要评论
1
18
关闭 站长推荐上一条 /3 下一条