1、事件组的介绍
事件组可以简单地认为就是一个整数:
每一位表示一个事件 每一位事件的含义由程序员决定,
比如:Bit0表示用来串口是否就绪,Bit1表示按键是否被按下 这些位,值为1表示事件发生了,值为0表示事件没发生 一个或多个任务、ISR都可以去写这些位;
一个或多个任务、ISR都可以去读这些位 可以等待某一位、某些位中的任意一个,也可以等待多位。
需要注意的是,事件组并不传递数据,数据的传递需要另想办法,比如使用队列进行数据的传递,事件的等待,不能只等待一位或者几位,需要在等待函数里面进行配置。但是每次等待所有事件还是一个事件是指定的,不能只的等待某个事件。使用事件的等待函数的时候会体会到这一点。
关于事件的一些使用函数,这里就不在一一说明,看东山老师的FreeRTOS完全手册就可以了。
2、事件组的使用
首先介绍几个事件组要使用的函数:(这里只介绍动态创建的,静态的使用的比较少,就不再介绍了)
EventGroupHandle_t xEventGroupCreate( void );是事件创建函数。只需要利用返回值就可以了。
void vEventGroupDelete( EventGroupHandle_t xEventGroup );是事件组的删除函数,传入事件组的句柄就可以了。
xEventGroupSetBits();设置事件函数:
/* 设置事件组中的位 * xEventGroup: 哪个事件组 * uxBitsToSet: 设置哪些位? * 如果uxBitsToSet的bitX, bitY为1, 那么事件组中的bitX, bitY被设置为1 * 可以用来设置多个位,比如 0x15 就表示设置bit4, bit2, bit0 * 返回值: 返回原来的事件值(没什么意义, 因为很可能已经被其他任务修改了) */
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait );等待事件函数。
下面是关于事件的一个使用的实例,事件是不能传递数据的,这里使用的队列进行数据的传递,在使用队列时发现,两个task同时使用一个队列的时候,数据的传输会有问题,然后就改用两个队列针对两个task进行队列数据的传输,数据的传输正常。代码的逻辑就是两个task在设置事件之前,将数据传输到对应的队列中,然后设置事件,第三个task3首先开始等待事件,当事件等待完成之后,就开始接受队列中的数据,然后串口打印出来。
TaskHandle_t vCheckTask1_hander;
TaskHandle_t vCheckTask2_hander;
TaskHandle_t vCheckTask3_hander;
QueueHandle_t QueueUsart1;
QueueHandle_t QueueUsart2;
QueueHandle_t QueueUsart3;
QueueSetHandle_t xQueueUsartHandle;
QueueHandle_t SemaphoreHandle;
QueueHandle_t xSemaMutexHandle;
QueueHandle_t xSemaRecursiveHandle;
EventGroupHandle_t EventGroupHandle;
static void vCheckTask1( void *pvParameters )
{
uint16_t data=10;
for( ;; )
{
printf("task1\r\n");
xQueueSend(QueueUsart1,&data,portMAX_DELAY);
xEventGroupSetBits(EventGroupHandle,(0x00|(1<<0)));
}
}
static void vCheckTask2( void *pvParameters )
{
uint16_t data=20;
for( ;; )
{
printf("task2\r\n");
xQueueSend(QueueUsart2,&data,portMAX_DELAY);
xEventGroupSetBits(EventGroupHandle,(0x00|(1<<1)));
}
}
static void vCheckTask3( void *pvParameters )
{
uint16_t data1,data2;
while(1)
{
xEventGroupWaitBits(EventGroupHandle,(0x00|(1<<1)|(1<<0)),pdTRUE,pdTRUE,portMAX_DELAY);
xQueueReceive(QueueUsart1,&data1,0);
xQueueReceive(QueueUsart2,&data2,0);
printf("data1:%d,data2:%d\r\n",data1,data2);
}
}
int main( void )
{
prvSetupHardware();
EventGroupHandle=xEventGroupCreate();
QueueUsart1=xQueueCreate(2,sizeof(int));
QueueUsart2=xQueueCreate(2,sizeof(int));
xSemaRecursiveHandle=xSemaphoreCreateRecursiveMutex();
xTaskCreate( vCheckTask1, "Check1", 100, NULL,1, &vCheckTask1_hander);
xTaskCreate( vCheckTask2, "Check2", 100, NULL,1, &vCheckTask2_hander);
xTaskCreate( vCheckTask3, "Check3", 100, NULL,2, &vCheckTask3_hander);
vTaskStartScheduler();
return 0;
}
下面是debug调试的结果:
下面代码是,数据队列传输等待事件为0,也就不等待的代码,需要添加一些演示函数,不然task1和task2交替运行会产生问题。
static void vCheckTask1( void *pvParameters )
{
uint16_t data=10;
for( ;; )
{
printf("task1\r\n");
xQueueSend(QueueUsart1,&data,0);
xEventGroupSetBits(EventGroupHandle,(0x00|(1<<0)));
vTaskDelay(10);
}
}
static void vCheckTask2( void *pvParameters )
{
uint16_t data=20;
for( ;; )
{
printf("task2\r\n");
xQueueSend(QueueUsart2,&data,0);
xEventGroupSetBits(EventGroupHandle,(0x00|(1<<1)));
vTaskDelay(10);
}
}
static void vCheckTask3( void *pvParameters )
{
uint16_t data1,data2;
while(1)
{
xEventGroupWaitBits(EventGroupHandle,(0x00|(1<<1)|(1<<0)),pdTRUE,pdTRUE,portMAX_DELAY);
xQueueReceive(QueueUsart1,&data1,0);
xQueueReceive(QueueUsart2,&data2,0);
printf("data1:%d,data2:%d\r\n",data1,data2);
}
}
int main( void )
{
prvSetupHardware();
EventGroupHandle=xEventGroupCreate();
QueueUsart1=xQueueCreate(2,sizeof(int));
QueueUsart2=xQueueCreate(2,sizeof(int));
xSemaRecursiveHandle=xSemaphoreCreateRecursiveMutex();
xTaskCreate( vCheckTask1, "Check1", 100, NULL,1, &vCheckTask1_hander);
xTaskCreate( vCheckTask2, "Check2", 100, NULL,1, &vCheckTask2_hander);
xTaskCreate( vCheckTask3, "Check3", 100, NULL,2, &vCheckTask3_hander);
vTaskStartScheduler();
return 0;
}
3、时间组之间的配合使用
利用时间组之间的配合实现同步点的使用,在配合使用的时候,需要注意事件等待函数是否清除标志位的情况。下面是同步点的一种使用 ,在时间等待函数的使用下,实现了三个任务函数之间的等待。
static void vCheckTask1( void *pvParameters )
{
uint16_t data=10;
for( ;; )
{
printf("task1 complete\r\n");
xQueueSend(QueueUsart1,&data,0);
xEventGroupSetBits(EventGroupHandle,(0x00|(1<<0)));
xEventGroupWaitBits(EventGroupHandle,(0x00|(1<<2)),pdTRUE,pdTRUE,portMAX_DELAY);
vTaskDelay(10);
}
}
static void vCheckTask2( void *pvParameters )
{
uint16_t data=20;
for( ;; )
{
xEventGroupWaitBits(EventGroupHandle,(0x00|(1<<0)),pdTRUE,pdFALSE,portMAX_DELAY);
printf("task2 complete\r\n");
xQueueSend(QueueUsart2,&data,0);
xEventGroupSetBits(EventGroupHandle,(0x00|(1<<1)));
vTaskDelay(10);
}
}
static void vCheckTask3( void *pvParameters )
{
uint16_t data1,data2;
while(1)
{
xEventGroupWaitBits(EventGroupHandle,(0x00|(1<<1)|(1<<0)),pdTRUE,pdFALSE,portMAX_DELAY);
xQueueReceive(QueueUsart1,&data1,0);
xQueueReceive(QueueUsart2,&data2,0);
printf("data1:%d,data2:%d\r\n",data1,data2);
xEventGroupSetBits(EventGroupHandle,(0x00|(1<<2)));
}
}
int main( void )
{
prvSetupHardware();
EventGroupHandle=xEventGroupCreate();
QueueUsart1=xQueueCreate(2,sizeof(int));
QueueUsart2=xQueueCreate(2,sizeof(int));
xSemaRecursiveHandle=xSemaphoreCreateRecursiveMutex();
xTaskCreate( vCheckTask1, "Check1", 100, NULL,1, &vCheckTask1_hander);
xTaskCreate( vCheckTask2, "Check2", 100, NULL,2, &vCheckTask2_hander);
xTaskCreate( vCheckTask3, "Check3", 100, NULL,3, &vCheckTask3_hander);
vTaskStartScheduler();
return 0;
}
上面代码的逻辑:首先创建了三个任务函数,首先是任务函数task3首先运行(任务的优先级最高),进入task3之后,首先等待另外两个任务函数中的事件完成,进入休眠等待,然后进入优先级次之地task2,在task2中等待task1事件地完成,进入休眠状态,然后程序执行到task1,在task1中发送数据,设置事件,并且清除之前地事件标志位。然后task2被唤醒开始执行,并且设置自己地事件标志位,之后task3任务函数等待到task1和task2地两个标志位,开始执行task3任务函数。
需要注意地是:执行地过程中任务函数task2和task3中地等待事件地函数,不能清除标志位。
下面是debug的仿真调试的结果:
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait );
上面这个函数是同步点使用的函数,特点就是设置事件的位和等待事件的位可以同时使用这个函数进行设置。下图是该函数的入口参数:
下面是这个函数的一个应用的实例:
TaskHandle_t vCheckTask1_hander;
TaskHandle_t vCheckTask2_hander;
TaskHandle_t vCheckTask3_hander;
QueueHandle_t QueueUsart1;
QueueHandle_t QueueUsart2;
QueueHandle_t QueueUsart3;
QueueSetHandle_t xQueueUsartHandle;
QueueHandle_t SemaphoreHandle;
QueueHandle_t xSemaMutexHandle;
QueueHandle_t xSemaRecursiveHandle;
EventGroupHandle_t EventGroupHandle;
static void vCheckTask1( void *pvParameters )
{
uint16_t data=1;
for( ;; )
{
data++;
printf("task1 complete\r\n");
xQueueSend(QueueUsart1,&data,0);
xEventGroupSync(EventGroupHandle,(0x00|1<<0),(0x00|1<<2),portMAX_DELAY);
vTaskDelay(10);
}
}
static void vCheckTask2( void *pvParameters )
{
uint16_t data=2;
for( ;; )
{
data++;
printf("task2 complete\r\n");
xQueueSend(QueueUsart2,&data,0);
xEventGroupSync(EventGroupHandle,(0x00|1<<1),(0x00|1<<0),portMAX_DELAY);
vTaskDelay(10);
}
}
static void vCheckTask3( void *pvParameters )
{
uint16_t data1,data2;
while(1)
{
xEventGroupSync(EventGroupHandle,(0x00|1<<2),(0x00|1<<0|1<<1),portMAX_DELAY);
xQueueReceive(QueueUsart1,&data1,0);
xQueueReceive(QueueUsart2,&data2,0);
printf("data1:%d,data2:%d\r\n",data1,data2);
}
}
int main( void )
{
prvSetupHardware();
EventGroupHandle=xEventGroupCreate();
QueueUsart1=xQueueCreate(2,sizeof(int));
QueueUsart2=xQueueCreate(2,sizeof(int));
xSemaRecursiveHandle=xSemaphoreCreateRecursiveMutex();
xTaskCreate( vCheckTask1, "Check1", 100, NULL,1, &vCheckTask1_hander);
xTaskCreate( vCheckTask2, "Check2", 100, NULL,2, &vCheckTask2_hander);
xTaskCreate( vCheckTask3, "Check3", 100, NULL,3, &vCheckTask3_hander);
vTaskStartScheduler();
return 0;
}
下面是上面程序的debug仿真结果: