学习目标

  1. 理解什么是事件组
  2. 理解事件组标志位
  3. 掌握事件组开发流程

    学习内容

    概念

    在FreeRTOS中,事件组(Event Group)是一种用于任务间同步和通信的机制。事件组允许任务等待和检测多个事件的状态,并在事件发生时进行通知。

事件组由一组标志位(或事件位)组成,每个标志位代表一个特定的事件。任务可以等待某些标志位被置位或清除,也可以设置或清除标志位。

以下是事件组的一些关键概念:

  1. 事件组句柄(Event Group Handle):

事件组句柄是用于标识和操作事件组的变量。它可以在创建事件组时由FreeRTOS分配,也可以通过函数返回。

  1. 标志位(Event Bit):

标志位是事件组中的单个位,它表示一个特定的事件。每个标志位可以被置位或清除,以表示事件发生或未发生。
标志位通常用二进制位来表示,如第0位、第1位等。可以使用位操作函数进行置位和清除操作。

  1. 等待事件(Waiting Events):

任务可以通过等待事件组中的一个或多个标志位来暂停执行,直到这些标志位满足特定的条件。
等待事件可以通过函数如xEventGroupWaitBits()、xEventGroupSync()等实现。

  1. 通知事件(Signaling Events):

任务可以通过设置或清除事件组中的标志位来通知其他任务事件的发生。
通知事件可以通过函数如xEventGroupSetBits()、xEventGroupClearBits()等实现。
通过使用事件组,任务可以进行有效的同步和通信,实现复杂的任务调度和协调。任务可以等待多个事件同时发生,也可以通过设置或清除标志位来触发其他任务的执行。
请注意,事件组是FreeRTOS中的一个特性,具体的使用方法和函数可能因不同的FreeRTOS版本而略有差异。建议参考相关的FreeRTOS文档和参考资料以获取更详细和准确的信息。

事件标志位

062.png

  1. 事件标志为一个32位的数字。也就是采用uint32_t来记录事件状态。
  2. 32位的状态分为高8位和低24位。
  3. 高8位位系统级事件和状态,由操作系统自行管理,无需开发人员关系。
  4. 低24位表示24种事件,每1个bit位表示一种事件,由开发人员控制这些事件位。
  5. 低24位中,每一位的取值是0或者1,0表示当前位对应的事件没有触发,1表示当前位的事件触发了。

    开发流程

  6. 创建事件组

  7. 开启一个任务,用来等待事件触发
  8. 开启一个任务,用来改变事件触发

063.png
事件组标志位像一个订阅中心一样,一方可以订阅事件的触发,一方可以改变事件的状态。

功能介绍

功能 描述
xEventGroupCreate 创建事件组
xEventGroupSetBits 将事件状态改为触发
xEventGroupWaitBits 等待事件触发

创建事件组:

  1. EventGroupHandle_t xEventGroupCreate();

返回值为事件组的句柄。

将事件状态改为触发:

  1. EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
  2. const EventBits_t uxBitsToSet )

参数说明:

  1. EventGroupHandle_t xEventGroup:事件组的句柄。
  2. const EventBits_t uxBitsToSet:事件标志位。

返回值:
当前触发的事件有哪些。

等待事件触发:

  1. EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
  2. const EventBits_t uxBitsToWaitFor,
  3. const BaseType_t xClearOnExit,
  4. const BaseType_t xWaitForAllBits,
  5. TickType_t xTicksToWait )

参数说明:

  1. EventGroupHandle_t xEventGroup:事件组的句柄。
  2. const EventBits_t uxBitsToWaitFor:等待事件组的哪个标志位。可以是多个标志位。
  3. const BaseType_t xClearOnExit:等待事件触发后,是否清楚这个事件,如果清除,其他的订阅者将不会收到,不清除,就会收到。pdTRUE表示清除。pdFALSE表示不清除。
  4. const BaseType_t xWaitForAllBits:指定是否等待所有的标志位都被设置。设置为 pdTRUE 表示等待所有标志位都被设置,设置为 pdFALSE 表示只要有任何一个标志位被设置就可以继续执行任务。
  5. TickType_t xTicksToWait:等待标志位被设置的超时时间,以 FreeRTOS 的 Tick 单位表示。可以使用 pdMS_TO_TICKS 宏将毫秒转换为 Tick 值。如果设置为 portMAX_DELAY,则表示无限等待,直到标志位被设置。

返回值表示已设置的标志位。

案例

开启三个任务,等待事件发生。通过按键触发事件发生。观察效果。

  1. #include "gd32f4xx.h"
  2. #include "systick.h"
  3. #include <stdio.h>
  4. #include "main.h"
  5. #include "FreeRTOS.h"
  6. #include "task.h"
  7. #include "event_groups.h"
  8. #include "Usart0.h"
  9. #define EVENT_BIT(N) (1 << N)
  10. #define EVENT_BIT_0 EVENT_BIT(0)
  11. #define EVENT_BIT_1 EVENT_BIT(1)
  12. #define EVENT_BIT_2 EVENT_BIT(2)
  13. #define EVENT_BIT_3 EVENT_BIT(3)
  14. #define EVENT_BIT_4 EVENT_BIT(4)
  15. #define EVENT_BIT_5 EVENT_BIT(5)
  16. #define EVENT_BIT_6 EVENT_BIT(6)
  17. #define EVENT_BIT_7 EVENT_BIT(7)
  18. #define EVENT_BIT_8 EVENT_BIT(8)
  19. #define EVENT_BIT_9 EVENT_BIT(9)
  20. #define EVENT_BIT_10 EVENT_BIT(10)
  21. #define EVENT_BIT_11 EVENT_BIT(11)
  22. #define EVENT_BIT_12 EVENT_BIT(12)
  23. #define EVENT_BIT_13 EVENT_BIT(13)
  24. #define EVENT_BIT_14 EVENT_BIT(14)
  25. #define EVENT_BIT_15 EVENT_BIT(15)
  26. #define EVENT_BIT_16 EVENT_BIT(16)
  27. #define EVENT_BIT_17 EVENT_BIT(17)
  28. #define EVENT_BIT_18 EVENT_BIT(18)
  29. #define EVENT_BIT_19 EVENT_BIT(19)
  30. #define EVENT_BIT_20 EVENT_BIT(20)
  31. #define EVENT_BIT_21 EVENT_BIT(21)
  32. #define EVENT_BIT_22 EVENT_BIT(22)
  33. #define EVENT_BIT_23 EVENT_BIT(23)
  34. TaskHandle_t task_handler;
  35. TaskHandle_t task_key_handler;
  36. TaskHandle_t task1_handler;
  37. TaskHandle_t task2_handler;
  38. TaskHandle_t task3_handler;
  39. EventGroupHandle_t event_group;
  40. void task1(void *pvParameters) {
  41. EventBits_t bits;
  42. while(1) {
  43. bits = xEventGroupWaitBits(event_group, EVENT_BIT_0, pdTRUE, pdFALSE, portMAX_DELAY);
  44. printf("task1: %d\r\n", bits);
  45. vTaskDelay(1000);
  46. }
  47. vTaskDelete(NULL);
  48. }
  49. void task2(void *pvParameters) {
  50. EventBits_t bits;
  51. while(1) {
  52. bits = xEventGroupWaitBits(event_group, EVENT_BIT_0, pdTRUE, pdTRUE, portMAX_DELAY);
  53. printf("task2: %d\r\n", bits);
  54. vTaskDelay(2000);
  55. }
  56. vTaskDelete(NULL);
  57. }
  58. void task3(void *pvParameters) {
  59. EventBits_t bits = 0;
  60. while(1) {
  61. bits = xEventGroupWaitBits(event_group, EVENT_BIT_0, pdTRUE, pdTRUE, portMAX_DELAY);
  62. printf("task3: %d\r\n", bits);
  63. vTaskDelay(3000);
  64. }
  65. vTaskDelete(NULL);
  66. }
  67. void task_key(void *pvParameters) {
  68. FlagStatus pre_state = RESET;
  69. while(1) {
  70. FlagStatus state = gpio_input_bit_get(GPIOA, GPIO_PIN_0);
  71. if(SET == state && pre_state == RESET) {
  72. // 当前高电平, 上一次为低电平,按下
  73. pre_state = state;
  74. printf("set bit \r\n");
  75. xEventGroupSetBits(event_group, EVENT_BIT_0);
  76. } else if(RESET == state && pre_state == SET) {
  77. // 当前高电平, 上一次为低电平,抬起
  78. pre_state = state;
  79. }
  80. vTaskDelay(20);
  81. }
  82. }
  83. void start_task(void *pvParameters) {
  84. taskENTER_CRITICAL();
  85. xTaskCreate(task_key, "task_key", 64, NULL, 0, &task_key_handler);
  86. xTaskCreate(task1, "task1", 64, NULL, 4, &task1_handler);
  87. xTaskCreate(task2, "task2", 64, NULL, 3, &task2_handler);
  88. xTaskCreate(task3, "task3", 64, NULL, 2, &task3_handler);
  89. vTaskDelete(task_handler);
  90. taskEXIT_CRITICAL();
  91. }
  92. void Usart0_recv(uint8_t *data, uint32_t len)
  93. {
  94. // printf("recv: %s\n", data);
  95. xEventGroupSetBitsFromISR(event_group, EVENT_BIT_0, NULL);
  96. }
  97. static void GPIO_config() {
  98. // 时钟初始化
  99. rcu_periph_clock_enable(RCU_GPIOA);
  100. // 配置GPIO模式
  101. gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
  102. }
  103. int main(void)
  104. {
  105. nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
  106. systick_config();
  107. GPIO_config();
  108. Usart0_init();
  109. event_group = xEventGroupCreate();
  110. if(event_group != NULL) {
  111. printf("group success\r\n");
  112. }
  113. xTaskCreate(start_task, "start_task", 128, NULL, 0, &task_handler);
  114. vTaskStartScheduler();
  115. while(1) {}
  116. }

练习题

  1. 实现事件触发