文章目录
- 事件
- 函数解析
- 示例
事件
事件,实际上是一种任务间通信的机制,主要用于实现多任务间的同步,其只能是事件类型的通信,无数据传输。与信号量不同的是,它可以实现一对多,多对多的同步。即可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒任务进行事件处理;同样,也可以是多个任务同步多个事件。
FreeRTOS中任务可以通过设置事件位来实现事件的触发和等待操作。但FreeRTOS的事件仅用于同步,不提供数据传输功能,其具有如下特点:
- 事件只与任务相关联,事件相互独立,一个32位的事件集合(EventBits_t类型的变量,实际可用与表示事件的只有24位),用于标识该任务发生的事件类型,其中每一位表示一种事件类型(0表示该事件类型未发生、1表示该事件类型已经发生),一共24种事件类型;
- 事件仅用于同步,不提供数据传输功能;
- 事件无排队性,即多次向任务设置同一事件(如果任务还未来得及读走),等效于只设置一次;
- 允许多任务对同一事件进行读写操作;
- 支持事件等待超时机制。
应用
事件在一定程度上可以代替信号量,用于任务与任务间、中断与任务间同步。
与信号量不同之处在于,事件是不可累计的,而信号量是可累计。并且,一个任务使用事件同步时,可以等待多个事件产生才进行同步;而信号量是单一的同步操作,只能与一个中断或任务同步,无法与多个中断或任务同步。
实际使用事件相关函数前,需要将configSUPPORT DYNAMIC ALLOCATION
置1。
函数解析
前奏
- 定义事件控制权柄所用的
EventGroupHandle_t
其实是一个结构体指针,其声明为typedef struct EventGroupDef_t * EventGroupHandle_t
。 - 事件控制块
EventGroup_t
,内部最多含有四个变量,其中两个变量uxEventBits
与xTasksWaitingForBits
是一直可使用,但变量uxEventGroupNumber
与ucStaticallyAllocated
收到FreeRTOS配置管理
typedef struct EventGroupDef_t
{
EventBits_t uxEventBits;
List_t xTasksWaitingForBits;
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxEventGroupNumber;
#endif
#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) \
&& ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated;
#endif
} EventGroup_t;
EventBits_t
:事件标志组存储在EventBits_t
类型的变量中。
若FreeRTOS配置时,将configUSE_16_BIT_TICKS
定义为1,那么EventBits_t
就为16位,其中只有8位存储时间组;否则,若configUSE_16_BIT_TICKS
定义为0,那么EventBits_t
就为32位,其中24位存储事件组。每一位代表一个事件是否发生(事件发生为1,否则为0)。倘若兄弟认为8位或24位事件组不够用,还可以使用与或非等逻辑运算来组成更多事件。🤣🤣🤣
xTasksWaitingForBits
:该变量是一个列表,内部存储等待此事件的所有任务。
uxEventGroupNumber
:记录事件组数量。
ucStaticallyAllocated
:记录时间组分配方式,如果事件组是静态分配的,则设置为 pdTRUE,以确保不尝试释放内存。
动态创建事件
原函数
EventGroupHandle_t xEventGroupCreate( void );
参数解析
- 返回值:类型为
EventGroupHandle_t
,其为事件的控制权柄。
函数说明
动态创建一个事件。
静态创建事件
原函数
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer );
参数解析
- StaticEventGroup_t * pxEventGroupBuffer:为事件组分配的空间;
- 返回值:类型为
EventGroupHandle_t
,其为事件的控制权柄
函数说明
静态创建事件组,创建时需要传入存储空间。
创建事件
原函数
void vEventGroupDelete( EventGroupHandle_t xEventGroup );
参数解析
- EventGroupHandle_t xEventGroup:传入事件组控制权柄;
函数说明
&esnp;删除事件组,只要将事件组控制权柄传入即可。删除只能删除已经创建成功的事件组。
事件触发
原函数
EventBits_t xEventGroupSetBits(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet
);
参数解析
- EventGroupHandle_t xEventGroup:事件组的控制权柄;
- const EventBits_t uxBitsToSet:需要触发事件的,也就是其在事件组的位置;
- 返回值:返回函数操作的事件组的值;
函数说明
触发事件组中某个事件(事件对应事件组的位置),其位置由uxBitsToSet
的值决定。
该函数内部有判断机制,即的判断该事件是否创建,只有事件组创建成功以及操作位有效,该函数才能正常运作。
中断中唤醒事件
原函数
BaseType_t xEventGroupSetBitsFromISR(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
BaseType_t * pxHigherPriorityTaskWoken
);
参数解析
- EventGroupHandle_t xEventGroup:事件组的操作句柄;
- const EventBits_t uxBitsToSet:需要触发事件的,也就是其在事件组的位置;
- BaseType_t * pxHigherPriorityTaskWoken:在使用之前必须初始化成pdFALSE;
- 返回值:类型为BaseType_t。若其值为pdFalse,则消息已经发送成功;否则,发送失败。
函数说明
是xEventGroupSetBits
函数的中断版,其功能是中断中唤醒某个事件。
等待事件唤醒
原函数
EventBits_t xEventGroupWaitBits(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait
);
参数解析
- EventGroupHandle_t xEventGroup:事件的操作句柄;
- const EventBits_t uxBitsToWaitFor:等待的事件,这里可以是多个事件,使用或运算即可连接;
- const BaseType_t xClearOnExit:当其值为pdTRUE时,一旦监测到匹配的事件,系统就会将删除由该事件引起的标志位;否则,当其值为pdFALSE时,将删除标志位;
- const BaseType_t xWaitForAllBits:当其值为pdTURE时,相应的
uxBitsToWaitFor
中多个事件应该使用&
连接;否则当其值为pdFALSE时,uxBitsToWaitFor
中多个事件应该使用|
连接。这样函数返回值才是对应标志位的值,事件才能够触发。- TickType_t xTicksToWait:最大超时时间,单位为系统节拍时间,使用
portMAX_DELAY
即相当于一直阻塞;- 返回值:类型为EventBits_t,返回事件中事件标志位,但是返回值很可能并不是用户指定的事件位,需要再进行判断再处理。
函数说明
xEventGroupWaitBits
函数的作用是等待事件触发。若需要等待的事件一直未触发,函数就会阻塞,阻塞时间为最大超时时间xTicksToWait
。
清除事件标志位
原函数
EventBits_t xEventGroupClearBits(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear
);
参数解析
- EventGroupHandle_t xEventGroup:事件组操作句柄;
- const EventBits_t uxBitsToClear:时间组中需要清除的位置;
- 返回值:类型EventBits_t,返回事件清除前的值;
函数说明
清除事件组中对应位置的标志位。在获取事件时对应标志位未擦除,那么可以使用该函数做显示擦除。
中断中清除事件标志位
原函数
BaseType_t xEventGroupClearBitsFromISR(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet
);
参数解析
- EventGroupHandle_t xEventGroup:事件组操作句柄;
- const EventBits_t uxBitsToClear:时间组中需要清除的位置;
- 返回值:类型EventBits_t,返回事件清除前的值;
函数说明
是函数xEventGroupClearBits
的中断版,功能上与之一样。
示例
声明8个事件,并且创建一个FreeRTOS事件,用于任务同步,再创建两个任务。完成:任务1,等待事件触发,7个事件中触发一个事件后反转对应LED的状态,当事件8触发时,统计其触发次数并且显示到LCD屏上; 任务2,每个1s发送一个事件触发请求,并且反转LED8的状态。
核心源码
//任务控制权柄
TaskHandle_t xHandleTsak[2];
// 事件控制权柄
EventGroupHandle_t myxEventGroupHandle_t = NULL;
// 声明事件
#define EVENT1 (0x01 << 0)
#define EVENT2 (0x01 << 1)
#define EVENT3 (0x01 << 2)
#define EVENT4 (0x01 << 3)
#define EVENT5 (0x01 << 4)
#define EVENT6 (0x01 << 5)
#define EVENT7 (0x01 << 6)
#define EVENT8 (0x01 << 7)
int main(void)
{
//存储创建任务的返回值
BaseType_t xReturn[2] ;
// 创建事件
myxEventGroupHandle_t = xEventGroupCreate();
if(myxEventGroupHandle_t == 0)
//点亮LED7
changeLedStateByLocation(LED8,ON);
//动态创建任务
xReturn[0] = xTaskCreate(
(TaskFunction_t )eventTask1,(const char *)"task1",
(uint16_t)128,(void*) NULL,1,&xHandleTsak[0]);
xReturn[1] = xTaskCreate(
(TaskFunction_t )eventTask2,(const char *)"task2",
(uint16_t)128,(void*) NULL,1,&xHandleTsak[1]);
if (pdPASS != xReturn[0])
//点亮LED6
changeLedStateByLocation(LED6,ON);
if (pdPASS != xReturn[1])
//点亮LED7
changeLedStateByLocation(LED7,ON);
if (pdPASS == xReturn[1] == xReturn[0])
vTaskStartScheduler();
return 0;
}
/********************************************
* 函数功能:事件测试函数1
* 函数参数:无
* 函数返回值:无
********************************************/
void eventTask1(void)
{
// 设置变量接收事件
EventBits_t r_event;
// 保存LED的状态
int ledState[] = {0,0,0,0,0,0,0};
// 记录LED的位置
uint16_t ledLocation[] = {LED1,LED2,LED3,LED4,LED5,LED6,LED7};
// 记录EVENT8触发次数
uint16_t eventTriggerCount = 0;
// 保存显示到LCD中的数据
char temp[30];
// 用于循环
int i = 0;
while(1)
{
//阻塞等待8个事件中任意一个事件
r_event = xEventGroupWaitBits(myxEventGroupHandle_t,EVENT1|EVENT2|EVENT3|EVENT4|EVENT5|EVENT6|EVENT7|EVENT8,
pdTRUE,pdFALSE,portMAX_DELAY);
//判断事件类型
if((r_event&EVENT1) !=0)
++ledState[0];
else if((r_event&EVENT2) !=0)
++ledState[1];
else if((r_event&EVENT3) !=0)
++ledState[2];
else if((r_event&EVENT4) !=0)
++ledState[3];
else if((r_event&EVENT5) !=0)
++ledState[4];
else if((r_event&EVENT6) !=0)
++ledState[5];
else if((r_event&EVENT7) !=0)
++ledState[6];
else
{
sprintf(temp," Event8 count:%d",++eventTriggerCount);
LCD_DisplayStringLine(Line4,(uint8_t*)temp);
changeAllLedByStateNumber(OFF);
}
// 设置每个LED灯状态
for(i=0;i<7;++i)
// 显示7个LED灯目前的状态
changeLedStateByLocation(ledLocation[i],ledState[i]%2);
}
}
/********************************************
* 函数功能:事件测试函数2
* 函数参数:无
* 函数返回值:无
********************************************/
void eventTask2(void)
{
// 记录本次需要触发事件
uint16_t event[] = {EVENT1,EVENT2,EVENT3,EVENT4,EVENT5,EVENT6,EVENT7,EVENT8};
int i = -1;
// 保存系统时间
static portTickType myPreviousWakeTime;
// 保存阻塞时间
const volatile TickType_t xDelay1000ms = pdMS_TO_TICKS( 1000UL );
// 获取当前时间
myPreviousWakeTime = xTaskGetTickCount();
while(1)
{
// 每次执行都翻转LED8状态 以保证肉眼看到该任务在运行
rollbackLedByLocation(LED8);
// 发送事件同步
xEventGroupSetBits(myxEventGroupHandle_t,event[++i%8]);
// 非阻塞延时1s
xTaskDelayUntil( &myPreviousWakeTime,xDelay1000ms );
}
}
结果
事件测试结果图
小编也有其他的一些相关文章,欢迎各位看官点击观看😉😉😉
- 【FreeRTOS】详细讲解FreeRTOS中任务管理并通过示例讲述其用法
- 【FreeRTOS】详细讲解FreeRTOS的软件定时器及通过示例讲述其用法
- 【FreeRTOS】详细讲解FreeRTOS中消息队列并通过示例讲述其用法
最后 ,欢迎大家留言或私信交流,共同进步!😁😁😁