FreeRTOS_事件组的使用

文章介绍了如何使用STM32F103ZET6开发板,基于HAL库和FreeRTOS的事件组功能实现多个任务的并行运行。事件组允许同时处理多个优先级不相同的任务,通过设置和清除事件位来控制任务执行。实验中创建了三个任务,一个检测按键并设置事件位,另外两个任务根据事件组状态控制LED闪烁。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前我们学习了队列和队列延伸出的信号量、互斥量的使用,但是这些有些局限性,只能同时进行一项任务,我们这次实验来学习如何同一时间使多个任务(优先级可以不相同)同时运行:这就是今天实验的主角:事件组

本次实验采用STM32F103ZET6主芯片的开发板,使用HAL库开发。

目录

事件组特点

事件组相关函数

实验部分

CobeMX设置

代码实现


事件组特点

事件组允许任务等待一个或者多个事件的组合,也可以一个事件对应多个任务,当事件置位时,这个事件对应的所有任务都会同时运行。

事件组是由一个32位的变量组成的:

 

它的24-31位是保留位,后面的0-23位是事件位,一个位就表示一个事件的发生与否(发生为1,未发生为0)。所以一个事件组最多可以处理24个事件。

事件组相关函数

事件位置位:xEventGroupSetBits()用于设置事件组中的一个或多个事件位,也可用于通知任务当前已设置的位。它的各项参数为:

xEventGroup事件组的句柄;

uxBitsToSet是一个位掩码,指定事件组中哪些位设置为1。例如:KEY10x04,则将事件组中的bit 2(bit 0开始)设置为1,而其他位不变。

注意不要在中断中使用它,中断中应使用中断安全版API:xEventGroupSetBitsFromISR();

事件位清零:xEventGroupClearBits()用于在任务函数中将事件组某些位清零,和上面事件位置位刚好相反。它的各项参数是:

xEventGroup事件组的句柄;

uxBitsToClear是一个位掩码,指定事件组中哪些位清零。例如:KEY10x04,则将事件组中的bit 2(bit 0开始)设置为0,而其他位不变;

这个函数的返回值是清零之前的事件组的值。

读取事件组当前值:xEventGroupGetBits()可以读取事件组的当前值,这个函数实际上执行的是事件位清零函数(xEventGroupClearBits()),这个函数只有一个参数

xEventGroup事件组的句柄;

返回值是事件组的值。

等待事件组条件成立:xEventGroupWaitBits()函数允许任务读取事件组的值,并且事件尚未发生,可以在阻塞状态下选择等待事件组中的一个或多个事件。它的各项参数:

xEventGroup事件组的句柄;

uxBitsToWaitFor是一个位掩码,指定要在事件组中测试的事件位(一个或多个);

xClearOnExit如果调用任务满足解除阻塞条件,并且xClearOnExit为pdTRUE,则在xEventGroupWaitBits()退出前,清除uxBitsToWaitFor指定的事件位。如果xClearOnExit为pdFALSE,则xEventGroupWaitBits()函数不会修改事件组的事件位。

xWaitForAllBits这位可以设置为pdFALSEpdTRUE,当设置为pdFALSEuxBitsToWaitFor任何位被设置就退出函数(超时后也会退出),为pdTRUE时,uxBitsToWaitFor中设置的所有位被设置才退出函数(超时后也会退出)

xTicksToWait最长的超时时间,如果在设定时间内没有达到上面的要求则会退出函数,如果设置为portMAX_DELAY表示等待事件无限长。

了解了这些基本参数我们进行实验来熟悉这些参数

实验部分

实验流程:创建一个事件组和三个任务;

      1. 一个任务检测KEY1和KEY2按键是否按下,如果按下就修改对应的事件组位;KEY3按下清除所有事件位。
      2. 另外两个任务检测当两个事件组位成立时分别使LED2和3闪烁几次。

CobeMX设置

本次实验我们需要用到LED,按键和LCD,这些之前都设置过,这里就不再强调,这里添加我这块板子的LED和按键对应硬件图:

 

 

 

然后开启FREERTOS选择模式V2,在Takes and Queues一栏配置三个任务,具体配置如图:

 

图 1 任务配置

再在Events一栏配置一个事件组,这里配置只需要设置事件组的名称,具体如图:

 

图 2 事件组配置

配置完成后就可以生成代码了。

代码实现

按键和LED宏函数的设定:

#define KEY3_Pin GPIO_PIN_2

#define KEY3_GPIO_Port GPIOE

#define KEY2_Pin GPIO_PIN_3

#define KEY2_GPIO_Port GPIOE

#define KEY1_Pin GPIO_PIN_4

#define KEY1_GPIO_Port GPIOE

#define LED2_Pin GPIO_PIN_5

#define LED2_GPIO_Port GPIOE

#define LED3_Pin GPIO_PIN_5

#define LED3_GPIO_Port GPIOB

这里只用到3个按键,所以只定义了三个按键宏。

在主函数中测试LCD,具体代码如下:

#include "lcd.h"

int main(void)

{

  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();

  MX_FSMC_Init();

  /* USER CODE BEGIN 2 */

       LCD_Init();

       LCD_ShowString(10, 0, 280, 16, 16, (uint8_t *)"this is the damo seven!");

       LCD_ShowString(10, 20, 280, 16, 16, (uint8_t *)"1.Press KEY1 and KEY2");

       LCD_ShowString(10, 40, 280, 16, 16, (uint8_t *)"to activate LED2 and LED3");

       LCD_ShowString(10, 60, 280, 16, 16, (uint8_t *)"2.Press KEY3 to clear events");

  /* USER CODE END 2 */

  osKernelInitialize();  /* Call init function for freertos objects (in freertos.c) */

  MX_FREERTOS_Init();

  osKernelStart();

  while (1)

  {

  }

}

然后进入freertos.c文件,现在开头处声明几个头文件:

#include "lcd.h"

#include "event_groups.h"  // 事件组相关头文件

为了后面方便书写,在这里定义几个事件掩码相关宏:

#define KEY1     0x01  // KEY1的事件位掩码为bit0

#define KEY2     0x02  // KEY2的事件位掩码为bit1

#define CLEAR    0x03  // 将KEY1和KEY2的事件位都清零

完成后配置任务函数。

在StartTask03函数中我们能需要将按键对应到事件组的具体位,具体设置我在注释中有详细注解。

/* USER CODE END Header_StartTask03 */

/* 在这个任务中检测KEY1和KEY2是否都按下,

        如果按下将事件组中对应的事件位置位。

        如果按下KEY3使整个事件组清零 */

void StartTask03 (void *argument)

{

  /* USER CODE BEGIN StartTask03 */

      

      

//    KEY key = KEY0;

  /* Infinite loop */

  for(;;)

  {

    if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET)

              {

                     xEventGroupSetBits(eventGroupHandle,KEY1);  // 置位KEY1的掩码

              }

              if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == GPIO_PIN_RESET)

              {

                    

                     xEventGroupSetBits(eventGroupHandle,KEY2);  // 置位KEY2的掩码

              }

              if(HAL_GPIO_ReadPin(KEY3_GPIO_Port, KEY3_Pin) == GPIO_PIN_RESET)

              {

                     xEventGroupClearBits(eventGroupHandle,KEY1 | KEY2);  // 将KEY1和KEY2的事件位都清零

              }

              vTaskDelay(200);

  }

  /* USER CODE END StartTask03 */

}

剩下的LED2和LED3两个任务本质上都没有区别,只是应对事件不同而已。直接上代码:

/* USER CODE BEGIN Header_Task_LED2 */

/**

  * 在这个实验中要是KEY1和2连着按下,不管间隔多久都可以,那么就会触发任务1和2

  * 然后会删除事件位,如果在两个按键中按下KEY3则不会触发任务。

  * xEventGroupWaitBits这个等待事件组条件成立的最后一位是配置等待时间的,

  * 这里设置一直等待,如果修改可以设置规定时间。

  * @brief  Function implementing the myTask_LED2 thread.

  * @param  argument: Not used

  * @retval None

  */

/* USER CODE END Header_Task_LED2 */

// 在这个任务中检测KEY1和KEY2的事件是否发生,如果发生将LED2闪烁5次

void Task_LED2(void *argument)

{

  /* USER CODE BEGIN Task_LED2 */

  /* Infinite loop */

  for(;;)

  {

    xEventGroupWaitBits(eventGroupHandle,CLEAR,pdTRUE,pdTRUE,portMAX_DELAY); // 等待事件发生,如果发生再进行操作

              uint8_t i;

              for(i = 0; i < 5; i++)

              {

                     HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin);

                     vTaskDelay(200);

                    

              }

  }

  /* USER CODE END Task_LED2 */

}



/* USER CODE BEGIN Header_Task_LED3 */

/**

* @brief Function implementing the myTask_LED3 thread.

* @param argument: Not used

* @retval None

*/

/* USER CODE END Header_Task_LED3 */

// 在这个任务中检测KEY1和KEY2的事件是否发生,如果发生将LED3闪烁5次。

void Task_LED3(void *argument)

{

  /* USER CODE BEGIN Task_LED3 */

  /* Infinite loop */

  for(;;)

  {

          xEventGroupWaitBits(eventGroupHandle,CLEAR,pdTRUE,pdTRUE,portMAX_DELAY);  /* 阻塞等待事件组条件成立

                                                                                                                                                                                                                                                                                 如果条件成立就触发LED灯闪烁 */       

              uint8_t i;

              for(i = 0; i < 5; i++)

              {

                     HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);

                     vTaskDelay(200);

              }

             

  }

  /* USER CODE END Task_LED3 */

}

完成代码部分后,将其编译烧录进开发板,可以看到屏幕上的提示信息,按照提示操作可以看到相关现象。本实验完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值