Why use event flags?
Event flag groups are one of the effective mechanisms for achieving multi-task synchronization. Some beginners who don't understand may ask how troublesome it is to use event flag groups.
Isn't it easier to create a global variable? Actually, it is not. When programming on bare metal, it is indeed more convenient to use global variables, but after adding RTOS
This is another case. Using global variables has the following three problems compared to event flag groups:
Using event flag groups allows the RTOS kernel to effectively manage tasks, which global variables cannot do. Task timeout mechanisms require
It is up to the users to implement it themselves.
When using global variables, we need to prevent access conflicts among multiple tasks. However, using event flag groups can solve this problem and users do not need to worry about it.
Using event flag groups can effectively solve the synchronization problem between interrupt service routines and tasks.
Implementation of FreeRTOS inter-task event flag group
The implementation of the inter-task event flag group refers to the use of the event flag group between tasks to implement the communication or synchronization mechanism of the tasks.
Next, let's talk about the implementation of event flags in FreeRTOS, according to the user's configuration in the FreeRTOSConfig.h file:
#define configUSE_16_BIT_TICKS 1
When the configuration macro definition configUSE_16_BIT_TICKS is 1, each time an event flag group is created, the event flags that the user can use are
8.
#define configUSE_16_BIT_TICKS 0
When the configuration macro definition configUSE_16_BIT_TICKS is 0, each time an event flag group is created, the user can use 24 event flags.
How should we understand the 8 and 24 event flags mentioned above? In fact, a 16-bit variable is defined, and only the lower 8 bits are used.
Or a 32-bit variable is defined, and only the lower 24 bits are used. Each bit uses 0 and 1 to represent the event flag.
FreeRTOS stores event flags in variables of type EventBits_t. What is this variable? The definition is as follows:
As can be seen from the above definition, the TickType_t data type can be a 16-bit or 32-bit number, which is just like what I just said above.
The configUSE_16_BIT_TICKS macro definition is echoed. The examples in the tutorial are all configured with the macro definition configUSE_16_BIT_TICKS
0, that is, each time the user creates an event flag group, there are 24 flags that can be set. As shown in the figure below, only bit0, bit1 and bit2 are used here.
Below we use the following block diagram to illustrate the implementation of FreeRTOS event flags so that everyone can have a vivid understanding.
Implementation of FreeRTOS interrupt mode event flag group
The implementation of the FreeRTOS interrupt mode event flag group refers to the use of event flags between interrupt functions and FreeRTOS tasks.
The following block diagram is used to illustrate the implementation of FreeRTOS event flags so that everyone can have a vivid understanding.
During the running process of Task1, the function xEventGroupWaitBits is called to wait for the event flag to be set. Task1 is run by
The row state enters the blocking state.
When Task1 is blocked, the serial port receives data and enters the serial port interrupt service program. Set Task1 in the serial port interrupt service program.
Waiting for the event flag, task Task1 enters the ready state from the blocked state, and under the action of the scheduler, it enters the running state from the ready state.
The above is a simple FreeRTOS interrupt mode event flag communication process. In practical applications, the interrupt mode message mechanism should pay attention to
The next four questions:
The execution time of the interrupt function should be as short as possible to prevent other exceptions with lower interrupt priority from not being responded to in time.
In actual applications, it is recommended not to implement message processing in interrupts. Users can send message notification tasks in the interrupt service program.
The task implements message processing, which can effectively ensure the real-time response of the interrupt service program. At the same time, this task also needs to be set to high priority.
Priority is set so that the task can be executed in time after exiting the interrupt function.
In the interrupt service routine, the event flag setting function dedicated to interrupts must be called, that is, the function ending with FromISR.
The difference between implementing interrupt service routines in an operating system and bare metal programming.
If the FreeRTOS event flag group API function is not called in the interrupt function of the FreeRTOS project, it is the same as bare metal programming.
the same.
If the FreeRTOS project's interrupt function calls the FreeRTOS event flag group API function, you must exit
Check whether there is a high-priority task ready. If there is, it is necessary to switch tasks after exiting the interrupt. This is similar to bare metal programming.
The program is slightly different, please refer to the experimental routine description (interrupt mode) for details:
In addition, it is strongly recommended that users use the STM32F103 with Cortex-M3 core and the STM32F407, F429 with Cortex-M4 core.
The NVIC priority group is set to 4, that is: NVIC_PriorityGroupConfig (NVIC_PriorityGroup_4);
It will be very convenient to manage the interrupt priority.
Users must set the priority group before starting FreeRTOS multitasking. Once set, remember not to modify it.
Event Flags Group API Functions
The FreeRTOS event flag group can be implemented using the following 11 functions:
xEventGroupCreate()
xEventGroupCreateStatic()
vEventGroupDelete()
xEventGroupWaitBits()
xEventGroupSetBits()
xEventGroupSetBitsFromISR()
xEventGroupClearBits()
xEventGroupClearBitsFromISR()
xEventGroupGetBits()
xEventGroupGetBitsFromISR()
Here we focus on the following 4 functions:
xEventGroupCreate()
xEventGroupWaitBits()
xEventGroupSetBits()
xEventGroupSetBitsFromISR()
Function xEventGroupCreate
Function prototype:
EventGroupHandle_t xEventGroupCreate( void );
Function description:
Function xEventGroupCreate is used to create an event flag group.
Return value, if created successfully, this function returns the handle of the event flag group, if the heap defined in the FreeRTOSConfig.h file
If there is insufficient space, NULL will be returned.
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 30 * 1024 ) )
Function xEventGroupSetBits
Function prototype:
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, /* Event flag group handle*/
const EventBits_t uxBitsToSet ); /* Event flag setting*/
Function description:
The function xEventGroupSetBits is used to set the specified event flag bit to 1.
The first parameter is the event flag group handle.
The second parameter represents 24 configurable event flag bits. EventBits_t is a defined 32-bit variable.
The lower 24 bits are used to set event flags. If any of the lower 24 bits of the variable uxBitsToSet is set to 1, then the set
The corresponding bit of the event flag group is set to 1. The bit set to 0 by the variable uxBitsToSet has no effect on the corresponding bit of the event flag.
For example, setting the variable uxBitsToSet = 0x0003 means setting bits 0 and 1 of the event flag to 1, and the other bits remain unchanged.
Returns the current event flag group value.
When using this function, please note the following issues:
1. Before use, make sure that the event flag group has been created by the function xEventGroupCreate.
2. This function is used to call in the task code, so it cannot be called in the interrupt service program. The interrupt service program uses
xEventGroupSetBitsFromISR
3. The flag bit set by the user through the parameter uxBitsToSet is not necessarily retained in the return value of this function. The following are two cases:
a. If setting a bit causes the task waiting on that bit to leave the Blocked state, the bit may be automatically cleared (see the xClearBitOnExit parameter of xEventGroupWaitBits()).
b. The task that calls this function is a low-priority task. After setting the event flag through this function, a task that waits for this event flag is set.
When the high-priority task is ready, it will immediately switch to the high-priority task for execution, and the corresponding event flag will be
xEventGroupWaitBits is cleared, and after returning from the high-priority task to the low-priority task, the return value of the function xEventGroupSetBits has been modified.
Function xEventGroupSetBitsFromISR
Function prototype:
BaseType_t xEventGroupSetBitsFromISR(
EventGroupHandle_t xEventGroup, /* Event flag group handle*/
const EventBits_t uxBitsToSet, /* Event flag setting*/
BaseType_t *pxHigherPriorityTaskWoken ); /* Whether the high priority task is awakened or not */
Function description:
Function xEventGroupSetBitsFromISR is used to set the specified event flag bit to 1. (Interrupt mode)
The first parameter is the event flag group handle.
The second parameter represents 24 event flag bits that can be set. EventBits_t is a 32-bit variable (detailed explanation in section 18.1.2).
The lower 24 bits are used to set event flags. If any of the lower 24 bits of the variable uxBitsToSet is set to 1, the set
The corresponding bit of the event flag group is set to 1. The bit set to 0 by the variable uxBitsToSet has no effect on the corresponding bit of the event flag.
For example, setting the variable uxBitsToSet = 0x0003 means setting bits 0 and 1 of the event flag to 1, and the other bits remain unchanged.
The third parameter is used to save whether a high priority task is ready. If the value of this parameter is pdTRUE after the function is executed,
It means there is a high priority task to be executed, otherwise there is no task.
Return value: if the message is successfully sent to the daemon task (that is, the timer task of FreeRTOS), pdPASS is returned; otherwise
pdFAIL is returned. In addition, pdFAIL is also returned if the message queue in the daemon task is full.
When using this function, please note the following issues:
1. Before using, make sure that the event flag has been created by the function xEventGroupCreate. At the same time, in FreeRTOSConfig.h
The following three macro definitions are enabled in the file:
#define INCLUDE_xEventGroupSetBitFromISR 1
#define configUSE_TIMERS 1
#define INCLUDE_xTimerPendFunctionCall 1
2. The function xEventGroupSetBitsFromISR is used to call the interrupt service program, so it cannot be called in the task code.
The number used in the task code is xEventGroupSetBits.
3. The operation of the event flag group by the function xEventGroupSetBitsFromISR is an uncertain operation because it is unknown how many bits are currently in the event flag group.
The task is waiting for this event flag. FreeRTOS does not allow nondeterministic operations in interrupt service routines and critical sections.
Executed in the interrupt service routine, this function is used to give the FreeRTOS daemon task (that is, the FreeRTOS timer task)
The message is sent to the daemon task to set the event flag.
Operation, change the critical section to be completed by the scheduling lock. In this way, the problem of uncertain operation being executed in the interrupt service program and the critical section is solved.
Got resolved.
4. Since the setting operation of the event flag by the function xEventGroupSetBitsFromISR is executed in the daemon task, if
To make the setting operation take effect immediately, that is, to allow tasks waiting for this event flag to be executed promptly, you need to set the priority of the daemon task.
It takes precedence over all other tasks using this event flag group.
5. Through the following usage examples, focus on the standard usage of the third parameter of the function xEventGroupSetBitsFromISR. Beginners should pay attention to
Necessary attention.
Function xEventGroupWaitBits
Function prototype:
EventBits_t xEventGroupWaitBits(
const EventGroupHandle_t xEventGroup, /* Event flag group handle*/
const EventBits_t uxBitsToWaitFor, /* Wait for the event flag to be set */
const BaseType_t xClearOnExit, /* Choose whether to clear the set event flag*/
const BaseType_t xWaitForAllBits, /* Choose whether to wait for all flags to be set*/
TickType_t xTicksToWait ); /* Set the waiting time */
Function description:
Function xEventGroupWaitBits waits for the event flag to be set.
The first parameter is the event flag group handle.
The second parameter indicates waiting for the specified flag in the 24 event flag bits. EventBits_t is a defined 32-bit variable (detailed explanation 18.1.2
The lower 24 bits are used to set event flags. For example, setting the variable uxBitsToWaitFor = 0x0003 means waiting for an event.
Bit 0 and bit 1 of the device flag are set to 1. This parameter must not be set to 0.
The third parameter selects whether to clear the event flag that has been set. If this parameter is set to pdTRUE, and the function
xEventGroupWaitBits returns within the overflow time set by the parameter xTicksToWait, then the corresponding event flag is set
The bit will be cleared to 0. If this parameter is set to pdFALSE, it has no effect on event flags that are already set.
The fourth parameter selects whether to wait for all flags to be set. If this parameter is set to pdTRUE, wait for the second parameter to be set.
The function can return only when all the flag bits specified by uxBitsToWaitFor are set to 1.
The overflow time set by xTicksToWait will also be returned. If this parameter is set to pdFALSE, the second parameter
The function returns if any flag bit specified by uxBitsToWaitFor is set to 1, and also returns if the overflow time is exceeded.
The fifth parameter sets the waiting time, in clock cycles. If it is set to portMAX_DELAY, it means permanent waiting.
Return value: the value of the event flag group returned when the function exits because the set time expires or the specified event flag is set to 1.
When using this function, please note the following issues:
1. This function must not be called in an interrupt service routine.
2. Here we will focus on the return value of this function. Through the return value, the user can detect which event flag is set to 1.
If the set waiting time times out, some event flags in the function's return value may be set to 1.
If the function returns because the specified event flag is set to 1, and the parameter xClearOnExit of this function is set to pdTRUE,
Then the return value of this function is the value of the event flag group before it is cleared.
In addition, during the time between the task that calls this function leaving the blocking state and exiting the function xEventGroupWaitBits, if a
If a high-priority task preempts the execution and modifies the event flag, the return value of this function will be the same as the current event flag group number.
The values are different.
Experimental analysis field:
static void AppTaskCreate(void)
{
xTaskCreate(vTaskWork, /* Task function */
"vTaskWork", /* Task name */
512, /* Task stack size, unit word, which is 4 bytes*/
NULL, /* Task parameters */
1, /* Task priority */
&xHandleTaskWork ); /* Task handle */
xTaskCreate( vTaskLed1, /* Task function */
"vTaskLed1", /* Task name */
512, /* Task stack size, unit word, which is 4 bytes*/
NULL, /* Task parameters */
2, /* Task priority */
&xHandleTaskLED1); /* Task handle */
xTaskCreate( vTaskBeep, /* Task function */
"vTaskBeep", /* Task name */
512, /* Task stack size, unit word, which is 4 bytes*/
NULL, /* Task parameters */
3, /* Task priority */
&xHandleTaskBeep ); /* Task handle */
}
/*********************************************************************************
* @Function name: vTaskBeep
* @ Function description: Beep task
* @param: pvParameters, passed in when the task is created, optional
* @return value: None
********************************************************************************/
void vTaskBeep(void *pvParameters)
{
EventBits_t uxBits;
const TickType_t xTicksToWait = 5000; /* Maximum delay 100ms */
while(1)
{
/* Wait for K1 button to be pressed to set bit0 and K2 button to be pressed to set bit1 */
uxBits = xEventGroupWaitBits(xCreatedEventGroup, /* event flag group handle*/
BIT_ALL, /* Wait for bit0 and bit1 to be set */
pdTRUE, /* bit0 and bit1 are cleared before exiting. Here, both bit0 and bit1 are set to indicate "exit"*/
pdTRUE, /* Setting pdTRUE means waiting for both bit1 and bit0 to be set*/
xTicksToWait); /* Waiting delay time*/
if((uxBits & BIT_ALL) == BIT_ALL)
{
/* Received a message with both bit1 and bit0 set */
printf("Received a message with both bit0 and bit1 set\r\n");
}
else
{
/* Timeout. Also note that when only one key press message is received, the corresponding bit of the variable uxBits is also set*/
BEEP_TOGGLE;
}
}
}
/***Key processing task***/
static void vTaskWork(void *pvParameters)
{
EventBits_t uxBits;
while(1)
{
if (key1_flag==1)
{
key1_flag=0;
/* Set bit0 of event flag group */
uxBits = xEventGroupSetBits(xCreatedEventGroup, BIT_0);
if((uxBits & BIT_0) != 0)
{
printf("K1 key is pressed, bit0 of event flag is set\r\n");
}
else
{
printf("K1 key is pressed, bit0 of event flag is cleared, indicating that task vTaskBeep has received the situation where bit0 and bit1 are set\r\n");
}
}
if(key2_flag==1)
{
key2_flag=0;
uxBits = xEventGroupSetBits(xCreatedEventGroup, BIT_1);
if((uxBits & BIT_1) != 0)
{
printf("K2 key is pressed, bit1 of event flag is set\r\n");
}
else
{
printf("K2 key is pressed, bit1 of event flag is cleared, indicating that task vTaskBeep has received the situation where bit0 and bit1 are set\r\n");
}
}
vTaskDelay(20);
}
}
The created task has a lower priority than the Beep task in event waiting. Press K1 first, then press K2, and the print will be as follows:
There is no doubt about the first output. In the second line, since the priority of event waiting Beep is higher than key processing, when K2 is pressed, the scheduler first returns to the high-priority task Beep and prints out the message that K1 and K2 are both pressed so that bit0 and bit1 are set. After calling the xEventGroupWaitBits function in the Beep task, the two bits bit1 and bit0 set to 1 will be cleared. At this time, when the scheduler returns to the low-priority key processing task again, the return value of xEventGroupSetBits has been updated to the cleared value, so the third line prints the cleared message.
Now, we set the priority of key processing to be higher than Beep task, and the print output is as follows:
The first output is also without doubt. When K1 is pressed, bit0 is set. When I press K2, the scheduler will not return to the low-priority Beep task immediately, but will continue to execute itself (this experiment sets the highest priority for key processing) until it is blocked, so there will be a second line of printing. However, please note that the second line of printing when K2 is pressed still shows that it is cleared, because event waiting is used in the Beep task, and when K2 is pressed, the freertos operating system will know that the event of waiting for two keys to be pressed has been triggered. At this time, in the key processing task, the return value of xEventGroupSetBits is not the currently obtained set value, but the value after the xEventGroupSetBits function automatically clears it to zero. Therefore, the second line prints the clearing message, and the third line prints all set, why not clearing it to zero? Because the return value of xEventGroupWaitBits at this time is the value of the event flag group before clearing it to zero.
You may find it a bit strange. The xEventGroupSetBits function is a function for setting information, but it is affected by the xEventGroupWaitBits function and the calling form. Even if the priority of the task calling the xEventGroupWaitBits function is lower than our key task, it is precisely because of this that we really transmit event information in real time. Imagine if my two key events have been triggered, but I can't know it immediately in the key processing task, such real-time performance obviously does not meet the requirements. Even in the bare metal, we change the value of an element through an interrupt, and it must be after the interrupt changes that the value is updated when it is used at any time. Therefore, as a real-time operating system, the behavior of freertos is understandable.
The interrupt method will not be demonstrated again, but you just need to use xEventGroupSetBitsFromISR in the interrupt service function. The usage is as follows:
/*
*********************************************************************************************************
* Function name: TIM_CallBack1 and TIM_CallBack2
* Function description: The callback function of the timer interrupt, this function is called by bsp_StartHardTimer.
* Parameters: None
* Return value: None
*********************************************************************************************************
*/
static void TIM_CallBack1(void)
{
BaseType_t xResult;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
/* Send event flag to task vTaskBeep*/
xResult = xEventGroupSetBitsFromISR(xCreatedEventGroup, /* event flag group handle*/
BIT_0 , /* set bit0 */
&xHigherPriorityTaskWoken );
/* The message was sent successfully */
if( xResult != pdFAIL )
{
/* If xHigherPriorityTaskWoken = pdTRUE, then after exiting the interrupt, switch to the current highest priority task for execution*/
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
Previous article:STM32 learning: preliminary understanding of NVIC
Next article:STM32 learning: discussion on timer programming
- Popular Resources
- Popular amplifiers
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- Huawei's Strategic Department Director Gai Gang: The cumulative installed base of open source Euler operating system exceeds 10 million sets
- Download from the Internet--ARM Getting Started Notes
- Learn ARM development(22)
- Learn ARM development(21)
- Learn ARM development(20)
- Learn ARM development(19)
- Learn ARM development(14)
- Learn ARM development(15)
- Analysis of the application of several common contact parts in high-voltage connectors of new energy vehicles
- Wiring harness durability test and contact voltage drop test method
- Espressif ESP32-C3-DevKitM-1 review is coming soon. Let’s learn about this board first.
- After deleting a wire that was previously laid, AD09 got stuck.
- Saiyuan MCU SC93F8332 lights up the RGB light of WS2812B
- Implementation of a Programmable Fully Digital Phase-Locked Loop
- Canaan wants to record a video about K510. What kind of content do you hope to see? (Attached is the official information about K510)
- Share some difficult problems with the LM25116EN chip
- [RVB2601 Creative Application Development] Dynamically loading MBRE Postscript
- Distance of ESD tip discharge needle
- SensorTile.box Trial (3) Expert Mode Trial
- Introduction to CEDV Electricity Calculation Method