In this blog post, we will talk about Event Group in freeRTOS. So far, we have learnt a task can be put in Blocked state to wait for an event to happen. For example, a task that calls the vTaskDelay() function is placed to Blocked state until the delay has passed. Another example is a task that tries to take a semaphore which is not available. In this case, the calling task is placed into the Blocked state until the event that the semaphore becomes available again. Those are examples when tasks stay in Blocked state waiting for a single event to happen. A natural question might be how to make a task to wait in Blocked state until multiple events happen? One possibility is to create multiple semaphores in which each semaphore corresponds to an event. A task then attempts to take multiple semaphores and blocked until all of them are available. A another approach is to use Event Group which we will discuss in this post.
Event group introduction
An Event Group allows a task to wait in Blocked state until one or more events happen. We will revisit the button interrupt example in the previous post. In that example, a binary semaphore is used to defer processing from an interrupt service routine (ISR) to a task. We’ll see how an event group can be used instead of a binary semaphore and get an understanding of event group.
An event group can be thought of as a variable of 32-bit in which 24 bits are usable. Each bit represents an event has happened or not. A task can check for a bit or a group of bits in event group to see if an event or a combination of events has happened or not. If not, the task can choose to go to Blocked state to wait for the event (or combination of events) to happen. That is the simple working principle of event group. In our example, we want each time a button is pressed, a message is printed on the Serial Monitor. We can define, for example, bit 0 of event group represents button state. When button is not pressed, it is 0. When button is pressed, it is 1. Whenever button is pressed, a ISR runs and set the bit to 1.
Event Group APIs
Create an event group
xEventGroupCreate() is the function to create an event group. If the event group is created successfully, this function returns a variable of type EventGroupHandle_t which we can use to refer to the event group. This function has the prototype
Setting an event group bit
xEventGroupSetBitsFromISR() is the function to set a bit or bits in an event group from the interrupt service routine (ISR). This function has prototype
Waiting for event group bits
xEventGroupWaitBits() is the function to read the event group value and wait in Blocked state for one or more bits in the event group to be set. This function has the prototype
Using Event Group on Arduino
In our project, we first need to create an Event Group as follow
Next, we will need to setup an interrupt handler which will be triggered when a button is pressed. We have done this in the previous post by calling attachInterrupt() function. In the interrupt handler, we need to call xEventGroupSetBitsFromISR() to set bit 0.
The main task will attempt to read event group value and enter to Blocked state to wait until the bit 0 is set.
In the task 1 main function, we declare a variable eValue of type EventBits_t. In the task 1 main loop, we call the API function xEventGroupWaitBits() and pass in the following arguments, in order:
- e: the Event Group handle that we created using xEventGroupCreate()
- 0: The bit 0 of the event group that we check for if it is set or not
- pdTRUE: we are passing pdTRUE to xClearOnExit to indicate that we want to set bit 0 to 0 when the function returns
- pdFALSE: passing pdFALSE to xWaitForAllBits means we only care about the bit 0 of the event group. Whenever this bit is set, the function returns.
- portMAX_DELAY: we don’t specify a timeout so that the calling task (task 1) will remain in Blocked state to wait for the event bit is set.
When the button is pressed, the bit 0 of event group is set, xEventGroupWaitBits returns the event group value before it is cleared which is 1.