FreeRTOS函數(shù)功能總結(jié)
1. 任務(wù)相關(guān)函數(shù)
1.1 創(chuàng)建任務(wù)xTaskCreate

1.2 刪除任務(wù)vTaskDelete

怎么刪除任務(wù)?舉個(gè)不好的例子:
自刀:vTaskDelete(NULL)
被刀:別的任務(wù)執(zhí)行vTaskDelete(pvTaskCode)
,pvTaskCode是自己的句柄
刀人:執(zhí)行vTaskDelete(pvTaskCode)
,pvTaskCode是別的任務(wù)的句柄
1.3 暫停任務(wù)vTaskSuspend
參數(shù)xTaskToSuspend表示要暫停的任務(wù),如果為NULL,表示暫停自己。
1.4 優(yōu)先級(jí)相關(guān)函數(shù)
1.4.1 獲得任務(wù)的優(yōu)先級(jí)uxTaskPriorityGet
使用參數(shù)xTask來指定任務(wù),設(shè)置為NULL表示獲取自己的優(yōu)先級(jí)。
1.4.2?設(shè)置任務(wù)的優(yōu)先級(jí)vTaskPrioritySet
使用參數(shù)xTask來指定任務(wù),設(shè)置為NULL表示設(shè)置自己的優(yōu)先級(jí);
參數(shù)uxNewPriority表示新的優(yōu)先級(jí),取值范圍是0~(configMAX_PRIORITIES – 1)。
1.5 啟動(dòng)任務(wù)調(diào)度器vTaskStartScheduler
1.6 空閑任務(wù)鉤子函數(shù)vApplicationIdleHook
空閑任務(wù)(Idle任務(wù))的作用:釋放被刪除的任務(wù)的內(nèi)存。
為什么必須要有空閑任務(wù)?
????一個(gè)良好的程序,它的任務(wù)都是事件驅(qū)動(dòng)的:平時(shí)大部分時(shí)間處于阻塞狀態(tài)。有可能我們自己創(chuàng)建的所有任務(wù)都無法執(zhí)行,但是調(diào)度器必須能找到一個(gè)可以運(yùn)行的任務(wù):所以,我們要提供空閑任務(wù)。
????要注意的是:如果使用vTaskDelete()
來刪除任務(wù),那么你就要確??臻e任務(wù)有機(jī)會(huì)執(zhí)行,否則就無法釋放被刪除任務(wù)的內(nèi)存。
使用鉤子函數(shù)的前提
在FreeRTOS\Source\tasks.c
中,可以看到如下代碼,所以前提就是:
????把這個(gè)宏定義為1:configUSE_IDLE_HOOK;
????實(shí)現(xiàn)vApplicationIdleHook
函數(shù)。

2. Tick相關(guān)函數(shù)
2.1 延時(shí)函數(shù)vTaskDelay
至少等待指定個(gè)數(shù)的Tick Interrupt才能變?yōu)榫途w狀態(tài)
????注意,基于Tick實(shí)現(xiàn)的延時(shí)并不精確,比如vTaskDelay(2)的本意是延遲2個(gè)Tick周期,有可能經(jīng)過1個(gè)Tick多一點(diǎn)就返回了。
使用vTaskDelay函數(shù)時(shí),建議以ms為單位,使用pdMS_TO_TICKS把時(shí)間轉(zhuǎn)換為Tick。
這樣的代碼就與configTICK_RATE_HZ無關(guān),即使配置項(xiàng)configTICK_RATE_HZ改變了,我們也不用去修改代碼。
2.2 絕對(duì)延時(shí)函數(shù)vTaskDelayUntil
等待到指定的絕對(duì)時(shí)刻,才能變?yōu)榫途w態(tài)
使用vTaskDelay(n)時(shí),進(jìn)入、退出vTaskDelay的時(shí)間間隔至少是n個(gè)Tick中斷;
使用xTaskDelayUntil(&Pre, n)時(shí),前后兩次退出xTaskDelayUntil的時(shí)間至少是n個(gè)Tick中斷。
2.3 Heap相關(guān)的函數(shù)
2.3.1 pvPortMalloc/vPortFree
作用:分配內(nèi)存、釋放內(nèi)存。
如果分配內(nèi)存不成功,則返回值為NULL。
2.3.2 xPortGetFreeHeapSize
????當(dāng)前還有多少空閑內(nèi)存,這函數(shù)可以用來優(yōu)化內(nèi)存的使用情況。
????比如當(dāng)所有內(nèi)核對(duì)象都分配好后,執(zhí)行此函數(shù)返回2000,那么configTOTAL_HEAP_SIZE就可減小2000。注意:在heap_3中無法使用。
2.3.3 xPortGetMinimumEverFreeHeapSize
返回:程序運(yùn)行過程中,空閑內(nèi)存容量的最小值。
注意:只有heap_4、heap_5支持此函數(shù)。
2.3.4 malloc失敗的鉤子函數(shù)
在pvPortMalloc函數(shù)內(nèi)部:
所以,如果想使用這個(gè)鉤子函數(shù):
????在FreeRTOSConfig.h中,把configUSE_MALLOC_FAILED_HOOK定義為1
????提供vApplicationMallocFailedHook函數(shù)
????pvPortMalloc失敗時(shí),才會(huì)調(diào)用此函數(shù)
3. 隊(duì)列相關(guān)函數(shù)
3.1?創(chuàng)建隊(duì)列
隊(duì)列的創(chuàng)建有兩種方法:動(dòng)態(tài)分配內(nèi)存、靜態(tài)分配內(nèi)存
3.1.1 動(dòng)態(tài)分配內(nèi)存創(chuàng)建隊(duì)列xQueueCreate

3.1.2 靜態(tài)分配內(nèi)存創(chuàng)建隊(duì)列xQueueCreateStatic

示例:
3.2 復(fù)位隊(duì)列xQueueReset
隊(duì)列剛被創(chuàng)建時(shí),里面沒有數(shù)據(jù);使用過程中可以調(diào)用xQueueReset()
把隊(duì)列恢復(fù)為初始狀態(tài),此函數(shù)原型為:
3.3 刪除隊(duì)列vQueueDelete
刪除隊(duì)列的數(shù)函為vQueueDelete()
,只能刪除使用動(dòng)態(tài)方法創(chuàng)建的隊(duì)列,它會(huì)釋放內(nèi)存。原型如下:
3.4 寫隊(duì)列
可以把數(shù)據(jù)寫到隊(duì)列頭部,也可以寫到尾部,這些函數(shù)有兩個(gè)版本:在任務(wù)中使用、在ISR中使用。函數(shù)原型如下:

3.5 讀隊(duì)列xQueueReceive
使用xQueueReceive()
函數(shù)讀隊(duì)列,讀到一個(gè)數(shù)據(jù)后,隊(duì)列中該數(shù)據(jù)會(huì)被移除。這個(gè)函數(shù)有兩個(gè)版本:在任務(wù)中使用、在ISR中使用。函數(shù)原型如下:

3.6 查詢隊(duì)列
可以查詢隊(duì)列中有多少個(gè)數(shù)據(jù)、有多少空余空間。函數(shù)原型如下:
3.7 覆蓋/偷看
當(dāng)隊(duì)列長度為1時(shí),可以使用xQueueOverwrite()
或xQueueOverwriteFromISR()
來覆蓋數(shù)據(jù)。 注意,隊(duì)列長度必須為1。當(dāng)隊(duì)列滿時(shí),這些函數(shù)會(huì)覆蓋里面的數(shù)據(jù),這也意味著這些函數(shù)不會(huì)被阻塞。 函數(shù)原型如下:
????如果想讓隊(duì)列中的數(shù)據(jù)供多方讀取,也就是說讀取時(shí)不要移除數(shù)據(jù),要留給后來人。
????那么可以使用"窺視",也就是xQueuePeek()或xQueuePeekFromISR()。這些函數(shù)會(huì)從隊(duì)列中復(fù)制出數(shù)據(jù),但是不移除數(shù)據(jù)。這也意味著,如果隊(duì)列中沒有數(shù)據(jù),那么"偷看"時(shí)會(huì)導(dǎo)致阻塞;一旦隊(duì)列中有數(shù)據(jù),以后每次"偷看"都會(huì)成功。 函數(shù)原型如下:
4. 信號(hào)量函數(shù)
使用信號(hào)量時(shí),先創(chuàng)建、然后去添加資源、獲得資源。使用句柄來表示一個(gè)信號(hào)量。
4.1 創(chuàng)建信號(hào)量
要先創(chuàng)建信號(hào)量句柄才能使用信號(hào)量;使用信號(hào)量時(shí),要使用句柄來表明使用哪個(gè)信號(hào)量。
對(duì)于二進(jìn)制信號(hào)量、計(jì)數(shù)型信號(hào)量,它們的創(chuàng)建函數(shù)不一樣:

創(chuàng)建二進(jìn)制信號(hào)量的函數(shù)原型如下:
創(chuàng)建計(jì)數(shù)型信號(hào)量的函數(shù)原型如下:
4.2 刪除信號(hào)量vSemaphoreDelete
對(duì)于動(dòng)態(tài)創(chuàng)建的信號(hào)量,不再需要它們時(shí),可以刪除它們以回收內(nèi)存。
vSemaphoreDelete可以用來刪除二進(jìn)制信號(hào)量、計(jì)數(shù)型信號(hào)量,函數(shù)原型如下:
4.3 give/take
二進(jìn)制信號(hào)量、計(jì)數(shù)型信號(hào)量的give、take操作函數(shù)是一樣的。這些函數(shù)也分為2個(gè)版本:給任務(wù)使用,給ISR使用。列表如下:

xSemaphoreGive的函數(shù)原型如下:
xSemaphoreGive函數(shù)的參數(shù)與返回值列表如下:

xSemaphoreGiveFromISR函數(shù)的參數(shù)與返回值列表如下:

xSemaphoreTake的函數(shù)原型如下:
xSemaphoreTake函數(shù)的參數(shù)與返回值列表如下:

xSemaphoreTakeFromISR的函數(shù)原型如下:
xSemaphoreTakeFromISR函數(shù)的參數(shù)與返回值列表如下:

5. 互斥量函數(shù)
5.1 創(chuàng)建互斥量
互斥量是一種特殊的二進(jìn)制信號(hào)量。
使用互斥量時(shí),先創(chuàng)建、然后去獲得、釋放它。使用句柄來表示一個(gè)互斥量。
創(chuàng)建互斥量的函數(shù)有2種:動(dòng)態(tài)分配內(nèi)存,靜態(tài)分配內(nèi)存,函數(shù)原型如下:
要想使用互斥量,需要在配置文件FreeRTOSConfig.h中定義:
5.2 互斥量其他函數(shù)
要注意的是,互斥量不能在ISR中使用。
各類操作函數(shù),比如刪除、give/take,跟一般是信號(hào)量是一樣的。
6.? 遞歸鎖
遞歸鎖(Recursive Mutexes),它的特性如下:
????任務(wù)A獲得遞歸鎖M后,它還可以多次去獲得這個(gè)鎖
????"take"了N次,要"give"N次,這個(gè)鎖才會(huì)被釋放
遞歸鎖的函數(shù)根一般互斥量的函數(shù)名不一樣,參數(shù)類型一樣,列表如下:

函數(shù)原型如下:
7.? 事件組函數(shù)
7.1 創(chuàng)建事件組
要先創(chuàng)建事件組句柄才能使用事件組;使用事件組時(shí),要使用句柄來表明使用哪個(gè)事件組。
有兩種創(chuàng)建方法:動(dòng)態(tài)分配內(nèi)存、靜態(tài)分配內(nèi)存。函數(shù)原型如下:
7.2 刪除事件組vEventGroupDelete
對(duì)于動(dòng)態(tài)創(chuàng)建的事件組,不再需要它們時(shí),可以刪除它們以回收內(nèi)存。
vEventGroupDelete可以用來刪除事件組,函數(shù)原型如下:
7.3 設(shè)置事件xEventGroupSetBits
可以設(shè)置事件組的某個(gè)位、某些位,使用的函數(shù)有2個(gè):
在任務(wù)中使用xEventGroupSetBits()
在ISR中使用xEventGroupSetBitsFromISR()
有一個(gè)或多個(gè)任務(wù)在等待事件,如果這些事件符合這些任務(wù)的期望,那么任務(wù)還會(huì)被喚醒。
函數(shù)原型如下:
? 值得注意的是,ISR中的函數(shù),比如隊(duì)列函數(shù)
xQueueSendToBackFromISR
、信號(hào)量函數(shù)xSemaphoreGiveFromISR
,它們會(huì)喚醒某個(gè)任務(wù),最多只會(huì)喚醒1個(gè)任務(wù)。
? 值得注意的是,ISR中的函數(shù),比如隊(duì)列函數(shù)
xQueueSendToBackFromISR
、信號(hào)量函數(shù)xSemaphoreGiveFromISR
,它們會(huì)喚醒某個(gè)任務(wù),最多只會(huì)喚醒1個(gè)任務(wù)。
???? 如果后臺(tái)任務(wù)的優(yōu)先級(jí)比當(dāng)前被中斷的任務(wù)優(yōu)先級(jí)高,xEventGroupSetBitsFromISR會(huì)設(shè)置*pxHigherPriorityTaskWoken為pdTRUE。
????如果daemon task成功地把隊(duì)列數(shù)據(jù)發(fā)送給了后臺(tái)任務(wù),那么xEventGroupSetBitsFromISR的返回值就是pdPASS。
7.4 等待事件xEventGroupWaitBits
使用
xEventGroupWaitBits
來等待事件,可以等待某一位、某些位中的任意一個(gè),也可以等待多位;等到期望的事件后,還可以清除某些位。
函數(shù)原型如下:
????一個(gè)任務(wù)在等待事件發(fā)生時(shí),它處于阻塞狀態(tài);
?函數(shù)參數(shù)說明列表如下:

舉例如下:

?可以使用xEventGroupWaitBits()等待期望的事件,它發(fā)生之后再使用xEventGroupClearBits()來清除。但是這兩個(gè)函數(shù)之間,有可能被其他任務(wù)或中斷搶占,它們可能會(huì)修改事件組。
????可以使用設(shè)置xClearOnExit為pdTRUE,使得對(duì)事件組的測試、清零都在xEventGroupWaitBits()函數(shù)內(nèi)部完成,這是一個(gè)原子操作。
7.5 同步xEventGroupSync
有一個(gè)事情需要多個(gè)任務(wù)協(xié)同,比如:
任務(wù)A:炒菜、任務(wù)B:買酒、任務(wù)C:擺臺(tái)
A、B、C做好自己的事后,還要等別人做完;大家一起做完,才可開飯。
使用xEventGroupSync()
函數(shù)可以同步多個(gè)任務(wù):
????可以設(shè)置某位、某些位,表示自己做了什么事
????可以等待某位、某些位,表示要等等其他任務(wù)
????期望的時(shí)間發(fā)生后,xEventGroupSync()
才會(huì)成功返回。
? xEventGroupSync
成功返回后,會(huì)清除事件。
xEventGroupSync
函數(shù)原型如下:
參數(shù)列表如下:

8. 任務(wù)通知函數(shù)
8.1 發(fā)出/取出通知
任務(wù)通知有2套函數(shù),簡化版、專業(yè)版,列表如下:
????簡化版函數(shù)的使用比較簡單,它實(shí)際上也是使用專業(yè)版函數(shù)實(shí)現(xiàn)的
????專業(yè)版函數(shù)支持很多參數(shù),可以實(shí)現(xiàn)很多功能

8.2 xTaskNotifyGive/ulTaskNotifyTake
在任務(wù)中使用xTaskNotifyGive函數(shù),在ISR中使用vTaskNotifyGiveFromISR函數(shù),都是直接給其他任務(wù)發(fā)送通知:
1.使得通知值加一
2.并使得通知狀態(tài)變?yōu)?#34;pending",也就是taskNOTIFICATION_RECEIVED,表示有數(shù)據(jù)了、待處理
3.可以使用ulTaskNotifyTake函數(shù)來取出通知值:
4.如果通知值等于0,則阻塞(可以指定超時(shí)時(shí)間)
5.當(dāng)通知值大于0時(shí),任務(wù)從阻塞態(tài)進(jìn)入就緒態(tài)
6.在ulTaskNotifyTake返回之前,還可以做些清理工作:把通知值減一,或者把通知值清零
使用ulTaskNotifyTake函數(shù)可以實(shí)現(xiàn)輕量級(jí)的、高效的二進(jìn)制信號(hào)量、計(jì)數(shù)型信號(hào)量。
這幾個(gè)函數(shù)的原型如下:
xTaskNotifyGive函數(shù)的參數(shù)說明如下:

vTaskNotifyGiveFromISR函數(shù)的參數(shù)說明如下:

ulTaskNotifyTake函數(shù)的參數(shù)說明如下:

8.3 xTaskNotify/xTaskNotifyWait
xTaskNotify
?函數(shù)功能更強(qiáng)大,可以使用不同參數(shù)實(shí)現(xiàn)各類功能,比如:
1.讓接收任務(wù)的通知值加一:這時(shí)xTaskNotify()等同于xTaskNotifyGive()
2.設(shè)置接收任務(wù)的通知值的某一位、某些位,這就是一個(gè)輕量級(jí)的、更高效的事件組
3.把一個(gè)新值寫入接收任務(wù)的通知值:上一次的通知值被讀走后,寫入才成功。這就是輕量級(jí)的、長度為1的隊(duì)列
4.用一個(gè)新值覆蓋接收任務(wù)的通知值:無論上一次的通知值是否被讀走,覆蓋都成功。類似xQueueOverwrite()函數(shù),這就是輕量級(jí)的郵箱。
xTaskNotify()
比xTaskNotifyGive()
更靈活、強(qiáng)大,使用上也就更復(fù)雜。xTaskNotifyFromISR()
是它對(duì)應(yīng)的ISR版本。
這兩個(gè)函數(shù)用來發(fā)出任務(wù)通知,使用哪個(gè)函數(shù)來取出任務(wù)通知呢?
使用xTaskNotifyWait()
函數(shù)!它比ulTaskNotifyTake()
更復(fù)雜:
1.可以讓任務(wù)等待(可以加上超時(shí)時(shí)間),等到任務(wù)狀態(tài)為"pending"(也就是有數(shù)據(jù))
2.還可以在函數(shù)進(jìn)入、退出時(shí),清除通知值的指定位
這幾個(gè)函數(shù)的原型如下:
xTaskNotify函數(shù)的參數(shù)說明如下:

eNotifyAction參數(shù)說明:

9. 軟件定時(shí)器
9.1 創(chuàng)建定時(shí)器
要使用定時(shí)器,需要先創(chuàng)建它,得到它的句柄。
有兩種方法創(chuàng)建定時(shí)器:動(dòng)態(tài)分配內(nèi)存、靜態(tài)分配內(nèi)存。函數(shù)原型如下:
回調(diào)函數(shù)的類型是:
9.2 刪除定時(shí)器
動(dòng)態(tài)分配的定時(shí)器,不再需要時(shí)可以刪除掉以回收內(nèi)存。刪除函數(shù)原型如下:
定時(shí)器的很多API函數(shù),都是通過發(fā)送"命令"到命令隊(duì)列,由守護(hù)任務(wù)來實(shí)現(xiàn)。
如果隊(duì)列滿了,"命令"就無法即刻寫入隊(duì)列。我們可以指定一個(gè)超時(shí)時(shí)間xTicksToWait
,等待一會(huì)。
9.3 啟動(dòng)/停止定時(shí)器
啟動(dòng)定時(shí)器就是設(shè)置它的狀態(tài)為運(yùn)行態(tài)(Running、Active)。
停止定時(shí)器就是設(shè)置它的狀態(tài)為冬眠(Dormant),讓它不能運(yùn)行。
涉及的函數(shù)原型如下:
????注意,這些函數(shù)的xTicksToWait表示的是,把命令寫入命令隊(duì)列的超時(shí)時(shí)間。命令隊(duì)列可能已經(jīng)滿了,無法馬上把命令寫入隊(duì)列里,可以等待一會(huì)。
????xTicksToWait不是定時(shí)器本身的超時(shí)時(shí)間,不是定時(shí)器本身的"周期"。
????創(chuàng)建定時(shí)器時(shí),設(shè)置了它的周期(period)。xTimerStart()函數(shù)是用來啟動(dòng)定時(shí)器。假設(shè)調(diào)用xTimerStart()的時(shí)刻是tX,定時(shí)器的周期是n,那么在tX+n時(shí)刻定時(shí)器的回調(diào)函數(shù)被調(diào)用。
????如果定時(shí)器已經(jīng)被啟動(dòng),但是它的函數(shù)尚未被執(zhí)行,再次執(zhí)行xTimerStart()函數(shù)相當(dāng)于執(zhí)行xTimerReset(),重新設(shè)定它的啟動(dòng)時(shí)間。
9.4 復(fù)位定時(shí)器
????從定時(shí)器的狀態(tài)轉(zhuǎn)換圖可以知道,使用xTimerReset()函數(shù)可以讓定時(shí)器的狀態(tài)從冬眠態(tài)轉(zhuǎn)換為運(yùn)行態(tài),相當(dāng)于使用xTimerStart()函數(shù)。
????如果定時(shí)器已經(jīng)處于運(yùn)行態(tài),使用xTimerReset()函數(shù)就相當(dāng)于重新確定超時(shí)時(shí)間。假設(shè)調(diào)用xTimerReset()的時(shí)刻是tX,定時(shí)器的周期是n,那么tX+n就是重新確定的超時(shí)時(shí)間。
復(fù)位函數(shù)的原型如下:
9.5 修改定時(shí)器周期xTimerChangePeriod
????從定時(shí)器的狀態(tài)轉(zhuǎn)換圖可以知道,使用xTimerChangePeriod()函數(shù),處理能修改它的周期外,還可以讓定時(shí)器的狀態(tài)從冬眠態(tài)轉(zhuǎn)換為運(yùn)行態(tài)。
????修改定時(shí)器的周期時(shí),會(huì)使用新的周期重新計(jì)算它的超時(shí)時(shí)間。假設(shè)調(diào)用xTimerChangePeriod()函數(shù)的時(shí)間tX,新的周期是n,則tX+n就是新的超時(shí)時(shí)間。
相關(guān)函數(shù)的原型如下:
9.6 獲取/設(shè)置定時(shí)器ID
定時(shí)器的結(jié)構(gòu)體如下,里面有一項(xiàng)pvTimerID
,它就是定時(shí)器ID:
怎么使用定時(shí)器ID,完全由程序來決定:
1.可以用來標(biāo)記定時(shí)器,表示自己是什么定時(shí)器
2.可以用來保存參數(shù),給回調(diào)函數(shù)使用
10. 資源管理
10.1 屏蔽中斷
屏蔽中斷有兩套宏:任務(wù)中使用、ISR中使用:
1.任務(wù)中使用:taskENTER_CRITICA()/taskEXIT_CRITICAL()
2.ISR中使用:taskENTER_CRITICAL_FROM_ISR()/taskEXIT_CRITICAL_FROM_ISR()
10.1.1 在任務(wù)中屏蔽中斷
在任務(wù)中屏蔽中斷的示例代碼如下:
任務(wù)調(diào)度依賴于中斷、依賴于API函數(shù),所以:這兩段代碼之間,不會(huì)有任務(wù)調(diào)度產(chǎn)生。
10.1.2 在ISR中屏蔽中斷
要使用含有"FROM_ISR"后綴的宏,示例代碼如下:
10.2 暫停/恢復(fù)調(diào)度器
????如果有別的任務(wù)來跟你競爭臨界資源,你可以把中斷關(guān)掉:這當(dāng)然可以禁止別的任務(wù)運(yùn)行,但是這代價(jià)太大了。它會(huì)影響到中斷的處理。
????如果只是禁止別的任務(wù)來跟你競爭,不需要關(guān)中斷,暫停調(diào)度器就可以了:在這期間,中斷還是可以發(fā)生、處理。
示例代碼如下: