目錄
?
1 事件標(biāo)志組概念及其應(yīng)用
1.1 事件標(biāo)志組定義
1.2 FreeRTOS事件標(biāo)志組介紹
1.3 FreeRTOS事件標(biāo)志組工作原理
2 事件標(biāo)志組應(yīng)用
2.1 功能需求
2.2 API
?2.3 功能實(shí)現(xiàn)
3 事件標(biāo)志組原理
3.1 事件標(biāo)志組控制塊
3.2 事件標(biāo)志組獲取標(biāo)志位
3.3?等待事件標(biāo)志觸發(fā)
3.4 事件標(biāo)志組設(shè)置標(biāo)志位
3.5 事件標(biāo)志組任務(wù)同步
1 事件標(biāo)志組概念及其應(yīng)用
1.1 事件標(biāo)志組定義
門鑰匙代表或的問題,每個(gè)都可以開啟
公交車代表與的問題,到齊了才可以走
FreeRTOS事件標(biāo)志組一共8個(gè)bit。
如Task 1或Tast 2去觸發(fā)第1位,第1位或第三位能觸發(fā)任務(wù)Task3
如Task 6、7、8同時(shí)置位觸發(fā)Task 4
1.2 FreeRTOS事件標(biāo)志組介紹
任務(wù)可以和事件標(biāo)志組進(jìn)行關(guān)聯(lián),如bit23,任務(wù)阻塞等待消息,和消息隊(duì)列一樣有個(gè)超時(shí)機(jī)制
當(dāng)任意任務(wù)或者中斷觸發(fā)置位的時(shí)候,任務(wù)會(huì)從阻塞變?yōu)榫途w態(tài)。
1.3 FreeRTOS事件標(biāo)志組工作原理
?創(chuàng)建事件控制塊,關(guān)聯(lián)事件,等待事件觸發(fā)
2 事件標(biāo)志組應(yīng)用
2.1 功能需求
- 1、使用事件標(biāo)志組檢測多個(gè)按鍵輸入(K3、K4、K5、K6)
- 2、當(dāng)檢測到任何一個(gè)按鍵按下,串口打印輸出按鍵信息
- 3、當(dāng)4路按鍵都已經(jīng)按下,觸發(fā)蜂鳴器報(bào)警
2.2 API
CubeMX中未提供,需要自己創(chuàng)建
?EventBits_t 返回值返回的是所有24位的值
守護(hù)任務(wù):處理操作系統(tǒng)不想在中斷中處理的任務(wù),是一種特殊類型的任務(wù),它在系統(tǒng)中扮演著重要的角色。它被用于監(jiān)控和處理FreeRTOS內(nèi)部的錯(cuò)誤和異常情況,以確保系統(tǒng)的穩(wěn)定性。守護(hù)任務(wù)可以使用軟件定時(shí)器來執(zhí)行一些周期性的任務(wù)。通過創(chuàng)建一個(gè)周期性的軟件定時(shí)器,守護(hù)任務(wù)可以在固定的時(shí)間間隔內(nèi)執(zhí)行特定的操作。例如,守護(hù)任務(wù)可以使用軟件定時(shí)器來定期檢查任務(wù)堆棧的使用情況、檢測任務(wù)優(yōu)先級(jí)錯(cuò)誤或處理未處理的中斷等。軟件定時(shí)器提供了一種簡便的方式來觸發(fā)守護(hù)任務(wù)的執(zhí)行,以確保系統(tǒng)中的重要任務(wù)得到及時(shí)處理。
中斷中不允許上下文切換,都是由守護(hù)任務(wù)來執(zhí)行的。
?使用WaitBits,會(huì)讓任務(wù)進(jìn)入阻塞態(tài)
?參數(shù)解釋:
-
xEventGroup
:事件組句柄,表示要操作的事件組。 -
uxBitsToWaitFor
:等待的事件位,即需要等待其中的哪些事件發(fā)生。可以使用位掩碼形式指定多個(gè)事件位。若指定為0,則表示不等待任何事件,直接返回當(dāng)前事件組的位狀態(tài)。 -
uxBitsToSet
:設(shè)置的事件位,即在等待期間發(fā)生事件后,需要設(shè)置哪些事件位。同樣,可以使用位掩碼指定多個(gè)事件位。 -
xTicksToWait
:等待的超時(shí)時(shí)間,以FreeRTOS的Tick單位表示??梢栽O(shè)置為portMAX_DELAY表示無限等待,或者具體的等待時(shí)間。
返回值:
- 返回已經(jīng)發(fā)生的事件位,即滿足等待條件的事件。如果等待發(fā)生事件時(shí)超時(shí),則返回0。
使用xEvetnGroupSync函數(shù)的具體步驟如下:
- 創(chuàng)建或獲取一個(gè)事件組句柄。
- 使用xEvetnGroupSync函數(shù)等待指定的事件位??梢栽O(shè)置需要等待的事件位、需要設(shè)置的事件位和等待超時(shí)時(shí)間。
- 根據(jù)返回值判斷等待是否成功,根據(jù)已發(fā)生的事件位執(zhí)行相應(yīng)的操作。
注意,xEvetnGroupSync函數(shù)是一個(gè)阻塞函數(shù),即在等待期間會(huì)阻塞當(dāng)前任務(wù)的執(zhí)行。如果有其他任務(wù)在等待相同的事件組,則它們可能會(huì)被喚醒以執(zhí)行后續(xù)操作。因此,在使用xEvetnGroupSync函數(shù)時(shí)需要謹(jǐn)慎設(shè)計(jì),以避免出現(xiàn)死鎖或優(yōu)先級(jí)反轉(zhuǎn)等問題。
?2.3 功能實(shí)現(xiàn)
STM32CubeMX功能配置
GPIO略
根據(jù)接口說明 ,事件標(biāo)志組中斷中需要開啟守護(hù)任務(wù)
?
按鍵中斷及事件標(biāo)志組創(chuàng)建
//freertos.c
//...略
#include "event_groups.h"
EventGroupHandle_t KeyEventGroup; //全局變量句柄
void MX_FREERTOS_Init(void) {
//創(chuàng)建
KeyEventGroup = xEventGroupCreate();
if(KeyEventGroup == NULL){
printf("KeyEventGroup Create Error\r\n");
}
//...略
}
按鍵檢測任務(wù)和蜂鳴器報(bào)警任務(wù)
?
//gpio.c
#include "event_groups.h"
//...略
/*
#define KEY3_EVENT_BIT (1<<0)
#define KEY4_EVENT_BIT (1<<1)
#define KEY5_EVENT_BIT (1<<2)
#define KEY6_EVENT_BIT (1<<3)
typedef enum
{
KEY_DOWN,
KEY_UP,
KEY_RESET
}teKeyStatus;
*/
teKeyStatus KeyStatus;
extern EventGroupHandle_t KeyEventGroup;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
if(Key3_Pin == GPIO_Pin)
{
if(HAL_GPIO_ReadPin(Key3_GPIO_Port,Key3_Pin) == GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(Key3_GPIO_Port,Key3_Pin) == GPIO_PIN_RESET)
{
//設(shè)置事件
xEventGroupSetBitsFromISR(KeyEventGroup,KEY3_EVENT_BIT,NULL);
}
}
}
if(Key4_Pin == GPIO_Pin)
{
if(HAL_GPIO_ReadPin(Key4_GPIO_Port,Key4_Pin) == GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(Key4_GPIO_Port,Key4_Pin) == GPIO_PIN_RESET)
{
//設(shè)置事件
xEventGroupSetBitsFromISR(KeyEventGroup,KEY4_EVENT_BIT,NULL);
}
}
}
if(Key5_Pin == GPIO_Pin)
{
if(HAL_GPIO_ReadPin(Key5_GPIO_Port,Key5_Pin) == GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(Key5_GPIO_Port,Key5_Pin) == GPIO_PIN_RESET)
{
//設(shè)置事件
xEventGroupSetBitsFromISR(KeyEventGroup,KEY5_EVENT_BIT,NULL);
}
}
}
if(Key6_Pin == GPIO_Pin)
{
if(HAL_GPIO_ReadPin(Key6_GPIO_Port,Key6_Pin) == GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(Key6_GPIO_Port,Key6_Pin) == GPIO_PIN_RESET)
{
//設(shè)置事件
xEventGroupSetBitsFromISR(KeyEventGroup,KEY6_EVENT_BIT,NULL);
}
}
}
}
//freertos.c
void Delay_Task(void const * argument)
{
/* USER CODE BEGIN Delay_Task */
EventBits_t KeyEventBits;
/* Infinite loop */
for(;;)
{
KeyEventBits = xEventGroupWaitBits(KeyEventGroup,
KEY3_EVENT_BIT|KEY4_EVENT_BIT|KEY5_EVENT_BIT|KEY6_EVENT_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
printf("Key is Down Key Event Bit is %x\r\n",KeyEventBits);
osDelay(10);
}
}
void High_Task(void const * argument)
{
EventBits_t KeyEventBits;
for(;;)
{
KeyEventBits = xEventGroupWaitBits(KeyEventGroup,
KEY3_EVENT_BIT|KEY4_EVENT_BIT|KEY5_EVENT_BIT|KEY6_EVENT_BIT,
pdTRUE,
pdTRUE,
portMAX_DELAY);
if(KeyEventBits == (KEY3_EVENT_BIT|KEY4_EVENT_BIT|KEY5_EVENT_BIT|KEY6_EVENT_BIT))
{
printf("Buzzer is Toggle\r\n");
HAL_GPIO_TogglePin(Buzzer_GPIO_Port,Buzzer_Pin);
}
osDelay(10);
}
}
3 事件標(biāo)志組原理
3.1 事件標(biāo)志組控制塊
#define eventCLEAR_EVENTS_ON_EXIT_BIT 0x01000000UL //表示退出是否清楚已經(jīng)觸發(fā)的標(biāo)志位 25bit表示
#define eventUNBLOCKED_DUE_TO_BIT_SET 0x02000000UL //解除阻塞是否,已經(jīng)設(shè)置標(biāo)志位 26位
#define eventWAIT_FOR_ALL_BITS 0x04000000UL //是與邏輯還是或邏輯
#define eventEVENT_BITS_CONTROL_BYTES 0xff000000UL //用于分解出,事件標(biāo)志組位使用
typedef struct xEventGroupDefinition
{
//事件標(biāo)志組
EventBits_t uxEventBits;
//任務(wù)等待的列表
List_t xTasksWaitingForBits; /*< List of tasks waiting for a bit to be set. */
} EventGroup_t;
3.2 事件標(biāo)志組獲取標(biāo)志位
?全局變量一定要保護(hù),所以要進(jìn)入臨界段
#define xEventGroupGetBits( xEventGroup ) xEventGroupClearBits( xEventGroup, 0 )
/*
參數(shù):
1、控制塊/句柄
2、要清除位
返回值:
事件標(biāo)志位
*/ 1<<0
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
{
EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
EventBits_t uxReturn;
/*
1、進(jìn)入臨界段
2、獲取當(dāng)前事件標(biāo)志位
3、清除要設(shè)置的事件標(biāo)志位
4、退出臨界段
5、返回事件標(biāo)志組值
*/
taskENTER_CRITICAL();
{
/* The value returned is the event group value prior to the bits being
cleared. */
uxReturn = pxEventBits->uxEventBits;
/* Clear the bits. */
pxEventBits->uxEventBits &= ~uxBitsToClear;
}
taskEXIT_CRITICAL();
return uxReturn;
}
/*
參數(shù):
1、事件控制塊
返回值:
事件標(biāo)志位
*/
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )
{
UBaseType_t uxSavedInterruptStatus;
EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
EventBits_t uxReturn;
//禁止中斷 帶返回值
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
//獲取事件標(biāo)志位
uxReturn = pxEventBits->uxEventBits;
}
//恢復(fù)中斷,在進(jìn)入禁止之前的狀態(tài)
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
return uxReturn;
}
3.3?等待事件標(biāo)志觸發(fā)
?復(fù)位列表項(xiàng):表示事件信息
/*
參數(shù):
1、事件控制塊
2、要等待出發(fā)的標(biāo)志位
3、退出是否要清除
4、與邏輯還是或邏輯
5、阻塞等待時(shí)間
返回值:
當(dāng)前事件標(biāo)志位
*/
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;
//掛起調(diào)度器
vTaskSuspendAll();
{
//獲取當(dāng)前的事件標(biāo)志位
const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits;
/* 檢查是否觸發(fā)
參數(shù):
1、當(dāng)前的事件標(biāo)志位
2、要等待觸發(fā)的事件標(biāo)志位
3、觸發(fā)邏輯???
返回值:
pdFALSE pdTRUE
*/
xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits );
if( xWaitConditionMet != pdFALSE )
{
/* 已經(jīng)觸發(fā) */
uxReturn = uxCurrentEventBits;
xTicksToWait = ( TickType_t ) 0;
/* 清楚已經(jīng)觸發(fā)的標(biāo)志 */
if( xClearOnExit != pdFALSE )
{
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else if( xTicksToWait == ( TickType_t ) 0 )
{
/* 不需要超時(shí),直接返回標(biāo)志位. */
uxReturn = uxCurrentEventBits;
}
else
{
/* 事件沒有觸發(fā),并且需要超時(shí)*/
if( xClearOnExit != pdFALSE )
{
//uxControlBits = 0x01000000UL;
uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( xWaitForAllBits != pdFALSE )
{
//uxControlBits = 0x05000000UL;
uxControlBits |= eventWAIT_FOR_ALL_BITS;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 把任務(wù)添加到事件列表中
參數(shù):
1、列表的地址
2、傳入列表項(xiàng)值
3、任務(wù)阻塞時(shí)間
*/
vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait );
uxReturn = 0;
traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor );
}
}
//恢復(fù)調(diào)度器
xAlreadyYielded = xTaskResumeAll();
//再次判斷是否需要超時(shí)
if( xTicksToWait != ( TickType_t ) 0 )
{
if( xAlreadyYielded == pdFALSE )
{
//進(jìn)行上下文切換 ->pendSV
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/*
任務(wù)已經(jīng)恢復(fù)
1、復(fù)位列表項(xiàng)中的值 復(fù)位為任務(wù)有優(yōu)先級(jí)
*/
uxReturn = uxTaskResetEventItemValue();
//是不是通過事件置位解除的任務(wù)
if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
{
//進(jìn)入臨界段
taskENTER_CRITICAL();
{
/* 獲取當(dāng)前事件位. */
uxReturn = pxEventBits->uxEventBits;
/* 再此判斷是否已經(jīng)置位 */
if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE )
{
//如果需要清除,清除觸發(fā)后的標(biāo)志位
if( xClearOnExit != pdFALSE )
{
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL();
xTimeoutOccurred = pdFALSE;
}
else
{
/* The task unblocked because the bits were set. */
}
/* 返回當(dāng)前事件標(biāo)志位. */
uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
}
traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred );
return uxReturn;
}
3.4 事件標(biāo)志組設(shè)置標(biāo)志位
/*
參數(shù):
1、事件控制塊
2、要設(shè)置的事件位
返回值:
1、當(dāng)前事件標(biāo)志位
*/
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const 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 );
//獲取列表尾節(jié)點(diǎn)
pxListEnd = listGET_END_MARKER( pxList ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
//掛起調(diào)度器
vTaskSuspendAll();
{
//獲取頭節(jié)點(diǎn)
pxListItem = listGET_HEAD_ENTRY( pxList );
/* 設(shè)置事件標(biāo)志位 */
pxEventBits->uxEventBits |= uxBitsToSet;
/* 循環(huán)遍歷整個(gè)列表項(xiàng),直到列表頭節(jié)點(diǎn)等于尾節(jié)點(diǎn)(指針) */
while( pxListItem != pxListEnd )
{
//獲取下個(gè)列表項(xiàng)
pxNext = listGET_NEXT( pxListItem );
//獲取當(dāng)前列表項(xiàng)的值
uxBitsWaitedFor = listGET_LIST_ITEM_VALUE( pxListItem );
//標(biāo)記,是否找到需要處理的節(jié)點(diǎn)
xMatchFound = pdFALSE;
/* 拆分*/
uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES;
uxBitsWaitedFor &= ~eventEVENT_BITS_CONTROL_BYTES;
if( ( uxControlBits & eventWAIT_FOR_ALL_BITS ) == ( EventBits_t ) 0 )
{
//或邏輯
/* 等待位已經(jīng)置位 */
if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) != ( EventBits_t ) 0 )
{
//找到了已經(jīng)觸發(fā)的節(jié)點(diǎn)
xMatchFound = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
//表示所有等待的位都已經(jīng)觸發(fā)
else if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) == uxBitsWaitedFor )
{
/*找到觸發(fā)的節(jié)點(diǎn) */
xMatchFound = pdTRUE;
}
else
{
/* Need all bits to be set, but not all the bits were set. */
}
if( xMatchFound != pdFALSE )
{
/* 是否需要清除 */
if( ( uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT ) != ( EventBits_t ) 0 )
{
//做個(gè)標(biāo)記
uxBitsToClear |= uxBitsWaitedFor;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 把任務(wù)從事件列表中移除
參數(shù):
1、列表項(xiàng)
2、事件標(biāo)志位+解鎖處理標(biāo)志位 內(nèi)部寫入了列表項(xiàng)的value里面
*/
( void ) xTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET );
}
/* 當(dāng)前列表項(xiàng)指向下個(gè),繼續(xù)遍歷*/
pxListItem = pxNext;
}
/* 清除設(shè)置后的標(biāo)志位 */
pxEventBits->uxEventBits &= ~uxBitsToClear;
}
//開啟調(diào)度器
( void ) xTaskResumeAll();
return pxEventBits->uxEventBits;
}
/*
參數(shù):
1、事件控制塊
2、要設(shè)置的標(biāo)志位
3、NULL
*/
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken )
{
BaseType_t xReturn;
//調(diào)用軟件定時(shí)器函數(shù),,用于發(fā)送消息到軟件定時(shí)器任務(wù),進(jìn)行處理
xReturn = xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken );
return xReturn;
}
/*
設(shè)置事件標(biāo)志位的回調(diào)函數(shù),內(nèi)部其實(shí)就是調(diào)用xEventGroupSetBits
*/
void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet )
{
( void ) xEventGroupSetBits( pvEventGroup, ( EventBits_t ) ulBitsToSet );
}
BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken )
{
DaemonTaskMessage_t xMessage;
BaseType_t xReturn;
/* 封裝消息 */
xMessage.xMessageID = tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR;
xMessage.u.xCallbackParameters.pxCallbackFunction = xFunctionToPend;
xMessage.u.xCallbackParameters.pvParameter1 = pvParameter1;
xMessage.u.xCallbackParameters.ulParameter2 = ulParameter2;
//通過消息隊(duì)列和軟件定時(shí)器任務(wù)進(jìn)行通信
xReturn = xQueueSendFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken );
/*
分析:
軟件定時(shí)器任務(wù)要等待消息隊(duì)列,之后解析處理,最終調(diào)用xEventGroupSetBits
*/
return xReturn;
}
3.5 事件標(biāo)志組任務(wù)同步
文章來源:http://www.zghlxwxcb.cn/news/detail-631935.html
?待完成文章來源地址http://www.zghlxwxcb.cn/news/detail-631935.html
到了這里,關(guān)于FreeRTOS源碼分析-10 互斥信號(hào)量的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!