不可被忽視的作業系統( FreeRTOS )【1】
- 2020 年 4 月 6 日
- 筆記
把大多數人每個星期的雙休過過成了奢侈的節假日放假,把每天23點後定義為自己的自由時間,應該如何去思考這個問題 ?
雙休的兩天里,不!是放假的兩天里,終於有較長的時間好好的學習一下一直斷斷續續的FreeRTOS了,本來打算一天加一個晚上結束戰鬥,最後還是高估了自己。FreeRTOS之所以能被這麼多人使用,其必定是複雜多變的,筆者總結了其中的一些常用函數,其中參考的是正點原子的系列教程以及ESP32 IDF 4.0 關於FreeRTOS的數據手冊和官方API手冊。
認識FreeRTOS
● FreeRTOS開源免費
●FreeRTOS已經被越來越多的使用
● 作業系統,開發時方便實現多任務調度
● FreeRTOS的內核支援搶佔式,合作式和時間片調度
● 提供了一個用於低功耗的Tickless模式
● 高效的軟體定時器
● 強大的跟蹤執行功能
●堆棧溢出檢測功能
● 任務數量不限
●FreeRTOS系統簡單、小巧、易用,通常情況下內核佔用4k-9k位元組的空間
●高可移植性,程式碼主要C語言編寫
●任務與任務、任務與中斷之間可以使用任務通知、消息隊列、二值訊號量、數值型訊號量、遞歸互斥訊號量和互斥訊號量進行通訊和同步
Freerots是一個迷你的實時作業系統內核。作為一個輕量級的作業系統,功能包括:任務管理、時間管理、訊號量、消息隊列、記憶體管理、記錄功能、軟體定時器、協程等,可基本滿足較小系統的需求。FreeRTOS作業系統是完全免費的作業系統,具有源碼公開、可移植、可裁剪、調度策略靈活的特點。
FreeRTOS,可以分為兩部分Free和RTOS, Free就是免費的、自由的、不受約束的意思, RTOS全稱是Real Time Operating System,中文名就是實時作業系統。可以看出FreeROTS就是一個免費的RTOS類系統。這裡要注意, RTOS不是指某一個確定的系統,而是指一類系統。比如uC/os, FreeRTOS, RTX, RT-Thread等這些都是RTOS類作業系統。作業系統允許多個任務同時運行,這個叫做多任務,實際上,一個處理器核心在某一時刻只能運行一個任務。作業系統中任務調度器的責任就是決定在某一時刻究竟運行哪個任務,任務調度在各個任務之間的切換非常快!這就給人們造成了同一時刻有多個任務同時運行的錯覺。作業系統的分類方式可以由任務調度器的工作方式決定,比如有的作業系統給每個任務分配同樣的運行時間,時間到了就輪到下一個任務, Unix作業系統就是這樣的。RTOS的任務調度器被設計為可預測的,而這正是嵌入式實時作業系統所需要的,實時環境中要求作業系統必須對某一個事件做出實時的響應,因此系統任務調度器的行為必須是可預測的。像FreeRTOS這種傳統的RTOS類作業系統是由用戶給每個任務分配一個任務優先順序,任務調度器就可以根據此優先順序來決定下一刻應該運行哪個任務。FreeRTOS是RTOS系統的一種, FreeRTOS十分的小巧,可以在資源有限的微控制器中運行,當然了, FreeRTOS不僅局限於在微控制器中使用。但從文件數量上來看FreeRTOS要比uC/OSII和uC/OSII小的多。
嵌入式系統比較
前後台系統
我們經常在嵌入式開發是都是在main函數里一個while(1)循環,再加上一些中斷函數,可以認為其是一個單任務系統,也稱之為前後台系統,其前台的意思是中斷,後台的意思是main函數里的while(1)循環。
有以下特點:
l 簡單,消耗資源少
l 任務排隊執行,無優先順序區分
RTOS系統
多任務實現將一個大的功能分成每一小塊,每一小塊分別由每一個任務進行管理,多任務並不是同時執行多個任務,本質上CPU在某一時間段只能被一個任務佔用,但因為每個任務所佔用的時間很短,所以看上去就像同一時間段執行了多個任務。
有以下特點:
l 消耗資源較大
l 通過任務優先順序管控,優先順序高的任務可以隨時打斷低任務,功能實時性高
ESP32中的FreeRTOS
本章將圍繞ESP32中的FreeRTOS實現展開
原始的FreeRTOS設計為在單個內核上運行。但是ESP32是雙核,包含協議CPU(稱為CPU 0或PRO_CPU)和應用程式CPU(稱為CPU 1或APP_CPU)。這兩個內核實際上是相同的,並且共享相同的記憶體。這允許兩個內核在它們之間交替運行任務。
ESP32中關於任務大小使用的是位元組為單位。但標準的FreeRTOS使用的是字,在標準中創建任務時如果堆棧為16位寬,而usStackDepth為100,則將分配200位元組(16位=2個位元組,2*100=200個位元組)用作任務的堆棧。再舉一個例子,如果堆棧為32位寬,而usStackDepth為400,則將分配1600個位元組(32位=4個位元組,4*400=1600個位元組)用作任務的堆棧。
外鏈接
字:在電腦中,一串數碼作為一個整體來處理或運算的,稱為一個電腦字,簡稱字。
位元組:是指一小組相鄰的二進位數碼。通常是8位作為一個位元組。它是構成資訊的一個小單位,並作為一個整體來參加操作,比字小,是構成字的單位。
2、所代表的含義不同:
電腦記憶體中,最小的存儲單位是“位(bit)”,8個“位”構成一個“位元組(byte)”.
通常若干個位元組組成一個“字”。
任務
l 任務使用無限制,可創建任務數量無最大值
l 任務支援優先順序,一個優先順序下可以有多個任務,取值0到(configMAX_PRIORITIES – 1),其中configMAX_PRIORITIES在FreeRTOSConfig.h中定義,ESP32中為25, 數字越高優先順序越高。
l 每個任務維護自己的堆棧(用於任務被搶佔後存儲上下文),從而導致更高的RAM使用率
l 任務實現函數必須是無返回值void類型的,其內部是通常是一個無限循環,如while(1)
l 任務實現循環中需要有能引起任務調度的內容,通常是延時函數,如vTaskDelay(),也可以是其他只要能讓FreeRTOS發生任務切換的API函數都可以,比如請求訊號量、隊列等,甚至直接調用任務調度器。只不過最常用的就是FreeRTOS的延時函數。
l 任務函數一般不允許跳出循環,如果一定要跳出循環的話在跳出循環以後一定要調用函數vTaskDelete(NULL);刪除此任務以釋放記憶體
l 任務狀態:
運行態
當一個任務正在運行時,那麼就說這個任務處於運行態,處於運行態的任務就是當前正在使用處理器的任務。如果使用的是單核處理器的話那麼不管在任何時刻永遠都只有一個任務處於運行態。
就緒態
處於就緒態的任務是那些已經準備就緒(這些任務沒有被阻塞或者掛起),可以運行的任務,但是處於就緒態的任務還沒有運行,因為有一個同優先順序或者更高優先順序的任務正在運行!
阻塞態
如果一個任務當前正在等待某個外部事件的話就說它處於阻塞態,比如說如果某個任務調用了函數vTaskDela()的話就會進入阻塞態,直到延時周期完成。任務在等待隊列、訊號量、事件組、通知或互斥訊號量的時候也會進入阻塞態。任務進入阻塞態會有一個超時時間,當超過這個超時時間任務就會退出阻塞態,即使所等待的事件還沒有來臨!
掛起態
暫停任務,像阻塞態一樣,任務進入掛起態以後也不能被調度器調用進入運行態,但是進入掛起態的任務沒有超時時間。任務進入和退出掛起態通過調用函數vTaskSuspend()和xTaskResume()。
l 任務堆棧用於存儲任務被打斷時的上下文,以便任務再次被運行時恢復現場
l 任務創建成功將返回pdPASS,一般創建失敗的原因是系統堆記憶體不足
l ISR是中斷可調用函數的標識
l 任務調度:
FreeRTOS如果在單核CPU上運行的話,那就決定了運行時只能有一個任務獲得執行。任務調度其實就是任務切換的意思,能引起任務調度的函數有:
vTaskDelay()延時,任務的延時就是讓任務進入阻塞狀態,交出cpu的使用權。
創建任務
在FreeRTOS實現內部,任務使用兩個記憶體塊。第一個塊用於保存任務的數據結構。任務將第二個塊用作其堆棧。如果使用xTaskCreate()創建任務,則兩個記憶體塊將自動在xTaskCreate()函數內部動態分配。
參數:
pvTaskCode:指向任務的實現方法,該方法通常是一個無限循環,如果要退出的話必須使用vTaskDelete(NULL);刪除此任務以釋放記憶體
constpcName:任務名稱,該名稱方便輸出一下調試資訊,configMAX_TASK_NAME_LEN(ESP32中是16)定義的最大長度-默認為16。
usStackDepth:指定為位元組數的任務堆棧的大小。請注意,這與原始FreeRTOS不同。
constpvParameters:任務參數
uxPriority:任務優先順序,0到(configMAX_PRIORITIES – 1),其中configMAX_PRIORITIES在FreeRTOSConfig.h中定義, 當前ESP32SDK中該值為( #define configMAX_PRIORITIES ( 25 ) )低優先順序數字表示低優先順序任務,同一優先順序下將進行輪詢執行,可以使用taskYIELD()儘快讓出CPU使用權。
constpvCreatedTask: 用於傳回可引用創建的任務的句柄,方便後續操控任務,可以設置為NULL
返回值
如果成功創建任務並將其添加到就緒列表,則為pdPASS,否則,在文件projdefs.h中定義的錯誤程式碼
static BaseType_t xTaskCreate(TaskFunction_t pvTaskCode, const char *constpcName, const uint32_t usStackDepth, void *constpvParameters, UBaseType_t uxPriority, TaskHandle_t *constpvCreatedTask)
小試牛刀
程式總共有三個循環輸出隨機數:
1.主循環,可獲取的資源大
2.任務0循環,可獲取的資源被創建任務函數時限制
3.任務1循環,可獲取的資源被創建任務函數時限制
1 #include <stdio.h> 2 #include "bootloader_random.h"//隨機數相關 3 #include "freertos/FreeRTOS.h"//freertos相關 4 #include "freertos/task.h" 5 6 //任務0處理函數 7 void Task_Run_0(){ 8 uint32_t ranv=0; 9 while(1){ 10 ranv=esp_random();//獲取一個隨機值,正負數 11 printf("【%s】隨機數輸出:%drn","任務0",ranv); 12 vTaskDelay(1000 / portTICK_PERIOD_MS);//延時1S 13 } 14 vTaskDelete(NULL); 15 } 16 17 //任務1處理函數 18 void Task_Run_1(void *datas){ 19 uint32_t ranv=0; 20 char *strdata=(char *)datas; 21 while(1){ 22 ranv=esp_random();//獲取一個隨機值,正負數 23 printf("【%s】隨機數輸出:%drn",strdata,ranv); 24 vTaskDelay(1000 / portTICK_PERIOD_MS);//延時1S 25 } 26 vTaskDelete(NULL); 27 } 28 29 //主函數 30 void app_main() 31 { 32 printf("rn--------------DONGIXAODONG FreeRTOS-----------------rn"); 33 //未啟用RF獲取隨機值 34 bootloader_random_enable();//開啟隨機值獲取 35 36 //啟動任務0,簡化 37 //函數,名字,位元組大小,參數,優先順序[0,25-1](最高優先為configMAX_PRIORITIES – 1),任務句柄 38 BaseType_t t0res=xTaskCreate(Task_Run_0,"DONG Task_Run_0",1024*2,NULL,16,NULL); 39 if(t0res==pdPASS){ 40 printf("任務0啟動成功....rn"); 41 } 42 43 //啟動任務1,標準 44 TaskHandle_t xHandle1 = NULL; 45 //函數,名字,位元組大小,參數,優先順序[0,25-1](最高優先為configMAX_PRIORITIES – 1),任務句柄 46 BaseType_t t1res=xTaskCreate(Task_Run_1,"DONG Task_Run_1",1024*2,(void *)"任務1",16,&xHandle1); 47 if(t1res==pdPASS){ 48 printf("任務1啟動成功....rn"); 49 } 50 51 uint32_t ranv=0; 52 while(1){ 53 ranv=esp_random();//獲取一個隨機值,正負數 54 printf("【main】隨機數輸出:%drn",ranv); 55 vTaskDelay(1000 / portTICK_PERIOD_MS);//延時1S 56 } 57 }
獲取當前任務的句柄
返回:
調用該函數的任務的任務句柄
TaskHandle_t xTaskGetCurrentTaskHandle()
掛起(暫停)任務調度器:
FreeRTOS如果在單核CPU上運行的話,那就決定了運行時只能有一個任務獲得執行。任務調度其實就是任務切換的意思
關閉和開啟任務調度器是為了某一個任務在操作過程中不被其它任務打斷效果
任務掛起函數被調用幾次便要恢復幾次,因為其內部有一個掛起計數,支援嵌套操作
在不禁用中斷的情況下掛起調度程式。
調度程式掛起時不會發生上下文切換。
調用vTaskSuspendAll()之後,調用任務將繼續執行,而不會被交換出來,直到對xTaskResumeAll()進行了調用。
掛起調度程式時,不得調用可能導致上下文切換的API函數(例如vTaskDelayUntil(),xQueueSend()等)
void vTaskSuspendAll( )
恢復掛起(暫停)的任務調度器
關閉和開啟任務調度器是為了某一個任務在操作過程中不被其它任務打斷效果
通過調用vTaskSuspendAll()掛起後恢復調度程式活動。
xTaskResumeAll()僅恢復調度程式。它不會取消暫停先前通過調用vTaskSuspend()而暫停的任務。
BaseType_t xTaskResumeAll( )
小試牛刀(掛起和恢復任務調度器)
1 void vTask1(void * pvParameters) 2 { 3 while(1){ 4 //任務程式碼在這裡。 5 //在某一時刻,任務需要執行長時間的操作 6 //它不想換出來,並不希望被高優先順序的任務打斷 7 /*它不能使用: 8 taskENTER_CRITICAL ()/taskEXIT_CRITICAL()的長度 9 因為操作可能會導致中斷被錯過 10 */ 11 //防止實時內核交換任務。 12 vTaskSuspendAll (); 13 14 //在這裡開始做你想做的不會被打斷的工作 15 //在這段時間裡,中斷仍然會發生 16 //時間內核滴答計數將被維護。 17 //工作完成後 18 //重新啟動內核,我們想要強制 19 //上下文切換——但是如果恢復調度器是沒有意義的 20 //已經導致了上下文切換。 21 if(!xTaskResumeAll ()) 22 { 23 taskYIELD ();//強制切換一次上下文,讓高優先順序的搶佔 24 } 25 } 26 }
刪除任務
必須將INCLUDE_vTaskDelete定義為1才能使用此功能
從RTOS實時內核的管理中刪除任務。
要刪除的任務將從所有準備就緒,阻止,暫停和事件列表中刪除
參數:
任務句柄,將刪除指定任務,如果傳遞值為NULL,則刪除調用任務刪除的當前任務
void vTaskDelete(TaskHandle_txTaskToDelete)
延時
將任務延遲給定的滴答數。
任務保持阻塞的實際時間取決於滴答率。常數portTICK_PERIOD_MS可用於根據滴答速率計算實時時間-解析度為一個滴答周期。
vTaskDelay()指定相對於調用vTaskDelay()的時間,任務希望解除阻塞的時間。例如,將阻止時間段指定為100個滴答聲將導致任務在調用vTaskDelay()之後取消阻止100個滴答聲。因此,vTaskDelay()不能提供一種控制周期性任務頻率的好方法,因為通過程式碼的路徑以及其他任務和中斷活動將影響vTaskDelay()的調用頻率,從而影響時間接下來執行任務的位置。請參閱vTaskDelayUntil(),了解旨在簡化固定頻率執行的替代API函數。它通過指定調用任務應解除阻止的絕對時間(而不是相對時間)來實現。
方法1相對延時函數 vTaskDelay:
必須將INCLUDE_vTaskDelay定義為1,此功能才可用。
參數:xTicksToDelay:調用任務應阻塞的時間(以滴答周期為單位)
vTaskDelay函數傳遞的參數是延時幾個息屏節拍,查看系統時鐘,系統時鐘是1KHZ,那麼系統延時一個節拍就是1MS
vTaskDelay(1000 / portTICK_PERIOD_MS);//延時1000ms=1S
方法2絕對延時函數vTaskDelayUntil:
必須將INCLUDE_vTaskDelayUntil定義為1,此功能才可用。
TickType_t xLastWakeTime是類似用來記錄開始時間的句柄
TickType_t xLastWakeTime = xTaskGetTickCount(); vTaskDelayUntil( &xLastWakeTime, (1000 / portTICK_PERIOD_MS); //延時1000ms=1S
兩個比較
vTaskDelayUntil與vTaskDelay()不同:vTaskDelay()將導致任務從調用vTaskDelay()時起以指定的滴答數阻塞。因此,很難單獨使用vTaskDelay()來生成固定的執行頻率,因為任務開始執行與調用vTaskDelay()的任務之間的時間可能不固定[該任務可能採用不同的路徑,儘管調用之間的程式碼不同,或者每次執行時可能被打斷或搶佔不同的次數]。vTaskDelay()指定相對於調用該函數的時間的喚醒時間,而vTaskDelayUntil()指定其希望解除阻止的絕對(精確)時間。
xTicksToDelay延時只是交出CPU時間比如說20MS,但是它沒計算這個任務本身運行消耗的時間和其它中間環節耗用的時間,獲取在執行時被高優先順序打斷可能,因此它的延時是個大概值,具有不確定性;第2個就不一樣了,你可以把它想像成一個時鐘,比方說它記錄了下上次延時的時候是9.30分鐘,你再次延時30分鐘,那麼它在10:00就是準時切換成本次任務,所以說它是比較精準的延時。https://bbs.21ic.com/icview-412527-1-1.html
設置任務優先順序
必須將INCLUDE_vTaskPrioritySet定義為1才能使用此功能
參數1:任務句柄
參數2:新的優先順序,與創建任務時的【uxPriority】類似
void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority )
獲取任務優先順序
必須將INCLUDE_uxTaskPriorityGet定義為1才能使用此功能
參數:
任務句柄,將指定獲取某個任務的優先順序,如果傳遞值為NULL,則獲取調用該函數的當前任務的優先順序值
任務中使用函數
UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask )
中斷服務中使用函數
UBaseType_t uxTaskPriorityGetFromISR(TaskHandle_t xTask )
小試牛刀
1 //主函數 2 void app_main() 3 { 4 //------ 5 //啟動任務2,標準 6 TaskHandle_t xHandle2 = NULL; 7 //函數,名字,位元組大小,參數,優先順序[0,25-1](最高優先為configMAX_PRIORITIES – 1),任務句柄 8 BaseType_t t2res=xTaskCreate(Task_Run_2,"DONG Task_Run_2",1024*2,(void *)"任務2",5,&xHandle2); 9 if(t2res==pdPASS){ 10 printf("任務2啟動成功....rn"); 11 } 12 13 UBaseType_t t2pri=uxTaskPriorityGet(xHandle2);//獲取任務2的優先順序,輸出5 14 UBaseType_t thispri=uxTaskPriorityGet(NULL);//獲取main函數的優先順序,輸出1 15 printf("t2任務的優先順序為:%d,當前任務(main)的優先順序為:%drn",t2pri,thispri); 16 while(1){ 17 vTaskDelay(1000 / portTICK_PERIOD_MS);//延時1S 18 } 19 }
查詢任務狀態
必須將INCLUDE_eTaskGetState定義為1才能使用此功能。
返回值: enum 枚舉類型
eNoAction= 0,/*運行態,任務正在查詢自身的狀態,所以必須運行。* /
eReady, / * 就緒態,被查詢的任務在已讀或暫掛的就緒列表中。* /
eBlocked, / * 阻塞態,被查詢的任務處於阻塞狀態。* /
eSuspended, / *掛起態,被查詢的任務處於掛起狀態,或者處於阻塞狀態,超時時間為無限。* /
edeleted / * !<正在查詢的任務已被刪除,但其TCB尚未被釋放。* /
1 //主函數 2 void app_main() 3 { 4 //----------- 5 //啟動任務2,標準 6 TaskHandle_t xHandle2 = NULL; 7 //函數,名字,位元組大小,參數,優先順序[0,25-1](最高優先為configMAX_PRIORITIES – 1),任務句柄 8 BaseType_t t2res=xTaskCreate(Task_Run_2,"DONG Task_Run_2",1024*2,(void *)"任務2",5,&xHandle2); 9 if(t2res==pdPASS){ 10 printf("任務2啟動成功....rn"); 11 } 12 eTaskState t2_sta=eTaskGetState(xHandle2); 13 printf("t2任務的運行狀態:%drn",t2_sta);//輸出 2 14 while(1){ 15 vTaskDelay(1000 / portTICK_PERIOD_MS);//延時1S 16 } 17 }
暫停(掛起)任務
必須將INCLUDE_vTaskSuspend定義為1,此功能才可用。
然後暫停後如同播放音樂是按下了暫停鍵,當恢復時將會從暫停處再次開始執行,並不會重新開始於while循環外,所以暫停是會保留上下文的。
暫停任務後,無論其優先順序如何,任務將永遠不會獲得任何微控制器處理時間。
對vTaskSuspend的調用不是累積性的-即,在同一任務上兩次或兩次以上調用vTaskSuspend()仍然只需要對vTaskResume()進行一次調用即可準備掛起的任務。
參數
任務句柄,如果是NULL,則表示掛起調用該函數的任務
void vTaskSuspend( TaskHandle_t xTaskToSuspend )
恢復暫停(掛起)的任務
必須將INCLUDE_vTaskSuspend定義為1,此功能才可用。
通過一次調用vTaskResume(),已被一個或多個vTaskSuspend()調用暫停的任務將可以再次運行
參數
任務句柄,如果是NULL,則表示掛起調用該函數的任務
void vTaskResume( TaskHandle_t xTaskToResume )
中斷服務中恢復暫停(掛起)的任務
必須將INCLUDE_xTaskResumeFromISR定義為1,此功能才可用。
通過一次調用xTaskResumeFromISR(),已被一個或多個vTaskSuspend()調用暫停的任務將可以再次運行。
如果在掛起任務之前中斷可能到達,則xTaskResumeFromISR()不應用於將任務與中斷同步-因為這可能導致中斷丟失。使用訊號量作為同步機制可以避免這種情況的發生。
返回:
如果繼續執行任務,則為pdTRUE,這將導致上下文切換,否則為pdFALSE。ISR使用它來確定在ISR之後是否可能需要上下文切換。
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume )
獲取任務系統總數量
實時內核當前正在管理的任務數。這包括所有準備就緒,已阻止和已暫停的任務。空閑任務已刪除但尚未釋放的任務也將包括在計數中。
UBaseType_t tasknum=uxTaskGetNumberOfTasks(); printf("系統任務總數量:%drn",tasknum);
獲取任務的名稱
必須將FreeRTOSConfig.h中的INCLUDE_pcTaskGetTaskName設置為1,pcTaskGetTaskName()才可用
返回任務創建時給的的任務名稱
參數:
任務句柄,傳遞NULL表示當前調用函數任務
//主函數 void app_main() { char *main_task= pcTaskGetTaskName(NULL); printf("當前任務的名稱是:%s",main_task);//當前任務的名稱是:main }
獲取系統時間計數器值
返回調用調用vTaskStartScheduler()以來的滴答計數
//任務中使用 TickType_t xTaskGetTickCount( void ) //中斷服務函數使用 TickType_t xTaskGetTickCountFromISR( void )
小試牛刀
1 #include <stdio.h> 2 #include "freertos/FreeRTOS.h"//freertos相關 3 #include "freertos/task.h" 4 //任務0處理函數 5 void Task_Run_0(){ 6 TickType_t counts=0; 7 while(1){ 8 counts=xTaskGetTickCount(); 9 printf("【%s】系統任務計數器值:%drn","任務0",counts); 10 vTaskDelay(3000 / portTICK_PERIOD_MS);//延時1S 11 } 12 } 13 //主函數 14 void app_main() 15 { 16 printf("rn--------------DONGIXAODONG FreeRTOS-----------------rn"); 17 18 //啟動任務0,簡化 19 //函數,名字,位元組大小,參數,優先順序[0,25-1](最高優先為configMAX_PRIORITIES – 1),任務句柄 20 BaseType_t t0res=xTaskCreate(Task_Run_0,"DONG Task_Run_0",1024*2,NULL,7,NULL); 21 if(t0res==pdPASS){ 22 printf("任務0啟動成功....rn"); 23 } 24 TickType_t counts=0; 25 while(1){ 26 counts=xTaskGetTickCount(); 27 printf("【%s】系統任務計數器值:%drn","Main",counts); 28 vTaskDelay(1000 / portTICK_PERIOD_MS);//延時1S 29 } 30 }
任務堆棧使用的高水位線中斷服務
要使此功能可用,必須在FreeRTOSConfig.h中將INCLUDE_uxTaskGetStackHighWaterMark設置為1。
自任務開始以來,高水位標記是已存在的最小可用堆棧空間,後面將會保留最高使用率(以位元組為單位,而不是原始FreeRTOS中的單詞)。返回的數字越小,任務越接近其堆棧溢出。
參數:
與要檢查的堆棧關聯的任務的句柄。將xTask設置為NULL可檢查調用任務的堆棧。
返回:
自創建xTask引用的任務以來,可用堆棧空間最小值(以位元組為單位,而不是原始FreeRTOS中的字數)。
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask )
獲取系統中的所有任務資訊
必須在FreeRTOSConfig.h中將configUSE_TRACE_FACILITY定義為1,才能使uxTaskGetSystemState()可用。
uxTaskGetSystemState()為系統中的每個任務填充TaskStatus_t結構。TaskStatus_t結構包含任務句柄的成員,任務名稱,任務優先順序,任務狀態以及任務消耗的運行時間總量。有關完整的成員列表,請參見此文件中的TaskStatus_t結構定義。
注意:此功能僅用於調試用途,因為其使用會導致調度程式長時間處於掛起狀態。
參數:
pxTaskStatusArray:指向TaskStatus_t結構數組的指針。對於受RTOS控制的每個任務,該數組必須至少包含一個TaskStatus_t結構。可以使用uxTaskGetNumberOfTasks()API函數來確定RTOS控制下的任務數。
uxArraySize:pxTaskStatusArray參數指向的數組的大小。該大小指定為數組中的索引數,或者數組中包含的TaskStatus_t結構數,而不是數組中的位元組數。
pulTotalRunTime:如果在FreeRTOSConfig.h中將configGENERATE_RUN_TIME_STATS設置為1,則* pulTotalRunTime由uxTaskGetSystemState()設置為總運行時間(由運行時間統計時鐘定義,請參見http://www.freertos.org/rtos-run-自目標啟動以來。time-stats.html)。可以將pulTotalRunTime設置為NULL以省略總運行時間資訊。
返回:
uxTaskGetSystemState()填充的TaskStatus_t結構的數量。該值應等於uxTaskGetNumberOfTasks()API函數返回的數字,但如果uxArraySize參數中傳遞的值太小,則該數字將為零。
任務資訊結構體TaskStatus_t:
TaskHandle_t xHandle;/ * !<結構中其餘資訊所涉及的任務的句柄。* /
const char * pcTaskName;/ * !<指向任務名稱的指針。如果任務被刪除,則此值無效,因為該結構已被填充!*/ /*lint !e971不合格的字元類型只允許用於字元串和單個字元。* /
UBaseType_t xTaskNumber;/ * !<任務編號,越小表示越早被創建* /
eTaskState eCurrentState;/ * !<結構被填充時任務存在的狀態。* /
UBaseType_t uxCurrentPriority;/ * !<結構被填充時任務運行的優先順序(可能是繼承的)。* /
UBaseType_t uxBasePriority;/ * !<如果繼承了任務當前的優先順序,任務將返回的優先順序,以避免在獲取互斥鎖時發生無界的優先順序反轉。只有在FreeRTOSConfig.h中將configUSE_MUTEXES定義為1時才有效。* /
uint32_t ulRunTimeCounter;/ * !<到目前為止分配給任務的總運行時間,由運行時統計時鐘定義。見http://www.freertos.org/rtos-run-time-stats.html。只有在FreeRTOSConfig.h中將configGENERATE_RUN_TIME_STATS定義為1時才有效。* /
StackType_t * pxStackBase;/ * !<指向任務堆棧區域的最低地址。* /
uint32_t usStackHighWaterMark;/ * !<自任務創建以來為該任務保留的最小堆棧空間量。該值越接近於零,任務就越接近溢出其堆棧。
UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime )
小試牛刀
configUSE_TRACE_FACILITY定義為1
configUSE_MUTEXES定義為1
configGENERATE_RUN_TIME_STATS定義為0,所以下面輸出的時間是無效的
//輸出值:任務名稱,優先順序,運行時間,系統運行時間
DONG Task_Run_0我創建的任務
Main主函數任務
Tmr Svc 定時器任務
IDLE1 空閑任務
IDLE0 空閑任務
1 #include <stdio.h> 2 #include "freertos/FreeRTOS.h"//freertos相關 3 #include "freertos/task.h" 4 5 //獲取所有任務並顯示 6 //configUSE_TRACE_FACILITY 設置為1 7 void show_task() 8 { 9 volatile UBaseType_t uxArraySize, x;//存儲任務數量,x為for循環所使用的變數 10 uint32_t ulTotalRunTime;//運行時間存儲 11 //獲取系統的任務數量 12 uxArraySize = uxTaskGetNumberOfTasks(); 13 //申請記憶體空間以存儲用戶資訊 14 TaskStatus_t * pxTaskStatusArray = pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ) ); 15 //如果申請成功 16 if( pxTaskStatusArray != NULL ) 17 { 18 // 開始獲取所有任務資訊,參數(任務資訊存儲空間,獲取的任務數量,存儲系統運行時間),返回值為數量 19 uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalRunTime ); 20 21 // 循環獲取的任務數量輸出 22 for( x = 0; x < uxArraySize; x++ ) 23 { 24 //輸出值:任務名稱,優先順序,運行時間,系統運行時間 25 printf("%stt%dtt%dtt%drn", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].uxCurrentPriority, pxTaskStatusArray[ x ].ulRunTimeCounter, ulTotalRunTime ); 26 } 27 //不再需要該數組,釋放它所消耗的記憶體。 28 vPortFree( pxTaskStatusArray ); 29 } 30 } 31 32 //任務0處理函數 33 void Task_Run_0(){ 34 show_task();////任務查詢 35 while(1){ 36 printf("【%s】創建任務rn","任務0"); 37 vTaskDelay(3000 / portTICK_PERIOD_MS);//延時1S 38 } 39 } 40 41 //主函數 42 void app_main() 43 { 44 printf("rn--------------DONGIXAODONG FreeRTOS-----------------rn"); 45 46 //啟動任務0,簡化 47 //函數,名字,位元組大小,參數,優先順序[0,25-1](最高優先為configMAX_PRIORITIES – 1),任務句柄 48 BaseType_t t0res=xTaskCreate(Task_Run_0,"DONG Task_Run_0",1024*2,NULL,7,NULL); 49 if(t0res==pdPASS){ 50 printf("任務0啟動成功....rn"); 51 } 52 53 while(1){ 54 printf("【%s】主任務rn","main"); 55 vTaskDelay(1000 / portTICK_PERIOD_MS);//延時1S 56 } 57 }
列出所有任務的一些資訊
為了使此功能可用,必須將configUSE_TRACE_FACILITY和configUSE_STATS_FORMATTING_FUNCTIONS都定義為1
列出所有當前任務,以及它們的當前狀態和堆棧使用率高水位標記。
注意:
此功能將在其持續時間內禁用中斷。它不適合正常的應用程式運行時使用,而是作為調試輔助。
任務報告為已阻止(’B’),就緒(’R’),已刪除(’D’)或已暫停(’S’)。
vTaskList()調用uxTaskGetSystemState(),然後將uxTaskGetSystemState()輸出的一部分格式化為人類可讀的表,以顯示任務名稱,狀態和堆棧使用情況。
建議生產系統直接調用uxTaskGetSystemState()以獲得對原始統計數據的訪問,而不是通過調用vTaskList()間接進行。
參數:
pcWriteBuffer:一個緩衝區,上面提到的詳細資訊將以ASCII形式寫入其中。假定此緩衝區足夠大以包含生成的報告。每個任務大約40個位元組就足夠了。
輸出:
任務名稱、任務狀態、任務優先順序、任務堆棧歷史最小剩餘量、任務編號(越先開啟值越低)
void vTaskList( char * pcWriteBuffer )
小試牛刀
輸出:名稱、狀態、優先順序、歷史最小剩餘堆棧、任務編號
char reslist[1000]; vTaskList(reslist); printf("獲取任務詳情:rn%srn",reslist);
獲取任務運行時間百分比
configGENERATE_RUN_TIME_STATS和configUSE_STATS_FORMATTING_FUNCTIONS需要設置為1
void vTaskGetRunTimeStats( char *pcWriteBuffer )
小試牛刀
//輸出任務名稱,運行時間,所佔系統運行時間比例
IDLE1 空閑任務
IDLE0 空閑任務
空閑任務運行時間佔比越大越好,如果有某個任務所暫用運行時間百分比大的話,需要考慮將任務拆分
char reslist[1000]; vTaskGetRunTimeStats(reslist); printf("獲取任務運行時間:rn%srn",reslist);
消息隊列
頭文件:freertos / include / freertos / queue.h
隊列是為了任務與任務、任務與中斷之間的通訊而準備的,可以在任務與任務、任務與中斷之間傳遞消息,隊列中可以存儲有限的、大小固定的數據項目。任務與任務、任務與中斷之間要交流的數據保存在隊列中,叫做隊列項目。隊列所能保存的最大數據項目數量叫做隊列的長度,創建隊列的時候會指定數據項目的大小和隊列的長度。由於隊列用來傳遞消息的,所以也稱為消息隊列。FreeRTOS中的訊號量的也是依據隊列實現的!所以有必要深入的了解FreeRTOS的隊列。
隊列通常時先進先出的,也可以設置為先進後出,FreeRTOS中隊列通常傳遞的是內容,而不是指針
入隊:
隊列中沒有消息,可以設置為不等待立即返回,等待指定時間節拍、一直等待有空位
出隊:
隊列滿了會阻塞,可以設置為不等待立即返回、等待指定的時間節拍、一直等待有消息為止
隊列創建
創建一個新的隊列實例。這將分配新隊列所需的存儲,並返回該隊列的句柄。
參數:
uxQueueLength 隊列可以存儲的條數,隊列可以包含的最大項目數。
unsigned int uxItemSize 隊列中每條的最大存儲位元組,隊列中每個項目所需的位元組數。項目按副本而不是引用排隊,因此這是將為每個過賬項目複製的位元組數。隊列中的每個項目都必須具有相同的大小。
返回:
如果創建成功則返回隊列句柄,無法創建則返回0
QueueHandle_t xQueueCreate( uxQueueLength, uxItemSize )
發送消息到隊列中
將項目發布到隊列中。該項目按副本而不是參考排隊。不得從中斷服務程式中調用此函數。有關在ISR中可以使用的替代方法,請參見xQueueSendFromISR()
參數:
xQueue:要發布項目的隊列的句柄。
pvItemToQueue:內容,指向要放在隊列中的項目的指針。創建隊列時已定義了隊列將要容納的項目大小,因此,這許多位元組將從pvItemToQueue複製到隊列存儲區域。
xTicksToWait:如果任務已滿,則該任務應阻止等待隊列上的可用空間的最長時間。如果將其設置為0並且隊列已滿,則呼叫將立即返回。時間以滴答周期定義,因此如果需要,應使用常數portTICK_PERIOD_MS轉換為實時。portMAX_DELAY表示一直等待
返回:
如果項目已成功發布,則為pdTRUE,否則為errQUEUE_FULL。
官方手冊建議使用函數如下:
發送消息到隊列頭:
BaseType_t xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait )
發送消息到隊列尾
BaseType_t xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait )
發送到消息隊列尾
BaseType_t xQueueSend( xQueue, pvItemToQueue, xTicksToWait )
上面三個函數本質上調用了xQueueGenericSend
其最後一個參數:
xCopyPosition:可以使用值queueSEND_TO_BACK將項目放置在隊列的後面,或將queueSEND_TO_FRONT放置在隊列的前面(對於高優先順序消息)
#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT ) #define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) #define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
發送消息到隊列中(複寫)
僅用於長度為1的隊列-因此隊列為空或已滿。
將項目發布到隊列中。如果隊列已滿,則覆蓋隊列中保存的值。該項目按副本而不是參考排隊。
不得從中斷服務程式中調用此函數。有關可以在ISR中使用的替代方法,請參見xQueueOverwriteFromISR()。
xQueueOverwrite()是一個宏,它調用xQueueGenericSend(),因此具有與xQueueSendToFront()相同的返回值。但是,pdPASS是唯一可以返回的值,因為即使隊列已滿,xQueueOverwrite()也會寫入隊列
參數
xQueue:將數據發送到的隊列的句柄。
pvItemToQueue:指向要放在隊列中的項目的指針。創建隊列時已定義了隊列將要容納的項目大小,因此,這許多位元組將從pvItemToQueue複製到隊列存儲區域。
BaseType_t xQueueOverwrite( xQueue,pvItemToQueue )
其本質調用的函數為
#define xQueueOverwrite( xQueue, pvItemToQueue ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE )
中斷服務函數中發消息到隊列
將項目發布到隊列中。在中斷服務程式中可以安全地使用此功能。
項目通過複製而不是引用進行排隊,因此最好僅將小項目排隊,尤其是從ISR調用時。在大多數情況下,最好存儲一個指向正在排隊的項目的指針。
參數:
xQueue:要發布項目的隊列的句柄
pvItemToQueue:內容,指向要放在隊列中的項目的指針。創建隊列時已定義了隊列將要容納的項目大小,因此,這許多位元組將從pvItemToQueue複製到隊列存儲區域。
pxHigherPriorityTaskWoken:判斷是否需要手動切換上下文,如果發送到隊列導致任務取消阻止,並且未阻止的任務的優先順序高於當前運行的任務,則xQueueGenericSendFromISR()會將* pxHigherPriorityTaskWoken設置為pdTRUE。如果xQueueGenericSendFromISR()將此值設置為pdTRUE,則應在退出中斷之前請求上下文切換taskYIELD ();。
發送消息到隊列頭
BaseType_t xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken )
發送消息到隊列尾
BaseType_t xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken )
發送到消息隊列尾
BaseType_t xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken )
上面三個函數本質上調用了xQueueGenericSendFromISR
其最後一個參數
xCopyPosition:可以使用值queueSEND_TO_BACK將項目放置在隊列的後面,或將queueSEND_TO_FRONT放置在隊列的前面(對於高優先順序消息)
#define xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT ) #define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) #define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
中斷服務函數中發消息到隊列(複寫)
可以在中斷服務程式(ISR)中使用的xQueueOverwrite()版本。
僅用於可容納單個項目的隊列-因此隊列為空或已滿。
將項目發布到隊列中。如果隊列已滿,則覆蓋隊列中保存的值。該項目按副本而不是參考排隊。
參數:
xQueue:要發布項目的隊列的句柄
pvItemToQueue:指向要放在隊列中的項目的指針。創建隊列時已定義了隊列將要容納的項目大小,因此,這許多位元組將從pvItemToQueue複製到隊列存儲區域。
pxHigherPriorityTaskWoken:如果發送到隊列導致任務取消阻止,並且未阻止的任務的優先順序高於當前運行的任務,則xQueueOverwriteFromISR()會將* pxHigherPriorityTaskWoken設置為pdTRUE。如果xQueueOverwriteFromISR()將此值設置為pdTRUE,則應在退出中斷之前請求上下文切換。
返回
QueueOverwriteFromISR()是一個調用xQueueGenericSendFromISR()的宏,因此其返回值與xQueueSendToFrontFromISR()相同。但是,pdPASS是唯一可以返回的值,因為即使隊列已滿,xQueueOverwriteFromISR()也會寫入隊列。
BaseType_t xQueueOverwriteFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken )
本質調用函數
#define xQueueOverwriteFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueOVERWRITE )
獲取消息隊列消息
從隊列中接收項目。該項目以副本形式接收,因此必須提供足夠大小的緩衝區。創建隊列時定義了複製到緩衝區中的位元組數。
不得在中斷服務程式中使用此功能。另請參見xQueueReceiveFromISR。
參數:
xQueue:要從中接收項目的隊列的句柄。
pvBuffer:指向將接收到的項目複製到的緩衝區的指針。
xTicksToWait:如果隊列在調用時為空,則任務應等待等待接收項目的最長時間。時間以滴答周期定義,因此如果需要,應使用常數portTICK_PERIOD_MS轉換為實時。如果隊列為空並且xTicksToWait為0,則xQueueGenericReceive()將立即返回。portMAX_DELAY表示一直等待
返回:
如果從隊列成功接收到項目,則為pdTRUE,否則為pdFALSE。
僅僅取出消息隊列中的內容,不刪除已經取出的消息
BaseType_t xQueuePeek( xQueue, pvBuffer, xTicksToWait )
取出並刪除接收的內容
BaseType_t xQueueReceive( xQueue, pvBuffer, xTicksToWait )
上面兩個函數本質上調用了xQueueReceiveFromISR
其最後一個參數
xJustPeek:當設置為true時,從隊列接收的項目實際上並未從隊列中刪除-意味著對xQueueReceive()的後續調用將返回相同的項目。設置為false時,將從隊列中接收的項目也將從隊列中刪除。
#define xQueuePeek( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdTRUE ) #define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE )
中斷服務函數中獲取隊列消息
xQueuePeekFromISR
可以從中斷服務程式(ISR)
僅僅取出消息隊列中的內容,不刪除已經取出的內容(項目)
xQueue:要從中接收項目的隊列的句柄。
pvBuffer:指向將接收到的項目複製到的緩衝區的指針
返回:
如果從隊列成功接收到項目,則為pdTRUE,否則為pdFALSE。
BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue,void * const pvBuffer )
xQueueReceiveFromISR
可以從中斷服務程式(ISR)取出並刪除接收的內容(項目)
參數
xQueue:要從中接收項目的隊列的句柄。
pvBuffer:指向將接收到的項目複製到的緩衝區的指針。
pxHigherPriorityTaskWoken:任務可能等待隊列中的可用空間被阻塞。如果xQueueReceiveFromISR使此類任務解除阻止,則* pxTaskWoken將設置為pdTRUE,否則* pxTaskWoken將保持不變。如果值為pdTRUE,則應在退出中斷之前請求上下文切換taskYIELD ();。
注意:
此函數比xQueuePeekFromISR多一個參數,是因為如果取出隊列數據後,刪除該項目可能會使得觸發優先順序高的任務停止阻塞,所以要查看返回值啟動任務切換
返回:
如果從隊列成功接收到項目,則為pdTRUE,否則為pdFALSE。
BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue,void * const pvBuffer,BaseType_t * const pxHigherPriorityTaskWoken )
獲取存儲在隊列中的消息數
參數:
xQueue:查詢隊列的句柄
返回:
隊列中可用的消息數,未取出的消息數
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue )
獲取隊列的剩餘可用條數
返回隊列中可用的可用空間數。這等於如果沒有刪除任何項目,則在隊列變滿之前可以發送到隊列的項目數
參數:
xQueue:查詢隊列的句柄
返回:
隊列中剩餘可用條數(最大為創建任務時的uxQueueLength 值,其表示隊列可以存儲的條數,隊列可以包含的最大項目數。)
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue )
隊列刪除
刪除隊列-釋放分配用於存儲放置在隊列中的項目的所有記憶體。
參數:要刪除的隊列的句柄
void vQueueDelete( QueueHandle_t xQueue )
重置隊列
將隊列重置回其原始的空狀態。如果成功重置隊列,則返回pdPASS。如果無法重置隊列,則返回pdFAIL,因為隊列上有阻塞的任務正在等待從隊列接收或發送到隊列,因此無法重置隊列。
參數:xQueue:要重置的隊列
BaseType_t xQueueReset( xQueue )
小試牛刀(隊列)
1 #include <stdio.h> 2 #include "freertos/FreeRTOS.h"//freertos相關 3 #include "freertos/task.h" 4 #include "freertos/queue.h" 5 6 //自定義隊列消息,不一定是結構體 7 struct myMsg 8 { 9 uint32_t d_id; 10 char d_msg[50]; 11 }; 12 //存儲消息隊列句柄 13 QueueHandle_t Dong_uint32_Queue, Dong_myMsg_Queue; 14 15 //創建消息隊列 16 void dong_creat_queue() 17 { 18 //創建能夠存儲10個uint32_t的隊列 19 Dong_uint32_Queue = xQueueCreate( 10, sizeof( uint32_t ) ); 20 if( Dong_uint32_Queue == 0 ) 21 { 22 printf("Dong_uint32_Queue 隊列創建失敗"); 23 } 24 //創建一個能夠包含10個指向myMsg結構的隊列。 25 //此處傳遞的是結構體,並不是結構體指針 26 Dong_myMsg_Queue = xQueueCreate( 10, sizeof(struct myMsg) ); 27 if( Dong_myMsg_Queue == 0 ) 28 { 29 printf("Dong_myMsg_Queue 隊列創建失敗"); 30 } 31 } 32 33 //任務0處理函數 34 void Task_Run_0(){ 35 uint32_t resi=0; 36 struct myMsg resmymsg; 37 while(1){ 38 printf("rn【%s】////////開始接收/////////rn","任務0"); 39 40 xQueueReceive(Dong_uint32_Queue,&resi,portMAX_DELAY); 41 printf("【%s】獲取到Dong_uint32_Queue內容:%drn","任務0",resi); 42 xQueueReceive(Dong_myMsg_Queue,&resmymsg,portMAX_DELAY); 43 printf("【%s】獲取到Dong_myMsg_Queue內容:%d(%s)rn","任務0",resmymsg.d_id,resmymsg.d_msg); 44 45 printf("rn【%s】////////完成接收/////////rn","任務0"); 46 } 47 } 48 49 //主函數,優先順序為1 50 void app_main() 51 { 52 printf("rn--------------DONGIXAODONG FreeRTOS-----------------rn"); 53 54 //創建消息隊列 55 dong_creat_queue(); 56 57 //啟動任務0,簡化 58 //函數,名字,位元組大小,參數,優先順序[0,16](16最優先),任務句柄 59 BaseType_t t0res=xTaskCreate(Task_Run_0,"DONG Task_Run_0",1024*2,NULL,7,NULL); 60 if(t0res==pdPASS){ 61 printf("任務0啟動成功....rn"); 62 } 63 64 BaseType_t res=0; 65 uint32_t i=0; 66 struct myMsg mymsg; 67 while(1){ 68 //賦值 69 i++; 70 mymsg.d_id=i; 71 sprintf(mymsg.d_msg,"dongxiaodong%d",i); 72 73 //輸出發送標誌 74 printf("rn【%s】*****開始發送*******rn","main"); 75 76 //發送隊列1 77 res= xQueueGenericSend( Dong_uint32_Queue, ( void * ) &i,( TickType_t ) 10,queueSEND_TO_BACK ); 78 if(res == pdPASS ) 79 { 80 printf("【%s】Dong_uint32_Queue 發送成功rn","main"); 81 } 82 //發送隊列2 83 res= xQueueGenericSend( Dong_myMsg_Queue, ( void * ) &mymsg, ( TickType_t ) 0, queueSEND_TO_BACK ); 84 if(res == pdPASS ) 85 { 86 printf("【%s】Dong_myMsg_Queue 發送成功rn","main"); 87 } 88 89 printf("rn【%s】*****結束髮送*******rn","main"); 90 91 vTaskDelay(3000 / portTICK_PERIOD_MS);//延時3S 92 } 93 }
因篇幅問題,剩下相關筆記將於下一篇文章進行總結,剩下部分包括:
-
訊號量
-
計時器
-
事件組
-
任務通知
參考:
https://zhidao.baidu.com/question/7412988.html
ESP32文檔:https://docs.espressif.com/projects/esp-idf/en/v4.0/api-reference/system/freertos.html
正點原子