事件标志组
1. 事件标志组介绍
信号量只能实现任务与单个事件或任务间的同步。但是某些任务可能会需要与多个事件或任务进行同步,此时就可以使用事件标志组来解决。事件标志组能够实现某个任务与多个事件或任务间的同步。
- 事件位:用来表明某个事件是否发生,通常用作事件标志
- 事件组:一组事件位组成一个事件组,事件组中的事件位通过编号来访问
事件标志组的数据类型为 EventGroupHandle_t ,事件标志组中的所有事件位都存储在一个无符号的 EventBits_t 类型的变量中;该变量为16位数据类型时,事件标志组可以存储8个事件位;该变量为32位数据类型时,事件标志组可以存储24个事件位(高8位均有其他用途)
typedef TickType_t EventBits_t;
#if( configUSE_16_BIT_TICKS == 1 )
typedef uint16_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffff
#else
typedef uint32_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
#define portTICK_TYPE_IS_ATOMIC 1
#endif
|
2. 递归互斥信号量的API函数
2.1 创建递归互斥信号量
EventGroupHandle_t xEventGroupCreate(void)
EventGroupHandle_t xEventGroupCreateStatic(StaticEventGroup_t * pxEventGroupBuffer)
返回值:创建成功返回事件标志组句柄;失败返回NULL
|
2.2 设置事件位
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup,
nst EventBits_t uxBitsToClear)
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup,
nst EventBits_t uxBitsToSet)
返回值:将指定事件位清零之前的事件组值;将指定事件位置1后的事件组值
BaseType_t xEventGroupClearBitsFromISR(EventGroupHandle_t xEventGroup,
onst EventBits_t uxBitsToClear)
BaseType_t xEventGroupSetBitsFromISR(EventGroupHandle_t xEventGroup,
onst EventBits_t uxBitsToSet,
aseType_t *pxHigherPriorityTaskWoken)
返回值:清零或置1成功返回pdPASS;清零或置1失败返回pdFALSE
|
指定事件位清零函数xEventGroupClearBits的源码如下示:
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup,
onst EventBits_t uxBitsToClear){
EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
EventBits_t uxReturn;
taskENTER_CRITICAL();
{
uxReturn = pxEventBits->uxEventBits;
pxEventBits->uxEventBits &= ~uxBitsToClear;
}
taskEXIT_CRITICAL();
return uxReturn;
}
|
指定事件位置1函数xEventGroupSetBits的源码如下示:
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup,
nst EventBits_t uxBitsToSet){
ListItem_t *pxListItem, *pxNext;
ListItem_t const *pxListEnd;
List_t *pxList;
EventBits_t uxBitsToClear = 0, uxBitsWaitedFor, uxControlBits;
EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
BaseType_t xMatchFound = pdFALSE;
pxList = &( pxEventBits->xTasksWaitingForBits );
pxListEnd = listGET_END_MARKER( pxList );
vTaskSuspendAll();
{
pxListItem = listGET_HEAD_ENTRY( pxList );
pxEventBits->uxEventBits |= uxBitsToSet;
while(pxListItem != pxListEnd){
pxNext = listGET_NEXT(pxListItem);
uxBitsWaitedFor = listGET_LIST_ITEM_VALUE(pxListItem);
xMatchFound = pdFALSE;
uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES;
uxBitsWaitedFor &= ~eventEVENT_BITS_CONTROL_BYTES;
if((uxControlBits & eventWAIT_FOR_ALL_BITS) == (EventBits_t)0){
if((uxBitsWaitedFor & pxEventBits->uxEventBits) != (EventBits_t)0){
xMatchFound = pdTRUE;
}
else{
mtCOVERAGE_TEST_MARKER();
}
}
else if((uxBitsWaitedFor&pxEventBits->uxEventBits) == uxBitsWaitedFor){
xMatchFound = pdTRUE;
}
else{
}
if( xMatchFound != pdFALSE ){
if((uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT)!=(EventBits_t)0){
uxBitsToClear |= uxBitsWaitedFor;
}
else{
mtCOVERAGE_TEST_MARKER();
}
(void)xTaskRemoveFromUnorderedEventList(pxListItem,pxEventBits->uxEventBits|eventUNBLOCKED_DUE_TO_BIT_SET);
}
pxListItem = pxNext;
}
pxEventBits->uxEventBits &= ~uxBitsToClear;
}
(void) xTaskResumeAll();
return pxEventBits->uxEventBits;
}
|
2.3 获取事件标志组值
EventBits_t xEventGroupGetBits(EventGroupHandle_t xEventGroup)
EventBits_t xEventGroupGetBitsFromISR(EventGroupHandle_t xEventGroup)
返回值:当前事件标志组的值
|
用在任务中获取当前事件标志组的值函数是一个宏定义,如下示:
#define xEventGroupGetBits(xEventGroup) xEventGroupClearBits(xEventGroup, 0)
|
用在中断服务函数获取当前事件标志组的值函数源码如下示:
EventBits_t xEventGroupGetBitsFromISR(EventGroupHandle_t xEventGroup){
UBaseType_t uxSavedInterruptStatus;
EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
EventBits_t uxReturn;
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
uxReturn = pxEventBits->uxEventBits;
}
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
return uxReturn;
}
|
2.4 等待指定的事件位
EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup,
cnst EventBits_t uxBitsToWaitFor,
cnst BaseType_t xClearOnExit,
cnst BaseType_t xWaitForAllBits,
TckType_t xTicksToWait)
参 数:xClearOnExit若为pdTURE,则表示设置的位在函数退出时会被清零;
aitForAllBits若为pdTURE,则表示所有要设置的位都置1或阻塞时间到,函数才会返回
为pdFALSE,则表示只要要设置的某一位置1或阻塞时间到,函数就会返回
返回值:返回所等待的事件位置1后的事件标志组的值,或返回阻塞时间到
|
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait){
EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
EventBits_t uxReturn, uxControlBits = 0;
BaseType_t xWaitConditionMet, xAlreadyYielded;
BaseType_t xTimeoutOccurred = pdFALSE;
vTaskSuspendAll();
{
const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits;
xWaitConditionMet = prvTestWaitCondition(uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits);
if( xWaitConditionMet != pdFALSE ){
uxReturn = uxCurrentEventBits;
xTicksToWait = ( TickType_t ) 0;
if( xClearOnExit != pdFALSE ){
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
}
else{
mtCOVERAGE_TEST_MARKER();
}
}
else if(xTicksToWait == (TickType_t) 0){
uxReturn = uxCurrentEventBits;
}
else{
if( xClearOnExit != pdFALSE ){
uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT;
}
else{
mtCOVERAGE_TEST_MARKER();
}
if( xWaitForAllBits != pdFALSE ){
uxControlBits |= eventWAIT_FOR_ALL_BITS;
}
else{
mtCOVERAGE_TEST_MARKER();
}
vTaskPlaceOnUnorderedEventList(&( pxEventBits->xTasksWaitingForBits), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait );
uxReturn = 0;
traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor );
}
}
xAlreadyYielded = xTaskResumeAll();
if(xTicksToWait != (TickType_t ) 0){
if( xAlreadyYielded == pdFALSE ){
portYIELD_WITHIN_API();
}
else{
mtCOVERAGE_TEST_MARKER();
}
uxReturn = uxTaskResetEventItemValue();
if((uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET) == (EventBits_t)0){
taskENTER_CRITICAL();
{
uxReturn = pxEventBits->uxEventBits;
if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE ){
if( xClearOnExit != pdFALSE ){
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
}
else{
mtCOVERAGE_TEST_MARKER();
}
}
else{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL();
xTimeoutOccurred = pdFALSE;
}
else{
}
uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
}
traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred );
return uxReturn;
}
|
3. 事件标志组应用实例
本实例介绍 FreeRTOS 事件标志组的创建、将相应的事件位置1、等待相应事件位置1等函数的操作与使用
使用STM32CubeMX将FreeRTOS移植到工程中,创建三个任务、一个事件标志组、开启一个按键中断
- EventSetBit_Task:读取按键值,根据不同的键值将相应的事件位置1
- EventGroup_Task:等待事件标志组的多个事件位都置1后执行相应处理
- EventQuery_Task:查询事件组的值,即各个事件位的值
3.1 STM32CubeMX设置
-
RCC设置外接HSE,时钟设置为72M
-
PA0设置为GPIO外部中断、下拉模式、并开启中断;PE2/PE3/PE4设置为GPIO输入模式、上拉模式
-
USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位;
-
激活FreeRTOS,添加任务,设置任务名称、优先级、堆栈大小、函数名称等参数
- 使能xEventGroupSetBitsFromISR()函数
- 使用FreeRTOS操作系统,需要将HAL库的Timebase Source从SysTick改为其他定时器,选好定时器后,系统会自动配置TIM
- 输入工程名,选择路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h' files per IP ;点击GENERATE CODE,生成工程代码
3.2 MDK-ARM软件编程
- 创建按键驱动文件key.c和key.h,参考 按键输入 例程
- 在freertos.c文件中,添加一个事件标志组(STM32CubeMX中不提供添加方法),定义事件位;并在freertos初始化函数MX_FREERTOS_Init()中创建事件标志组
EventGroupHandle_t EventGroupHandler;
#define EVENTBIT_0 (1<<0)
#define EVENTBIT_1 (1<<1)
#define EVENTBIT_2 (1<<2)
|
EventGroupHandler = xEventGroupCreate();
if(EventGroupHandler == NULL)
printf("EventGroup Create Failed!\r\n");
|
- 添加EventSetBitTask、EventGroupTask和EventQueryTask任务函数代码
void EventSetBitTask(void const * argument){
uint8_t key;
for(;;){
if(EventGroupHandler != NULL){
key = KEY_Scan(0);
switch(key){
case KEY_LEFT_PRES :
xEventGroupSetBits(EventGroupHandler,EVENTBIT_1);
printf("EVENTBIT_1 Set Successed!\r\n");
break;
case KEY_RIGHT_PRES :
xEventGroupSetBits(EventGroupHandler,EVENTBIT_2);
printf("EVENTBIT_2 Set Successed!\r\n");
break;
}
}
osDelay(10);
}
}
void EventGroup_Task(void const * argument){
EventBits_t EventValue;
for(;;){
if(EventGroupHandler != NULL){
EventValue = xEventGroupWaitBits(EventGroupHandler,
EVENTBIT_0|EVENTBIT_1|EVENTBIT_2,
pdTRUE,
pdTRUE,
portMAX_DELAY);
printf("WaitBits successed!Value of EventGroup is %d\r\n",EventValue);
}
else{
osDelay(10);
}
}
}
void EventQuery_Task(void const * argument){
EventBits_t NewValue, LastValue;
for(;;){
if(EventGroupHandler != NULL){
NewValue = xEventGroupGetBits(EventGroupHandler);
if(NewValue != LastValue){
LastValue = NewValue;
printf("The Query Value of EventGroup is %d\r\n",LastValue);
}
}
osDelay(50);
}
}
|
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
BaseType_t ret;
HAL_Delay(10);
if(GPIO_Pin == GPIO_PIN_0){
ret = xEventGroupSetBitsFromISR(EventGroupHandler,EVENTBIT_0,NULL);
if(ret != pdFAIL)
printf("EVENTBIT_0 Set from ISR successed!\r\n");
}
}
|
3.3 下载验证
编译无误下载到开发板后,打开串口调试助手,依次按KEY_UP、KEY_LEFT和KEY_RIGHT按键,串口输出如下调试信息:
- 按下KEY_UP,在中断中将EVENTBIT_0位置1
- 按下KEY_LEFT和KEY_RIGHT,EVENTBIT_1和EVENTBIT_2位置1
- 三个位都置1后,xEventGroupWaitBits将各位清除
|