FreeRTOS study notes (6)-events

One, the header file

#include "FreeRTOS.h"
#include "event_groups.h"

2. Event

2.1 Basic concepts

Event is a mechanism for realizing communication between tasks. It is mainly used to realize synchronization between multiple tasks, but event communication can only be event-type communication without data transmission. Unlike semaphore, it can achieve one-to-many and many-to-many synchronization. That is, a task can wait for the occurrence of multiple events: it can wake up the task for event processing when any event occurs; it can also wake up the task for event processing after several events have occurred. Similarly, multiple tasks can synchronize multiple events.

Each event group requires only a small amount of RAM space to save the state of the event group. The event group is stored in a variable of type EventBits_t, which is defined in the event group structure. In STM32, we generally define configUSE_16_BIT_TICKS as 0, then uxEventBits is 32 bits, and 24 bits are used to implement event flag groups. Each bit represents an event, and the task is associated with one or more events through "logical AND" or "logical OR" to form an event group. The "logical OR" of events is also called independent synchronization, which means that all events that the task is interested in can be awakened when any one occurs; the "logical AND" of events is called associative synchronization, which refers to The task is awakened only when several events that are of interest to the task occur, and the time of the event can be asynchronous.

In a multitasking environment, tasks and interrupts often need to be synchronized. When an event occurs, the waiting tasks will be notified, that is, a synchronization between a task and a task, an interrupt and a task is formed. Events can provide one-to-many and many-to-many synchronization operations. One-to-many synchronization model: a task waits for the trigger of multiple events, which is more common; many-to-many synchronization model: multiple tasks wait for the trigger of multiple events.
Tasks can trigger and wait for events by setting event bits. FreeRTOS events are only used for synchronization and do not provide data transmission functions.

The events provided by FreeRTOS have the following characteristics:

  • Events are only associated with tasks, and events are independent of each other. A 32-bit event set (EventBits_t type variable, only 24 bits can actually be used to represent the event), used to identify the type of event that occurred in the task, and each bit represents one There are 24 event types (0 means that the event type has not occurred, 1 means that the event type has occurred), a total of 24 event types.
  • The event is only used for synchronization and does not provide data transmission function.
  • Events are non-queuing, that is, setting the same event to the task multiple times (if the task can't be read in the future) is equivalent to setting it only once.
  • Allow multiple tasks to read and write to the same event.
  • Support event waiting timeout mechanism.

In FreeRTOS events, when each event is acquired, the user can select the event of interest and choose to read the event information mark. It has three attributes, namely logical AND, logical OR and whether to clear the mark. When the task is waiting for the event to be synchronized, it can be judged whether the currently received event meets the requirements through the event bit and event information mark that the task is interested in. If it meets the requirements, it means that the task is waiting for the corresponding event, and the system will wake up the waiting task; otherwise, the task Will continue to wait according to the blocking timeout specified by the user.

2.2 Operation mechanism


When receiving an event, you can receive a single or multiple event types of the event according to the parameter event type of interest. After the event is successfully received, the xClearOnExit option must be used to clear the received event type, otherwise the received event will not be cleared, so the user needs to explicitly clear the event bit. Users can customize the read mode by passing in the parameter xWaitForAllBits, whether to wait for all events of interest or to wait for any event of interest.

When setting an event, write the specified event type to the specified event, and set the corresponding event bit of the event set to 1. You can write multiple event types at one time. The success of setting the event may trigger task scheduling. When clearing an event, according to the input parameter event handle and the type of event to be cleared, clear the corresponding bit of the event to 0. Events are not associated with tasks. Events are independent of each other. A 32-bit variable (event collection, only 24 bits actually used to represent events) is used to identify the type of event that occurs in the task, and each bit represents an event type. (0 means that the event type has not occurred, 1 means that the event type has occurred), a total of 24 event types.

Event wake-up mechanism, when a task enters a blocking state waiting for one or more events to occur, it will be awakened when an event occurs.

Task 1 is interested in event 3 or event 5 (logical OR). When one of these events occurs, it will be awakened and perform corresponding operations. Task 2 is interested in event 3 and event 5 (logical AND). Task 2 will be awakened only when both event 3 and event 5 occur. If only one of the events occurs, then the task will continue. Wait for the event to happen. If the clear event bit xClearOnExit is set in the receive event function, then the event flags of event 3 and event 5 will be cleared after the task wakes up, otherwise the event flag will still exist.

3. Related API description

3.1 xEventGroupCreate

Used to create an event group and return the corresponding handle.

function EventGroupHandle_t xEventGroupCreate( void )
parameter no
return value Event handler

To use this function must be FreeRTOSConfig.hin the configSUPPORT_DYNAMIC_ALLOCATIONdefined to be enabled.

At the same time to FreeRTOS/source/event_groups.cadd the C file to the project.

3.2 vEventGroupDelete

When the system no longer uses the event object, you can release the system resources by deleting the event object control block.

function void vEventGroupDelete( EventGroupHandle_t xEventGroup )
parameter Event handler
return value no

3.3 xEventGroupSetBits (task)

Used to set the specified bit in the event group. When the bit is set, the task blocked in that bit will be unlocked. When using this function interface, the event flag is set by the event flag specified by the parameter, and then the event waiting list waiting on the event object is traversed to determine whether the event activation requirement of the task matches the current event object flag value, if so , Then wake up the task. Simply put, it is to set the event flag bit of our own definition to 1, and see if there is a task waiting for this event, and wake it up if there is. Note that this function is not allowed to be used in interrupts.

function EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )
parameter xEventGroup: Event handle
uxBitsToSet: Specify the event flag bit in the event. If uxBitsToSet is set to 0x08, only bit 3 is set. If uxBitsToSet is set to 0x09, both bit 3 and bit 0 need to be set.
return value Returns the value in the event group when xEventGroupSetBits() is called

3.4 xEventGroupSetBitsFromISR (Interrupt)

xEventGroupSetBitsFromISR() is the interrupt version of xEventGroupSetBits(), used to set the specified bits in the event group. Setting the flag bit in the event group is an indeterminate operation, because the number of tasks blocked on the flag bit of the event group is uncertain. FreeRTOS does not allow uncertain operations to occur in interrupts and critical sections, so xEventGroupSetBitsFromISR() sends a message to the guardian task of FreeRTOS, so that the operation of setting the event group is completed in the guardian task, which is based on the scheduling lock The mechanism of non-critical section is realized. Points to note: As mentioned above, the setting of the event flag in the interrupt is done in the guard task (also called the software timer service task). Therefore, the guardian task of FreeRTOS is the same as other tasks. The system scheduler performs task scheduling according to its priority, but the priority of the guardian task must be higher than the priority of any task to ensure that the task can be switched immediately when needed. So as to achieve the purpose of fast processing, because this is to set the event flag in the interrupt, and its priority is defined by the macro configTIMER_TASK_PRIORITY in FreeRTOSConfig.h.

function BaseType_t xEventGroupSetBitsFromISR (EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t * pxHigherPriorityTaskWoken)
parameter xEventGroup: Event handle
uxBitsToSet: Specify the event flag bit in the event. If uxBitsToSet is set to 0x08, only bit 3 is set. If uxBitsToSet is set to 0x09, both bit 3 and bit 0 need to be set.
pxHigherPriorityTaskWoken: pxHigherPriorityTaskWoken must be initialized to pdFALSE before use. Calling xEventGroupSetBitsFromISR() will send a message to the guard task, if the priority of the guard task is higher than the priority of the currently interrupted task (in general, the priority of the guard task needs to be set to the highest priority of all tasks) , PxHigherPriorityTaskWoken will be set to pdTRUE, and then perform a context switch before the interrupt exit
return value After the message is successfully sent to the guard task, it returns pdTRUE, otherwise it returns pdFAIL. If the timer service queue is full, pdFAIL will be returned

To use this function must be FreeRTOSConfig.hin the configUSE_TIMERSand INCLUDE_xTimerPendFunctionCallto 1 to enable the definition.

At the same time to FreeRTOS/source/event_groups.cadd the C file to the project.

3.5 xEventGroupWaitBits

Used to obtain one or more event occurrence flags in the event group. When the event flag bit to be read is not set, the task will enter the blocking waiting state.

function EventBits_t xEventGroupWaitBits(const EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait )
parameter xEventGroup: event handle
uxBitsToWaitFor: a bitwise or value that specifies which positions in the event group need to be waited for 1. If you need to wait for bit 0 and/or bit 2, then uxBitsToWaitFor is configured as 0x05 (0101b). If you need to wait for bits 0 and/or bit 1 and/or bit 2, then uxBitsToWaitFor is configured as 0x07(0111b)
xClearOnExit: pdTRUE: When xEventGroupWaitBits() waits until the task wake-up event is met, the system will clear the event specified by the formal parameter uxBitsToWaitFor Flag bit. pdFALSE: The event flag bit specified by the formal parameter uxBitsToWaitFor will not be cleared.
xWaitForAllBits: pdTRUE: When the bits specified by the formal parameter uxBitsToWaitFor are all set, xEventGroupWaitBits() satisfies the task wake-up condition, which is also a "logical AND" waiting event, and returns the corresponding event flag bit without timeout value. pdFALSE: When any one of the bits specified by the formal parameter uxBitsToWaitFor is set, this is also commonly referred to as a "logical OR" waiting event. The function returns the value of the corresponding event flag bit if there is no timeout.
xTicksToWait: the maximum timeout time, the unit is the system tick period, the constant portTICK_PERIOD_MS is used to assist in converting the time into MS
return value Which event flag bits in the return event are set, the return value is probably not the event bit specified by the user, and the return value needs to be judged and processed

3.6 xEventGroupClearBits (task)

用于清除事件组指定的位,如果在获取事件的时候没有将对应的标志位清除,那么就需要用这个函数来进行显式清除,xEventGroupClearBits() 函数不能在中断中使用。

函数 EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
参数 xEventGroup: 事件句柄
uxBitsToClear: 指定事件组中的哪个位需要清除。如设置 uxBitsToSet 为 0x08 则只清除位 3,如果设置 uxBitsToSet 为 0x09 则位 3 和位 0 都需要被清除
返回值 事件在还没有清除指定位之前的值

3.7 xEventGroupClearBitsFromISR(中断)

用于清除事件组指定的位,如果在获取事件的时候没有将对应的标志位清除,那么就需要用这个函数来进行显式清除。中断清除事件标志位的操作在守护任务(也叫定时器服务任务)里面完成。守护进程的优先级 FreeRTOSConfig.h 中的宏 configTIMER_TASK_PRIORITY 来定义 。

函数 BaseType_t xEventGroupClearBitsFromISR(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
参数 xEventGroup: 事件句柄
uxBitsToClear: 指定事件组中的哪个位需要清除。如设置 uxBitsToSet 为 0x08 则只清除位 3,如果设置 uxBitsToSet 为 0x09 则位 3 和位 0 都需要被清除
返回值 事件在还没有清除指定位之前的值

四、示例

4.1 任务式

/* FreeRTOS 头文件 */
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"
/* 开发板硬件 bsp 头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_key.h"

/**************************** 任务句柄 ********************************/
/*
* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
* 这个句柄可以为 NULL。
*/
static TaskHandle_t AppTaskCreate_Handle = NULL;/* 创建任务句柄 */
static TaskHandle_t LED_Task_Handle = NULL;/* LED_Task 任务句柄 */
static TaskHandle_t KEY_Task_Handle = NULL;/* KEY_Task 任务句柄 */
/**************************** 内核对象句柄 ****************************/
/*
* 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
* 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
* 们就可以通过这个句柄操作这些内核对象。
*
* 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
* 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
* 来完成的
*
*/
static EventGroupHandle_t Event_Handle = NULL;

/************************** 宏定义 *********************************/
/*
* 当我们在写应用程序的时候,可能需要用到一些宏定义。
*/
#define KEY1_EVENT (0x01 << 0)//设置事件掩码的位 0 
#define KEY2_EVENT (0x01 << 1)//设置事件掩码的位 1

/*
*************************************************************************
* 函数声明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用于创建任务 */
 
static void LED_Task(void* pvParameters);/* LED_Task 任务实现 */
static void KEY_Task(void* pvParameters);/* KEY_Task 任务实现 */

static void BSP_Init(void);/* 用于初始化板载相关资源 */

int main(void)
{
    
    
    BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为 pdPASS */

    /* 开发板硬件初始化 */
    BSP_Init();

    /* 创建 AppTaskCreate 任务 */
    xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate,/* 任务入口函数 */
                            (const char* )"AppTaskCreate",/* 任务名字 */
                            (uint16_t )512, /* 任务栈大小 */
                            (void* )NULL,/* 任务入口函数参数 */
                            (UBaseType_t )1, /* 任务的优先级 */
                            (TaskHandle_t* )&AppTaskCreate_Handle);/*任务控制块指针 */
    /* 启动任务调度 */
    if (pdPASS == xReturn)
    {
    
    
        vTaskStartScheduler(); /* 启动任务,开启调度 */
    }
    else
    {
    
    
        return -1;
    }
  
    while (1); /* 正常不会执行到这里 */
}

/***********************************************************************
* @ 函数名 : AppTaskCreate
* @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
* @ 参数 : 无
* @ 返回值 : 无
**********************************************************************/
static void AppTaskCreate(void)
{
    
    
    BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为 pdPASS */
   
    taskENTER_CRITICAL(); //进入临界区
  
    /* 创建 Event_Handle */ 
    Event_Handle = xEventGroupCreate(); 
    if (NULL != Event_Handle) 
    {
    
    
        printf("Event_Handle 事件创建成功!\r\n"); 
    }
  
    /* 创建 LED_Task 任务 */
    xReturn = xTaskCreate((TaskFunction_t )LED_Task, /* 任务入口函数 */
                            (const char* )"LED_Task",/* 任务名字 */
                            (uint16_t )512, /* 任务栈大小 */
                            (void* )NULL, /* 任务入口函数参数 */
                            (UBaseType_t )2, /* 任务的优先级 */
                            (TaskHandle_t* )&LED_Task_Handle);/* 任务控制块指针 */
    if (pdPASS == xReturn)
    {
    
    
        printf("创建 LED_Task 任务成功!\r\n");
    }
    /* 创建 KEY_Task 任务 */
    xReturn = xTaskCreate((TaskFunction_t )KEY_Task, /* 任务入口函数 */
                            (const char* )"KEY_Task",/* 任务名字 */
                            (uint16_t )512, /* 任务栈大小 */
                            (void* )NULL,/* 任务入口函数参数 */
                            (UBaseType_t )3, /* 任务的优先级 */
                            (TaskHandle_t* )&KEY_Task_Handle);/* 任务控制块指针 */
    if (pdPASS == xReturn)
    {
    
    
        printf("创建 KEY_Task 任务成功!\n");\
    }
  
    vTaskDelete(AppTaskCreate_Handle); //删除 AppTaskCreate 任务
  
    taskEXIT_CRITICAL(); //退出临界区
}

/**********************************************************************
* @ 函数名 : LED_Task
* @ 功能说明: LED_Task 任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void LED_Task(void* parameter) 
{
    
     
    EventBits_t r_event; /* 定义一个事件接收变量 */ 
    /* 任务都是一个无限循环,不能返回 */ 
    while (1) 
    {
    
     
        /************************************************************* 
        * 等待接收事件标志 
        * 
        * 如果 xClearOnExit 设置为 pdTRUE,那么在 xEventGroupWaitBits()返回之前, 
        * 如果满足等待条件(如果函数返回的原因不是超时),那么在事件组中设置 
        * 的 uxBitsToWaitFor 中的任何位都将被清除。 
        * 如果 xClearOnExit 设置为 pdFALSE, 
        * 则在调用 xEventGroupWaitBits()时,不会更改事件组中设置的位。 
        *
        * xWaitForAllBits 如果 xWaitForAllBits 设置为 pdTRUE,则当 uxBitsToWaitFor 中 
        * 的所有位都设置或指定的块时间到期时,xEventGroupWaitBits()才返回。 
        * 如果 xWaitForAllBits 设置为 pdFALSE,则当设置 uxBitsToWaitFor 中设置的任何 
        * 一个位置 1 或指定的块时间到期时,xEventGroupWaitBits()都会返回。 
        * 阻塞时间由 xTicksToWait 参数指定。 
        *********************************************************/ 
        r_event = xEventGroupWaitBits(Event_Handle, /* 事件对象句柄 */ 
                              KEY1_EVENT|KEY2_EVENT,/* 接收任务感兴趣的事件 */ 
                              pdTRUE, /* 退出时清除事件位 */ 
                              pdTRUE, /* 满足感兴趣的所有事件 */ 
                              portMAX_DELAY);/* 指定超时事件,一直等 */ 
  
        if ((r_event & (KEY1_EVENT|KEY2_EVENT)) == (KEY1_EVENT|KEY2_EVENT)) 
        {
    
     
            /* 如果接收完成并且正确 */ 
            printf ( "KEY1 与 KEY2 都按下\n"); 
            LED1_TOGGLE; //LED1 反转 
        } 
        else 
        {
    
    
            printf ( "事件错误!\n"); 
        }
    } 
}

/**********************************************************************
* @ 函数名 : KEY_Task
* @ 功能说明: KEY_Task 任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void KEY_Task(void* parameter) 
{
    
     
    /* 任务都是一个无限循环,不能返回 */ 
    while (1) 
    {
    
    
        //如果 KEY2 被按下 
        if ( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON ) 
        {
    
     
            printf ( "KEY1 被按下\n" ); 
            /* 触发一个事件 1 */ 
            xEventGroupSetBits(Event_Handle,KEY1_EVENT); 
        } 
        //如果 KEY2 被按下 
        if ( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON ) 
        {
    
     
            printf ( "KEY2 被按下\n" ); 
            /* 触发一个事件 2 */ 
            xEventGroupSetBits(Event_Handle,KEY2_EVENT); 
        } 
        vTaskDelay(20); //每 20ms 扫描一次 
    } 
}

/***********************************************************************
* @ 函数名 : BSP_Init
* @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
* @ 参数 :
* @ 返回值 : 无
*********************************************************************/
static void BSP_Init(void)
{
    
    
    /*
    * STM32 中断优先级分组为 4,即 4bit 都用来表示抢占优先级,范围为:0~15
    * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
    * 都统一用这个优先级分组,千万不要再分组,切忌。
    */
    NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
  
    /* LED 初始化 */
    LED_GPIO_Config();
  
    /* 串口初始化 */
    USART_Config();
  
    /* 按键初始化 */
    Key_GPIO_Config();
}

4.2 中断式

#define BIT_0 ( 1 << 0 )
#define BIT_4 ( 1 << 4 )

/* 假定事件组已经被创建 */
EventGroupHandle_t xEventGroup;
 
/* 中断 ISR */
void anInterruptHandler( void )
{
    
    
    BaseType_t xHigherPriorityTaskWoken, xResult;
   
    /* xHigherPriorityTaskWoken 在使用之前必须先初始化为 pdFALSE */ 
    xHigherPriorityTaskWoken = pdFALSE; 
  
    /* 置位事件组 xEventGroup 的的 Bit0 和 Bit4 */ 
    xResult = xEventGroupSetBitsFromISR(xEventGroup, BIT_0 | BIT_4, &xHigherPriorityTaskWoken ); 
  
    /* 信息是否发送成功 */
    if ( xResult != pdFAIL ) 
    {
    
    
        /* 如果 xHigherPriorityTaskWoken 的值为 pdTRUE 则进行一次上下文切换*/
        portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); 
    }
}

• 由 Leung 写于 2020 年 11 月 24 日

• 参考:野火FreeRTOS视频与PDF教程

Guess you like

Origin blog.csdn.net/qq_36347513/article/details/110092407