FreeRTOS【15】事件组使用

1.开发背景

基于以上的章节,了解了 FreeRTOS 多线程间的信号量、队列的使用,已经满足了日常使用场景。其中信号量可以实现线程同步,对标的是裸机的 Flag 标识,但是在裸机中经常使用的不止一个标识,如果用二值信号量去实现无疑是增加了系统内存开销,申请多个信号量,实现本质是队列,消耗比较大并且带有迟滞性,所以 FreeRTOS 针对这种情况设计了事件组。

事件组是按 bit 操作的,每一个位都可以触发事件,需要注意的是时钟的配置会影响可用的位配置,源码解释如果设置 configUSE_16_BIT_TICKS 系统时钟 16 位,只能用 0~7bit,否则可以使用 0~23 bit。

2.开发需求

设计实验1:位事件设置和等待

1)创建 2 个线程,设置位事件线程和等待位事件线程

2)设置位事件线程分别设置 bit0、bit0 和 bit4、bit4

3)等待位事件线程等待信号并输出对应日志

设计试验2:位事件同步

1)创建 2 个线程,同步线程1 控制 bit0, 同步线程2 控制 bit1

2)同步线程1 每 1s 同步一次,同步线程 2 每 2s 同步一次

3)观察线程同步的实时性。

3.开发环境

window10 + MDK + STM32F429 + FreeRTOS10.3.1

4.实现步骤

4.1 位事件设置和等待

4.1.1 实现编码

重点关注 xEventGroupWaitBits 的参数

uxBitsToWaitFor: 等待关注的事件(bit)

xClearOnExit: 是否清空,一般设置为是,清空不会反复触发

xWaitForAllBits: 是否等待所有位都满足条件才触发,看需求

cpp 复制代码
#include "appTest.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "mspDwt.h"
#include "mspGpio.h"
#include "mspExti.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "stream_buffer.h"
#include "event_groups.h"

#include "appLog.h"

typedef struct
{
    /* 事件组 */
    EventGroupHandle_t event;
    
    /* 任务线程 */
    TaskHandle_t taskEventWait; // 接收线程
    TaskHandle_t taskEventSet;  // 发送线程
    
}Ctrl_t;

/* 文件指针 */
static Ctrl_t s_ctrl = {0};
static Ctrl_t *p = &s_ctrl;
static void TaskEventSet(void *pvParameters);
static void TaskEventWait(void *pvParameters);

#define BIT_0   ( 1 << 0 )
#define BIT_1   ( 1 << 1 )
#define BIT_2   ( 1 << 2 )
#define BIT_3   ( 1 << 3 )
#define BIT_4   ( 1 << 4 )

#define ALL_SYNC_BITS ( BIT_0 | BIT_4 )

/* 接收线程 */
static void TaskEventWait(void *pvParameters)
{
    vTaskDelay(100);
   
    for ( ; ; )
    {
        EventBits_t uxBits = xEventGroupWaitBits(p->event, ALL_SYNC_BITS, pdTRUE, pdFALSE, portMAX_DELAY);
        
        switch (uxBits)
        {
            case ALL_SYNC_BITS:
                Log_Debug("%s 触发 bit0 和 bit4\r\n", __func__);
                break;
            
            case BIT_0:
                Log_Debug("%s 触发 bit0\r\n", __func__);
                break;
            
            case BIT_4:
                Log_Debug("%s 触发 bit4\r\n", __func__);
                break;
            
            default:
                break;
        }
        
        vTaskDelay(100);
    }
}

/* 发送线程 */
static void TaskEventSet(void *pvParameters)
{
    vTaskDelay(1000);
    EventBits_t uxBits = 0;
    
    /* 设置 bit0 */
    uxBits &= 0x00000000;
    uxBits |= BIT_0;
    xEventGroupSetBits(p->event, uxBits);
    vTaskDelay(100);
    
    /* 设置 bit0 和 bit4 */
    uxBits &= 0x00000000;
    uxBits |= BIT_0 | BIT_4;
    xEventGroupSetBits(p->event, uxBits);
    vTaskDelay(100);
    
    /* 设置 bit4 */
    uxBits &= 0x00000000;
    uxBits |= BIT_4;
    xEventGroupSetBits(p->event, uxBits);
    vTaskDelay(100);
    
    for ( ; ; )
    {
        vTaskDelay(1000);
    }
}

/* 测试初始化 */
void aTest_Init(void)
{
    /* 创建事件组 */
    p->event = xEventGroupCreate();
    
    /* 创建动态任务 */
    xTaskCreate(TaskEventWait, "TaskEventWait", 500, NULL, 5, &p->taskEventWait);
    xTaskCreate(TaskEventSet,  "TaskEventSet",  500, NULL, 5, &p->taskEventSet);
}

/* Key2 PC13   Key0 PH3 Key1 PH2 */
void Exti13_TriggerInterrupt(void)
{
    mspExti_Close(13);
    if (mspGpio_GetInput("PC13") == 0)
    {
        
    }
}
4.1.2 结果显示

4.2 位事件同步

位事件同步主要实现多线程同步执行,相互等待,调用 xEventGroupSync,会先设置位在阻塞等待位,相当与 xEventGroupSetBits() + xEventGroupWaitBits();

4.2.1 实现编码
cpp 复制代码
#include "appTest.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "mspDwt.h"
#include "mspGpio.h"
#include "mspExti.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "stream_buffer.h"
#include "event_groups.h"

#include "appLog.h"

typedef struct
{
    /* 事件组 */
    EventGroupHandle_t event;
    
    /* 任务线程 */
    TaskHandle_t taskEventSync1;  // 线程
    TaskHandle_t taskEventSync2;  // 线程
    
}Ctrl_t;

/* 文件指针 */
static Ctrl_t s_ctrl = {0};
static Ctrl_t *p = &s_ctrl;
static void EventSyncTask1(void *pvParameters);
static void EventSyncTask2(void *pvParameters);

#define BIT_0   ( 1 << 0 )
#define BIT_1   ( 1 << 1 )
#define BIT_2   ( 1 << 2 )
#define BIT_3   ( 1 << 3 )
#define BIT_4   ( 1 << 4 )

#define ALL_SYNC_BITS (BIT_0 | BIT_1)

/* 线程 */
static void EventSyncTask1(void *pvParameters)
{
    vTaskDelay(100);
   
    for ( ; ; )
    {
        /* 设置 bit0 */
        Log_Debug("%s Sync Set Bit0\r\n", __func__);
        EventBits_t uxBits = xEventGroupSync(p->event, BIT_0, ALL_SYNC_BITS, portMAX_DELAY);
        Log_Debug("%s Sync OK\r\n", __func__);
        
        vTaskDelay(1000);
    }
}

/* 线程 */
static void EventSyncTask2(void *pvParameters)
{
    vTaskDelay(1000);
    
    for ( ; ; )
    {
        /* 设置 bit1 */
        Log_Debug("%s Sync Set Bit1\r\n", __func__);
        EventBits_t uxBits = xEventGroupSync(p->event, BIT_1, ALL_SYNC_BITS, portMAX_DELAY);
        Log_Debug("%s Sync OK\r\n", __func__);
        
        vTaskDelay(2000);
    }
}

/* 测试初始化 */
void aTest_Init(void)
{
    /* 创建事件组 */
    p->event = xEventGroupCreate();
    
    /* 创建动态任务 */
    xTaskCreate(EventSyncTask1, "EventSyncTask1", 500, NULL, 5, &p->taskEventSync1);
    xTaskCreate(EventSyncTask2, "EventSyncTask2", 500, NULL, 5, &p->taskEventSync2);
}

/* Key2 PC13   Key0 PH3 Key1 PH2 */
void Exti13_TriggerInterrupt(void)
{
    mspExti_Close(13);
    if (mspGpio_GetInput("PC13") == 0)
    {
    }
}
4.2.2 结果显示
相关推荐
青鱼293 天前
时间片在FreeRTOS中的含义解析
freertos·时间片轮转
济6173 天前
FreeRTOS 控制任务设计 (2)--- 运动学逆解 + PID 闭环 + PWM 驱动全流程实现
stm32·单片机·嵌入式·freertos
济6173 天前
FreeRTOS 控制任务设计 (1)--- 双模式闭环控制:IDLE/RUN 状态机与任务通知机制
stm32·单片机·嵌入式·freertos
W.W.H.7 天前
FreeRTOS移植(保姆级教程)
经验分享·单片机·操作系统·freertos·rtos
波特率11520011 天前
FreeRTOS当中的Mail Queue使用教程(CMSIS_v1)
单片机·操作系统·freertos
济61712 天前
FreeRTOS 通信任务设计(3)---基于状态机的串口协议帧解析
stm32·嵌入式·freertos
济61712 天前
FreeRTOS 通信任务设计(4终)----从字节流到有效帧的完美闭环
stm32·嵌入式·freertos
济61715 天前
FreeRTOS 通信任务设计(2)----UART+DMA 环形缓冲 + 空闲中断+ 流缓冲区--- 高效接收方案详解
stm32·单片机·嵌入式·freertos
济61715 天前
FreeRTOS 通信任务设计(1)---STM32 串口 DMA + 协议帧解析 + CRC 校验全流程详解
stm32·嵌入式·freertos
Wave84517 天前
FreeRTOS软件定时器详解
stm32·单片机·freertos