国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁(yè) > news >正文

大連做網(wǎng)站的公司有哪些網(wǎng)上教育培訓(xùn)機(jī)構(gòu)排名

大連做網(wǎng)站的公司有哪些,網(wǎng)上教育培訓(xùn)機(jī)構(gòu)排名,國(guó)家高新技術(shù)企業(yè)圖片,代理上網(wǎng)目錄 1.特性1.1 優(yōu)勢(shì)以及限制1.2 TCB結(jié)構(gòu)體中的存儲(chǔ) 2.函數(shù)2.1 兩類(lèi)函數(shù)2.2 簡(jiǎn)化版2.2.1 Give2.2.2 Take 2.3 專(zhuān)業(yè)版2.3.1 Give2.3.2 Take 3.實(shí)現(xiàn)輕量級(jí)信號(hào)量4.實(shí)現(xiàn)輕量級(jí)隊(duì)列5.實(shí)現(xiàn)輕量級(jí)事件組6.源碼分析6.1 等待通知6.2 發(fā)送通知 所謂"任務(wù)通知",你可…

目錄

  • 1.特性
    • 1.1 優(yōu)勢(shì)以及限制
    • 1.2 TCB結(jié)構(gòu)體中的存儲(chǔ)
  • 2.函數(shù)
    • 2.1 兩類(lèi)函數(shù)
    • 2.2 簡(jiǎn)化版
      • 2.2.1 Give
      • 2.2.2 Take
    • 2.3 專(zhuān)業(yè)版
      • 2.3.1 Give
      • 2.3.2 Take
  • 3.實(shí)現(xiàn)輕量級(jí)信號(hào)量
  • 4.實(shí)現(xiàn)輕量級(jí)隊(duì)列
  • 5.實(shí)現(xiàn)輕量級(jí)事件組
  • 6.源碼分析
    • 6.1 等待通知
    • 6.2 發(fā)送通知

img

所謂"任務(wù)通知",你可以反過(guò)來(lái)讀"通知任務(wù)"。發(fā)送者和接收者是多對(duì)1的關(guān)系

我們使用隊(duì)列、信號(hào)量、事件組等等方法時(shí),并不知道對(duì)方是誰(shuí)。使用任務(wù)通知時(shí),可以明確指定:通知哪個(gè)任務(wù)。

使用隊(duì)列、信號(hào)量、事件組時(shí),我們都要事先創(chuàng)建對(duì)應(yīng)的結(jié)構(gòu)體,雙方通過(guò)中間的結(jié)構(gòu)體通信:

img

使用任務(wù)通知時(shí),任務(wù)結(jié)構(gòu)體TCB中就包含了內(nèi)部對(duì)象,可以直接接收別人發(fā)過(guò)來(lái)的"通知":img

1.特性

任務(wù)通知(Task Notification)是 FreeRTOS 為任務(wù)間通信和同步提供的一種輕量級(jí)機(jī)制,其主要特點(diǎn)在于直接嵌入在任務(wù)的控制塊(TCB)中,不需要額外創(chuàng)建隊(duì)列、信號(hào)量或事件組結(jié)構(gòu),因此具有高效率和低內(nèi)存占用的優(yōu)勢(shì)。

1.1 優(yōu)勢(shì)以及限制

優(yōu)勢(shì):

  • 任務(wù)通知操作直接更新目標(biāo)任務(wù)的TCB中的通知值和狀態(tài),省去了復(fù)雜的數(shù)據(jù)拷貝和對(duì)象管理過(guò)程。
  • 相比于隊(duì)列、信號(hào)量或事件組,任務(wù)通知所需的CPU周期更少,適用于需要頻繁發(fā)送通知的場(chǎng)景。
  • 任務(wù)通知的數(shù)據(jù)(通常為一個(gè)32位整數(shù))嵌入在任務(wù)的TCB中,不需要額外分配單獨(dú)的結(jié)構(gòu)體內(nèi)存。
  • 這對(duì)于嵌入式系統(tǒng)或資源受限的環(huán)境尤為重要,可以減少系統(tǒng)整體內(nèi)存開(kāi)銷(xiāo)。

限制:

  1. 不能發(fā)送數(shù)據(jù)給ISR:
    • 由于 ISR 沒(méi)有自己的 TCB,因此無(wú)法直接使用任務(wù)通知機(jī)制發(fā)送數(shù)據(jù)給 ISR。
    • 不過(guò) ISR 可以通過(guò)任務(wù)通知的 API(例如 xTaskNotifyFromISR())將數(shù)據(jù)發(fā)送給任務(wù),從而實(shí)現(xiàn)中斷到任務(wù)的通信。
  1. 數(shù)據(jù)只能給目標(biāo)任務(wù)獨(dú)享:
    • 通知的數(shù)據(jù)存放在目標(biāo)任務(wù)的 TCB 中,只有該任務(wù)能讀取和操作。
    • 如果需要廣播或共享數(shù)據(jù),使用隊(duì)列、信號(hào)量或事件組更合適。
  1. 無(wú)法緩沖多個(gè)數(shù)據(jù):
    • 每個(gè)任務(wù)的通知存儲(chǔ)區(qū)域僅能保存一個(gè)32位通知值,相當(dāng)于深度為1的緩沖區(qū)。
    • 如果通知還未被讀取而又發(fā)送了新的通知,之前的通知數(shù)據(jù)會(huì)被覆蓋(視具體 API 行為而定)。
  1. 無(wú)法廣播給多個(gè)任務(wù):
    • 任務(wù)通知只能針對(duì)單個(gè)任務(wù)進(jìn)行,不能像事件組那樣同時(shí)喚醒多個(gè)任務(wù)。
    • 這意味著當(dāng)需要將同一通知發(fā)送給多個(gè)任務(wù)時(shí),任務(wù)通知就不適用了。
  1. 發(fā)送方無(wú)法阻塞等待:
    • 如果目標(biāo)任務(wù)的通知區(qū)域正被使用,發(fā)送任務(wù)不能像隊(duì)列那樣進(jìn)入阻塞狀態(tài)等待目標(biāo)任務(wù)清空通知值。
    • 發(fā)送操作會(huì)立即返回錯(cuò)誤,發(fā)送方需要自行處理這種情況。

1.2 TCB結(jié)構(gòu)體中的存儲(chǔ)

在TCB結(jié)構(gòu)體中:

typedef struct tskTaskControlBlock       /* The old naming convention is used to prevent breaking kernel aware debuggers. */
{//省略#if ( configUSE_TASK_NOTIFICATIONS == 1 )volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];#endif//省略
} tskTCB;

ulNotifiedValue,通知值:

  • ulNotifiedValue 是一個(gè) volatile uint32_t 數(shù)組。每個(gè)數(shù)組元素存儲(chǔ)一個(gè)任務(wù)通知值( 存儲(chǔ)實(shí)際的通知數(shù)據(jù),可以表示一個(gè)計(jì)數(shù)、一個(gè)位域(類(lèi)似于事件組)或任意32位數(shù)值 )。
  • volatile 關(guān)鍵字確保編譯器不會(huì)對(duì)這些變量進(jìn)行優(yōu)化,因?yàn)樗鼈兛赡茉谌蝿?wù)切換或中斷中被修改。
  • 通知值由發(fā)送任務(wù)或 ISR 寫(xiě)入,接收任務(wù)可以讀取這個(gè)值以獲取額外信息。
  • 數(shù)組大小由宏 configTASK_NOTIFICATION_ARRAY_ENTRIES 決定。默認(rèn)情況下這個(gè)值通常為 1,但也可以配置為大于1,從而讓任務(wù)擁有多個(gè)通知槽(數(shù)組中的每個(gè)槽都可以獨(dú)立使用)。

ucNotifyState,通知狀態(tài):

  • taskNOT_WAITING_NOTIFICATION (0): 表示任務(wù)當(dāng)前沒(méi)有在等待通知。
  • taskWAITING_NOTIFICATION (1): 表示任務(wù)正在等待通知。
  • taskNOTIFICATION_RECEIVED (2): 表示任務(wù)已接收到通知(處于 pending 狀態(tài))。

img

2.函數(shù)

2.1 兩類(lèi)函數(shù)

任務(wù)通知在 FreeRTOS 中提供了一種輕量級(jí)的任務(wù)間通信與同步機(jī)制。為了滿足不同場(chǎng)景的需求,任務(wù)通知提供了兩套 API:簡(jiǎn)化版專(zhuān)業(yè)版

img

2.2 簡(jiǎn)化版

2.2.1 Give

xTaskNotifyGive 與 vTaskNotifyGiveFromISR

這兩個(gè)函數(shù)用于向目標(biāo)任務(wù)發(fā)送通知,主要特點(diǎn)是:

  • 將目標(biāo)任務(wù)的通知值增加 1(類(lèi)似于計(jì)數(shù)器),
  • 更新通知狀態(tài)為 “pending”,表明有通知等待處理。

xTaskNotifyGive:

  • 在任務(wù)上下文中調(diào)用,用于將通知值加 1,表示向目標(biāo)任務(wù)發(fā)送一個(gè)簡(jiǎn)單的通知或事件。
BaseType_t xTaskNotifyGive(TaskHandle_t xTaskToNotify);// xTaskToNotify:目標(biāo)任務(wù)的句柄(通常在創(chuàng)建任務(wù)時(shí)獲得)。它指明了通知應(yīng)該發(fā)給哪個(gè)任務(wù)。
// 返回值:此函數(shù)必定返回 pdPASS,表示通知已成功發(fā)送。
  • 當(dāng)發(fā)送者任務(wù)想通知目標(biāo)任務(wù)“有新事件發(fā)生”或“計(jì)數(shù)加1”時(shí),調(diào)用該函數(shù)非常高效。
  • 不需要傳遞復(fù)雜數(shù)據(jù),只是簡(jiǎn)單地增加計(jì)數(shù)并改變通知狀態(tài)。

vTaskNotifyGiveFromISR:

  • 在中斷服務(wù)例程(ISR)中調(diào)用,功能與 xTaskNotifyGive 類(lèi)似,用于在中斷中向任務(wù)發(fā)送通知。
void vTaskNotifyGiveFromISR(TaskHandle_t xTaskHandle, BaseType_t *pxHigherPriorityTaskWoken);/*
xTaskHandle:目標(biāo)任務(wù)的句柄。
pxHigherPriorityTaskWoken:一個(gè)指向 BaseType_t 的指針,供 ISR 使用,若通知導(dǎo)致某個(gè)等待任務(wù)進(jìn)入就緒態(tài)且其優(yōu)先級(jí)高于當(dāng)前任務(wù),則將該變量設(shè)置為 pdTRUE,指示在中斷結(jié)束后需要進(jìn)行上下文切換。
返回值:此函數(shù)不返回值,主要通過(guò) pxHigherPriorityTaskWoken 參數(shù)反饋信息。
*/
  • 當(dāng)中斷發(fā)生時(shí),需要快速通知任務(wù)(例如傳感器數(shù)據(jù)就緒、外設(shè)事件等),使用該函數(shù)可以迅速將通知發(fā)送給任務(wù),并確保高優(yōu)先級(jí)任務(wù)能在中斷退出前被調(diào)度。

2.2.2 Take

該函數(shù)用于在任務(wù)中等待并取出通知,是任務(wù)獲取通知的主要接口之一,類(lèi)似于“獲取”操作。

  • 如果通知值為 0,則任務(wù)會(huì)阻塞等待,直到在指定超時(shí)時(shí)間內(nèi)有通知值增加。
  • 當(dāng)通知值大于0時(shí),任務(wù)從阻塞狀態(tài)恢復(fù),并在返回之前,根據(jù)參數(shù)選擇將通知值清零或僅減一。
uint32_t ulTaskNotifyTake(BaseType_t xClearCountOnExit, TickType_t xTicksToWait);

參數(shù):

  • xClearCountOnExit:

    • pdTRUE:在函數(shù)返回前,將目標(biāo)任務(wù)的通知值清零。
      這種方式適用于二進(jìn)制信號(hào)量風(fēng)格,即接收到通知后立即清除。
    • pdFALSE:在返回前,不會(huì)完全清零,而是將通知值減 1。
      這適用于計(jì)數(shù)型信號(hào)量的場(chǎng)景,允許連續(xù)接收多個(gè)通知。
  • xTicksToWait:

    • 指定任務(wù)進(jìn)入阻塞等待的時(shí)間(以 Tick 為單位)。
    • 如果設(shè)為 0,則函數(shù)立即返回當(dāng)前通知值,不進(jìn)行阻塞。
    • 如果設(shè)為 portMAX_DELAY,則任務(wù)將無(wú)限等待直到收到通知。
    • 其他值則表示等待的 Tick 數(shù),可以使用 pdMS_TO_TICKS() 將毫秒轉(zhuǎn)換為 Tick 數(shù)。

返回值:

  • 返回在清零或減一之前的通知值:

    • 如果在等待期間有通知到來(lái),則返回大于 0 的值(表示接收到的通知數(shù)量);
    • 如果一直沒(méi)有通知到來(lái)而超時(shí),則返回 0。

當(dāng)一個(gè)任務(wù)需要等待某個(gè)事件或計(jì)數(shù)器增加時(shí),調(diào)用 ulTaskNotifyTake() 可使任務(wù)阻塞,直到事件發(fā)生。

例如,生產(chǎn)者任務(wù)使用 xTaskNotifyGive() 發(fā)出通知,消費(fèi)者任務(wù)則調(diào)用 ulTaskNotifyTake() 來(lái)等待通知并“消費(fèi)”該通知。


img

2.3 專(zhuān)業(yè)版

2.3.1 Give

任務(wù)上下文:

BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify,uint32_t ulValue,eNotifyAction eAction );

ISR:

BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify,uint32_t ulValue,eNotifyAction eAction,BaseType_t *pxHigherPriorityTaskWoken );
  • xTaskToNotify / 任務(wù)句柄:指定要發(fā)送通知的目標(biāo)任務(wù)。這個(gè)句柄在任務(wù)創(chuàng)建時(shí)獲得。

  • ulValue:32 位數(shù)值,其具體作用由 eAction 決定:

    • 當(dāng)使用 eNoAction 時(shí),不會(huì)對(duì)通知值做任何修改,只是更新通知狀態(tài)為“pending”(即有通知)。
    • 當(dāng)使用 eSetBits 時(shí),通知值將按位“或”(OR)運(yùn)算,即:
      新通知值 = 原通知值 | ulValue。
      這種方式適合將各個(gè)事件以位的形式累積,類(lèi)似輕量級(jí)事件組。
    • 當(dāng)使用 eIncrement 時(shí),忽略 ulValue,通知值將遞增 1。
      這種方式實(shí)現(xiàn)了計(jì)數(shù)功能,與 xTaskNotifyGive() 的行為一致。
    • 當(dāng)使用 eSetValueWithoutOverwrite 時(shí),只有當(dāng)目標(biāo)任務(wù)的通知狀態(tài)不是“pending”(表示沒(méi)有未讀通知)時(shí),才將通知值設(shè)為 ulValue;如果當(dāng)前已有未處理通知,則此次調(diào)用不會(huì)更新通知值,并返回 pdFAIL。
      這種方式適用于需要確保新通知不會(huì)覆蓋舊通知的場(chǎng)合。
    • 當(dāng)使用 eSetValueWithOverwrite 時(shí),無(wú)論當(dāng)前通知狀態(tài)如何,通知值都將被設(shè)為 ulValue,即直接覆蓋之前的通知值。
      這種方式類(lèi)似于輕量級(jí)郵箱,可以確保寫(xiě)入新值,不受舊通知的影響。
  • eAction (eNotifyAction)
    指定如何操作通知值,上述說(shuō)明中列出的各個(gè)取值分別實(shí)現(xiàn)不同的通知行為。

  • pxHigherPriorityTaskWoken (僅用于 ISR 版本)
    是一個(gè)指針,指向一個(gè) BaseType_t 變量。

    • 當(dāng) ISR 發(fā)送通知后,如果被通知的任務(wù)正處于阻塞狀態(tài)且其優(yōu)先級(jí)高于當(dāng)前任務(wù),則該變量將被設(shè)置為 pdTRUE,提示中斷服務(wù)例程在退出時(shí)進(jìn)行上下文切換。
  • 返回值:

    • 對(duì)于大多數(shù)調(diào)用,返回值為 pdPASS 表示成功。
    • 唯一可能返回 pdFAIL 的情況是在使用 eSetValueWithoutOverwrite 時(shí),如果目標(biāo)任務(wù)的通知狀態(tài)已經(jīng)處于“pending”,說(shuō)明之前的通知還未被讀取,此時(shí)調(diào)用將失敗,不更新通知值。

使用場(chǎng)景:

  • 作為事件組:
    如果希望將不同事件以位的形式累加到同一通知值中,可以使用 eSetBits。多個(gè)通知可以同時(shí)累加,不會(huì)相互覆蓋。
  • 作為計(jì)數(shù)型信號(hào)量:
    使用 eIncrement 使通知值遞增 1,每次通知代表一個(gè)事件或資源釋放。
  • 作為郵箱或單深度隊(duì)列:
    使用 eSetValueWithOverwrite 將新的數(shù)據(jù)寫(xiě)入通知值,無(wú)論是否有未讀通知;或使用 eSetValueWithoutOverwrite 防止覆蓋未處理的數(shù)據(jù)(如果需要確保數(shù)據(jù)不被覆蓋)。
  • 在 ISR 中發(fā)送通知:
    使用 xTaskNotifyFromISR,可以在中斷上下文中快速發(fā)送通知給任務(wù),同時(shí)利用 pxHigherPriorityTaskWoken 實(shí)現(xiàn)實(shí)時(shí)響應(yīng)。

2.3.2 Take

xTaskNotifyWait 用于在任務(wù)中等待通知到來(lái),并提供在進(jìn)入等待和退出等待時(shí)清除通知值特定位的功能。這使得任務(wù)在等待期間可以“清理”舊的通知數(shù)據(jù),確保只處理新到來(lái)的通知。

BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,uint32_t ulBitsToClearOnExit,uint32_t *pulNotificationValue,TickType_t xTicksToWait );
  • ulBitsToClearOnEntry

    • 在進(jìn)入等待前,清除通知值的哪些位(僅在通知狀態(tài)不是“pending”的情況下執(zhí)行)。
    • 例如,傳入 0x01 表示在進(jìn)入等待之前先清除通知值中的 bit0。
    • 如果傳入 ULONG_MAX,則表示清除所有位(即將通知值清零)。
  • ulBitsToClearOnExit

    • 當(dāng)?shù)却晒?#xff08;非超時(shí)返回)時(shí),在函數(shù)退出前清除通知值的哪些位。
    • 在清除之前,通知值會(huì)先被賦值給 *pulNotificationValue,以便任務(wù)獲取通知值。
    • 例如,傳入 0x03 表示清除通知值的 bit0 和 bit1;傳入 ULONG_MAX 則表示清除所有位。
  • pulNotificationValue

    • 用來(lái)取出通知值。
    • 在函數(shù)退出時(shí),使用ulBitsToClearOnExit清除之前,把通知值賦給"*pulNoti?cationValue"。
    • 如果不需要取出通知值,可以設(shè)為NULL。
  • xTicksToWait

    • 指定任務(wù)等待通知的最大時(shí)間(Tick 數(shù))。
    • 0 表示不等待,立即返回當(dāng)前通知值;
    • portMAX_DELAY 表示無(wú)限等待,直到通知狀態(tài)變?yōu)椤皃ending”;
    • 其他值表示等待指定的 Tick 數(shù)(可以通過(guò) pdMS_TO_TICKS() 轉(zhuǎn)換為 Tick 數(shù))。

返回值:

  • 返回 pdPASS 表示任務(wù)成功獲得通知,即等待期間通知狀態(tài)變?yōu)榱恕皃ending”,任務(wù)解除阻塞。
  • 返回 pdFAIL 表示任務(wù)在指定時(shí)間內(nèi)沒(méi)有收到通知而超時(shí)返回。

一般工作流程:

  • 進(jìn)入等待之前

    • 調(diào)用 xTaskNotifyWait 時(shí),首先根據(jù) ulBitsToClearOnEntry 清除通知值中指定的位(如果當(dāng)前通知狀態(tài)不是 pending),從而丟棄舊通知。
  • 阻塞等待

    • 任務(wù)進(jìn)入阻塞狀態(tài),直到目標(biāo)任務(wù)的通知狀態(tài)變?yōu)椤皃ending”(即有新通知到來(lái)),或等待時(shí)間超時(shí)。
  • 退出等待前

    • 在成功獲得通知后,函數(shù)先將當(dāng)前通知值保存到 *pulNotificationValue(如果提供),然后根據(jù) ulBitsToClearOnExit 清除通知值中相應(yīng)的位。

這種機(jī)制允許任務(wù)在等待通知時(shí)同時(shí)對(duì)通知值進(jìn)行預(yù)處理和后處理,以確保任務(wù)接收到的是新鮮的數(shù)據(jù)或正確的事件標(biāo)志。

3.實(shí)現(xiàn)輕量級(jí)信號(hào)量

創(chuàng)建信號(hào)量時(shí),可以指定最大值、初始值;使用任務(wù)通知實(shí)現(xiàn)輕量級(jí)的信號(hào)量時(shí)呢

  • 不能設(shè)置最大值
  • 初始值為0,不能指定初始值
  • 最小值是0,跟信號(hào)量是一樣的
static int sum = 0;
static volatile int flagCalcEnd = 0;
static volatile int flagUARTused = 0;static SemaphoreHandle_t xSemCalc;
static SemaphoreHandle_t xSemUART;static TaskHandle_t xHandleTask2;void Task1Function(void * param)
{volatile int i = 0;while (1){for (i = 0; i < 10000; i++)sum++;//printf("1");for (i = 0; i < 10; i++){// xSemaphoreGive(xSemCalc);xTaskNotifyGive(xHandleTask2);}vTaskDelete(NULL);}
}void Task2Function(void * param)
{int i = 0;int val;while (1){//if (flagCalcEnd)flagCalcEnd = 0;//xSemaphoreTake(xSemCalc, portMAX_DELAY);val = ulTaskNotifyTake(pdFALSE, portMAX_DELAY); //pdFALSE:每取出一次通知值減1flagCalcEnd = 1;printf("sum = %d, NotifyVal = %d, i = %d\r\n", sum, val, i++);}
}/*-----------------------------------------------------------*/int main( void )
{TaskHandle_t xHandleTask1;#ifdef DEBUGdebug();
#endifprvSetupHardware();printf("Hello, world!\r\n");xSemCalc = xSemaphoreCreateCounting(10, 0);xSemUART = xSemaphoreCreateBinary();xSemaphoreGive(xSemUART);xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);xTaskCreate(Task2Function, "Task2", 100, NULL, 1, &xHandleTask2);/* Start the scheduler. */vTaskStartScheduler();/* Will only get here if there was not enough heap space to create theidle task. */return 0;
}

4.實(shí)現(xiàn)輕量級(jí)隊(duì)列

隊(duì)列、使用任務(wù)通知實(shí)現(xiàn)的輕量級(jí)隊(duì)列,有什么異同?

  • 隊(duì)列:可以容納多個(gè)數(shù)據(jù),數(shù)據(jù)大小可以指定
  • 任務(wù)通知:只有1個(gè)數(shù)據(jù),數(shù)據(jù)時(shí)32位的
  • 隊(duì)列:寫(xiě)隊(duì)列時(shí),可以阻塞
  • 任務(wù)通知:寫(xiě)隊(duì)列時(shí),不可以阻塞
  • 隊(duì)列:如果隊(duì)列長(zhǎng)度是1,可以選擇覆蓋隊(duì)列
  • 任務(wù)通知:可以覆蓋,也可不覆蓋
static int sum = 0;
static volatile int flagCalcEnd = 0;
static volatile int flagUARTused = 0;
static QueueHandle_t xQueueCalcHandle;
static QueueHandle_t xQueueUARTcHandle;static TaskHandle_t xHandleTask2;int InitUARTLock(void)
{	int val;xQueueUARTcHandle = xQueueCreate(1, sizeof(int));if (xQueueUARTcHandle == NULL){printf("can not create queue\r\n");return -1;}xQueueSend(xQueueUARTcHandle, &val, portMAX_DELAY);return 0;
}void GetUARTLock(void)
{	int val;xQueueReceive(xQueueUARTcHandle, &val, portMAX_DELAY);
}void PutUARTLock(void)
{	int val;xQueueSend(xQueueUARTcHandle, &val, portMAX_DELAY);
}void Task1Function(void * param)
{volatile int i = 0;while (1){for (i = 0; i < 10000; i++)sum++;//printf("1");//flagCalcEnd = 1;//vTaskDelete(NULL);for (i = 0; i < 10; i++){//xQueueSend(xQueueCalcHandle, &sum, portMAX_DELAY);//xTaskNotify(xHandleTask2, sum, eSetValueWithoutOverwrite);  // 不覆蓋xTaskNotify(xHandleTask2, sum, eSetValueWithOverwrite); // 覆蓋valuesum++;}vTaskDelete(NULL);//sum = 1;}
}void Task2Function(void * param)
{int val;int i = 0;while (1){//if (flagCalcEnd)flagCalcEnd = 0;//xQueueReceive(xQueueCalcHandle, &val, portMAX_DELAY);xTaskNotifyWait(0, 0, &val, portMAX_DELAY);flagCalcEnd = 1;printf("sum = %d, i = %d\r\n", val, i++);}
}/*-----------------------------------------------------------*/int main( void )
{TaskHandle_t xHandleTask1;#ifdef DEBUGdebug();
#endifprvSetupHardware();printf("Hello, world!\r\n");xQueueCalcHandle = xQueueCreate(10, sizeof(int));if (xQueueCalcHandle == NULL){printf("can not create queue\r\n");}InitUARTLock();xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);xTaskCreate(Task2Function, "Task2", 100, NULL, 1, &xHandleTask2);/* Start the scheduler. */vTaskStartScheduler();/* Will only get here if there was not enough heap space to create theidle task. */return 0;
}

5.實(shí)現(xiàn)輕量級(jí)事件組

任務(wù)通知并不能實(shí)現(xiàn)真正的事件組,為什么?

  • 它不能等待指定的事件
  • 它不能等待若干個(gè)事件中的任意一個(gè)
  • 一旦有事件,總會(huì)喚醒任務(wù)

發(fā)送方可以設(shè)置事件,但是接收方不能挑選事件,即使不是它關(guān)心的事件,它也會(huì)被喚醒

  • TCB結(jié)構(gòu)體中的uint_32的通知值,不管修改哪一位表示事件的發(fā)生,正在等待的接收方都會(huì)被通知
static int sum = 0;
static int dec = 0;
static volatile int flagCalcEnd = 0;
static volatile int flagUARTused = 0;
static QueueHandle_t xQueueCalcHandle;static EventGroupHandle_t xEventGroupCalc;
static TaskHandle_t xHandleTask3;void Task1Function(void * param)
{volatile int i = 0;while (1){for (i = 0; i < 100000; i++)sum++;xQueueSend(xQueueCalcHandle, &sum, 0);/* 設(shè)置事件0 *///xEventGroupSetBits(xEventGroupCalc, (1<<0));xTaskNotify(xHandleTask3, (1<<0), eSetBits);printf("Task 1 set bit 0\r\n");vTaskDelete(NULL);}
}void Task2Function(void * param)
{volatile int i = 0;while (1){for (i = 0; i < 1000000; i++)dec--;xQueueSend(xQueueCalcHandle, &dec, 0);/* 設(shè)置事件1 *///xEventGroupSetBits(xEventGroupCalc, (1<<1));xTaskNotify(xHandleTask3, (1<<1), eSetBits);printf("Task 2 set bit 1\r\n");vTaskDelete(NULL);}
}void Task3Function(void * param)
{int val1, val2;int bits;while (1){/*等待事件  *///xEventGroupWaitBits(xEventGroupCalc, (1<<0)|(1<<1), pdTRUE, pdTRUE, portMAX_DELAY);xTaskNotifyWait(0, 0, &bits, portMAX_DELAY);if ((bits & 0x3) == 0x3){vTaskDelay(20);xQueueReceive(xQueueCalcHandle, &val1, 0);xQueueReceive(xQueueCalcHandle, &val2, 0);printf("val1 = %d, val2 = %d\r\n", val1, val2);}else{vTaskDelay(20);printf("have not get all bits, get only 0x%x\r\n", bits);}}
}/*-----------------------------------------------------------*/int main( void )
{TaskHandle_t xHandleTask1;#ifdef DEBUGdebug();
#endifprvSetupHardware();printf("Hello, world!\r\n");/* 創(chuàng)建事件組 */xEventGroupCalc = xEventGroupCreate();xQueueCalcHandle = xQueueCreate(2, sizeof(int));if (xQueueCalcHandle == NULL){printf("can not create queue\r\n");}xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);xTaskCreate(Task3Function, "Task3", 100, NULL, 1, &xHandleTask3);/* Start the scheduler. */vTaskStartScheduler();/* Will only get here if there was not enough heap space to create theidle task. */return 0;
}

6.源碼分析

上面就提到過(guò)一個(gè)任務(wù)的"通知狀態(tài)"有三種:

  • taskNOT_WAITING_NOTIFICATION:任務(wù)沒(méi)有在等待通知
  • taskWAITING_NOTIFICATION:任務(wù)在等待通知
  • taskNOTIFICATION_RECEIVED:任務(wù)接收到了通知,也被稱(chēng)為 pending(有數(shù)據(jù)了,待處理)

一個(gè)任務(wù)想等待別人發(fā)來(lái)通知,可以調(diào)用ulTaskNotifyTake xTaskNotifyWait

  • 可能別人早就發(fā)來(lái)通知:"通知狀態(tài)"為taskNOTIFICATION_RECEIVED,那么函數(shù)立刻返回
  • 可能別人還沒(méi)發(fā)來(lái)通知:這些函數(shù)把"通知狀態(tài)"從taskNOT_WAITING_NOTIFICATION改為taskWAITING_NOTIFICATION,然后休眠

別的任務(wù)可以使用xTaskNotifyGivexTaskNotify 給某個(gè)任務(wù)發(fā)通知:

  • 會(huì)馬上喚醒對(duì)方
  • 無(wú)條件喚醒對(duì)方,不管對(duì)方期待什么數(shù)據(jù)

6.1 等待通知

這里以專(zhuān)業(yè)版的 xTaskGenericNotifyWait進(jìn)行訴說(shuō):

BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWait,uint32_t ulBitsToClearOnEntry,uint32_t ulBitsToClearOnExit,uint32_t * pulNotificationValue,TickType_t xTicksToWait )
{BaseType_t xReturn;/* 確保傳入的索引有效,索引必須小于任務(wù)通知數(shù)組的總條目數(shù) */configASSERT( uxIndexToWait < configTASK_NOTIFICATION_ARRAY_ENTRIES );/* 進(jìn)入臨界區(qū),確保接下來(lái)的操作原子執(zhí)行,不被中斷 */taskENTER_CRITICAL();{/* 如果當(dāng)前任務(wù)的通知狀態(tài)未標(biāo)記為“已接收通知”,則說(shuō)明當(dāng)前沒(méi)有通知等待結(jié)果* 只有當(dāng)通知狀態(tài)不是 taskNOTIFICATION_RECEIVED 時(shí),才允許任務(wù)進(jìn)入等待狀態(tài) */if( pxCurrentTCB->ucNotifyState[ uxIndexToWait ] != taskNOTIFICATION_RECEIVED ){/* 在進(jìn)入等待之前,根據(jù) ulBitsToClearOnEntry 參數(shù)清除任務(wù)通知值中的指定位* 這樣做可以在等待開(kāi)始前清除舊的通知位(例如將通知值清零) */pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] &= ~ulBitsToClearOnEntry;/* 將任務(wù)的通知狀態(tài)標(biāo)記為“等待通知” */pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskWAITING_NOTIFICATION;/* 如果指定的等待時(shí)間大于0,則任務(wù)將進(jìn)入阻塞狀態(tài)等待通知 */if( xTicksToWait > ( TickType_t ) 0 ){/* 將當(dāng)前任務(wù)添加到延時(shí)列表中,等待 xTicksToWait 個(gè)時(shí)鐘節(jié)拍后超時(shí) */prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );/* 跟蹤記錄任務(wù)進(jìn)入等待通知阻塞狀態(tài) */traceTASK_NOTIFY_WAIT_BLOCK( uxIndexToWait );/* 盡管處于臨界區(qū)內(nèi),內(nèi)核各移植層允許在 API 中進(jìn)行任務(wù)切換(yield)* 這可能會(huì)導(dǎo)致立即進(jìn)行上下文切換,但應(yīng)用代碼一般不會(huì)直接調(diào)用此 yield */portYIELD_WITHIN_API();}else{/* 如果 xTicksToWait 為0,則不進(jìn)入延時(shí)列表 */mtCOVERAGE_TEST_MARKER();}}else{/* 如果任務(wù)的通知狀態(tài)已經(jīng)是“已接收通知”,則無(wú)需改變狀態(tài) */mtCOVERAGE_TEST_MARKER();}}/* 退出第一個(gè)臨界區(qū) */taskEXIT_CRITICAL();/* 第二個(gè)臨界區(qū)用于處理解除阻塞后的通知值讀取和狀態(tài)恢復(fù) */taskENTER_CRITICAL();{/* 跟蹤記錄任務(wù)通知等待完成 */traceTASK_NOTIFY_WAIT( uxIndexToWait );/* 如果調(diào)用者提供了 pulNotificationValue 指針,則將當(dāng)前任務(wù)的通知值寫(xiě)入該指針中* 這個(gè)通知值可能在等待期間被更新 */if( pulNotificationValue != NULL ){*pulNotificationValue = pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ];}/* 判斷通知狀態(tài):* 如果通知狀態(tài)不是 taskNOTIFICATION_RECEIVED,則說(shuō)明任務(wù)未因通知解除阻塞,而是因超時(shí)解除 */if( pxCurrentTCB->ucNotifyState[ uxIndexToWait ] != taskNOTIFICATION_RECEIVED ){/* 沒(méi)有收到通知,返回 pdFALSE */xReturn = pdFALSE;}else{/* 如果通知狀態(tài)為 taskNOTIFICATION_RECEIVED,則任務(wù)在等待期間收到了通知* 根據(jù) ulBitsToClearOnExit 參數(shù)清除通知值中的相應(yīng)位 */pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] &= ~ulBitsToClearOnExit;/* 設(shè)置返回值為 pdTRUE,表示成功接收到通知 */xReturn = pdTRUE;}/* 無(wú)論通知是否接收,將任務(wù)的通知狀態(tài)重置為“未等待通知” */pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskNOT_WAITING_NOTIFICATION;}/* 退出第二個(gè)臨界區(qū) */taskEXIT_CRITICAL();/* 返回任務(wù)是否成功接收到通知 */return xReturn;
}

根據(jù)當(dāng)前任務(wù)的TCB結(jié)構(gòu)體pxCurrentTCB中的狀態(tài)標(biāo)志來(lái)決定需不需要等待

  • 如果是taskNOTIFICATION_RECEIVED,已經(jīng)收到通知但是被pending了,不需要等待
  • 如果是其它狀態(tài),將狀態(tài)設(shè)置為taskWAITING_NOTIFICATION,等待通知,然后調(diào)用prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );將該任務(wù)放入到任務(wù)等待鏈表當(dāng)中,使其等待通知

下半部分則是用于處理解除阻塞后的通知值讀取和狀態(tài)恢復(fù)(第二個(gè)臨界區(qū)),注釋很清楚了,這里不贅述

6.2 發(fā)送通知

還是以專(zhuān)業(yè)版的xTaskNotify為例子:

#define xTaskNotify( xTaskToNotify, ulValue, eAction ) \xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), NULL )
BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify,UBaseType_t uxIndexToNotify,uint32_t ulValue,eNotifyAction eAction,uint32_t * pulPreviousNotificationValue )
{/* 定義指向目標(biāo)任務(wù)控制塊(TCB)的指針 */TCB_t * pxTCB;/* 默認(rèn)返回值設(shè)置為 pdPASS,表示通知成功 */BaseType_t xReturn = pdPASS;/* 用于保存原始通知狀態(tài)的變量 */uint8_t ucOriginalNotifyState;/* 斷言檢查:確保傳入的通知索引在任務(wù)通知數(shù)組范圍內(nèi) */configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES );/* 斷言檢查:確保要通知的任務(wù)不為空 */configASSERT( xTaskToNotify );/* 將任務(wù)句柄轉(zhuǎn)換為 TCB 指針,便于訪問(wèn)任務(wù)的通知數(shù)據(jù) */pxTCB = xTaskToNotify;/* 進(jìn)入臨界區(qū),確保以下操作是原子性的,防止中斷或任務(wù)切換干擾 */taskENTER_CRITICAL();{/* 如果調(diào)用者要求獲取通知前的值,則將目標(biāo)任務(wù)對(duì)應(yīng)索引的通知值保存到 pulPreviousNotificationValue */if( pulPreviousNotificationValue != NULL ){*pulPreviousNotificationValue = pxTCB->ulNotifiedValue[ uxIndexToNotify ];}/* 保存當(dāng)前任務(wù)的通知狀態(tài),以便后續(xù)判斷任務(wù)是否處于等待通知狀態(tài) */ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ];/* 將任務(wù)的通知狀態(tài)設(shè)置為“已接收通知”,表示本次調(diào)用已觸發(fā)通知 */pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED;/* 根據(jù)通知操作類(lèi)型(eAction)來(lái)更新任務(wù)的通知值 */switch( eAction ){case eSetBits:/* eSetBits:將 ulValue 指定位與當(dāng)前通知值進(jìn)行按位或運(yùn)算 */pxTCB->ulNotifiedValue[ uxIndexToNotify ] |= ulValue;break;case eIncrement:/* eIncrement:將當(dāng)前通知值遞增 */( pxTCB->ulNotifiedValue[ uxIndexToNotify ] )++;break;case eSetValueWithOverwrite:/* eSetValueWithOverwrite:無(wú)條件覆蓋當(dāng)前通知值,設(shè)置為 ulValue */pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue;break;case eSetValueWithoutOverwrite:/* eSetValueWithoutOverwrite:僅當(dāng)通知狀態(tài)之前未標(biāo)記為“已接收通知”時(shí)才寫(xiě)入新值 */if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ){pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue;}else{/* 如果目標(biāo)任務(wù)已處于“已接收通知”狀態(tài),則不允許覆蓋,返回失敗 */xReturn = pdFAIL;}break;case eNoAction:/* eNoAction:不改變通知值,僅更新通知狀態(tài),用于僅喚醒任務(wù) */break;default:/* 默認(rèn)情況:如果傳入了未處理的操作類(lèi)型,則強(qiáng)制斷言失敗 */configASSERT( xTickCount == ( TickType_t ) 0 );break;}/* 記錄通知事件,用于調(diào)試和跟蹤 */traceTASK_NOTIFY( uxIndexToNotify );/* 檢查目標(biāo)任務(wù)是否正處于等待通知的阻塞狀態(tài) */if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ){/* 如果是,則將該任務(wù)從阻塞列表中移除 */listREMOVE_ITEM( &( pxTCB->xStateListItem ) );/* 并將任務(wù)添加到就緒列表中,以便它可以被調(diào)度執(zhí)行 */prvAddTaskToReadyList( pxTCB );/* 確保該任務(wù)不在任何事件列表中 */configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL );#if ( configUSE_TICKLESS_IDLE != 0 ){/* 如果使用 Tickless Idle 模式,可能需要重置下一個(gè)任務(wù)解除阻塞時(shí)間,* 以便更快進(jìn)入睡眠模式 */prvResetNextTaskUnblockTime();}#endif/* 如果被通知的任務(wù)優(yōu)先級(jí)高于當(dāng)前任務(wù),* 則立即進(jìn)行任務(wù)切換,使得高優(yōu)先級(jí)任務(wù)盡快運(yùn)行 */if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ){taskYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else{/* 如果目標(biāo)任務(wù)原來(lái)并不處于等待通知狀態(tài),則不需要將其從阻塞列表中移除 */mtCOVERAGE_TEST_MARKER();}}/* 退出臨界區(qū) */taskEXIT_CRITICAL();/* 返回操作結(jié)果,pdPASS 表示通知成功,pdFAIL 表示未能寫(xiě)入通知值(如 eSetValueWithoutOverwrite 情況) */return xReturn;
}

通知操作類(lèi)型(如設(shè)置位、遞增、覆蓋等)更新目標(biāo)任務(wù)的通知值,并在必要時(shí)喚醒等待通知的任務(wù) 。注釋很清楚了,這里就不再額外講了。看過(guò)之前關(guān)于隊(duì)列、信號(hào)量、互斥量的相關(guān)內(nèi)部機(jī)制講解,其實(shí)看這個(gè)也是很簡(jiǎn)單了

http://aloenet.com.cn/news/47637.html

相關(guān)文章:

  • 廣漢網(wǎng)站建設(shè)2022年最新最有效的營(yíng)銷(xiāo)模式
  • 壽光專(zhuān)業(yè)做網(wǎng)站網(wǎng)絡(luò)營(yíng)銷(xiāo)推廣策略有哪些
  • 山東網(wǎng)站建設(shè).com關(guān)鍵詞挖掘查詢(xún)工具愛(ài)站網(wǎng)
  • 上百度推廣 免費(fèi)做網(wǎng)站泰安百度公司代理商
  • 房租 做網(wǎng)站百度網(wǎng)頁(yè)版鏈接
  • 建設(shè)綜合購(gòu)物網(wǎng)站建站abc
  • 視頻網(wǎng)站建設(shè) 方案網(wǎng)絡(luò)營(yíng)銷(xiāo)的類(lèi)型
  • 優(yōu)化對(duì)網(wǎng)站真的非常有用嗎廣告聯(lián)盟怎么加入
  • 東營(yíng)建設(shè)信息網(wǎng)老網(wǎng)站深圳百度地圖
  • wordpress 獲取文章圖片標(biāo)題網(wǎng)絡(luò)營(yíng)銷(xiāo)優(yōu)化
  • 維啟網(wǎng)站建設(shè)商品推廣軟文800字
  • 餐飲手機(jī)微網(wǎng)站怎么做今日頭條熱搜
  • wordpress菜單右上角北侖seo排名優(yōu)化技術(shù)
  • 怎么做誠(chéng)信通網(wǎng)站的店招鄭州網(wǎng)絡(luò)推廣代理顧問(wèn)
  • 河南建設(shè)監(jiān)理協(xié)會(huì)官方網(wǎng)站自動(dòng)點(diǎn)擊競(jìng)價(jià)廣告軟件
  • 網(wǎng)站建設(shè)技術(shù)協(xié)議書(shū)營(yíng)銷(xiāo)策劃方案公司
  • 網(wǎng)站制作完成之后進(jìn)入了什么階段百度一下網(wǎng)頁(yè)版瀏覽器
  • 一元購(gòu)物網(wǎng)站怎么做百度推廣seo自學(xué)
  • 寧夏 網(wǎng)站開(kāi)發(fā)最近10條重大新聞
  • 在工商網(wǎng)站上怎么做電話的變更網(wǎng)絡(luò)營(yíng)銷(xiāo)的十種方法
  • 做臨時(shí)工有哪些網(wǎng)站谷歌瀏覽器 安卓下載
  • 怎么做ebay網(wǎng)站設(shè)計(jì)個(gè)人網(wǎng)站
  • b2b 網(wǎng)站開(kāi)發(fā)91關(guān)鍵詞
  • 新鄉(xiāng)市做網(wǎng)站直銷(xiāo)系統(tǒng)網(wǎng)站色盲測(cè)試圖看圖技巧
  • 免費(fèi)的海報(bào)模板網(wǎng)站優(yōu)化關(guān)鍵詞的方法
  • 海淀區(qū)玉泉小學(xué)網(wǎng)站 建設(shè)方百度搜索大數(shù)據(jù)查詢(xún)
  • 婚禮策劃網(wǎng)站模板中文網(wǎng)絡(luò)營(yíng)銷(xiāo)課程學(xué)什么
  • 中國(guó)建設(shè)教育網(wǎng)官方網(wǎng)站哈爾濱推廣優(yōu)化公司
  • 網(wǎng)站建設(shè)便宜的公司北京公司排名seo
  • 鄭州建設(shè)網(wǎng)站定制seo優(yōu)化網(wǎng)站推廣全域營(yíng)銷(xiāo)獲客公司