FreeRTOS相關轉載-(朱工的專欄)

FreeRTOS系列第1篇---爲何選擇FreeRTOS?

1.爲何學習RTOS?

         做爲基於ARM七、Cortex-M3硬件開發的嵌入式工程師,我一直反對使用RTOS。不只由於不恰當的使用RTOS會給項目帶來額外的穩定性風險,更重要的是我認爲絕大多數基於ARM七、Cortex-M3硬件的項目,還沒複雜到使用RTOS的地步,使用狀態機就足夠了。html

         對於現代的微處理器,特別是資源相對豐富ARM七、Cortex-M3硬件來講,RTOS佔用的硬件資源已經愈來愈能夠忽略。因此在當今環境下,咱們無需擔憂RTOS會拖累性能。相反,RTOS提供的事件驅動型設計方式,使得RTOS只是在處理實際任務時纔會運行,這可以更合理的利用CPU。在實際項目中,若是程序等待一個超時事件,傳統的無RTOS狀況下,要麼在原地一直等待而不能執行其它任務,要麼使用複雜(相對RTOS提供的任務機制而言)的狀態機機制。若是使用RTOS,則能夠很方便的將當前任務阻塞在該事件下,而後自動去執行別的任務,這顯然更方便,而且能夠高效的利用CPU。處理這類事件,是我使用RTOS的最大動力,但考慮到系統的穩定性,我不得再也不三權衡RTOS可能帶來的一些弊端:git

  1. 大多數RTOS代碼都具備必定規模,任何代碼均可能帶來BUG,況且是代碼具備必定規模的RTOS,所以引入RTOS的同時也可能會引入該RTOS的BUG,這些RTOS自己的BUG一旦被觸發,影響多是是災難性的。
  2. 熟練的使用RTOS是一項技能,須要專業的知識儲備和長期的經驗積累。不將RTOS分析透徹,很容易爲項目埋下錯誤。典型的,像中斷優先級、任務堆棧分配、可重入等,都是更容易出錯的地方。
  3. RTOS的優先級嵌套使得任務執行順序、執行時序更難分析,甚至變成不可能。任務嵌套對所需的最大堆棧RAM大小估計也變得困難。這對於不少對安全有嚴格要求的場合是不可想象的。
  4. RTOS應該用於任務複雜的場合,以致於對任務調度的需求能夠抵消RTOS所帶來的穩定性影響,但大部分的應用並不是複雜到須要RTOS。

         以上緣由是我拒絕在實際項目中使用RTOS的理由,可是否使用RTOS跟是否學習RTOS徹底是兩碼事。我認爲任何嵌入式軟件設計人員都應該至少學習一種RTOS,不只是須要掌握RTOS背後的操做系統原理、學習RTOS的編程方式,更是爲未來作準備。程序員

         即使我認爲如今的物聯網有點言過其實,但我依然看好物聯網的發展前景。隨着物聯網的發展,將來的嵌入式產品必然更爲複雜、鏈接性更強以及須要更豐富的用戶界面。當處理這些任務時,一個好的RTOS就變得不可缺乏了。算法

         書到用時方恨少,我但願本身永遠不會有這種感受。因此從如今起,我要開始深刻一個RTOS,探索它背後的原理,掌握其編程方法,避免其缺陷和陷阱,並將它安全的用在未來的項目中。編程

2.爲何選用FreeRTOS?

         對比了許多RTOS,最終選擇FreeRTOS,緣由是多方面的:小程序

  1. SafeRTOS即是基於FreeRTOS而來,前者是通過安全認證的RTOS,所以對於FreeRTOS的安全性也有了信心。
  2.  大量開發者使用,並保持高速增加趨勢。20十一、20十二、201三、201四、201五、2017年(暫時沒有2016年的數據)的EEtimes雜誌嵌入式系統市場報告顯示,FreeRTOS在RTOS內核使用榜和RTOS內核計劃使用榜上都名列前茅。更多的人使用能夠促進發現BUG,加強穩定性。
  3. 簡單。內核只有3個.c文件,所有圍繞着任務調度,沒有任何其它干擾,便於理解學習。並且,我根本不須要其它繁多的功能,只要任務調度就夠了。
  4. 文檔齊全。在FreeRTOS官方網站上,能夠找到全部你須要的資料。
  5. 免費、開放源碼。徹底能夠免費用於商業產品,開放源碼更便於學習操做系統原理、從全局掌握FreeRTOS運行機理、以及對操做系統進行深度裁剪以適應本身的硬件。

         學習的資料來源主要是FreeRTOS的官方網站(www.freertos.org)和源代碼。FreeRTOS的創始人RichardBarry編寫了大量的移植代碼和配套文檔,我只不過是沿着Richard Barry鋪好的路前進,因此,這沒什麼困難的。數組

         最後,感謝RichardBarry的付出,感謝Richard Barry的無私開源精神!xcode

 

附錄1:2010~2017年EEtimes雜誌嵌入式市場調查報告有關RTOS使用榜截圖緩存


2010和2011年RTOS使用榜安全

 

2012和2013年RTOS使用榜

 

2013年和2014年RTOS使用榜

 


2014年和2015年RTOS使用榜


2017年RTOS使用榜

 

 

FreeRTOS系列第2篇---FreeRTOS入門指南

FreeRTOS能夠被移植到不少不一樣架構的處理器和編譯器。每個RTOS移植都附帶一個已經配置好的演示例程,能夠方便快速啓動開發。更好的是,每一個演示例程都附帶一個說明網頁,提供如何定位RTOS演示工程源代碼、如何編譯演示例程、如何配置硬件平臺的所有信息。

      演示例程說明網頁還提供基本的RTOS移植細節信息,包括如何編寫FreeRTOS兼容的中斷服務例程,不一樣架構的中斷處理會稍有不一樣。

      經過下面的簡單說明,能夠在幾分鐘內運行RTOS。

1.查找相關文檔頁

       FreeRTOS具備詳細的開發說明文檔,能夠在其官方網站上查看。首先打開官方網站,目前的網站地址是:http://www.freertos.org。在首頁左側的導航欄中,展開"Supported Devices & Demos"菜單項,單擊"OfficiallySupported Demos"連接,去查看FreeRTOS支持的微控制器製造商列表。單擊微控制器製造商名稱,進入具體的製造商文檔頁面列表。

2.獲取RTOS源代碼

       到FreeRTOS官方網站下載源碼,下載包包含RTOS內核源碼和官方移植演示工程。解壓縮後放到合適的目錄下。(若是你不想訪問慢吞吞的國外網站,我在CSDN作了一個鏡像,能夠 點擊此處 ,這篇文章中有最新的大部分FreeRTOS源碼包下載連接)

       每個RTOS移植包都附帶有預先配置好的演示例程 ,已經建立好了全部必須的RTOS源文件幷包含了必須的RTOS頭文件。推薦在提供的演示例程的基礎上進行本身的FreeRTOS應用編程。

3.FreeRTOS源碼目錄結構

       FreeRTOS下載包中包含每一個處理器移植和演示例程的源碼。將全部移植包放入一個下載文件中大大簡化了分類處理,可是下載包中的文件數量也多的驚人!不管如何,目錄結構仍是很是簡單的,而且FreeRTOS實時內核僅僅只有3個文件(若是須要,還有一些附加文件,好比軟件定時器、事件組以及協程)。

      下載包目錄包含兩個子目錄:FreeRTOS和FreeRTOS-Plus。以下所示:

  • FreeRTOS-Plus          包含FreeRTOS+組件和演示例程;
  • FreeRTOS                   包含FreeRTOS實時內核源文件和演示例程。

      FreeRTOS-Plus目錄樹包含多個自述文件(Readme)。接下來本文只描述FreeRTOS內核的核心源文件和演示例程,它們又被分紅兩個主要的子目錄,以下所示:

  1. FreeRTOS  
  2.   |+-- Demo     包含演示例程工程;  
  3.   |+-- Source   包含實時內核源文件。  

      RTOS代碼的核心包含在三個文件中:tasks.c、queue.c、list.c。這三個文件位於FreeRTOS/Source目錄。在該目錄下還包含三個可選的文件:timers.c、event_groups.c、croutine.c,分別實現軟件定時、事件組和協程功能。

      FreeRTOS/Source目錄結構以下所示:

  1. FreeRTOS  
  2.     | +-- Source  FreeRTOS內核代碼文件  
  3.     |   |+-- include          FreeRTOS內核代碼頭文件  
  4.     |   |+-- Portable         處理器特定代碼  
  5.     |   |  |+--Compiler x     支持編譯器x的全部移植包  
  6.     |   |  |+--Compiler y     支持編譯器y的全部移植包  
  7.     |   |  |+--MemMang        內存堆實現範例  

      每一個支持的處理器架構須要一小段與處理器架構相關的RTOS代碼。這個是RTOS移植層,它位於FreeRTOS/Source/Portable/[相應編譯器]/[相應CPU架構]子目錄。

      對於FreeRTOS,堆棧設計也屬於移植層。FreeRTOS/Source/portable/MemMang目錄下heap_x.c文件給出了多種堆棧方案,後續文章將會詳細介紹堆棧操做。

      移植層目錄舉例:

  • 若是在GCC編譯器下使用TriCore1782:TriCore特定文件(port.c)位於FreeRTOS/Source/Portable/GCC/TriCore_1782目錄下。FreeRTOS/Source/Portable 子目錄下的全部文件,除了FreeRTOS/Source/Portable/MemMang目錄外均可以忽略或刪除。
  • 若是在IAR編譯器下使用Renesas RX600:RX600特定文件(port.c)位於FreeRTOS/Source/Portable/IAR/RX600目錄下。FreeRTOS/Source/Portable 子目錄下的全部文件,除了FreeRTOS/Source/Portable/MemMang目錄外均可以忽略或刪除。

      FreeRTOS下載包中還包含各類處理器架構和編譯器的演示例程。大多數的演示例程代碼對全部移植都是通用的,位於FreeRTOS/Demo/Common/Minimal目錄。FreeRTOS/Demo/Common/Full目錄下的是歷史遺留代碼,僅用於PC。

      FreeRTOS/Demo目錄結構以下所示:

  1. FreeRTOS  
  2.   |+-- Demo  
  3.   |  |+-- Common          全部例程均可以使用的演示例程文件  
  4.   |  |+-- Dir x           用於x平臺的演示例程工程文件  
  5.   |  |+-- Dir y           用於y平臺的演示例程工程文件  

      FreeRTOS/Demo目錄下剩餘的子目錄包含預先配置好的工程,能夠用於構建我的演示例程。子目錄的命名與移植平臺和編譯器相關。每個RTOS移植包都有本身的說明文檔。

      演示例程目錄舉例:

  • 若是以英飛凌TriBoard開發板硬件構建TriCoreGCC演示例程:TriCore演示例程工程文件位於FreeRTOS/Demo/TriCore_TC1782_TriBoard_GCC目錄。目錄FreeRTOS/Demo下的全部子目錄(Common目錄除外)均可以忽略或刪掉。
  • 若是以RX62N硬件構建Renesas RX600 IAR演示例程:IAR工程文件位於FreeRTOS/Demo/RX600_RX62N-RDK_IAR目錄。目錄FreeRTOS/Demo下的全部子目錄(Common目錄除外)均可以忽略或刪掉。

4.編譯工程

      根據上一節FreeRTOS源碼目錄結構說明的RTOS演示工程的所在的位置,打開並編譯演示工程。

5.運行演示例程

      演示例程附帶的說明網頁會介紹如何配置硬件、下載程序和執行演示例程。說明網頁還會提供演示例程的功能信息,這樣你就能夠判斷演示例程執行是否正確。

 

 

 

FreeRTOS系列第3篇---FreeRTOS移植指南

 FreeRTOS下載包中已經包含不少演示例程- 每個例程都是針對於:

  1. 特定的微控制器;
  2. 特定的開發工具(編譯器、調試器等等);
  3. 特定的硬件平臺(樣機或評估板)。

      能夠在官方網站首頁左側的樹形菜單 'Supported Devices' 中找到這些例程介紹。 

      惋惜的是不可能爲全部微控制器、編譯器和評估板提供演示例程。所以,官方提供的演示例程可能不徹底符合你正在使用的開發平臺。本章描述如何經過修改或合併官方提供的演示例程,來知足本身的開發平臺需求(包括微處理器和編譯器)。

      修改一個現有的評估板例程,使之運行到另外一個同類評估板上,一般是比較簡單的,稍微複雜些的是跨編譯器移植。本文介紹這兩狀況下的修改,只是對類似的平臺有效。然而,將FreeRTOS移植到一個全新的平臺、未支持的處理器架構,並非件簡單的事情。本文不討論如何將FreeRTOS移植到一個全新平臺。

1.修改例程使之運行到不一樣評估板

      本節描述如何經過修改一個官方提供的演示例程,使之運行到另外一個評估板,這裏兩個評估板使用同系列微處理器,使用相同編譯器。在這個例子中,將運行於SAM7S-EK硬件開發板上的IAR SAM7S演示例程,修改使之運行到Olimex SAM7-P64開發板。(注:兩塊開發板都是使用ATMEL公司的ARM7微處理器,前者使用AT91SAM7S256,後者使用AT91SAM7S64)

1.1初始編譯

      做爲修改練習的起點,被修改的演示例程是要能使用的。所以,在未作任何修改以前,首先檢查下載的演示例程可否被正確的編譯。絕大多數狀況下,演示例程編譯後是沒有任何錯誤和警告的。

      關於演示例程所在目錄,參考《FreeRTOS系列第2篇---FreeRTOS入門指南》一文的第三節。

1.2修改LED IO端口

       LED燈是用來指示演示例程運行的最簡單方法,所以點亮新硬件平臺上的LED燈一般是最容易的。

      兩個不一樣評估板上的LED鏈接到相同的IO端口一般是不太可能的,所以,一些小幅度修改是必須的。

      在partest.c文件中的vParTestInitialise() 函數包含IO端口的模式和方向配置。在main.c文件中的prvSetupHardware()函數包含更多的硬件初始化(好比,使能IO外設的時鐘模塊),可能須要根據不一樣的使用進行一些修改。

      根據目標評估板的硬件,在上面兩個函數中作必要的修改,而後寫一段簡單程序,來檢查硬件LED是否無缺。這個簡單程序不使用FreeRTOS,只是爲了確保硬件LED可以正常工做。所以,註釋掉以前的main()函數,使用下面的例子代替:

  1. int main( void )   
  2. {   
  3.     volatile unsigned long ul;   /* 禁止編譯器優化此變量 */   
  4.     /* 初始化LED IO爲輸出-注:prvSetupHardware()也可能會被調用*/   
  5.     vParTestInitialise();   
  6.     /*不斷開啓LED */   
  7.     for( ;; )   
  8.     {   
  9.         /* 咱們暫時不使用RTOS,這裏只是使用一個很是粗糙的延時*/   
  10.         for( ul = 0; ul < 0xfffff; ul++ )   
  11.         {   
  12.         }   
  13.         /* 開啓4個LED */   
  14.         vParTestToggleLED( 0 );   
  15.         vParTestToggleLED( 1 );   
  16.         vParTestToggleLED( 2 );   
  17.         vParTestToggleLED( 3 );   
  18.     }   
  19.     return 0;   
  20. }  

1.3 RTOS調度器簡介

      一旦肯定硬件LED能夠正常工做,就能夠恢復原來的main()函數。

      做爲入門級的多任務應用程序應該儘可能的簡單,LED閃爍測試程序經常擔任這樣的角色,能夠堪比經典的「Hello Wold」。這個任務幾乎在全部演示例程中都能看到,在main()函數中調用vStartLEDFlashTasks() (使用協程版本時調用vStartFlashCoRoutines())來實現。若是你使用的演示例程main()函數中並無調用vStartLEDFlashTasks()(或vStartFlashCoRoutines()),那麼須要你將FreeRTOS/Demo/Common/Minimal/Flash.c文件添加到你的工程,並在main()函數手動的增長vStartLEDFlashTasks()函數。

      除了調用vStartLEDFlashTasks()外,註釋掉全部用於啓動一個或多個演示任務的函數。最後的main()函數僅調用三個函數:prvSetupHardware()、vStartLEDFlashTasks()和vTaskStartScheduler()。例如(基於典型的main()函數):

  1. int main( void )   
  2. {   
  3.     /* 設置用於演示的微控制器硬件 */   
  4.     prvSetupHardware();   
  5.     /* 留下這個函數 */   
  6.     vCreateFlashTasks();    
  7.     /* 全部的其它建立任務的函數通通註釋掉         
  8.         vCreatePollQTasks(); 
  9.         vCreateComTestTasks(); 
  10.         //等等… 
  11.         xTaskCreate( vCheckTask,"check", STACK_SIZE, NULL, TASK_PRIORITY, NULL ); 
  12.     */   
  13.     /*啓動RTOS調度器. */   
  14.     vTaskStartScheduler();   
  15.     /* 永遠不會執行到這裏! */   
  16.     return 0;   
  17. }  

      這是一個很是簡單的應用程序,正確執行後,LED0~2(包括2)或分別按照不一樣的頻率閃爍。

1.4 收尾工做

      一旦簡單的LED閃爍例程正確執行後,你能夠恢復以前註釋掉的全部的演示任務。

      如下要點需牢記:

  • 若是你使用的演示例程最初並無調用vTaskCreateFlashTasks()函數,而是手動的增長了這個函數,那麼應該再手動的刪除掉這個函數。主要有兩個方面的緣由:第一是這個LED閃爍任務用到的IO可能也被演示例程的其它任務使用,第二是演示例程可能已經佔用了全部的RAM,已經沒有空餘RAM用來增長新的任務。
  • 標準的「通信測試(comtest)」(若是演示例程中有的話)任務使用到微控制器的一個UART外設。檢測硬件是可用的。
  • 有些外設不進行修改就想用於任何不一樣的硬件或接口是不可能的,好比LCD。

2.合併或修改官方演示工程

      本節主要描述如何修改一個現存的工程或者按照需求合併兩個現存的工程。好比,你但願使用GCC編譯器建立一個STR9演示工程(demo project),而且你下載的FreeRTOS軟件包中並無GCC版本的STR9演示例程,可是FreeRTOS下載包中有IAR版本的STR9演示例程和GCC版本的STR75x演示例程。則能夠經過這兩個現存的工程來創       建GCC版本的STR9演示工程。能夠有兩種方式完成:

      使用GCC版本的STR75x演示工程,修改使之符合指定的微處理器(STR9評估板上的微處理器)。

      使用GCC建立一個新的工程。從IAR版本的STR9演示工程中獲取文件和配置信息,使之符合GCC編譯器需求。

2.1識別用於特定微控制器的FreeRTOS內核文件

      對於一個特定平臺,大多數(不是所有)硬件接口代碼包含在一個叫作FreeRTOS/source/portable/[編譯器]/[微控制器/port.c的文件中,和它對應的頭文件是FreeRTOS/source/portable/[編譯器]/[微控制器]/portmacro.h。

     對於一些編譯器來講,port.c和portmacro.h就是所須要的所有硬件接口代碼。另外一些還須要一些彙編文件,這些文件叫作portasm.s或者portasm.asm。

     最後,僅對於ARM7 GCC移植,一樣存在一個相似的硬件接口文件:portISR.c,portISR.c是從port.c中分離出來的,這些代碼必須在ARM模式下編譯,port.c中剩餘的代碼既能夠在ARM模式下編譯,也可在THUMB模式下編譯。

2.2識別用於特定編譯器的文件

      編譯器能夠爲嵌入式系統提供某些特定的C語言擴展。好比某個特定關鍵字能夠標識出一個函數是中斷處理服務函數。

      擴展的C語言部分,是不屬於標準C語言規範的。所以,編譯器與編譯器之間是有差異的。FreeRTOS的文件中就包含相似的非標準C語言語法,在文件夾FreeRTOS/source/portable中(上文中提到的特定微控制器硬件接口代碼也在這個文件中)。此外,一些演示例程會使用到中斷服務程序,這些中斷服務程序並不屬於FreeRTOS的一部分,而且如何定義和使用這些中斷服務程序也是編譯器所特定的。

2.3硬件底層文件

      C啓動文件和連接腳本都屬於處理器和編譯器特定的。不推薦嘗試從無到有的建立這些文件,應該到FreeRTOS演示工程中尋找一個合適的來修改。

      要特別當心ARM7啓動文件。它必須將IRQ中斷服務程序入口地址配置到快速中斷處理向量表或者普通中斷向量表中。這兩種狀況,演示工程都提供了例子。

      連接腳本必須正確的描述當前使用處理器的內存映射。

2.4工程設置

      每個工程一般都會定義一些宏,這些預處理宏定義了一些要被編譯的特定的硬件接口代碼。要包含portmacro.h文件才能識別這些宏。好比,當使用GCC編譯MegaAVR硬件接口代碼時,宏GCC_MEGA_AVR必須被定義;當使用IAR編譯MegaAVR硬件接口代碼時,宏IAR_MEGA_AVR必須被定義等等。參考演示例程工程以及FreeRTOS/source/include/portable.h文件能夠查找當前工程定義了那些宏。若是預處理宏未定義,那麼portmacro.h文件所在目錄的路徑必須被包含到預處理器的搜索路徑中。

      其它的編譯器設置,好比優化選項,也是很關鍵的。能夠參考提供的演示工程。

具備IDE的編譯器一般具備目標微控制器選項並將它做爲工程設置的一部分,因此新的工程也必須適應新的目標微控制器,一樣的,若是使用到makefile文件,則makefile文件也必須更新以符合新的目標微控制器。

2.5配置系統節拍時鐘中斷

      調用函數prvSetupTimerInterrupt()來配置系統節拍中斷,這個函數能夠在如下路徑的文件中找到:FreeRTOS/source/portable/[compiler]/[microcontroller]/port.c

2.6 RAM和ROM的使用

      FreeRTOS內存管理一章中描述了FreeRTOS如何使用RAM,而且描述了RAM是如何分配給RTOS內核的。

      若是你要將演示例程移植到一個RAM稍小的微處理器上,那麼你可能須要減小configTOTAL_HEAP_SIZE的值(位於FreeRTOSConfig.h),而且減小演示例程的任務個數。能夠經過簡單的註釋掉不須要的任務來實現。

      若是你要將演示例程移植到一個ROM較小的微處理器中,那麼你可能須要減小應用例程的文件數目,他們位於FreeRTOS/Demo/common文件夾目錄下。同時你還要刪除main函數中對他們的調用函數。

 

注:可能你是經過搜索引擎找到這篇文章,滿懷但願的點進來,覺得能解決本身移植的全部問題,可是看完後卻發現本文站的角度過高,並非特別適合對移植一無所知的你。先別急着覺得本文是標題黨而點踩,可能有一篇文章適合你,這篇文章以Cortex-M3硬件平臺爲例,詳細的介紹移植過程,請點擊這裏

 

 

 

FreeRTOS系列第4篇---FreeRTOS編碼標準及風格指南

1.編碼標準

      FreeRTOS的核心源代碼聽從MISRA編碼標準指南。這個標準篇幅稍長,你能夠在MISRA官方網站花少許錢買到,這裏再也不復制任何標準。

      FreeRTOS源代碼不符合MISRA標準的項目以下所示:

  • 有兩個API函數有多個返回點。MISRA編碼標準強制規定:一個函數在其結尾應該有單一的返回點。
  • 指針算數運算,在建立任務時,爲了兼容八、1六、20、2四、32位總線,不可避免的使用了指針算數運算。MISRA編碼標準強制規定:指針的算術運算只能用在指向數組或數組元素的指針上。
  • 默認狀況下,跟蹤宏爲空語句,所以不符合MISRA的規定。MISRA編碼標準強制規定:預處理指令在句法上應該是有意義的。

      FreeRTOS能夠在不少不一樣編譯器中編譯,其中的一些編譯器比同類有更高級特性。由於這個緣由,FreeRTOS不使用任何非C語言標準的特性或語法。一個例外狀況是頭文件stdint.h。在文件夾FreeRTOS/Source/include下包含一個叫作stdint.readme的文件,若是你的編譯器不提供stdint類型定義,能夠將stdint.readme文件重命名爲stdint.h。

2命名規則

      RTOS內核和演示例程源代碼使用如下規則:

      1> 變量

  • uint32_t類型的變量使用前綴ul,這裏’u’表示’unsigned’,’l’表示’long’
  • uint16_t類型的變量使用前綴us,這裏’u’表示’unsigned’,’s’表示’short’
  • uint8_t類型的變量使用前綴uc,這裏’u’表示’unsigned’,’c’表示’char’
  • 非stdint類型的變量使用前綴x,好比基本的Type_t和TickType_t類型,這些類型在移植層定義,定義成符合處理器架構的最高效類型;
  • 非stdint類型的無符號變量使用前綴ux,好比UbaseType_t(unsigned BaseType_t)
  • size_t類型的變量使用前綴x;
  • 枚舉類型變量使用前綴e
  • 指針類型變量在類型基礎上附加前綴p,好比指向uint16_t的指針變量前綴爲pus
  • 與MISRA指南一致,char類型變量僅被容許保存ASCII字符,前綴爲c
  • 與MISRA指南一致,char *類型變量僅容許指向ASCII字符串,前綴爲pc

      2> 函數

  • 在文件做用域範圍的函數前綴爲prv
  • API函數的前綴爲它們的返回類型,當返回爲空時,前綴爲v
  • API函數名字起始部分爲該函數所在的文件名。好比vTaskDelete函數定義在tasks.c,而且該函數返回空。

      3> 宏

  • 宏的名字起始部分爲該宏定義所在的文件名的一部分。好比configUSE_PREEMPTION定義在FreeRTOSConfig.h文件中。
  • 除了前綴,宏剩下的字母所有爲大寫,兩個單詞間用下劃線(’_’)隔開。

3數據類型

      只有stdint.h和RTOS本身定義的數據類型能夠使用,但也有例外狀況,以下所示:

  • char:與MISRA編碼標準指南一致,char類型變量僅被容許保存ASCII字符
  • char *:與MISRA編碼標準指南一致,char *類型變量僅容許指向ASCII字符串。當標準庫函數指望一個char *參數時,這樣作能夠消除一些編譯器警告;特別是考慮到有些編譯器將char類型當作signed類型,還有些編譯器將char類型當作unsigned類型。

      有三種類型會在移植層定義,它們是:

  • TickType_t:若是configUSE_16_BIT_TICKS爲非零(條件爲真),TickType_t定義爲無符號16位類型。若是configUSE_16_BIT_TICKS爲零(條件爲假),TickType_t定義爲無符號32位類型。注:32位架構的微處理器應設置configUSE_16_BIT_TICKS爲零。
  • BaseType_t:定義爲微處理器架構效率最高的數據類型。好比,在32位架構處理器上,BaseType_t應該定義爲32位類型。在16位架構處理器上,BaseType_t應該定義爲16位類型。若是BaseType_t定義爲char,對於函數返回值必定要確保使用的是signed char,不然可能形成負數錯誤。
  • UbaseType_t:這是一個無符號BaseType_t類型

3.4風格指南

  • 縮進:縮進使用製表符,一個製表符等於4個空格。
  • 註釋:註釋單行不超過80列,特殊狀況除外。不使用C++風格的雙斜線(//)註釋
  • 佈局:FreeRTOS的源代碼被設計成儘量的易於查看和閱讀。下面的代碼片中,第一部分展現文件佈局,第二部分展現C代碼設計格式。
  1. /* 首先在這裏包含庫文件... */    
  2. #include <stdlib.h>    
  3.       
  4. /* ...而後是FreeRTOS的頭文件... */    
  5. #include "FreeRTOS.h"    
  6.       
  7. /* ...緊接着包含其它頭文件. */    
  8. #include "HardwareSpecifics.h"    
  9.      
  10. /* 隨後是#defines, 在合理的位置添加括號. */    
  11. #define A_DEFINITION    ( 1 )    
  12.     
  13. /*  
  14.  * 隨後是Static (文件內部的)函數原型,   
  15.  * 若是註釋有多行,參照本條註釋風格---每一行都以’*’起始.  
  16.  */    
  17. static void prvAFunction( uint32_t ulParameter );    
  18.       
  19. /* 文件做用域變量(本文件內部使用)緊隨其後,要在函數體定義以前. */    
  20. static BaseType_t xMyVariable.    
  21.     
  22. /* 每個函數的結束都有一行破折號,破折號與下面的第一個函數之間留一行空白。*/    
  23.      
  24. /*-----------------------------------------------------------*/    
  25.      
  26. void vAFunction( void )    
  27. {    
  28.      /* 函數體在此定義,注意要用大括號括住 */    
  29. }    
  30. /*-----------------------------------------------------------*/    
  31.      
  32. static UBaseType_t prvNextFunction( void )    
  33. {    
  34.      /* 函數體在此定義. */    
  35. }    
  36. /*-----------------------------------------------------------*/  
  37.    
  38. /* 
  39.  * 函數名字老是佔一行,包括返回類型。 左括號以前沒有空格左括號以後有一個空格, 
  40.  * 每一個參數後面有一個空格參數的命名應該具備必定的描述性.  
  41.  */    
  42. void vAnExampleFunction( long lParameter1, unsigned short usParameter2 )   
  43. {    
  44. /* 變量聲明沒有縮進. */    
  45. uint8_t ucByte;    
  46.      
  47.     /* 代碼要對齊.  大括號佔獨自一行. */    
  48.     for( ucByte = 0U; ucByte < fileBUFFER_LENGTH; ucByte++ )    
  49.     {    
  50.          /* 這裏再次縮進. */    
  51.     }    
  52. }    
  53.     
  54. /*  
  55.  * for、while、do、if結構具備類似的模式。這些關鍵字和左括號之間沒有空格。 
  56.  * 左括號以後有一個空格,右括號前面也有一個空格,每一個分號後面有一個空格。 
  57.  * 每一個運算符的先後各一個空格。使用圓括號明確運算符的優先級。不容許有0 
  58.  * 之外的數字(魔鬼數)出現,必要時將這些數字換成能表示出數字含義的常量或 
  59.  * 宏定義。 
  60.  */    
  61. for( ucByte = 0U; ucByte < fileBUFFER_LENGTH; ucByte++ )    
  62. {    
  63. }    
  64.    
  65. while( ucByte < fileBUFFER_LENGTH )    
  66. {    
  67. }    
  68.      
  69. /*  
  70.  * 因爲運算符優先級的複雜性,咱們不能相信本身對運算符優先級時刻保持警戒 
  71.  * 並能正確的使用,所以對於多個表達式運算時,使用括號明確優先級順序  
  72.  */    
  73. if( ( ucByte < fileBUFFER_LENGTH ) && ( ucByte != 0U ) )    
  74. {    
  75.     ulResult = ( ( ulValue1 + ulValue2 ) - ulValue3 ) * ulValue4;    
  76. }    
  77.    
  78. /* 條件表達式也要像其它代碼那樣對齊。 */    
  79. #if( configUSE_TRACE_FACILITY == 1 )    
  80. {    
  81.      /* 向TCB增長一個用於跟蹤的計數器. */    
  82.      pxNewTCB->uxTCBNumber = uxTaskNumber;    
  83. }    
  84. #endif    
  85.     
  86. /*方括號先後各留一個空格*/    
  87. ucBuffer[ 0 ] = 0U;    
  88. ucBuffer[ fileBUFFER_LENGTH - 1U ] = 0U;  

FreeRTOS系列第5篇---FreeRTOS在Cortex-M3上的移植

1. FreeRTOS下載包的文件結構

      在FreeRTOS官方網站能夠下載到最新版的FreeRTOS包,我這裏使用的是V8.2.3版本。

      下載包內的總文件數量多的使人生畏,但文件結構卻很簡潔。《FreeRTOS入門指南》一文的第3節詳細描述了下載包文件結構,咱們這裏只是簡單提一下。

      下載包根目錄下包含兩個子目錄:FreeRTOS和FreeRTOS-Plus。其中,FreeRTOS-Plus文件夾中包含一些FreeRTOS+組件和演示例程(組件大都收費),咱們不對這個文件夾下的內容多作了解,重點說一下FreeRTOS文件夾。

      FreeRTOS文件夾下包含兩個子目錄:Demo和Source。其中,Demo包含演示例程的工程文件,Source包含實時操做系統源代碼文件。

      FreeRTOS實時操做系統內核僅包含三個必要文件,此外還有三個可選文件。RTOS核心代碼位於三個源文件中,分別是tasks.c、queue.c和list.c。這三個文件位於FreeRTOS/Source目錄下,在同一目錄下還有3個可選的文件,叫作timers.c、event_groups.c和croutine.c,分別用於軟件定時器、事件組和協程。

      對於支持的處理器架構,RTOS須要一些與處理器架構相關的代碼。能夠稱之爲RTOS硬件接口層,它們位於FreeRTOS/Source/Portable/[相應編譯器]/[相應處理器架構]文件夾下。咱們此次要移植到Cortex-M3微控制,使用Keil MDK編譯器,因此須要的RTOS硬件接口代碼位於:FreeRTOS\Source\portable\RVDS\ARM_CM3文件夾下。

      堆棧分配也是屬於硬件接口層(移植層),在FreeRTOS/Source/portable/MemMang文件夾下具備各類類型的堆棧分配方案。這裏咱們使用heap_1.c提供的堆棧分配方案。關於FreeRTOS的內存管理,後續《FreeRTOS內存管理》一文中會詳細介紹FreeRTOS內存管理的特性和用法,《FreeRTOS內存管理分析》一文會從源碼級別分析FreeRTOS內存管理的具體實現,這裏不用多糾結,你也能夠快速的瀏覽一下這兩篇文章,裏面或許有許多不懂的,但不要着急,先放過它們。

      FreeRTOS文件夾下的Demo文件夾中還包括各類演示例程,涉及多種架構的處理器以及多種編譯器。FreeRTOS/Demo/Common/Minimal文件夾下的演示例程代碼中,絕大部分對全部移植硬件接口都是適用的。FreeRTOS/Demo/Common/Full文件夾下的代碼屬於歷史遺留代碼,僅用於PC移植層。

2. 移植前的一些準備

  • 一塊具備Cortex-M3微處理器的硬件板子,而且保證板子能夠正常運轉。
  • 下載FreeRTOS程序包(《FreeRTOS歷史版本更新記錄》一文中有下載地址,這是我在CSDN下載頻道作的鏡像文件。若是你能忍受下載網速慢,也能夠去官方網站下載。)
  • 下載CMSIS-M3,其實主要是須要裏面的core_cm3.h文件(能夠去ARM官方下載,若是你安裝了keil 5或比較新的Keil 4 MDK編譯器,在目錄:Keil\ARM\CMSIS文件夾下也能夠找到)

3.移植過程

3.1 添加RTOS核心代碼

         將tasks.c、queue.c和list.c這三個內核代碼加入工程,將port.c和heap_1.c這兩個與處理器相關代碼加入工程。port.c位於FreeRTOS\Source\portable\RVDS\ARM_CM3文件夾下,heap_1.c位於FreeRTOS/Source/portable/MemMang文件夾下。

3.2 添加頭文件路徑

  1.  ...\FreeRTOS\Source\portable\RVDS\ARM_CM3
  2.  …\FreeRTOS\Source\include

3.3 編寫FreeRTOSConfig.h文件

      對於剛接觸FreeRTOS的用戶來講,最簡單方法是找一個相似的Demo工程,複製該工程下的FreeRTOSConfig.h文件,在這個基礎上進行修改。詳細的配置說明將在後續《FreeRTOS內核配置說明》一文中給出,這裏依然沒必要糾結。

3.4 編寫一些鉤子函數

      若是你在FreeRTOSConfig.h中設置了configUSE_TICK_HOOK=1,則必須編寫voidvApplicationTickHook( void )函數。該函數利用時間片中斷,能夠很方便的實現一個定時器功能。詳見後續文章《FreeRTOS內核配置說明》有關宏configUSE_TICK_HOOK一節。

      若是你在FreeRTOSConfig.h中設置了configCHECK_FOR_STACK_OVERFLOW=1或=2,則必須編寫voidvApplicationStackOverflowHook( xTaskHandle pxTask, signed char *pcTaskName )函數,該函數用於檢測堆棧溢出,詳見後續文章《FreeRTOS內核配置說明》有關宏configCHECK_FOR_STACK_OVERFLOW一節。

3.5 檢查硬件

      爲了驗證你的硬件板子是否可靠的工做,首先編寫一個小程序片,好比閃爍一個LED燈或者發送一個字符等等,咱們這裏使用UART發送一個字符。代碼以下所示(假設你已經配置好了啓動代碼,並正確配置了UART):

 

  1. #include"task.h"  
  2. #include"queue.h"  
  3. #include"list.h"  
  4. #include"portable.h"     
  5. #include"debug.h"  
  6.   
  7. int main(void)  
  8. {  
  9.     init_rtos_debug();           //初始化調試串口  
  10.     MAP_UARTCharPut('A');        //發送一個字符  
  11.     while(1);  
  12. }  

      若是硬件能夠正常發送字符,說明硬件以及啓動代碼OK,能夠進行下一步。

3.6 掛接中斷

      在Cortex-M3硬件下,FreeRTOS使用SysTick做爲系統節拍時鐘,使用SVC和PendSVC進行上下文切換。異常中斷服務代碼位於port.c文件中,FreeRTOS的做者已經爲各類架構的CPU寫好了這些代碼,能夠直接拿來用,須要用戶作的,僅僅是將這些異常中斷入口地址掛接到啓動代碼中。

      在startup.s中,使用IMPORT關鍵字聲明要掛接的異常中斷服務函數名,而後將:

  1. DCD     SVC_Handler            換成:   DCD     vPortSVCHandler  
  2. DCD     PendSV_Handler         換成:   DCD     xPortPendSVHandler  
  3. DCD     SysTick_Handler        換成:   DCD     xPortSysTickHandler  

3.7 創建第一個任務Task

      在步驟3.5中,咱們爲了測試硬件是是否可以工做,編寫了一個發送字符的小函數,這裏咱們將把這個小函數做爲咱們第一個任務要執行的主要代碼:每隔1秒鐘,發送一個字符。代碼以下所示:

  1. voidvTask(void *pvParameters)  
  2. {  
  3.      while(1)  
  4.      {  
  5.          MAP_UARTCharPut(0x31);  
  6.          vTaskDelay(1000/portTICK_RATE_MS);  
  7.      }  
  8. }  

      FreeRTOS的任務以及編寫格式將在後續文章《FreeRTOS任務概述》一文中詳述,這裏只是一個很簡單的任務,先有有大致印象。這裏面有一個API函數vTaskDelay(),這個函數用於延時,具體用法將在後續文章《FreeRTOS任務控制》一文中詳細介紹,延時函數代碼級分析將在《FreeRTOS高級篇10---系統節拍時鐘分析》。這裏沒必要在乎太多的未知狀況,由於後面會一點點將這些未知空間探索一遍的。

3.8 設置節拍時鐘

      這裏咱們使用SysTick定時器做爲系統的節拍時鐘,設定每隔10ms產生一次節拍中斷。因爲FreeRTOS對移植作了很是多的工做,以致於咱們只須要在FreeRTOSConfig.h中配置好如下兩個宏定義便可:

  1. configCPU_CLOCK_HZ     (/*你的硬件平臺CPU系統時鐘,Fcclk*/)
  2. configTICK_RATE_HZ       ((portTickType)100)         

      第一個宏定義CPU系統時鐘,也就是CPU執行時的頻率。第二個宏定義FreeRTOS的時間片頻率,這裏定義爲100,代表RTOS一秒鐘能夠切換100次任務,也就是每一個時間片爲10ms。

      在prot.c中,函數vPortSetupTimerInterrupt()設置節拍時鐘。該函數根據上面的兩個宏定義的參數,計算SysTick定時器的重裝載數值寄存器,而後設置SysTick定時器的控制及狀態寄存器,設置以下:使用內核時鐘源、使能中斷、使能SysTick定時器。另外,函數vPortSetupTimerInterrupt()由函數vTaskStartScheduler()調用,這個函數用於啓動調度器。

3.9設置中斷優先級相關宏

      這裏特別重要,由於涉及到中斷優先級和中斷嵌套。這裏先給出基於Cortex-M3硬件(lpc177x_8x系列微控制器)的一個配置例子,在FreeRTOSConfig.h中:

  1. #ifdef __NVIC_PRIO_BITS  
  2.          #defineconfigPRIO_BITS   __NVIC_PRIO_BITS  
  3. #else  
  4.          #defineconfigPRIO_BITS   5  /*lpc177x_8x微處理器使用優先級寄存器的5位*/  
  5. #endif  
  6.    
  7. /*設置內核使用的中斷優先級*/  
  8. #define configKERNEL_INTERRUPT_PRIORITY      ( 31 << (8 - configPRIO_BITS) )  
  9. /*定義RTOS能夠屏蔽的最大中斷優先級,大於這個優先級的中斷,不受RTOS控制*/  
  10. #defineconfigMAX_SYSCALL_INTERRUPT_PRIORITY   ( 5<< (8 - configPRIO_BITS) )  

      後續文章《FreeRTOS內核配置說明》會詳細介紹這些宏的含義,對於Cortex-M內核,後續文章《Cortex-M內核使用FreeRTOS特別注意事項》一文,會講述這些宏與硬件的聯繫,那個時候你必定會清楚這些宏所定義的數字會對你的硬件產生什麼影響的。如今,咱們只須要知道他們很重要就足夠了,沒人能一口吃成胖子。

3.10 設置其它宏

      還須要在FreeRTOSConfig.h設置一些必要的宏,這些宏以下所示:

  1. #define configUSE_PREEMPTION 1            //配置爲1使用搶佔式內核,配置爲0使用時間片  
  2. #define configUSE_IDLE_HOOK  0            //設置爲1使用空閒鉤子;設置爲0不使用空閒鉤子  
  3. #define configMAX_PRIORITIES      ( 5 )   //應用程序任務中可用優先級數目  
  4. #define configUSE_TICK_HOOK       0       //就設置爲1使用時間片鉤子,設置爲0不使用  
  5. #define configMINIMAL_STACK_SIZE      ( ( unsigned short ) 80 )     //最小空閒堆棧  
  6. #define configTOTAL_HEAP_SIZE         ( ( size_t ) ( 5 * 1024 ) )   //內核總共可用RAM  

3.11 建立任務

      調用FreeRTOS提供的API函數來建立任務,代碼以下所示:

  1. xTaskCreate(vTask,"Task1",50,NULL,1,NULL);  

      關於詳細的建立任務API函數,會在後續文章《FreeRTOS任務建立和刪除》一文中介紹。

3.12 開啓調度器

      調用FreeRTOS提供的API函數來啓動調度器,代碼以下所示:

  1. vTaskStartScheduler();  

      關於詳細的開啓調度器API函數,會在後續文章《FreeRTOS內核控制》一文中介紹。

      此時的main函數代碼以下所示:

 

  1. int main(void)  
  2. {  
  3.     init_rtos_debug();                             //初始化調試串口  
  4.   
  5.      xTaskCreate(vTask,"Task1",50,NULL,1,NULL);  
  6.      vTaskStartScheduler();  
  7.      while(1);     
  8.   }  

4. 小結

      到這裏,一個最基本的FreeRTOS應用程序就已經運行起來,將硬件板子接到PC的RS232串口,能夠觀察到每隔一秒鐘,板子都會向PC發送一個指定的字符。

      回頭看一下移植過程,FreeRTOS移植到Cortex-M3硬件是多麼的簡單,這一方面歸功於FreeRTOS的設計師已經爲移植作了大量工做,同時,新一代的Cortex-M3硬件也爲操做系統增長了一些列便利特性,好比SysTick定時器和全新的中斷及異常。

      可是移植成功也只是萬里長征的第一步,由於這只是最簡單的應用。咱們還不清楚FreeRTOS背後的機理、調度算法的面貌、甚至連信號量也都沒有涉及。就本文的移植過程來看,咱們也刻意忽略了不少細節,好比FreeRTOSConfig.h文件中的宏都有什麼意義?改動後對RTOS有何影響?好比FreeRTOS任務API的細節、調度API的細節,再好比FreeRTOS的內存如何分配?如何進行堆棧溢出檢查等等。

      因此,先不要沾沾自喜,曲折的道路還遠沒到來呢。

      接下來的不少篇文章會圍繞這個最簡單的移植例程作詳細的講解,要把本篇文章中刻意隱藏的細節一一拿出來。這要一直持續到咱們介紹隊列、信號量、互斥量等通信機制爲止。

 

 

FreeRTOS系列第6篇---FreeRTOS內核配置說明

FreeRTOS內核是高度可定製的,使用配置文件FreeRTOSConfig.h進行定製。每一個FreeRTOS應用都必須包含這個頭文件,用戶根據實際應用來裁剪定製FreeRTOS內核。這個配置文件是針對用戶程序的,而非內核,所以配置文件通常放在應用程序目錄下,不要放在RTOS內核源碼目錄下。

       在下載的FreeRTOS文件包中,每一個演示例程都有一個FreeRTOSConfig.h文件。有些例程的配置文件是比較舊的版本,可能不會包含全部有效選項。若是沒有在配置文件中指定某個選項,那麼RTOS內核會使用默認值。典型的FreeRTOSConfig.h配置文件定義以下所示,隨後會說明裏面的每個參數。

  1. #ifndef FREERTOS_CONFIG_H  
  2. #define FREERTOS_CONFIG_H  
  3.    
  4. /*Here is a good place to include header files that are required across 
  5. yourapplication. */  
  6. #include "something.h"  
  7.    
  8. #define configUSE_PREEMPTION                    1  
  9. #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0  
  10. #define configUSE_TICKLESS_IDLE                 0  
  11. #define configCPU_CLOCK_HZ                      60000000  
  12. #define configTICK_RATE_HZ                      250  
  13. #define configMAX_PRIORITIES                    5  
  14. #define configMINIMAL_STACK_SIZE                128  
  15. #define configTOTAL_HEAP_SIZE                   10240  
  16. #define configMAX_TASK_NAME_LEN                 16  
  17. #define configUSE_16_BIT_TICKS                  0  
  18. #define configIDLE_SHOULD_YIELD                 1  
  19. #define configUSE_TASK_NOTIFICATIONS            1  
  20. #define configUSE_MUTEXES                       0  
  21. #define configUSE_RECURSIVE_MUTEXES             0  
  22. #define configUSE_COUNTING_SEMAPHORES           0  
  23. #define configUSE_ALTERNATIVE_API               0/* Deprecated! */  
  24. #define configQUEUE_REGISTRY_SIZE               10  
  25. #define configUSE_QUEUE_SETS                    0  
  26. #define configUSE_TIME_SLICING                  0  
  27. #define configUSE_NEWLIB_REENTRANT              0  
  28. #define configENABLE_BACKWARD_COMPATIBILITY     0  
  29. #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5  
  30.    
  31. /*Hook function related definitions. */  
  32. #define configUSE_IDLE_HOOK                     0  
  33. #define configUSE_TICK_HOOK                     0  
  34. #define configCHECK_FOR_STACK_OVERFLOW          0  
  35. #define configUSE_MALLOC_FAILED_HOOK            0  
  36.    
  37. /*Run time and task stats gathering related definitions. */  
  38. #define configGENERATE_RUN_TIME_STATS           0  
  39. #define configUSE_TRACE_FACILITY                0  
  40. #define configUSE_STATS_FORMATTING_FUNCTIONS    0  
  41.    
  42. /*Co-routine related definitions. */  
  43. #define configUSE_CO_ROUTINES                   0  
  44. #define configMAX_CO_ROUTINE_PRIORITIES         1  
  45.    
  46. /*Software timer related definitions. */  
  47. #define configUSE_TIMERS                        1  
  48. #define configTIMER_TASK_PRIORITY               3  
  49. #define configTIMER_QUEUE_LENGTH                10  
  50. #define configTIMER_TASK_STACK_DEPTH            configMINIMAL_STACK_SIZE  
  51.    
  52. /*Interrupt nesting behaviour configuration. */  
  53. #define configKERNEL_INTERRUPT_PRIORITY        [dependent of processor]  
  54. #define configMAX_SYSCALL_INTERRUPT_PRIORITY   [dependent on processor and application]  
  55. #define configMAX_API_CALL_INTERRUPT_PRIORITY  [dependent on processor and application]  
  56.    
  57. /*Define to trap errors during development. */  
  58. #define configASSERT( ( x ) )     if( ( x ) == 0) vAssertCalled( __FILE__, __LINE__ )  
  59.    
  60. /*FreeRTOS MPU specific definitions. */  
  61. #define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0  
  62.    
  63. /*Optional functions - most linkers will remove unused functions anyway. */  
  64. #define INCLUDE_vTaskPrioritySet                1  
  65. #define INCLUDE_uxTaskPriorityGet               1  
  66. #define INCLUDE_vTaskDelete                     1  
  67. #define INCLUDE_vTaskSuspend                    1  
  68. #define INCLUDE_xResumeFromISR                  1  
  69. #define INCLUDE_vTaskDelayUntil                 1  
  70. #define INCLUDE_vTaskDelay                      1  
  71. #define INCLUDE_xTaskGetSchedulerState          1  
  72. #define INCLUDE_xTaskGetCurrentTaskHandle       1  
  73. #define INCLUDE_uxTaskGetStackHighWaterMark     0  
  74. #define INCLUDE_xTaskGetIdleTaskHandle          0  
  75. #define INCLUDE_xTimerGetTimerDaemonTaskHandle  0  
  76. #define INCLUDE_pcTaskGetTaskName               0  
  77. #define INCLUDE_eTaskGetState                   0  
  78. #define INCLUDE_xEventGroupSetBitFromISR        1  
  79. #define INCLUDE_xTimerPendFunctionCall          0  
  80.    
  81. /* Aheader file that defines trace macro can be included here. */  
  82.    
  83. #end if/* FREERTOS_CONFIG_H*/  

1.configUSE_PREEMPTION

       爲1時RTOS使用搶佔式調度器,爲0時RTOS使用協做式調度器(時間片)。

      注:在多任務管理機制上,操做系統能夠分爲搶佔式和協做式兩種。協做式操做系統是任務主動釋放CPU後,切換到下一個任務。任務切換的時機徹底取決於正在運行的任務。

2.configUSE_PORT_OPTIMISED_TASK_SELECTION

       某些運行FreeRTOS的硬件有兩種方法選擇下一個要執行的任務:通用方法和特定於硬件的方法(如下簡稱「特殊方法」)。

       通用方法:

  • configUSE_PORT_OPTIMISED_TASK_SELECTION設置爲0或者硬件不支持這種特殊方法。
  • 能夠用於全部FreeRTOS支持的硬件。
  • 徹底用C實現,效率略低於特殊方法。
  • 不強制要求限制最大可用優先級數目

       特殊方法:

  • 並不是全部硬件都支持。
  • 必須將configUSE_PORT_OPTIMISED_TASK_SELECTION設置爲1。
  • 依賴一個或多個特定架構的彙編指令(通常是相似計算前導零[CLZ]指令)。
  • 比通用方法更高效。
  • 通常強制限定最大可用優先級數目爲32。

3.configUSE_TICKLESS_IDLE

       設置configUSE_TICKLESS_IDLE爲1使能低功耗tickless模式,爲0保持系統節拍(tick)中斷一直運行。

       一般狀況下,FreeRTOS回調空閒任務鉤子函數(須要設計者本身實現),在空閒任務鉤子函數中設置微處理器進入低功耗模式來達到省電的目的。由於系統要響應系統節拍中斷事件,所以使用這種方法會週期性的退出、再進入低功耗狀態。若是系統節拍中斷頻率過快,則大部分電能和CPU時間會消耗在進入和退出低功耗狀態上。

       FreeRTOS的tickless空閒模式會在空閒週期時中止週期性系統節拍中斷。中止週期性系統節拍中斷能夠使微控制器長時間處於低功耗模式。移植層須要配置外部喚醒中斷,當喚醒事件到來時,將微控制器從低功耗模式喚醒。微控制器喚醒後,會從新使能系統節拍中斷。因爲微控制器在進入低功耗後,系統節拍計數器是中止的,但咱們又須要知道這段時間能折算成多少次系統節拍中斷週期,這就須要有一個不受低功耗影響的外部時鐘源,即微處理器處於低功耗模式時它也在計時的,這樣在重啓系統節拍中斷時就能夠根據這個外部計時器計算出一個調整值並寫入RTOS 系統節拍計數器變量中。

4.configUSE_IDLE_HOOK

       設置爲1使用空閒鉤子(Idle Hook相似於回調函數),0忽略空閒鉤子。

       當RTOS調度器開始工做後,爲了保證至少有一個任務在運行,空閒任務被自動建立,佔用最低優先級(0優先級)。對於已經刪除的RTOS任務,空閒任務能夠釋放分配給它們的堆棧內存。所以,在應用中應該注意,使用vTaskDelete()函數時要確保空閒任務得到必定的處理器時間。除此以外,空閒任務沒有其它特殊功能,所以能夠任意的剝奪空閒任務的處理器時間。

       應用程序也可能和空閒任務共享同個優先級。

       空閒任務鉤子是一個函數,這個函數由用戶來實現,RTOS規定了函數的名字和參數,這個函數在每一個空閒任務週期都會被調用。

       要建立一個空閒鉤子:

  1.  設置FreeRTOSConfig.h 文件中的configUSE_IDLE_HOOK 爲1;
  2.  定義一個函數,函數名和參數以下所示:

 

  1. void vApplicationIdleHook(void );  

       這個鉤子函數不能夠調用會引發空閒任務阻塞的API函數(例如:vTaskDelay()、帶有阻塞時間的隊列和信號量函數),在鉤子函數內部使用協程是被容許的。

       使用空閒鉤子函數設置CPU進入省電模式是很常見的。

5.configUSE_MALLOC_FAILED_HOOK

       每當一個任務、隊列、信號量被建立時,內核使用一個名爲pvPortMalloc()的函數來從堆中分配內存。官方的下載包中包含5個簡單內存分配策略,分別保存在源文件heap_1.c、heap_2.c、heap_3.c、heap_4.c、heap_5.c中。        僅當使用這五個簡單策略之一時,宏configUSE_MALLOC_FAILED_HOOK纔有意義。

       若是定義並正確配置malloc()失敗鉤子函數,則這個函數會在pvPortMalloc()函數返回NULL時被調用。只有FreeRTOS在響應內存分配請求時發現堆內存不足纔會返回NULL。

       若是宏configUSE_MALLOC_FAILED_HOOK設置爲1,那麼必須定義一個malloc()失敗鉤子函數,若是宏configUSE_MALLOC_FAILED_HOOK設置爲0,malloc()失敗鉤子函數不會被調用,即使已經定義了這個函數。malloc()失敗鉤子函數的函數名和原型必須以下所示:

  1. void vApplicationMallocFailedHook( void);  

6.configUSE_TICK_HOOK

       設置爲1使用時間片鉤子(Tick Hook),0忽略時間片鉤子。

       注:時間片鉤子函數(Tick Hook Function)

       時間片中斷能夠週期性的調用一個被稱爲鉤子函數(回調函數)的應用程序。時間片鉤子函數能夠很方便的實現一個定時器功能。

       只有在FreeRTOSConfig.h中的configUSE_TICK_HOOK設置成1時才能夠使用時間片鉤子。一旦此值設置成1,就要定義鉤子函數,函數名和參數以下所示:

  1. void vApplicationTickHook( void );  

       vApplicationTickHook()函數在中斷服務程序中執行,所以這個函數必須很是短小,不能大量使用堆棧,不能調用以」FromISR" 或 "FROM_ISR」結尾的API函數。

       在FreeRTOSVx.x.x\FreeRTOS\Demo\Common\Minimal文件夾下的crhook.c文件中有使用時間片鉤子函數的例程。

7.configCPU_CLOCK_HZ

       寫入實際的CPU內核時鐘頻率,也就是CPU指令執行頻率,一般稱爲Fcclk。配置此值是爲了正確的配置系統節拍中斷週期。

8.configTICK_RATE_HZ

       RTOS 系統節拍中斷的頻率。即一秒中斷的次數,每次中斷RTOS都會進行任務調度。

系統節拍中斷用來測量時間,所以,越高的測量頻率意味着可測到越高的分辨率時間。可是,高的系統節拍中斷頻率也意味着RTOS內核佔用更多的CPU時間,所以會下降效率。RTOS演示例程都是使用系統節拍中斷頻率爲1000HZ,這是爲了測試RTOS內核,比實際使用的要高。(實際使用時不用這麼高的系統節拍中斷頻率)

       多個任務能夠共享一個優先級,RTOS調度器爲相同優先級的任務分享CPU時間,在每個RTOS 系統節拍中斷到來時進行任務切換。高的系統節拍中斷頻率會下降分配給每個任務的「時間片」持續時間。

9.configMAX_PRIORITIES

       配置應用程序有效的優先級數目。任何數量的任務均可以共享一個優先級,使用協程能夠單獨的給與它們優先權。見configMAX_CO_ROUTINE_PRIORITIES。

       在RTOS內核中,每一個有效優先級都會消耗必定量的RAM,所以這個值不要超過你的應用實際須要的優先級數目。

注:任務優先級

       每個任務都會被分配一個優先級,優先級值從0~ (configMAX_PRIORITIES - 1)之間。低優先級數表示低優先級任務。空閒任務的優先級爲0(tskIDLE_PRIORITY),所以它是最低優先級任務。

       FreeRTOS調度器將確保處於就緒狀態(Ready)或運行狀態(Running)的高優先級任務比一樣處於就緒狀態的低優先級任務優先獲取處理器時間。換句話說,處於運行狀態的任務永遠是高優先級任務。

       處於就緒狀態的相同優先級任務使用時間片調度機制共享處理器時間。

10.configMINIMAL_STACK_SIZE

       定義空閒任務使用的堆棧大小。一般此值不該小於對應處理器演示例程文件FreeRTOSConfig.h中定義的數值。

       就像xTaskCreate()函數的堆棧大小參數同樣,堆棧大小不是以字節爲單位而是以字爲單位的,好比在32位架構下,棧大小爲100表示棧內存佔用400字節的空間。

11.configTOTAL_HEAP_SIZE

       RTOS內核總計可用的有效的RAM大小。僅在你使用官方下載包中附帶的內存分配策略時,纔有可能用到此值。每當建立任務、隊列、互斥量、軟件定時器或信號量時,RTOS內核會爲此分配RAM,這裏的RAM都屬於configTOTAL_HEAP_SIZE指定的內存區。後續的內存配置會詳細講到官方給出的內存分配策略。

12.configMAX_TASK_NAME_LEN

       調用任務函數時,須要設置描述任務信息的字符串,這個宏用來定義該字符串的最大長度。這裏定義的長度包括字符串結束符’\0’。

13.configUSE_TRACE_FACILITY

       設置成1表示啓動可視化跟蹤調試,會激活一些附加的結構體成員和函數。

14.configUSE_STATS_FORMATTING_FUNCTIONS (V7.5.0新增)

       設置宏configUSE_TRACE_FACILITY和configUSE_STATS_FORMATTING_FUNCTIONS爲1會編譯vTaskList()和vTaskGetRunTimeStats()函數。若是將這兩個宏任意一個設置爲0,上述兩個函數不會被編譯。

15.configUSE_16_BIT_TICKS

       定義系統節拍計數器的變量類型,即定義portTickType是表示16位變量仍是32位變量。

       定義configUSE_16_BIT_TICKS爲1意味着portTickType表明16位無符號整形,定義configUSE_16_BIT_TICKS爲0意味着portTickType表明32位無符號整形。

       使用16位類型能夠大大提升8位和16位架構微處理器的性能,但這也限制了最大時鐘計數爲65535個’Tick’。所以,若是Tick頻率爲250HZ(4MS中斷一次),對於任務最大延時或阻塞時間,16位計數器是262秒,而32位是17179869秒。

16.configIDLE_SHOULD_YIELD

       這個參數控制任務在空閒優先級中的行爲。僅在知足下列條件後,纔會起做用。

  1. 使用搶佔式內核調度
  2. 用戶任務使用空閒優先級。

         經過時間片共享同一個優先級的多個任務,若是共享的優先級大於空閒優先級,並假設沒有更高優先級任務,這些任務應該得到相同的處理器時間。

         但若是共享空閒優先級時,狀況會稍微有些不一樣。當configIDLE_SHOULD_YIELD爲1時,其它共享空閒優先級的用戶任務就緒時,空閒任務馬上讓出CPU,用戶任務運行,這樣確保了能最快響應用戶任務。處於這種模式下也會有不良效果(取決於你的程序須要),描述以下:

       圖中描述了四個處於空閒優先級的任務,任務A、B和C是用戶任務,任務I是空閒任務。上下文切換週期性的發生在T0、T1…T6時刻。當用戶任務運行時,空閒任務馬上讓出CPU,可是,空閒任務已經消耗了當前時間片中的必定時間。這樣的結果就是空閒任務I和用戶任務A共享一個時間片。用戶任務B和用戶任務C所以得到了比用戶任務A更多的處理器時間。

       能夠經過下面方法避免:

  • 若是合適的話,將處於空閒優先級的各單獨的任務放置到空閒鉤子函數中;
  • 建立的用戶任務優先級大於空閒優先級;
  • 設置IDLE_SHOULD_YIELD爲0;

       設置configIDLE_SHOULD_YIELD爲0將阻止空閒任務爲用戶任務讓出CPU,直到空閒任務的時間片結束。這確保全部處在空閒優先級的任務分配到相同多的處理器時間,可是,這是以分配給空閒任務更高比例的處理器時間爲代價的。

17.configUSE_TASK_NOTIFICATIONS(V8.2.0新增)

       設置宏configUSE_TASK_NOTIFICATIONS爲1(或不定義宏configUSE_TASK_NOTIFICATIONS)將會開啓任務通知功能,有關的API函數也會被編譯。設置宏configUSE_TASK_NOTIFICATIONS爲0則關閉任務通知功能,相關API函數也不會被編譯。默認這個功能是開啓的。開啓後,每一個任務多增長8字節RAM。

       這是個頗有用的特性,一大亮點。

       每一個RTOS任務具備一個32位的通知值,RTOS任務通知至關於直接向任務發送一個事件,接收到通知的任務能夠解除任務的阻塞狀態(因等待任務通知而進入阻塞狀態)。相對於之前必須分別建立隊列、二進制信號量、計數信號量或事件組的狀況,使用任務通知顯然更靈活。更好的是,相比於使用信號量解除任務阻塞,使用任務通知能夠快45%(使用GCC編譯器,-o2優化級別)。

18.configUSE_MUTEXES

       設置爲1表示使用互斥量,設置成0表示忽略互斥量。讀者應該瞭解在FreeRTOS中互斥量和二進制信號量的區別。

       關於互斥量和二進制信號量簡單說:

  • 互斥型信號量必須是同一個任務申請,同一個任務釋放,其餘任務釋放無效。
  • 二進制信號量,一個任務申請成功後,能夠由另外一個任務釋放。
  • 互斥型信號量是二進制信號量的子集

19.configUSE_RECURSIVE_MUTEXES

       設置成1表示使用遞歸互斥量,設置成0表示不使用。

20.configUSE_COUNTING_SEMAPHORES

       設置成1表示使用計數信號量,設置成0表示不使用。

21.configUSE_ALTERNATIVE_API

       設置成1表示使用「替代」隊列函數('alternative' queue functions),設置成0不使用。替代API在queue.h頭文件中有詳細描述。

        注:「替代」隊列函數已經被棄用,在新的設計中不要使用它!

22.configCHECK_FOR_STACK_OVERFLOW

       每一個任務維護本身的棧空間,任務建立時會自動分配任務須要的佔內存,分配內存大小由建立任務函數(xTaskCreate())的一個參數指定。堆棧溢出是設備運行不穩定的最多見緣由,所以FreeeRTOS提供了兩個可選機制用來輔助檢測和改正堆棧溢出。配置宏configCHECK_FOR_STACK_OVERFLOW爲不一樣的常量來使用不一樣堆棧溢出檢測機制。

       注意,這個選項僅適用於內存映射未分段的微處理器架構。而且,在RTOS檢測到堆棧溢出發生以前,一些處理器可能先產生故障(fault)或異常(exception)來反映堆棧使用的惡化。若是宏configCHECK_FOR_STACK_OVERFLOW沒有設置成0,用戶必須提供一個棧溢出鉤子函數,這個鉤子函數的函數名和參數必須以下所示:

  1. void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName );  

       參數xTask和pcTaskName爲堆棧溢出任務的句柄和名字。請注意,若是溢出很是嚴重,這兩個參數信息也多是錯誤的!在這種狀況下,能夠直接檢查pxCurrentTCb變量。

       推薦僅在開發或測試階段使用棧溢出檢查,由於堆棧溢出檢測會增大上下文切換開銷。

       任務切換出去後,該任務的上下文環境被保存到本身的堆棧空間,這時極可能堆棧的使用量達到了最大(最深)值。在這個時候,RTOS內核會檢測堆棧指針是否還指向有效的堆棧空間。若是堆棧指針指向了有效堆棧空間以外的地方,堆棧溢出鉤子函數會被調用。

       這個方法速度很快,可是不能檢測到全部堆棧溢出狀況(好比,堆棧溢出沒有發生在上下文切換時)。設置configCHECK_FOR_STACK_OVERFLOW爲1會使用這種方法。

       當堆棧首次建立時,在它的堆棧區中填充一些已知值(標記)。當任務切換時,RTOS內核會檢測堆棧最後的16個字節,確保標記數據沒有被覆蓋。若是這16個字節有任何一個被改變,則調用堆棧溢出鉤子函數。

       這個方法比第一種方法要慢,但也至關快了。它能有效捕捉堆棧溢出事件(即便堆棧溢出沒有發生在上下文切換時),可是理論上它也不能百分百的捕捉到全部堆棧溢出(好比堆棧溢出的值和標記值相同,固然,這種狀況發生的機率極小)。

       使用這個方法須要設置configCHECK_FOR_STACK_OVERFLOW爲2.

23.configQUEUE_REGISTRY_SIZE

       隊列記錄有兩個目的,都涉及到RTOS內核的調試:

  1. 它容許在調試GUI中使用一個隊列的文本名稱來簡單識別隊列;
  2. 包含調試器須要的每個記錄隊列和信號量定位信息;

       除了進行內核調試外,隊列記錄沒有其它任何目的。

       configQUEUE_REGISTRY_SIZE定義能夠記錄的隊列和信號量的最大數目。若是你想使用RTOS內核調試器查看隊列和信號量信息,則必須先將這些隊列和信號量進行註冊,只有註冊後的隊列和信號量才能夠使用RTOS內核調試器查看。查看API參考手冊中的vQueueAddToRegistry() 和vQueueUnregisterQueue()函數獲取更多信息。

24.configUSE_QUEUE_SETS

       設置成1使能隊列集功能(能夠阻塞、掛起到多個隊列和信號量),設置成0取消隊列集功能。

25.configUSE_TIME_SLICING(V7.5.0新增)

       默認狀況下(宏configUSE_TIME_SLICING未定義或者宏configUSE_TIME_SLICING設置爲1),FreeRTOS使用基於時間片的優先級搶佔式調度器。這意味着RTOS調度器老是運行處於最高優先級的就緒任務,在每一個RTOS 系統節拍中斷時在相同優先級的多個任務間進行任務切換。若是宏configUSE_TIME_SLICING設置爲0,RTOS調度器仍然老是運行處於最高優先級的就緒任務,可是當RTOS 系統節拍中斷髮生時,相同優先級的多個任務之間再也不進行任務切換。

26.configUSE_NEWLIB_REENTRANT(V7.5.0新增)

       若是宏configUSE_NEWLIB_REENTRANT設置爲1,每個建立的任務會分配一個newlib(一個嵌入式C庫)reent結構。

27.configENABLE_BACKWARD_COMPATIBILITY

       頭文件FreeRTOS.h包含一系列#define宏定義,用來映射版本V8.0.0和V8.0.0以前版本的數據類型名字。這些宏能夠確保RTOS內核升級到V8.0.0或以上版本時,以前的應用代碼不用作任何修改。在FreeRTOSConfig.h文件中設置宏configENABLE_BACKWARD_COMPATIBILITY爲0會去掉這些宏定義,而且須要用戶確認升級以前的應用沒有用到這些名字。

28.configNUM_THREAD_LOCAL_STORAGE_POINTERS

       設置每一個任務的線程本地存儲指針數組大小。

       線程本地存儲容許應用程序在任務的控制塊中存儲一些值,每一個任務都有本身獨立的儲存空間,宏configNUM_THREAD_LOCAL_STORAGE_POINTERS指定每一個任務線程本地存儲指針數組的大小。API函數vTaskSetThreadLocalStoragePointer()用於向指針數組中寫入值,API函數pvTaskGetThreadLocalStoragePointer()用於從指針數組中讀取值。

       好比,許多庫函數都包含一個叫作errno的全局變量。某些庫函數使用errno返回庫函數錯誤信息,應用程序檢查這個全局變量來肯定發生了那些錯誤。在單線程程序中,將errno定義成全局變量是能夠的,可是在多線程應用中,每一個線程(任務)必須具備本身獨有的errno值,不然,一個任務可能會讀取到另外一個任務的errno值。

       FreeRTOS提供了一個靈活的機制,使得應用程序能夠使用線程本地存儲指針來讀寫線程本地存儲。具體參見後續文章《FreeRTOS系列第12篇---FreeRTOS任務應用函數》。

29.configGENERATE_RUN_TIME_STATS

       設置宏configGENERATE_RUN_TIME_STATS爲1使能運行時間統計功能。一旦設置爲1,則下面兩個宏必須被定義:

  1. portCONFIGURE_TIMER_FOR_RUN_TIME_STATS():用戶程序須要提供一個基準時鐘函數,函數完成初始化基準時鐘功能,這個函數要被define到宏portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()上。這是由於運行時間統計須要一個比系統節拍中斷頻率還要高分辨率的基準定時器,不然,統計可能不精確。基準定時器中斷頻率要比統節拍中斷快10~100倍。基準定時器中斷頻率越快,統計越精準,但能統計的運行時間也越短(好比,基準定時器10ms中斷一次,8位無符號整形變量能夠計到2.55秒,但若是是1秒中斷一次,8位無符號整形變量能夠統計到255秒)。
  2. portGET_RUN_TIME_COUNTER_VALUE():用戶程序須要提供一個返回基準時鐘當前「時間」的函數,這個函數要被define到宏portGET_RUN_TIME_COUNTER_VALUE()上。

       舉一個例子,假如咱們配置了一個定時器,每500us中斷一次。在定時器中斷服務例程中簡單的使長整形變量ulHighFrequencyTimerTicks自增。那麼上面提到兩個宏定義以下(能夠在FreeRTOSConfig.h中添加):

[objc] view plain copy
  1. extern volatile unsigned longulHighFrequencyTimerTicks;  
  2. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ( ulHighFrequencyTimerTicks = 0UL )  
  3. #define portGET_RUN_TIME_COUNTER_VALUE() ulHighFrequencyTimerTicks  

30.configUSE_CO_ROUTINES

       設置成1表示使用協程,0表示不使用協程。若是使用協程,必須在工程中包含croutine.c文件。

       注:協程(Co-routines)主要用於資源發很是受限的嵌入式系統(RAM很是少),一般不會用於32位微處理器。

       在當前嵌入式硬件環境下,不建議使用協程,FreeRTOS的開發者早已經中止開發協程。

31.configMAX_CO_ROUTINE_PRIORITIES

       應用程序協程(Co-routines)的有效優先級數目,任何數目的協程均可以共享一個優先級。使用協程能夠單獨的分配給任務優先級。見configMAX_PRIORITIES。

32.configUSE_TIMERS

       設置成1使用軟件定時器,爲0不使用軟件定時器功能。詳細描述見FreeRTOS software timers 。

33.configTIMER_TASK_PRIORITY

       設置軟件定時器服務/守護進程的優先級。詳細描述見FreeRTOS software timers 。

34.configTIMER_QUEUE_LENGTH

       設置軟件定時器命令隊列的長度。詳細描述見FreeRTOS software timers。

35.configTIMER_TASK_STACK_DEPTH

       設置軟件定時器服務/守護進程任務的堆棧深度,詳細描述見FreeRTOS software timers 。

36.configKERNEL_INTERRUPT_PRIORITY、configMAX_SYSCALL_INTERRUPT_PRIORITY和configMAX_API_CALL_INTERRUPT_PRIORITY

       這是移植和應用FreeRTOS出錯最多的地方,因此須要打起精神仔細讀懂。

       Cortex-M三、PIC2四、dsPIC、PIC3二、SuperH和RX600硬件設備須要設置宏configKERNEL_INTERRUPT_PRIORITY;PIC3二、RX600和Cortex-M硬件設備須要設置宏configMAX_SYSCALL_INTERRUPT_PRIORITY。

       configMAX_SYSCALL_INTERRUPT_PRIORITY和configMAX_API_CALL_INTERRUPT_PRIORITY,這兩個宏是等價的,後者是前者的新名字,用於更新的移植層代碼。

       注意下面的描述中,在中斷服務例程中僅能夠調用以「FromISR」結尾的API函數。

  • 僅須要設置configKERNEL_INTERRUPT_PRIORITY的硬件設備(也就是宏configMAX_SYSCALL_INTERRUPT_PRIORITY不會用到):configKERNEL_INTERRUPT_PRIORITY用來設置RTOS內核本身的中斷優先級。調用API函數的中斷必須運行在這個優先級;不調用API函數的中斷,能夠運行在更高的優先級,因此這些中斷不會被因RTOS內核活動而延時。 
  • configKERNEL_INTERRUPT_PRIORITY和configMAX_SYSCALL_INTERRUPT_PRIORITY都須要設置的硬件設備:configKERNEL_INTERRUPT_PRIORITY用來設置RTOS內核本身的中斷優先級。由於RTOS內核中斷不容許搶佔用戶使用的中斷,所以這個宏通常定義爲硬件最低優先級。configMAX_SYSCALL_INTERRUPT_PRIORITY用來設置能夠在中斷服務程序中安全調用FreeRTOS API函數的最高中斷優先級。優先級小於等於這個宏所表明的優先級時,程序能夠在中斷服務程序中安全的調用FreeRTOS API函數;若是優先級大於這個宏所表明的優先級,表示FreeRTOS沒法禁止這個中斷,在這個中斷服務程序中毫不能夠調用任何API函數。

       經過設置configMAX_SYSCALL_INTERRUPT_PRIORITY的優先級級別高於configKERNEL_INTERRUPT_PRIORITY能夠實現完整的中斷嵌套模式。這意味着FreeRTOS內核不能徹底禁止中斷,即便在臨界區。此外,這對於分段內核架構的微處理器是有利的。請注意,當一個新中斷髮生後,某些微處理器架構會(在硬件上)禁止中斷,這意味着從硬件響應中斷到FreeRTOS從新使能中斷之間的這段短期內,中斷是不可避免的被禁止的。

       不調用API的中斷能夠運行在比configMAX_SYSCALL_INTERRUPT_PRIORITY高的優先級,這些級別的中斷不會被FreeRTOS禁止,所以不會由於執行RTOS內核而被延時。

       例如:假如一個微控制器有8箇中斷優先級別:0表示最低優先級,7表示最高優先級(Cortex-M3和Cortex-M4內核優先數和優先級別正好與之相反,後續文章會專門介紹它們)。當兩個配置選項分別爲4和0時,下圖描述了每個優先級別能夠和不可作的事件:

  • configMAX_SYSCALL_INTERRUPT_PRIORITY=4
  • configKERNEL_INTERRUPT_PRIORITY=0

 

       這些配置參數容許很是靈活的中斷處理:

       在系統中能夠像其它任務同樣爲中斷處理任務分配優先級。這些任務經過一個相應中斷喚醒。中斷服務例程(ISR)內容應儘量的精簡---僅用於更新數據而後喚醒高優先級任務。ISR退出後,直接運行被喚醒的任務,所以中斷處理(根據中斷獲取的數據來進行的相應處理)在時間上是連續的,就像ISR在完成這些工做。這樣作的好處是當中斷處理任務執行時,全部中斷均可以處在使能狀態。

       中斷、中斷服務例程(ISR)和中斷處理任務是三碼事:當中斷來臨時會進入中斷服務例程,中斷服務例程作必要的數據收集(更新),以後喚醒高優先級任務。這個高優先級任務在中斷服務例程結束後當即執行,它多是其它任務也多是中斷處理任務,若是是中斷處理任務,那麼就能夠根據中斷服務例程中收集的數據作相應處理。

       configMAX_SYSCALL_INTERRUPT_PRIORITY接口有着更深一層的意義:在優先級介於RTOS內核中斷優先級(等於configKERNEL_INTERRUPT_PRIORITY)和configMAX_SYSCALL_INTERRUPT_PRIORITY之間的中斷容許全嵌套中斷模式並容許調用API函數。大於configMAX_SYSCALL_INTERRUPT_PRIORITY的中斷優先級毫不會由於執行RTOS內核而延時。

       運行在大於configMAX_SYSCALL_INTERRUPT_PRIORITY的優先級中斷是不會被RTOS內核所屏蔽的,所以也不受RTOS內核功能影響。這主要用於很是高的實時需求中。好比執行電機轉向。可是,這類中斷的中斷服務例程中毫不能夠調用FreeRTOS的API函數。

      爲了使用這個方案,應用程序要必須符合如下規則:調用FreeRTOS API函數的任何中斷,都必須和RTOS內核處於同一優先級(由宏configKERNEL_INTERRUPT_PRIORITY設置),或者小於等於宏configMAX_SYSCALL_INTERRUPT_PRIORITY定義的優先級。

37.configASSERT

       斷言,調試時能夠檢查傳入的參數是否合法。FreeRTOS內核代碼的關鍵點都會調用configASSERT( x )函數,若是參數x爲0,則會拋出一個錯誤。這個錯誤極可能是傳遞給FreeRTOS API函數的無效參數引發的。定義configASSERT()有助於調試時發現錯誤,可是,定義configASSERT()也會增大應用程序代碼量,增大運行時間。推薦在開發階段使用這個斷言宏。

       舉一個例子,咱們想把非法參數所在的文件名和代碼行數打印出來,能夠先定義一個函數vAssertCalled,該函數有兩個參數,分別接收觸發configASSERT宏的文件名和該宏所在行,而後經過顯示屏或者串口輸出。代碼以下:

[objc] view plain copy
  1. #define configASSERT( ( x ) )     if( ( x ) == 0 )vAssertCalled( __FILE__, __LINE__ )  

       這裏__FILE__和__LINE__是大多數編譯器預約義的宏,分別表示代碼所在的文件名(字符串格式)和行數(整形)。

       這個例子雖然看起來很簡單,但因爲要把整形__LINE__轉換成字符串再顯示,在效率和實現上,都不能讓人滿意。咱們能夠使用C標準庫assert的實現方法,這樣函數vAssertCalled只須要接收一個字符串形式的參數(推薦仔細研讀下面的代碼並理解其中的技巧):

[objc] view plain copy
  1. #define STR(x)  VAL(x)  
  2. #define VAL(x)  #x  
  3. #define configASSERT(x) ((x)?(void) 0 :xAssertCalld(__FILE__ ":" STR(__LINE__) " " #x"\n"))  

       這裏稍微講解一下,因爲內置宏__LINE__是整數型的而不是字符串型,把它轉化成字符串須要一個額外的處理層。宏STR和和宏VAL正是用來輔助完成這個轉化。宏STR用來把整形行號替換掉__LINE__,宏VAL用來把這個整形行號字符串化。忽略宏STR和VAL中的任何一個,只能獲得字符串」__LINE__」,這不是咱們想要的。

       這裏使用三目運算符’?:’來代替參數判斷if語句,這樣能夠接受任何參數或表達式,代碼也更緊湊,更重要的是代碼優化度更高,由於若是參數恆爲真,在編譯階段就能夠去掉沒必要要的輸出語句。

38.INCLUDE Parameters

       以「INCLUDE」起始的宏容許用戶不編譯那些應用程序不須要的實時內核組件(函數),這能夠確保在你的嵌入式系統中RTOS佔用最少的ROM和RAM。

每一個宏以這樣的形式出現:

       INCLUDE_FunctionName

      在這裏FunctionName表示一個你能夠控制是否編譯的API函數。若是你想使用該函數,就將這個宏設置成1,若是不想使用,就將這個宏設置成0。好比,對於API函數vTaskDelete():

[objc] view plain copy
  1. #define INCLUDE_vTaskDelete    1  

       表示但願使用vTaskDelete(),容許編譯器編譯該函數

[objc] view plain copy
  1. #define INCLUDE_vTaskDelete    0  

       表示禁止編譯器編譯該函數。

 

 

 

FreeRTOS系列第7篇---Cortex-M內核使用FreeRTOS特別注意事項

在閱讀本文以前,有兩個定義在FreeRTOSConfig.h中的宏,你必須先明白它們是什麼意思,《FreeRTOS內核配置說明》一文中,講解了這兩個宏:

  • configKERNEL_INTERRUPT_PRIORITY
  • configMAX_SYSCALL_INTERRUPT_PRIORITY

       FreeRTOS與Cortex-M內核可謂是絕配,以致於讓移植和使用FreeRTOS都變得更簡單起來。根據FreeRTOS官方反饋,在Cortex-M內核上使用FreeRTOS大多數的問題點是由不正確的優先級設置引發的。這個問題也是在乎料之中的,由於儘管Cortex-M內核的中斷模式是很是強大的,但對於那些使用傳統中斷優先級架構的工程師來講,Cortex-M內核中斷機制也有點笨拙(或者是說使用比較繁瑣),而且違反直覺(這個主要是由於Cortex-M中斷優先級數值越大表明的優先級反而越小)。本章打算描述Cortex-M的中斷優先級機制,並描述怎樣結合RTOS內核使用。

       說明:雖然Cortex-M內核的優先級方案看上去比較複雜,但每個官方發佈的FreeRTOS 接口包(在FreeRTOSV7.2.0\FreeRTOS\Source\portable文件夾中,通常爲port.c)內都會有正確配置的演示例程,能夠以此爲參考。

1.有效優先級

1.1Cortex-M 硬件詳述

       首先須要清楚有效優先級的總數,這取決於微控制器製造商怎麼使用Cortex內核。因此,並非全部的Cortex-M內核微處理器都具備相同的中斷優先級級別。

       Cortex-M構架自身最多容許256級可編程優先級(優先級配置寄存器最多8位,因此優先級範圍從0x00~0xFF),可是絕大多數微控制器製造商只是使用其中的一部分優先級。好比,TI Stellaris Cortex-M3和Cortex-M4微控制器使用優先級配置寄存器的3個位,能提供8級優先級。再好比,NXP LPC17xx Cortex-M3微控制器使用優先級配置寄存器的5個位,能提供32級優先級。

1.2應用到RTOS

       RTOS中斷嵌套方案將有效的中斷優先級分紅兩組:一組能夠經過RTOS臨界區屏蔽,另外一組不受RTOS影響,永遠都是使能的。宏configMAX_SYSCALL_INTERRUPT_PRIORITY在FreeRTOSConfig.h中配置,定義兩組中斷優先級的邊界。邏輯優先級高於此值的中斷不受RTOS影響。最優值取決於微控制器使用的優先級配置寄存器的位數。

2.與數值相反的優先級值和邏輯優先級設置

2.1Cortex-M 硬件詳述

       有必要先解釋一下優先級值和邏輯優先級:在Cortex-M內核中,假若有8級優先級,咱們說優先級值是0~7,但數值最大的優先級7卻表明着最低的邏輯優先級。不少使用傳統傳統中斷優先級架構的工程師會以爲這樣比較繞,違反直覺。如下內容提到的優先級要仔細區分是優先級數值仍是邏輯優先級。

       接下來須要清楚的是,在Cortex-M內核中,一箇中斷的優先級數值越低,邏輯優先級卻越高。好比,中斷優先級爲2的中斷能夠搶佔中斷優先級爲5的中斷,但反過來就不行。換句話說,中斷優先級2比中斷優先級5的優先級更高。

       這是Cortex-M內核最容易讓人犯錯之處,由於大多數的非Cortex-M內核微控制器的中斷優先級表述是與之相反的。

2.2應用到 RTOS

       以「FromISR」結尾的FreeRTOS函數是具備中斷調用保護的(執行這些函數會進入臨界區),可是就算是這些函數,也不能夠被邏輯優先級高於configMAX_SYSCALL_INTERRUPT_PRIORITY的中斷服務函數調用。(宏configMAX_SYSCALL_INTERRUPT_PRIORITY定義在頭文件FreeRTOSConfig.h中)。所以,任何使用RTOSAPI函數的中斷服務例程的中斷優先級數值大於等於configMAX_SYSCALL_INTERRUPT_PRIORITY宏的值。這樣就能保證中斷的邏輯優先級等於或低於configMAX_SYSCALL_INTERRUPT_PRIORITY。

       Cortex中斷默認狀況下有一個數值爲0的優先級。大多數狀況下0表明最高級優先級。所以,絕對不能夠在優先級爲0的中斷服務例程中調用RTOSAPI函數。

3.Cortex-M 內部優先級概述

3.1Cortex-M 硬件詳述

       Cortex-M內核的中斷優先級寄存器是以最高位(MSB)對齊的。好比,若是使用了3位來表達優先級,則這3個位位於中斷優先級寄存器的bit五、bit六、bit7位。剩餘的bit0~bit4能夠設置成任何值,但爲了兼容,最好將他們設置成1.

       Cortex-M優先級寄存器最多有8位,若是一個微控制器只使用了其中的3位,那麼這3位是以最高位對齊的,見下圖:

      某微控制器只使用了優先級寄存器中的3位,下圖展現了優先級數值5(二進制101B)是怎樣在優先級寄存器中存儲的。若是優先級寄存器中未使用的位置1,下圖也展現了爲何數值5(二進制0000 0101B)能夠當作數值191(二進制1011 1111)的。


      某微控制器只使用了優先級寄存器中的4位,下圖展現了優先級數值5(二進制101B)是怎樣在優先級寄存器中存儲的。若是優先級寄存器中未使用的位置1,下圖也展現了爲何數值5(二進制0000 0101B)能夠當作數值95(二進制0101 1111)的。


3.2應用到 RTOS

      上文中已經描述,那些在中斷服務例程中調用RTOS API函數的中斷邏輯優先級必須低於或等於configMAX_SYSCALL_INTERRUPT_PRIORITY(低邏輯優先級意味着高優先級數值)。

      CMSIS以及不一樣的微控制器供應商提供了能夠設置某個中斷優先級的庫函數。一些庫函數的參數使用最低位對齊,另外一些庫函數的參數可能使用最高位對齊,因此,使用時應該查閱庫函數的應用手冊進行正確設置。

      能夠在FreeRTOSConfig.h中設置宏configMAX_SYSCALL_INTERRUPT_PRIORITY和configKERNEL_INTERRUPT_PRIORITY的值。這兩個宏須要根據Cortex-M內核自身的狀況進行設置,要以最高有效位對齊。好比某微控制器使用中斷優先級寄存器中的3位,設置configKERNEL_INTERRUPT_PRIORITY的值爲5,則代碼爲:

[objc] view plain copy
  1. #define     configKERNEL_INTERRUPT_PRIORITY  (5<<(8-3))  

       宏configKERNEL_INTERRUPT_PRIORITY指定RTOS內核使用的中斷優先級,由於RTOS內核不能夠搶佔用戶任務,所以這個宏通常設置爲硬件支持的最小優先級。對於Cortex-M硬件,RTOS使用到硬件的PendSV和SysTick硬件中斷,在函數xPortStartScheduler()中(該函數在port.c中,由啓動調度器函數vTaskStartScheduler()調用),將PendSV和SysTick硬件中斷優先級寄存器設置爲宏configKERNEL_INTERRUPT_PRIORITY指定的值。

       有關代碼以下(位於port.c):

[objc] view plain copy
  1. /*PendSV優先級設置寄存器地址爲0xe000ed22 
  2.  SysTick優先級設置寄存器地址爲0xe000ed23*/  
  3. #define portNVIC_SYSPRI2_REG     ( * ( ( volatile uint32_t * ) 0xe000ed20 ))  
  4.    
  5. #define portNVIC_PENDSV_PRI ( ( (uint32_t)configKERNEL_INTERRUPT_PRIORITY ) << 16UL )  
  6. #define portNVIC_SYSTICK_PRI ( ( (uint32_t)configKERNEL_INTERRUPT_PRIORITY ) << 24UL )  
  7. /* …. */  
  8. /*確保PendSV 和SysTick爲最低優先級中斷 */  
  9. portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;  
  10. portNVIC_SYSPRI2_REG |=portNVIC_SYSTICK_PRI;  

4.臨界區

4.1Cortex-M 硬件詳述

      RTOS內核使用Cortex-M內核的BASEPRI寄存器來實現臨界區(注:BASEPRI爲優先級屏蔽寄存器,優先級數值大於或等於該寄存器的中斷都會被屏蔽,優先級數值越大,邏輯優先級越低,可是爲零時不屏蔽任何中斷)。這容許RTOS內核能夠只屏蔽一部分中斷,所以能夠提供一個靈活的中斷嵌套模式。

      那些須要在中斷調用時保護的API函數,FreeRTOS使用寄存器BASEPRI實現中斷保護臨界區。當進入臨界區時,將寄存器BASEPRI的值設置成configMAX_SYSCALL_INTERRUPT_PRIORITY,當退出臨界區時,將寄存器BASEPRI的值設置成0。不少Bug反饋都提到,當退出臨界區時不該該將寄存器設置成0,應該恢復它以前的狀態(以前的狀態不必定是0)。可是Cortex-M NVIC決不會容許一個低優先級中斷搶佔當前正在執行的高優先級中斷,無論BASEPRI寄存器中是什麼值。與進入臨界區前先保存BASEPRI的值,退出臨界區再恢復的方法相比,退出臨界區時將BASEPRI寄存器設置成0的方法能夠得到更快的執行速度。

4.2應用到RTOS kernel

      RTOS內核經過寫configMAX_SYSCALL_INTERRUPT_PRIORITY的值到BASEPRI寄存器的方法建立臨界區。中斷優先級0(具備最高的邏輯優先級)不能被BASEPRI寄存器屏蔽,所以,configMAX_SYSCALL_INTERRUPT_PRIORITY毫不能夠設置成0。

 

 

FreeRTOS系列第8篇---FreeRTOS內存管理

本文介紹內存管理的基礎知識,詳細源碼分析見《 FreeRTOS高級篇7---FreeRTOS內存管理分析

FreeRTOS提供了幾個內存堆管理方案,有複雜的也有簡單的。其中最簡單的管理策略也能知足不少應用的要求,好比對安全要求高的應用,這些應用根本不容許動態內存分配的。

FreeRTOS也容許你本身實現內存堆管理,甚至容許你同時使用兩種內存堆管理方案。同時實現兩種內存堆容許任務堆棧和其它RTOS對象放置到快速的內部RAM,應用數據放置到低速的外部RAM。

每當建立任務、隊列、互斥量、軟件定時器、信號量或事件組時,RTOS內核會爲它們分配RAM。標準函數庫中的malloc()和free()函數有些時候可以用於完成這個任務,可是:

  1. 在嵌入式系統中,它們並不老是能夠使用的;
  2. 它們會佔用更多寶貴的代碼空間;
  3. 它們沒有線程保護;
  4. 它們不具備肯定性(每次調用執行的時間可能會不一樣);

      所以,提供一個替代的內存分配方案一般是必要的。

      嵌入式/實時系統具備千差萬別的RAM和時間要求,所以一個RAM內存分配算法可能僅屬於一個應用的子集。

      爲了不這個問題,FreeRTOS在移植層保留內存分配API函數。移植層在RTOS核心代碼源文件以外(不屬於核心源代碼),這使得不一樣的應用程序能夠提供適合本身的應用實現。當RTOS內核須要RAM時,調用pvPortMallo()函數來代替malloc()函數。當RAM要被釋放時,調用vPortFree()函數來代替free()函數。

      FreeRTOS下載包中提供5種簡單的內存分配實現,本文稍後會進行描述。用戶能夠適當的選擇其中的一個,也能夠本身設計內存分配策略。

      FreeRTOS提供的內存分配方案分別位於不一樣的源文件(heap_1.c、heap_2.c、heap_3.c、heap_4.c、heap_5.c)之中,源文件位於下載包\FreeRTOS\Source\portable\MemMang文件夾中。其它實現方法能夠根據須要增長。若是要使用FreeRTOS提供的內存堆分配方案,選中的源文件必須被正確的包含到工程文件中。

1.heap_1.c

      這是全部實現中最簡單的一個。一旦分配內存以後,它甚至不容許釋放分配的內存。儘管這樣,heap_1.c仍是適用於大部分嵌入式應用程序。這是由於大多數深度嵌入式(deeplyembedded)應用只是在系統啓動時建立全部任務、隊列、信號量等,而且直到程序結束都會一直使用它們,永遠不須要刪除。

當須要分配RAM時,這個內存分配方案只是簡單的將一個大數組細分出一個子集來。大數組的容量大小經過FreeRTOSConfig.h文件中的configTOTAL_HEAP_SIZE宏來設置。

API函數xPortGetFreeHeapSize()返回未分配的堆棧空間總大小,能夠經過這個函數返回值對configTOTAL_HEAP_SIZE進行合理的設置。

      heap_1功能簡介:

  • 用於從不會刪除任務、隊列、信號量、互斥量等的應用程序(實際上大多數使用FreeRTOS的應用程序都符合這個條件)
  • 執行時間是肯定的而且不會產生內存碎片
  • 實現和分配過程很是簡單,須要的內存是從一個靜態數組中分配的,意味着這種內存分配一般只是適用於那些不進行動態內存分配的應用。

2.heap_2.c

      和方案1不一樣,這個方案使用一個最佳匹配算法,它容許釋放以前分配的內存塊。它不會把相鄰的空閒塊合成一個更大的塊(換句話說,這會形成內存碎片)。

有效的堆棧空間大小由位於FreeRTOSConfig.h文件中的configTOTAL_HEAP_SIZE宏來定義。

      API函數xPortGetFreeHeapSize()返回剩下的未分配堆棧空間的大小(可用於優化設置configTOTAL_HEAP_SIZE宏的值),可是不能提供未分配內存的碎片細節信息。

      heap_2功能簡介:

  • 能夠用於重複的分配和刪除具備相同堆棧空間的任務、隊列、信號量、互斥量等等,而且不考慮內存碎片的應用程序。
  • 不能用在分配和釋放隨機字節堆棧空間的應用程序
    • 若是一個應用程序動態的建立和刪除任務,而且分配給任務的堆棧空間老是一樣大小,那麼大多數狀況下heap_2.c是能夠使用的。可是,若是分配給任務的堆棧不老是相等,那麼釋放的有效內存可能碎片化,造成不少小的內存塊。最後會由於沒有足夠大的連續堆棧空間而形成內存分配失敗。在這種狀況下,heap_4.c是一個很好的選擇。
    • 若是一個應用程序動態的建立和刪除隊列,而且在每種狀況下隊列存儲區域(隊列存儲區域指隊列項數目乘以每一個隊列長度)都是一樣的,那麼大多數狀況下heap_2.c能夠使用。可是,若是隊列存儲區在每種狀況下並不老是相等,那麼釋放的有效內存可能碎片化,造成不少小的內存塊。最後會由於沒有足夠大的連續堆棧空間而形成內存分配失敗。在這種狀況下,heap_4.c是一個很好的選擇。
    • 應用程序直接調用pvPortMalloc() 和 vPortFree()函數,而不只是經過FreeRTOS API間接調用。
  • 若是你的應用程序中的隊列、任務、信號量、互斥量等等處在一個不可預料的順序,則可能會致使內存碎片問題,雖然這是小几率事件,但必須牢記。
  • 不具備肯定性,可是它比標準庫中的malloc函數具備高得多的效率。

      heap_2.c適用於須要動態建立任務的大多數小型實時系統(smallreal time)。

3.heap_3.c

      heap_3.c簡單的包裝了標準庫中的malloc()和free()函數,包裝後的malloc()和free()函數具有線程保護。

      heap_3.c功能簡介:

  • 須要連接器設置一個堆棧,而且編譯器庫提供malloc()和free()函數。
  • 不具備肯定性
  • 可能明顯的增大RTOS內核的代碼大小

      注:使用heap_3時,FreeRTOSConfig.h文件中的configTOTAL_HEAP_SIZE宏定義沒有做用。

4.heap_4.c

      這個方案使用一個最佳匹配算法,但不像方案2那樣。它會將相鄰的空閒內存塊合併成一個更大的塊(包含一個合併算法)。

      有效的堆棧空間大小由位於FreeRTOSConfig.h文件中的configTOTAL_HEAP_SIZE來定義。

      API函數xPortGetFreeHeapSize()返回剩下的未分配堆棧空間的大小(可用於優化設置configTOTAL_HEAP_SIZE宏的值),可是不能提供未分配內存的碎片細節信息。

      heap_4.c功能簡介:

  • 可用於重複分配、刪除任務、隊列、信號量、互斥量等等的應用程序。
  • 能夠用於分配和釋放隨機字節內存的狀況,並不像heap_2.c那樣產生嚴重碎片。
  • 不具備肯定性,可是它比標準庫中的malloc函數具備高得多的效率。

      heap_4.c還特別適用於移植層代碼,能夠直接使用pvPortMalloc()和 vPortFree()函數來分配和釋放內存。

5.heap_5.c(V8.1.0新增)

      這個方案一樣實現了heap_4.c中的合併算法,而且容許堆棧跨越多個非連續的內存區。

      Heap_5經過調用vPortDefineHeapRegions()函數實現初始化,在該函數執行完成前不容許使用內存分配和釋放。建立RTOS對象(任務、隊列、信號量等等)會隱含的調用pvPortMalloc(),所以必須注意:使用heap_5建立任何對象前,要先執行vPortDefineHeapRegions()函數。

      vPortDefineHeapRegions()函數只須要單個參數。該參數是一個HeapRegion_t結構體類型數組。HeapRegion_t在portable.h中定義,以下所示:

[objc] view plain copy
  1. typedef struct HeapRegion    
  2. {    
  3.     /* 用於內存堆的內存塊起始地址*/    
  4.     uint8_t *pucStartAddress;    
  5.     
  6.     /* 內存塊大小 */    
  7.     size_t xSizeInBytes;    
  8. } HeapRegion_t;  

      這個數組必須使用一個NULL指針和0字節元素做爲結束,起始地址必須從小到大排列。下面的代碼段提供一個例子。MSVCWin32模擬器演示例程使用了heap_5,所以能夠當作一個參考例程。

[objc] view plain copy
  1. /* 在內存中爲內存堆分配兩個內存塊.第一個內存塊0x10000字節,起始地址爲0x80000000,  
  2. 第二個內存塊0xa0000字節,起始地址爲0x90000000.起始地址爲0x80000000的內存塊的  
  3. 起始地址更低,所以放到了數組的第一個位置.*/    
  4. const HeapRegion_t xHeapRegions[] =    
  5. {    
  6.     { ( uint8_t * ) 0x80000000UL, 0x10000 },    
  7.     { ( uint8_t * ) 0x90000000UL, 0xa0000 },    
  8.     { NULL, 0 } /* 數組結尾. */    
  9. };    
  10.     
  11. /* 向函數vPortDefineHeapRegions()傳遞數組參數. */    
  12. vPortDefineHeapRegions( xHeapRegions );  

 

FreeRTOS系列第9篇---FreeRTOS任務概述

1. 任務和協程(Co-routines)

      應用程序能夠使用任務也能夠使用協程,或者二者混合使用,可是任務和協程使用不一樣的API函數,所以在任務和協程之間不能使用同一個隊列或信號量傳遞數據。

      一般狀況下,協程僅用在資源很是少的微處理器中,特別是RAM很是稀缺的狀況下。目前協程不多被使用到,所以對於協程FreeRTOS做者既沒有把它刪除也沒有進一步開發。

      因此本系列文章之後不會對協程過多描述,包括其API函數。

1.1任務的特性

      簡而言之:使用RTOS的實時應用程序可認爲是一系列獨立任務的集合。每一個任務在本身的環境中運行,不依賴於系統中的其它任務或者RTOS調度器。在任什麼時候刻,只有一個任務獲得運行,RTOS調度器決定運行哪一個任務。調度器會不斷的啓動、中止每個任務,宏觀看上去就像整個應用程序都在執行。做爲任務,不須要對調度器的活動有所瞭解,在任務切入切出時保存上下文環境(寄存器值、堆棧內容)是調度器主要的職責。爲了實現這點,每一個任務都須要有本身的堆棧。當任務切出時,它的執行環境會被保存在該任務的堆棧中,這樣當再次運行時,就能從堆棧中正確的恢復上次的運行環境。

1.2任務概要

  • 簡單
  • 沒有使用限制
  • 支持徹底搶佔
  • 支持優先級
  • 每一個任務都有本身的堆棧,消耗RAM較多
  • 若是使用搶佔,必須當心的考慮可重入問題

2. 任務狀態

      一個任務可爲下面中的一個:

  1. 運行:若是一個任務正在執行,那麼說這個任務處於運行狀態。此時它佔用處理器。
  2. 就緒:就緒的任務已經具有執行的能力(不一樣於阻塞和掛起),可是由於有一個同優先級或者更高優先級的任務處於運行狀態而尚未真正執行。
  3. 阻塞:若是任務當前正在等待某個時序或外部中斷,咱們就說這個任務處於阻塞狀態。好比一個任務調用vTaskDelay()後會阻塞到延時週期到爲止。任務也可能阻塞在隊列或信號量事件上。進入阻塞狀態的任務一般有一個「超時」週期,當事件超時後解除阻塞。
  4. 掛起:處於掛起狀態的任務一樣對調度器無效。僅當明確的分別調用vTaskSuspend() 和xTaskResume() API函數後,任務纔會進入或退出掛起狀態。不能夠指定超時週期事件(不能夠經過設定超時事件而退出掛起狀態)

3.任務優先級

      每一個任務都要被指定一個優先級,從0~configMAX_PRIORITIES,configMAX_PRIORITIES定義在FreeRTOSConfig.h中。

      若是某架構硬件支持CLZ(或相似)指令(計算前導零的數目,Cortex-M3是支持該指令的,從ARMv6T2才支持這個指令),而且打算在移植層使用這個特性來優化任務調度機制,須要有一些步驟,首先將FreeRTOSConfig.h中configUSE_PORT_OPTIMISED_TASK_SELECTION設置爲1,而且最大優先級數目configMAX_PRIORITIES不能大於32。除此以外,configMAX_PRIORITIES能夠設置爲任意值,可是考慮到configMAX_PRIORITIES設置越大,RAM消耗也越大,通常設置爲知足使用的最小值。

      低優先級數值表明低優先級。空閒任務(idle task)的優先級爲0(tskIDLE_PRIORITY)。

      FreeRTOS調度器確保處於最高優先級的就緒或運行態任務獲取處理器,換句話說,處於運行狀態的任務,只有其中的最高優先級任務纔會運行。

      任何數量的任務能夠共享同一個優先級。若是宏configUSE_TIME_SLICING未定義或着宏configUSE_TIME_SLICING定義爲1,處於就緒態的多個相同優先級任務將會以時間片切換的方式共享處理器。

4.實現一個任務

      一個任務具備如下結構:

[objc] view plain copy
  1. void vATaskFunction( voidvoid *pvParameters )  
  2. {  
  3.     for( ;; )  
  4.     {  
  5.         /*-- 應用程序代碼放在這裏. --*/  
  6.     }  
  7.    
  8.     /* 任務不能夠從這個函數返回或退出。在較新的FreeRTOS移植包中,若是 
  9.     試圖從一個任務中返回,將會調用configASSERT()(若是定義的話)。 
  10.     若是一個任務確實要退出函數,那麼這個任務應調用vTaskDelete(NULL) 
  11.     函數,以便處理一些清理工做。*/  
  12.     vTaskDelete( NULL );  
  13. }  

      任務函數返回爲void,參數只有一個void類型指針。全部的任務函數都應該是這樣。void類型指針能夠向任務傳遞任意類型信息。

      任務函數決不該該返回,所以一般任務函數都是一個死循環。

      任務由xTaskCreate()函數建立,由vTaskDelete()函數刪除。

5.空閒任務和空閒任務鉤子(idle task和Idle Task hook)

5.1空閒任務

      空閒任務是啓動RTOS調度器時由內核自動建立的任務,這樣能夠確保至少有一個任務在運行。空閒任務具備最低任務優先級,這樣若是有其它更高優先級的任務進入就緒態就能夠馬上讓出CPU。

      刪除任務後,空閒任務用來釋放RTOS分配給被刪除任務的內存。所以,在應用中使用vTaskDelete()函數後確保空閒任務能得到處理器時間就很重要了。除此以外,空閒任務沒有其它有效功能,因此能夠被合理的剝奪處理器時間,而且它的優先級也是最低的。

      應用程序任務共享空閒任務優先級(tskIDLE_PRIORITY)也是可能的。這種狀況如何配置能夠參考configIDLE_SHOULE_YIELD配置參數類獲取更多信息。

5.2空閒任務鉤子

      空閒任務鉤子是一個函數,每個空閒任務週期被調用一次。若是你想將任務程序功能運行在空閒優先級上,能夠有兩種選擇:

  1. 在一個空閒任務鉤子中實現這個功能:由於FreeRTOS必須至少有一個任務處於就緒或運行狀態,所以鉤子函數不能夠調用可能引發空閒任務阻塞的API函數(好比vTaskDelay()或者帶有超時事件的隊列或信號量函數)。
  2. 建立一個具備空閒優先級的任務去實現這個功能:這是個更靈活的解決方案,可是會帶來更多RAM開銷。

      建立一個空閒鉤子步驟以下:

  •   在FreeRTOSConfig.h頭文件中設置configUSE_IDLE_HOOK爲1;
  •   定義一個函數,名字和參數原型以下所示:
[objc] view plain copy
  1. void vApplicationIdleHook( void );  

         一般,使用這個空閒鉤子函數設置CPU進入低功耗模式

 

 

FreeRTOS系列第10篇---FreeRTOS任務建立和刪除

在FreeRTOS移植到Cortex-M3硬件平臺的文章中,咱們已經見過任務建立API,但那篇文章的重點在於如何移植FreeRTOS,本文將重點放在任務的建立和刪除API函數上面。

      任務建立和刪除API函數位於文件task.c中,須要包含task.h頭文件。

1.任務建立

1.1函數描述

[objc] view plain copy
  1. BaseType_t xTaskCreate(  
  2.          TaskFunction_t pvTaskCode,  
  3.          const charchar * const pcName,  
  4.          unsigned short usStackDepth,  
  5.          voidvoid *pvParameters,  
  6.          UBaseType_t uxPriority,  
  7.          TaskHandle_t * pvCreatedTask  
  8.        );  

      建立新的任務並加入任務就緒列表。

      若是使用FreeRTOS-MPU(在官方下載包中,爲Cortex-M3內核寫了兩個移植方案,一個是普通的FreeRTOS移植層,還有一個是FreeRTOS-MPU移植層。後者包含完整的內存保護),那麼推薦使用函數xTaskCreateRestricted()來代替xTaskCreate()。在使用FreeRTOS-MPU的狀況下,使用xTaskCreate()函數能夠建立運行在特權模式或用戶模式(見下面對函數參數uxPriority的描述)的任務。當運行在特權模式下,任務能夠訪問整個內存映射;當處於用戶模式下,任務僅能訪問本身的堆棧。不管在何種模式下,MPU都不會自動捕獲堆棧溢出,所以標準的FreeRTOS堆棧溢出檢測機制仍然會被用到。xTaskCreateRestricted()函數具備更大的靈活性。

1.2參數描述

  • pvTaskCode:指針,指向任務函數的入口。任務永遠不會返回(位於死循環內)。該參數類型TaskFunction_t定義在文件projdefs.h中,定義爲:typedefvoid (*TaskFunction_t)( void * )
  • pcName:任務描述。主要用於調試。字符串的最大長度由宏configMAX_TASK_NAME_LEN指定,該宏位於FreeRTOSConfig.h文件中。
  • usStackDepth:指定任務堆棧大小,可以支持的堆棧變量數量,而不是字節數。好比,在16位寬度的堆棧下,usStackDepth定義爲100,則實際使用200字節堆棧存儲空間。堆棧的寬度乘以深度必須不超過size_t類型所能表示的最大值。好比,size_t爲16位,則能夠表示的最大值是65535。
  • pvParameters:指針,當任務建立時,做爲一個參數傳遞給任務。
  • uxPriority:任務的優先級。具備MPU支持的系統,能夠經過置位優先級參數的portPRIVILEGE_BIT位,隨意的在特權(系統)模式下建立任務。好比,建立一個優先級爲2的特權任務,參數uxPriority能夠設置爲( 2 | portPRIVILEGE_BIT )
  • pvCreatedTask:用於回傳一個句柄(ID),建立任務後能夠使用這個句柄引用任務。

1.3返回值

      若是任務成功建立並加入就緒列表函數返回pdPASS,不然函數返回錯誤碼,具體參見projdefs.h

1.4用法舉例

[objc] view plain copy
  1. /* 建立任務. */  
  2. void vTaskCode( voidvoid * pvParameters )  
  3. {  
  4.     for( ;; )  
  5.     {  
  6.        /* 任務代碼放在這裏 */  
  7.     }  
  8. }  
  9.    
  10. /* 建立任務函數 */  
  11. void vOtherFunction( void )  
  12. {  
  13.     static unsigned char ucParameterToPass;  
  14.     xTaskHandlexHandle;  
  15.    
  16.      /* 建立任務,存儲句柄。注:傳遞的參數ucParameterToPass必須和任務具備相同的生存週期, 
  17.         所以這裏定義爲靜態變量。若是它只是一個自動變量,可能不會有太長的生存週期,由於 
  18.                 中斷和高優先級任務可能會用到它。 */  
  19.      xTaskCreate( vTaskCode, "NAME", STACK_SIZE,&ucParameterToPass, tskIDLE_PRIORITY, &xHandle );  
  20.    
  21.      /* 使用句柄刪除任務. */  
  22.     if( xHandle !=NULL )  
  23.     {  
  24.         vTaskDelete( xHandle );  
  25.     }  
  26. }  

2.任務刪除

2.1 任務描述

      voidvTaskDelete( TaskHandle_t xTask );

      從RTOS內核管理器中刪除一個任務。任務刪除後將會從就緒、阻塞、暫停和事件列表中移除。在文件FreeRTOSConfig.h中,必須定義宏INCLUDE_vTaskDelete 爲1,本函數纔有效。

      注:被刪除的任務,其在任務建立時由內核分配的存儲空間,會由空閒任務釋放。若是有應用程序調用xTaskDelete(),必須保證空閒任務獲取必定的微控制器處理時間。任務代碼本身分配的內存是不會自動釋放的,所以刪除任務前,應該將這些內存釋放。

2.2參數描述

xTask:被刪除任務的句柄。爲NULL表示刪除當前任務。

 

 

FreeRTOS系列第11篇---FreeRTOS任務控制  FreeRTOS任務控制API函數主要實現任務延時、任務掛起、解除任務掛起、任務優先級獲取和設置等功能。

1.相對延時

1.1函數描述

      void vTaskDelay( portTickTypexTicksToDelay )

      調用vTaskDelay()函數後,任務會進入阻塞狀態,持續時間由vTaskDelay()函數的參數xTicksToDelay指定,單位是系統節拍時鐘週期。常量portTICK_RATE_MS 用來輔助計算真實時間,此值是系統節拍時鐘中斷的週期,單位是毫秒。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskDelay 必須設置成1,此函數纔能有效。

      vTaskDelay()指定的延時時間是從調用vTaskDelay()後開始計算的相對時間。好比vTaskDelay(100),那麼從調用vTaskDelay()後,任務進入阻塞狀態,通過100個系統時鐘節拍週期,任務解除阻塞。所以,vTaskDelay()並不適用與週期性執行任務的場合。此外,其它任務和中斷活動,會影響到vTaskDelay()的調用(好比調用前高優先級任務搶佔了當前任務),所以會影響任務下一次執行的時間。API函數vTaskDelayUntil()可用於固定頻率的延時,它用來延時一個絕對時間。

1.2參數描述

  • xTicksToDelay:延時時間總數,單位是系統時鐘節拍週期。

1.3用法舉例

[objc] view plain copy
  1. voidvTaskFunction( voidvoid * pvParameters )  
  2.  {  
  3.      /* 阻塞500ms. */  
  4.      constportTickType xDelay = 500 / portTICK_RATE_MS;  
  5.    
  6.      for( ;; )  
  7.      {  
  8.          /* 每隔500ms觸發一次LED, 觸發後進入阻塞狀態 */  
  9.          vToggleLED();  
  10.          vTaskDelay( xDelay );  
  11.      }  
  12. }  

2.絕對延時

2.1函數描述

         void vTaskDelayUntil( TickType_t *pxPreviousWakeTime,

                      const TickType_txTimeIncrement );

      任務延時一個指定的時間。週期性任務能夠使用此函數,以確保一個恆定的頻率執行。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskDelayUntil 必須設置成1,此函數纔有效。

      這個函數不一樣於vTaskDelay()函數的一個重要之處在於:vTaskDelay()指定的延時時間是從調用vTaskDelay()以後(執行完該函數)開始算起的,可是vTaskDelayUntil()指定的延時時間是一個絕對時間。

      調用vTaskDelay()函數後,任務會進入阻塞狀態,持續時間由vTaskDelay()函數的參數指定,單位是系統節拍時鐘週期。所以vTaskDelay()並不適用於週期性執行任務的場合。由於調用vTaskDelay()到任務解除阻塞的時間不老是固定的而且該任務下一次調用vTaskDelay()函數的時間也不老是固定的(兩次執行同一任務的時間間隔自己就不固定,中斷或高優先級任務搶佔也可能會改變每一次執行時間)。

      vTaskDelay()指定一個從調用vTaskDelay()函數後開始計時,到任務解除阻塞爲止的相對時間,而vTaskDelayUntil()指定一個絕對時間,每當時間到達,則解除任務阻塞。

      應當指出的是,若是指定的喚醒時間已經達到,vTaskDelayUntil()馬上返回(不會有阻塞)。所以,使用vTaskDelayUntil()週期性執行的任務,不管任何緣由(好比,任務臨時進入掛起狀態)中止了週期性執行,使得任務少運行了一個或多個執行週期,那麼須要從新計算所須要的喚醒時間。這能夠經過傳遞給函數的指針參數pxPreviousWake指向的值與當前系統時鐘計數值比較來檢測,在大多數狀況下,這並非必須的。

      常量portTICK_RATE_MS 用來輔助計算真實時間,此值是系統節拍時鐘中斷的週期,單位是毫秒。

      當調用vTaskSuspendAll()函數掛起RTOS調度器時,不能夠使用此函數。

2.2參數描述

  • pxPreviousWakeTime:指針,指向一個變量,該變量保存任務最後一次解除阻塞的時間。第一次使用前,該變量必須初始化爲當前時間。以後這個變量會在vTaskDelayUntil()函數內自動更新。
  • xTimeIncrement:週期循環時間。當時間等於(*pxPreviousWakeTime + xTimeIncrement)時,任務解除阻塞。若是不改變參數xTimeIncrement的值,調用該函數的任務會按照固定頻率執行。

2.3用法舉例

[objc] view plain copy
  1. //每10次系統節拍執行一次  
  2.  void vTaskFunction( voidvoid * pvParameters )  
  3.  {  
  4.      static portTickType xLastWakeTime;  
  5.      const portTickType xFrequency = 10;  
  6.    
  7.      // 使用當前時間初始化變量xLastWakeTime  
  8.      xLastWakeTime = xTaskGetTickCount();  
  9.    
  10.      for( ;; )  
  11.      {  
  12.          //等待下一個週期  
  13.          vTaskDelayUntil( &xLastWakeTime,xFrequency );  
  14.    
  15.          // 須要週期性執行代碼放在這裏  
  16.      }  
  17.  }  

3.獲取任務優先級

3.1函數描述

      UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask );

      獲取指定任務的優先級。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskPriorityGet必須設置成1,此函數纔有效。

3.2參數描述

  • xTask:任務句柄。NULL表示獲取當前任務的優先級。

3.3返回值

      返回指定任務的優先級。

3.4用法舉例

[objc] view plain copy
  1. voidvAFunction( void )  
  2.  {  
  3.      xTaskHandlexHandle;  
  4.      // 建立任務,保存任務句柄  
  5.      xTaskCreate( vTaskCode, "NAME",STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );  
  6.      // ...  
  7.      // 使用句柄獲取建立的任務的優先級  
  8.      if( uxTaskPriorityGet( xHandle ) !=tskIDLE_PRIORITY )  
  9.      {  
  10.          // 任務能夠改變本身的優先級  
  11.      }  
  12.      // ...  
  13.      // 當前任務優先級比建立的任務優先級高?  
  14.      if( uxTaskPriorityGet( xHandle ) <uxTaskPriorityGet( NULL ) )  
  15.      {  
  16.          // 當前優先級較高  
  17.      }  
  18.  }  

4.設置任務優先級

4.1函數描述

      void vTaskPrioritySet( TaskHandle_txTask,

                       UBaseType_tuxNewPriority );

      設置指定任務的優先級。若是設置的優先級高於當前運行的任務,在函數返回前會進行一次上下文切換。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskPrioritySet 必須設置成1,此函數纔有效。

4.2參數描述

  • xTask:要設置優先級任務的句柄,爲NULL表示設置當前運行的任務。
  • uxNewPriority:要設置的新優先級。

4.3用法舉例

[objc] view plain copy
  1. voidvAFunction( void )  
  2.  {  
  3.      xTaskHandlexHandle;  
  4.      // 建立任務,保存任務句柄。  
  5.      xTaskCreate( vTaskCode, "NAME",STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );  
  6.      // ...  
  7.      // 使用句柄來提升建立任務的優先級  
  8.      vTaskPrioritySet( xHandle,tskIDLE_PRIORITY + 1 );  
  9.      // ...  
  10.      // 使用NULL參數來提升當前任務的優先級,設置成和建立的任務相同。  
  11.      vTaskPrioritySet( NULL, tskIDLE_PRIORITY +1 );  
  12.  }  

5.任務掛起

5.1函數描述

      void vTaskSuspend( TaskHandle_txTaskToSuspend );

      掛起指定任務。被掛起的任務毫不會獲得處理器時間,無論該任務具備什麼優先級。

      調用vTaskSuspend函數是不會累計的:即便屢次調用vTaskSuspend ()函數將一個任務掛起,也只需調用一次vTaskResume ()函數就能使掛起的任務解除掛起狀態。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskSuspend必須設置成1,此函數纔有效。

5.2參數描述

  • xTaskToSuspend:要掛起的任務句柄。爲NULL表示掛起當前任務。

5.3用法舉例

[objc] view plain copy
  1. voidvAFunction( void )  
  2.  {  
  3.      xTaskHandlexHandle;  
  4.      // 建立任務,保存任務句柄.  
  5.      xTaskCreate( vTaskCode, "NAME",STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );  
  6.      // ...  
  7.      // 使用句柄掛起建立的任務.  
  8.      vTaskSuspend( xHandle );  
  9.      // ...  
  10.      // 任務再也不運行,除非其它任務調用了vTaskResume(xHandle )  
  11.      //...  
  12.      // 掛起本任務.  
  13.      vTaskSuspend( NULL );  
  14.      // 除非另外一個任務使用handle調用了vTaskResume,不然永遠不會執行到這裏  
  15.  }  

6.恢復掛起的任務

6.1函數描述

      void vTaskResume( TaskHandle_txTaskToResume );

      恢復掛起的任務。

      經過調用一次或屢次vTaskSuspend()掛起的任務,能夠調用一次vTaskResume ()函數來再次恢復運行。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskSuspend必須置1,此函數纔有效。

6.2參數描述

  • xTaskToResume:要恢復運行的任務句柄。

6.3用法舉例

[objc] view plain copy
  1. voidvAFunction( void )  
  2.  {  
  3.          xTaskHandle xHandle;  
  4.      // 建立任務,保存任務句柄  
  5.      xTaskCreate( vTaskCode, "NAME",STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );  
  6.      // ...  
  7.      // 使用句柄掛起建立的任務  
  8.      vTaskSuspend( xHandle );  
  9.      // ...  
  10.      //任務再也不運行,除非其它任務調用了vTaskResume(xHandle )      
  11.           //...  
  12.      // 恢復掛起的任務.  
  13.      vTaskResume( xHandle );  
  14.      // 任務再一次獲得處理器時間  
  15.      // 任務優先級與以前相同  
  16.  }  

7.恢復掛起的任務(在中斷服務函數中使用)

7.1函數描述

      BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume );

      用於恢復一個掛起的任務,用在ISR中。

      經過調用一次或屢次vTaskSuspend()函數而掛起的任務,只需調用一次xTaskResumeFromISR()函數便可恢復運行。

      xTaskResumeFromISR()不可用於任務和中斷間的同步,若是中斷恰巧在任務被掛起以前到達,這就會致使一次中斷丟失(任務尚未掛起,調用xTaskResumeFromISR()函數是沒有意義的,只能等下一次中斷)。這種狀況下,能夠使用信號量做爲同步機制。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskSuspend 和 INCLUDE_xTaskResumeFromISR 必須設置成1,此函數纔有效。

7.2參數描述

  • xTaskToResume:要恢復運行的任務句柄。

7.3返回值

      若是恢復任務後須要上下文切換返回pdTRUE,不然返回pdFALSE。由ISR肯定是否須要上下文切換。

7.4用法舉例

[objc] view plain copy
  1. xTaskHandlexHandle;               //注意這是一個全局變量  
  2.    
  3.  void vAFunction( void )  
  4.  {  
  5.      // 建立任務並保存任務句柄  
  6.      xTaskCreate( vTaskCode, "NAME",STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );  
  7.    
  8.      // ... 剩餘代碼.  
  9.  }  
  10.    
  11.  void vTaskCode( voidvoid *pvParameters )  
  12.  {  
  13.      for( ;; )  
  14.      {  
  15.          // ... 在這裏執行一些其它功能  
  16.    
  17.          // 掛起本身  
  18.          vTaskSuspend( NULL );  
  19.    
  20.          //直到ISR恢復它以前,任務會一直掛起  
  21.      }  
  22.  }  
  23.    
  24.  void vAnExampleISR( void )  
  25.  {  
  26.      portBASE_TYPExYieldRequired;  
  27.    
  28.      // 恢復被掛起的任務  
  29.      xYieldRequired = xTaskResumeFromISR(xHandle );  
  30.    
  31.      if( xYieldRequired == pdTRUE )  
  32.      {  
  33.          // 咱們應該進行一次上下文切換  
  34.          // 注:  如何作取決於你具體使用,可查看說明文檔和例程  
  35.          portYIELD_FROM_ISR();  
  36.      }  
  37.  } 

FreeRTOS系列第12篇---FreeRTOS任務應用函數任務應用函數是一組輔助類函數,通常用於調試信息輸出、獲取任務句柄、獲取任務狀態、操做任務標籤值等等。

1.獲取任務系統狀態

1.1函數描述

[objc] view plain copy
  1. UBaseType_t uxTaskGetSystemState(  
  2.               TaskStatus_t * constpxTaskStatusArray,  
  3.               const UBaseType_tuxArraySize,  
  4.               unsigned longlong * constpulTotalRunTime );  

      該函數向TaskStatus_t結構體填充相關信息,系統中每個任務的信息均可以填充到TaskStatus_t結構體數組中,數組大小由uxArraySize指定。結構體TaskStatus_t定義以下:

 

[objc] view plain copy
  1. typedef struct xTASK_STATUS  
  2. {  
  3.    /* 任務句柄*/  
  4.    TaskHandle_t xHandle;  
  5.    
  6.    /* 指針,指向任務名*/  
  7.    const signed charchar *pcTaskName;  
  8.    
  9.    /*任務ID,是一個獨一無二的數字*/  
  10.    UBaseType_t xTaskNumber;  
  11.    
  12.    /*填充結構體時,任務當前的狀態(運行、就緒、掛起等等)*/  
  13.    eTaskState eCurrentState;  
  14.    
  15.    /*填充結構體時,任務運行(或繼承)的優先級。*/  
  16.    UBaseType_t uxCurrentPriority;  
  17.    
  18.    /* 當任務因繼承而改變優先級時,該變量保存任務最初的優先級。僅當configUSE_MUTEXES定義爲1有效。*/  
  19.    UBaseType_t uxBasePriority;  
  20.    
  21.    /* 分配給任務的總運行時間。僅當宏configGENERATE_RUN_TIME_STATS爲1時有效。*/  
  22.    unsigned long ulRunTimeCounter;  
  23.    
  24.    /* 從任務建立起,堆棧剩餘的最小數量,這個值越接近0,堆棧溢出的可能越大。 */  
  25.    unsigned short usStackHighWaterMark;  
  26. }TaskStatus_t;  

      注意,這個函數僅用來調試用,調用此函數會掛起全部任務,直到函數最後才恢復掛起的任務,所以任務可能被掛起很長時間。在文件FreeRTOSConfig.h中,宏configUSE_TRACE_FACILITY必須設置爲1,此函數纔有效。

1.2參數描述

  • pxTaskStatusArray:指向TaskStatus_t類型的結構體數組。這個數組至少要包含1個元素。RTOS控制的任務數量能夠使用API函數uxTaskGetNumberOfTasks()獲取。
  • uxArraySize:參數pxTaskStatusArray指向的數組大小,也就是該數組的索引數目。
  • pulTotalRunTime:若是在文件FreeRTOSConfig.h中設置宏configGENERATE_RUN_TIME_STATS1,則該函數將總運行時間寫入*pulTotalRunTime中。pulTotalRunTime能夠設置爲NULL,表示忽略總運行時間。

1.3返回值

         被填充的TaskStatus_t結構體數量。這個值應該等於經過調用API函數uxTaskGetNumberOfTasks()返回的值,但若是傳遞給uxArraySize參數的值過小,則返回0

1.4用法舉例

[objc] view plain copy
  1. /*本例演示如是使用uxTaskGetSystemState()函數來獲取運行時間信息,並將其轉化爲程序員更易識別的字符格式,這些轉化後的字符保存到pcWriteBuffer中。*/  
  2. void vTaskGetRunTimeStats(signed charchar *pcWriteBuffer )  
  3. {  
  4.    TaskStatus_t*pxTaskStatusArray;  
  5.    volatileUBaseType_t uxArraySize, x;  
  6.    unsignedlong ulTotalRunTime, ulStatsAsPercentage;  
  7.    
  8.    /* 防護性代碼,確保字符串有合理的結束*/  
  9.   *pcWriteBuffer = 0x00;  
  10.    
  11.    /* 獲取任務總數目*/  
  12.   uxArraySize = uxTaskGetNumberOfTasks ();  
  13.    
  14.    /*爲每一個任務的TaskStatus_t結構體分配內存,也能夠靜態的分配一個足夠大的數組 */  
  15.   pxTaskStatusArray = pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ));  
  16.    
  17.    if(pxTaskStatusArray != NULL )  
  18.    {  
  19.       /*獲取每一個任務的狀態信息 */  
  20.      uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize,&ulTotalRunTime );  
  21.    
  22.       /* 百分比計算 */  
  23.      ulTotalRunTime /= 100UL;  
  24.    
  25.       /* 避免除零錯誤 */  
  26.       if(ulTotalRunTime > 0 )  
  27.       {  
  28.          /* 將得到的每個任務狀態信息部分的轉化爲程序員容易識別的字符串格式*/  
  29.         for( x = 0; x < uxArraySize; x++ )  
  30.          {  
  31.            /* 計算任務運行時間與總運行時間的百分比。*/  
  32.            ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter /ulTotalRunTime;  
  33.    
  34.            if( ulStatsAsPercentage > 0UL )  
  35.            {  
  36.               sprintf( pcWriteBuffer, "%s\t\t%lu\t\t%lu%%\r\n",  
  37.                                 pxTaskStatusArray[ x ].pcTaskName,  
  38.                                 pxTaskStatusArray[ x ].ulRunTimeCounter,  
  39.                                 ulStatsAsPercentage );  
  40.            }  
  41.            else  
  42.            {  
  43.               /* 任務運行時間不足總運行時間的1%*/  
  44.               sprintf( pcWriteBuffer, "%s\t\t%lu\t\t<1%%\r\n",  
  45.                                 pxTaskStatusArray[ x ].pcTaskName,  
  46.                                  pxTaskStatusArray[x ].ulRunTimeCounter );  
  47.            }  
  48.    
  49.            pcWriteBuffer += strlen( ( charchar * ) pcWriteBuffer );  
  50.          }  
  51.       }  
  52.    
  53.       /* 釋放以前申請的內存*/  
  54.      vPortFree( pxTaskStatusArray );  
  55.    }  
  56. }  

2.獲取當前任務句柄

2.1函數描述

      TaskHandle_t xTaskGetCurrentTaskHandle(void );

      在文件FreeRTOSConfig.h中,宏INCLUDE_xTaskGetCurrentTaskHandle必須設置爲1,此函數纔有效。

2.2返回值

      返回當前任務(調用該函數的任務)的句柄。

3.獲取空閒任務句柄

3.1函數描述

      TaskHandle_t xTaskGetIdleTaskHandle(void );

      在文件FreeRTOSConfig.h中,宏INCLUDE_xTaskGetIdleTaskHandle必須設置爲1,此函數纔有效。

3.2返回值

      返回空閒任務句柄。空閒任務在RTOS調度器啓動時自動建立。

4.獲取任務堆棧最大使用深度

4.1函數描述

      UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );

      任務的堆棧空間會隨着任務執行以及中斷處理而增加或縮小。該函數能夠返回任務啓動後的最小剩餘堆棧空間。換句話說,能夠間接估算出一個任務最多須要多少堆棧空間。在文件FreeRTOSConfig.h中,宏INCLUDE_uxTaskGetStackHighWaterMark 必須設置成1,此函數纔有效。

4.2參數描述

  • xTask:任務句柄。NULL表示查看當前任務的堆棧使用狀況。

4.3返回值

      返回最小剩餘堆棧空間,以字爲單位。好比一個32爲架構處理器,返回值爲1表示有4字節堆棧空間沒有使用過。若是返回值爲0,則任務極可能已經發生了堆棧溢出。

4.4用法舉例

 

[objc] view plain copy
  1.  void vTask1( voidvoid * pvParameters )  
  2.  {  
  3.      UBaseType_tuxHighWaterMark;  
  4.   
  5.      /* 入口處檢測一次 */  
  6.      uxHighWaterMark =uxTaskGetStackHighWaterMark( NULL );  
  7.   
  8.      for( ;; )  
  9.      {  
  10.          /* 正常調用函數 */  
  11.          vTaskDelay( 1000 );  
  12.   
  13.          /* 測量堆棧使用狀況 */  
  14.          uxHighWaterMark =uxTaskGetStackHighWaterMark( NULL );  
  15.      }  
  16. }  

5.獲取任務狀態

5.1函數描述

      eTaskState eTaskGetState( TaskHandle_txTask );

      返回一個枚舉類型的任務狀態值。在文件FreeRTOSConfig.h中,宏INCLUDE_eTaskGetState必須設置爲1,此函數纔有效。

5.2參數描述

  • xTask:任務句柄

5.3返回值

         下表列出返回值和對應的任務狀態。

6.獲取任務描述內容

6.1函數描述

      char * pcTaskGetTaskName( TaskHandle_txTaskToQuery );

      獲取任務的描述內容,在文件FreeRTOSConfig.h中,宏INCLUDE_pcTaskGetTaskName必須設置成1,此函數纔有效。

6.2參數描述

  • xTaskToQuery:任務的句柄。NULL表示獲取當前任務的描述內容指針。

6.3返回值

      一個指針,指向任務描述字符串。

7.獲取系統節拍次數

7.1函數描述

      volatile TickType_t xTaskGetTickCount(void );

      這個函數不能在ISR中調用。在ISR中用xTaskGetTickCountFromISR(),原型爲volatileTickType_t xTaskGetTickCountFromISR( void )。

7.2返回值

      返回從vTaskStartScheduler函數調用後的系統時鐘節拍次數。

8.獲取調度器狀態

8.1函數描述

      BaseType_t xTaskGetSchedulerState( void);

      獲取調度器當前狀態。在文件FreeRTOSConfig.h中,宏INCLUDE_xTaskGetSchedulerStateconfigUSE_TIMERS必須定義爲1,此函數纔有效。

8.2返回值

      返回值是如下常量之一(定義在task.h):taskSCHEDULER_NOT_STARTED(未啓動)、taskSCHEDULER_RUNNING(正常運行)、taskSCHEDULER_SUSPENDED(掛起)。

9.獲取任務總數

9.1函數描述

      UBaseType_t uxTaskGetNumberOfTasks(void );

      獲取RTOS內核當前管理的任務總數。包含全部就緒、阻塞和掛起狀態的任務。對於一個刪除的任務,若是它的堆棧空間尚未被空閒任務釋放掉,則這個被刪除的任務也含在計數值中。

9.2返回值

      返回RTOS內核當前管理的任務總數。

10.獲取全部任務詳情

10.1函數描述

      void vTaskList( char *pcWriteBuffer );

      將每一個任務的狀態、堆棧使用狀況等以字符的形式保存到參數pcWriteBuffer指向的區域。vTaskList()函數調用usTaskGetSystemState()函數,而後將獲得的信息格式化爲程序員易讀的字符形式。輸出的內容例子以下圖所示,圖中State一欄中,B表示阻塞、R表示就緒、D表示刪除(等待清除內存)、S表示掛起或阻塞。


      注意,調用這個函數會掛起全部任務,這一過程可能持續較長時間,所以本函數僅在調試時使用。在文件FreeRTOSConfig.h中,宏configUSE_TRACE_FACILITY和configUSE_STATS_FORMATTING_FUNCTIONS必須定義爲1,此函數纔有效。

10.2參數描述

  • pcWriteBuffer:任務的信息會寫入這個緩衝區,爲ASCII表單形式。這個緩衝區要足夠大,以容納生成的報告,每一個任務大約須要40個字節。

11.獲取任務運行時間

11.1函數描述

      void vTaskGetRunTimeStats( char*pcWriteBuffer );

      這個函數用於統計每一個任務的運行時間。要使用這個函數必須知足一些條件,那就是必須有一個用於時間統計的定時器或計數器,這個定時器或計數器的精度要至少大於10倍的系統節拍週期。這個定時器或計數器的配置以及獲取定時時間是由兩個宏定義實現的,這兩個宏通常在文件FreeRTOSConfig.h中定義。配置定時器或計數器的宏爲portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(),獲取定時時間的宏爲portGET_RUN_TIME_COUNTER_VALUE。實現了這兩個宏定義後,還必須在文件FreeRTOSConfig.h中將宏configGENERATE_RUN_TIME_STATSconfigUSE_STATS_FORMATTING_FUNCTIONS設置爲1,此API函數纔有效。

      這個API函數調用usTaskGetSystemState()函數獲取每一個任務的狀態信息,並把其中的運行時間格式化爲程序員易讀的字符形式,並將這些信息保存到參數pcWriteBuffer指向的區域。

      注意,調用這個函數會掛起全部任務,這一過程可能持續較長時間,所以本函數僅在調試時使用。

11.2參數描述

  • pcWriteBuffer:任務的運行時間信息會寫入這個緩衝區,爲ASCII表單形式。這個緩衝區要足夠大,以容納生成的報告,每一個任務大約須要40個字節。

11.3用法舉例

      lpc17xx系列爲控制爲例,咱們使用定時器0來做爲統計基準時鐘。

11.3.1使能函數宏

      在文件FreeRTOSConfig.h中,設置宏configGENERATE_RUN_TIME_STATSconfigUSE_STATS_FORMATTING_FUNCTIONS1

11.3.2定時初始化定時器代碼
[objc] view plain copy
  1. void vConfigureTimerForRunTimeStats( void )  
  2. {  
  3.     /* 使能定時器0的外設電源,配置外設時鐘 */  
  4.     PCONP |= 0x02UL;  
  5.     PCLKSEL0 = (PCLKSEL0& (~(0x3<<2))) | (0x01 << 2);  
  6.    
  7.     /* 復位定時器 0 */  
  8.     T0TCR = 0x02;  
  9.    
  10.     /* 做爲計數器 */  
  11.     T0CTCR = 0x00;  
  12.    
  13.     /* 預分頻,設置合適的分辨率便可 */  
  14.     T0PR =  ( configCPU_CLOCK_HZ / 10000UL ) - 1UL;  
  15.    
  16.     /* 啓動計數器 */  
  17.     T0TCR = 0x01;  
  18. }  
11.3.3定義配置定時器和獲取定時時間宏

      在文件FreeRTOSConfig.h中,定義下列代碼:

[objc] view plain copy
  1. extern void vConfigureTimerForRunTimeStats( void );  
  2. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() vConfigureTimerForRunTimeStats()  
  3. #defineportGET_RUN_TIME_COUNTER_VALUE() T0TC  

12.設置任務標籤值

12.1函數描述

[objc] view plain copy
  1. voidvTaskSetApplicationTaskTag( TaskHandle_t xTask,  
  2.                  TaskHookFunction_tpxTagValue );  

      能夠給每一個任務分配一個標籤值。這個值通常用於應用程序,RTOS內核不會使用。在文件FreeRTOSConfig.h中,宏configUSE_APPLICATION_TASK_TAG必須設置爲1,此函數纔有效。

12.2參數描述

  • xTask:任務句柄。NULL表示當前任務。
  • pxTagValue:要分配給任務的標籤值。這是一個TaskHookFunction_t類型的函數指針,但也能夠給任務標籤分配任意的值。

      注:TaskHookFunction_t原型定義:typedef BaseType_t (*TaskHookFunction_t)(void * )

12.3用法舉例

[objc] view plain copy
  1. /* 在這個例子中,給任務設置一個整形標籤值。例子中使用了RTOS跟蹤鉤子宏。*/  
  2. void vATask( voidvoid *pvParameters )  
  3. {  
  4.   /* 爲本身的標籤分配一個整形值 */  
  5.  vTaskSetApplicationTaskTag( NULL, ( voidvoid * ) 1 );  
  6.    
  7.   for( ;; )  
  8.   {  
  9.     /* 任務主體代碼 */  
  10.   }  
  11. }  
  12. /*****************************************************************************/  
  13.    
  14. /*在這個任務中,給任務設置一個函數標籤值。首先定義一個回調函數,這個函數必須聲明爲TaskHookFunction_t類型。 */  
  15. static BaseType_t prvExampleTaskHook( voidvoid * pvParameter )  
  16. {  
  17.   /* 這裏爲用戶定義代碼 –多是記錄數據、更新任務狀態值等。*/  
  18.    
  19.   return 0;  
  20. }  
  21.    
  22. /* 將回調函數設置爲任務的標籤值。 */  
  23. void vAnotherTask( voidvoid *pvParameters )  
  24. {  
  25.   /* 註冊回調函數*/  
  26.  vTaskSetApplicationTaskTag( NULL, prvExampleTaskHook );  
  27.    
  28.   for( ;; )  
  29.   {  
  30.      /* 任務主體代碼 */  
  31.   }  
  32. }  
  33.    
  34. /* 每當任務切換時,會調用xTaskCallApplicationTaskHook 函數(見14.)。 */  
  35. #define traceTASK_SWITCHED_OUT() xTaskCallApplicationTaskHook(pxCurrentTCB,0 )  

13.獲取任務標籤值

13.1函數描述

      TaskHookFunction_txTaskGetApplicationTaskTag( TaskHandle_t xTask );

      返回分配給任務的標籤值。程序員定義標籤值,RTOS內核一般不會訪問標籤值。

      函數僅對高級用戶使用。在文件FreeRTOSConfig.h中,宏configUSE_APPLICATION_TASK_TAG必須設置爲1,此函數纔有效。

13.2參數描述

  • xTask:任務句柄。NULL表示當前任務。

13.3返回值

      返回指定任務的標籤值。

14.執行任務的應用鉤子函數

14.1函數描述

[objc] view plain copy
  1. BaseType_txTaskCallApplicationTaskHook(  
  2.                           TaskHandle_txTask,  
  3.                           void*pvParameter );  

      能夠爲每一個任務分配一個標籤值,當這個值是一個TaskHookFunction_t類型函數指針時,至關於應用程序向任務註冊了一個回調函數,而API函數xTaskCallApplicationTaskHook用來調用這個回調函數。

      通常這個函數配合RTOS跟蹤鉤子宏使用,見12.設置任務標籤值一節的用法舉例。

14.2參數描述

  • xTask:任務句柄。NULL表示當前任務。
  • pvParameter:做爲參數傳遞給應用鉤子函數

15.設置線程本地存儲指針

15.1函數描述

[objc] view plain copy
  1. void vTaskSetThreadLocalStoragePointer(TaskHandle_t xTaskToSet,  
  2.                                 BaseType_t xIndex,  
  3.                                  void*pvValue )  

      此函數僅用於高級用戶。

      線程本地存儲容許應用程序在任務的控制塊中存儲一些值,每一個任務都有本身獨立的儲存空間。

      好比,許多庫函數都包含一個叫作errno的全局變量。某些庫函數使用errno返回庫函數錯誤信息,應用程序檢查這個全局變量來肯定發生了那些錯誤。在單線程程序中,將errno定義成全局變量是能夠的,可是在多線程應用中,每一個線程(任務)必須具備本身獨有的errno值,不然,一個任務可能會讀取到另外一個任務的errno值。

      FreeRTOS提供了一個靈活的機制,使得應用程序能夠使用線程本地存儲指針來讀寫線程本地存儲。在文件FreeRTOSConfig.h中,宏configNUM_THREAD_LOCAL_STORAGE_POINTERS指定每一個任務線程本地存儲指針數組的大小。API函數vTaskSetThreadLocalStoragePointer()用於向指針數組中寫入值,API函數pvTaskGetThreadLocalStoragePointer()用於從指針數組中讀取值。

15.2參數描述

  • xTaskToSet:任務句柄。NULL表示當前任務。
  • xIndex:寫入到線程本地存儲數組的索引號,線程本篤存儲數組的大小由宏configNUM_THREAD_LOCAL_STORAGE_POINTERS設定,該宏在文件FreeRTOSConfig.h中。
  • pvValue:寫入到指定索引地址的數據值

15.3用法舉例

     參見16.獲取線程本地存儲指針一節。

16.讀取線程本地存儲指針

16.1函數描述

[objc] view plain copy
  1. void*pvTaskGetThreadLocalStoragePointer(  
  2.                         TaskHandle_txTaskToQuery,  
  3.                         BaseType_txIndex );  

      此函數僅用於高級用戶。從線程本地存儲指針數組中讀取值。更詳細描述見15.設置線程本地存儲指針一節。

16.2參數描寫

  • xTaskToQuery:任務句柄。NULL表示當前任務。
  • xIndex:寫入到線程本地存儲數組的索引號,線程本篤存儲數組的大小由宏configNUM_THREAD_LOCAL_STORAGE_POINTERS設定,該宏在文件FreeRTOSConfig.h中。

16.3返回值

      返回一個指針,這個指針存儲在線程本地存儲指針數組中,數組索引由參數xIndex指定。

16.4用法舉例

16.4.1存儲一個整形數
[objc] view plain copy
  1. uint32_tulVariable;  
  2.    
  3. /* 向當前任務的線程本地存儲數組下標爲1的位置寫入一個指向32位常量值的指針。*/  
  4. vTaskSetThreadLocalStoragePointer(NULL, 1, ( voidvoid * ) 0x12345678 );  
  5.    
  6. /*向當前任務的線程本地存儲數組下標爲0的位置寫入一個指向32整形值的指針*/  
  7. ulVariable= ERROR_CODE;  
  8. vTaskSetThreadLocalStoragePointer(NULL, 0, ( voidvoid * ) ulVariable );  
  9.    
  10. /*從當前任務的線程本地存儲數組下標爲5的位置讀取出一個指針並賦值給32位整形變量。*/  
  11. ulVariable= ( uint32_t ) pvTaskGetThreadLocalStoragePointer( NULL, 5 );  
16.4.2存儲結構提
[objc] view plain copy
  1. typedefstruct  
  2. {  
  3.     uint32_t ulValue1;  
  4.     uint32_t ulValue2;  
  5. }xExampleStruct;  
  6.    
  7. xExampleStruct*pxStruct;  
  8.    
  9. /*爲結構體分配內存*/  
  10. pxStruct= pvPortMalloc( sizeof( xExampleStruct ) );  
  11.    
  12. /*爲結構體成員賦值*/  
  13. pxStruct->ulValue1= 0;  
  14. pxStruct->ulValue2= 1;  
  15.    
  16. /*向當前任務的線程本地存儲數組下標爲0的位置寫入一個指向結構體變量的指針*/  
  17. vTaskSetThreadLocalStoragePointer(NULL, 0, ( voidvoid * ) pxStruct );  
  18.    
  19. /*從當前任務的線程本地存儲數組下標爲0的位置讀取出一個結構體指針*/  
  20. pxStruct= ( xExampleStruct * ) pvTaskGetThreadLocalStoragePointer( NULL, 0 );  

17.設置超時狀態

17.1函數描述

      void vTaskSetTimeOutState( TimeOut_t *const pxTimeOut );

      此函數僅用於高級用戶,一般與API函數xTaskCheckForTimeOut()共同使用。

      任務由於等待某事件而進入阻塞狀態,一般狀況下任務會設置一個等待超時週期。若是在等待事件超時,任務會退出阻塞狀態。想象一個這樣的應用,某任務等待一個事件而進入阻塞狀態,可是事件遲遲不發生,超時後任務退出阻塞狀態繼續執行任務。假如任務等待的事件仍然沒有發生,則任務又會阻塞在該事件下。只要任務等待的事件一直不發生,這個任務進入阻塞而後超時退出阻塞,再進入阻塞的循環就會一直存在。是否是能夠設定一個總超時時間,只要總阻塞時間大於這個總超時時間,則能夠結束這個任務或進行相應記錄?freeRTOS提供了兩個API函數來完成這個功能,這就是vTaskSetTimeOutState()和xTaskCheckForTimeOut()。

      vTaskSetTimeOutState()函數用於設置初始條件,以後調用xTaskCheckForTimeOut()函數檢查任務總阻塞時間是否超過總超時時間,若是沒有超過,則調整剩餘的超時時間計數器。

17.2參數描述

  • pxTimeOut:指向一個結構體的指針,該結構體用來保存肯定超時是否發生的必要信息。vTaskSetTimeOutState()函數會設置結構體的成員。

17.3用法舉例

      參見18.超時檢測

18.超時檢測

18.1函數描述

[objc] view plain copy
  1. BaseType_t xTaskCheckForTimeOut(TimeOut_t * const pxTimeOut,  
  2.                         TickType_t* const pxTicksToWait );  

      此函數僅用於高級用戶,一般與API函數vTaskSetTimeOutState共同使用。

      詳細描述見17.設置超時狀態

18.2參數描述

  • pxTimeOut:指向一個結構體的指針。該結構體保存肯定超時是否發生的必要信息。使用API函數vTaskSetTimeOutState初始化該結構體。
  • pxTicksToWait:TickType_t指針,指向的變量保存總超時時間。

18.3返回值

  • pdTRUE:總超時發生
  • pdFALSE:總超時未發生

18.4用法舉例

[objc] view plain copy
    1. /* 函數用於從RX緩衝區中接收uxWantedBytes字節數據,RX緩衝區由UART中斷填充。若是RX緩衝區沒有足夠的數據,則任務進入阻塞狀態,直到RX緩衝區有足夠數據或者發生超時。若是超時後仍然沒有足夠的數據,則任務會再次進入阻塞狀態,xTaskCheckForTimeOut()函數用於從新計算總超時時間以確保總阻塞狀態時間不超過MAX_TIME_TO_WAIT。若是總阻塞狀態時間大於了總超時時間,則無論RX緩衝區是否有充足數據,都將這些數據讀出來。 
    2.  */  
    3. size_txUART_Receive( uint8_t *pucBuffer, size_t uxWantedBytes )  
    4. {  
    5.     size_t uxReceived = 0;  
    6.     TickType_t xTicksToWait = MAX_TIME_TO_WAIT;  
    7.     TimeOut_t xTimeOut;  
    8.    
    9.    /* 初始化結構體變量xTimeOut。*/  
    10.    vTaskSetTimeOutState( &xTimeOut );  
    11.    
    12.    /* 無限循環,直到緩衝區包含足夠的數據或者阻塞超時發生。*/  
    13.    while( UART_bytes_in_rx_buffer(pxUARTInstance ) < uxWantedBytes )  
    14.    {  
    15.       /* RX緩衝區沒有足夠多的數據,表示任務已經進入過一次阻塞狀態。調用API函數xTaskCheckForTimeOut檢查總阻塞時間是否超過總超時時間,若是沒有,則調整剩餘的總超時時間。*/  
    16.       if( xTaskCheckForTimeOut( &xTimeOut,&xTicksToWait ) != pdFALSE )  
    17.       {  
    18.          /* 若是總阻塞時間大於總超時時間,則退出這個循環 */  
    19.          break;  
    20.       }  
    21.    
    22.       /* 在等待了xTicksToWait個系統節拍週期後,向接收中斷髮出通知,須要更多數據。 
    23. */  
    24.       ulTaskNotifyTake( pdTRUE, xTicksToWait );  
    25.    }  
    26.    
    27.     /*從RX緩衝區讀取uxWantedBytes個字節並放到pucBuffer緩衝區。*/  
    28.     uxReceived = UART_read_from_receive_buffer(pxUARTInstance,  pucBuffer,  uxWantedBytes );  
    29.    
    30.     return uxReceived;  

FreeRTOS系列第13篇---FreeRTOS內核控制內核控制的一些功能須要移植層提供,爲了方便移植,這些API函數用宏來實現,好比上下文切換、進入和退出臨界區、禁止和使能可屏蔽中斷。內核控制函數還包括啓動和中止調度器、掛起和恢復調度器以及用於低功耗模式的調整系統節拍函數。

1.強制上下文切換宏

      taskYIELD:用於強制上下文切換的宏。在中斷服務程序中的等價版本爲portYIELD_FROM_ISR,這也是個宏,其實現取決於移植層。

      用於上下文切換的實際代碼由移植層提供。對於Cortex-M3硬件,這個宏會引發PendSV中斷。

2.進入臨界區宏

      taskENTER_CRITICAL:用於進入臨界區的宏。在臨界區中不會發生上下文切換。

     進入臨界區的實際代碼由移植層提供,對於Cortex-M3硬件,先禁止全部RTOS可屏蔽中斷,這能夠經過向basepri 寄存器寫入configMAX_SYSCALL_INTERRUPT_PRIORITY來實現。basepri寄存器被設置成某個值後,全部優先級號大於等於此值的中斷都被禁止,但若被設置爲0,則不關閉任何中斷,0爲默認值。而後臨界區嵌套計數器增1。

3.退出臨界區宏

      taskEXIT_CRITICAL:用於退出臨界區的宏。

      退出臨界區的實際代碼有移植層提供,對於Cortex-M3硬件,先將臨界區嵌套計數器減1,若是臨界區計數器爲零,則使能全部RTOS可屏蔽中斷,這能夠經過向basepri 寄存器寫入0來實現。

4.禁止可屏蔽中斷宏

      taskDISABLE_INTERRUPTS:禁止全部RTOS可屏蔽中斷。在調用宏taskENTER_CRITICAL進入臨界區時,也會間接調用該宏禁止全部RTOS可屏蔽中斷。

5.使能可屏蔽中斷宏

      taskENABLE_INTERRUPTS:使能全部RTOS可屏蔽中斷。在調用宏taskEXIT_CRITICAL退出臨界區時,也會間接調用該宏使能全部RTOS可屏蔽中斷。

6.啓動調度器

6.1函數描述

      void vTaskStartScheduler( void );

      啓動RTOS調度器,以後RTOS內核控制哪一個任務執行以及什麼時候執行。

      當調用vTaskStartScheduler()後,空閒任務被自動建立。若是configUSE_TIMERS被設置爲1,定時器後臺任務也會被建立。

      若是vTaskStartScheduler()成功執行,則該函數不會返回,直到有任務調用了vTaskEndScheduler()。若是由於RAM不足而沒法建立空閒任務,該函數也可能執行失敗,並會馬上返回調用處。

7.中止調度器

7.1函數描述

      void vTaskEndScheduler( void );

      僅用於x86硬件架構中。

      中止RTOS內核系統節拍時鐘。全部建立的任務自動刪除並中止多任務調度。

8.掛起調度器

8.1函數描述

      void vTaskSuspendAll( void );

      掛起調度器,但不由止中斷。當調度器掛起時,不會進行上下文切換。調度器掛起後,正在執行的任務會一直繼續執行,內核再也不調度(意味着當前任務不會被切換出去),直到該任務調用了xTaskResumeAll ()函數。

      內核調度器掛起期間,那些能夠引發上下文切換的API函數(如vTaskDelayUntil()、xQueueSend()等)決不可以使用。

9.恢復被掛起的調度器

9.1函數描述

      BaseType_t xTaskResumeAll( void );

      恢復因調用vTaskSuspendAll()函數而掛起的實時內核調度器。xTaskResumeAll()僅恢復調度器,它不會恢復那些被vTaskSuspend()函數掛起的任務。

9.2返回值

      返回pdTRUE 表示恢復調度器引發了一次上下文切換,不然,返回pdFALSE。

9.3用法舉例

[objc] view plain copy
  1. voidvTask1( voidvoid * pvParameters )  
  2. {  
  3.     for( ;; )  
  4.     {  
  5.         /* 任務代碼寫在這裏 */  
  6.   
  7.         /* ... */  
  8.   
  9.         /* 有些時候,某個任務但願能夠連續長時間的運行,但這時不能使用taskENTER_CRITICAL ()/taskEXIT_CRITICAL ()的方法,這樣會屏蔽掉中斷,引發中斷丟失,包括系統節拍時鐘。能夠使用vTaskSuspendAll ()中止RTOS內核調度:*/  
  10.         xTaskSuspendAll ();  
  11.   
  12.         /* 執行操做代碼放在這裏。這樣不用進入臨界區就能夠連續長時間執行了。在這期間,中斷仍然會獲得響應,RTOS內核系統節拍時鐘也會繼續保持運做 */  
  13.   
  14.         /* ... */  
  15.   
  16.         /* 操做結束,從新啓動RTOS內核 。咱們想強制進行一次上下文切換,可是若是恢復調度器的時候已經執行了上下文切換,再執行一次是沒有意義的,所以會進行一次判斷。*/  
  17.         if( !xTaskResumeAll () )  
  18.         {  
  19.              taskYIELD ();  
  20.         }  
  21.     }  
  22. }  

10.調整系統節拍

10.1函數描述

      void vTaskStepTick( TickType_txTicksToJump );

      若是RTOS使能tickless空閒功能,每當只有空閒任務被執行時,系統節拍時鐘中斷將會中止,微控制器進入低功耗模式。當微控制器退出低功耗後,系統節拍計數器必須被調整,將進入低功耗的時間彌補上。

      若是FreeRTOS移植文件中定義了宏portSUPPRESS_TICKS_AND_SLEEP()實體,則函數vTaskStepTick用於在這個宏portSUPPRESS_TICKS_AND_SLEEP()實體內部調整系統節拍計數器。函數vTaskStepTick是一個全局函數,因此也能夠在宏portSUPPRESS_TICKS_AND_SLEEP()實體中重寫該函數。

      在文件FreeRTOSConfig.h中,宏configUSE_TICKLESS_IDLE必須設置爲1,此函數纔有效。

10.2參數描述

  • xTickToJump:時間值,單位是系統節拍週期,表示微處理器進入低功耗的時間,函數根據這個值來調整系統節拍計數器的值。

10.3用法舉例

[objc] view plain copy
  1. /* 首先定義宏portSUPPRESS_TICKS_AND_SLEEP()。宏參數指定要進入低功耗(睡眠)的時間,單位是系統節拍週期。*/  
  2. #defineportSUPPRESS_TICKS_AND_SLEEP( xIdleTime ) vApplicationSleep( xIdleTime )  
  3.    
  4. /* 定義被宏portSUPPRESS_TICKS_AND_SLEEP()調用的函數 */  
  5. void vApplicationSleep(TickType_t xExpectedIdleTime )  
  6. {  
  7.     unsigned long ulLowPowerTimeBeforeSleep,ulLowPowerTimeAfterSleep;  
  8.    
  9.     /* 從時鐘源獲取當前時間,當微控制器進入低功耗的時候,這個時鐘源必須在運行 */  
  10.     ulLowPowerTimeBeforeSleep =ulGetExternalTime();  
  11.    
  12.     /*中止系統節拍時鐘中斷。*/  
  13.     prvStopTickInterruptTimer();  
  14.    
  15.     /* 配置一箇中斷,當指定的睡眠時間達到後,將處理器從低功耗中喚醒。這個中斷源必須在微控制器進入低功耗時也能夠工做。*/  
  16.     vSetWakeTimeInterrupt( xExpectedIdleTime );  
  17.    
  18.     /*進入低功耗 */  
  19.     prvSleep();  
  20.    
  21.     /* 肯定微控制器進入低功耗模式持續的真正時間。由於其它中斷也可能使得微處理器退出低功耗模式。注意:在調用宏portSUPPRESS_TICKS_AND_SLEEP()以前,調度器應該被掛起,portSUPPRESS_TICKS_AND_SLEEP()返回後,再將調度器恢復。所以,這個函數未完成前,不會執行其它任務。*/  
  22.     ulLowPowerTimeAfterSleep =ulGetExternalTime();  
  23.           
  24.     /*調整內核系統節拍計數器。*/  
  25.     vTaskStepTick( ulLowPowerTimeAfterSleep –ulLowPowerTimeBeforeSleep );  
  26.    
  27.     /*從新啓動系統節拍時鐘中斷。*/  
  28.     prvStartTickInterruptTimer();  

 

FreeRTOS系列第14篇---FreeRTOS任務通知

注:本文介紹任務通知的基礎知識,詳細源碼分析見《FreeRTOS高級篇8---FreeRTOS任務通知分析

      每一個RTOS任務都有一個32位的通知值,任務建立時,這個值被初始化爲0。RTOS任務通知至關於直接向任務發送一個事件,接收到通知的任務能夠解除阻塞狀態,前提是這個阻塞事件是因等待通知而引發的。發送通知的同時,也能夠可選的改變接收任務的通知值。

      能夠經過下列方法向接收任務更新通知:

  • 不覆蓋接收任務的通知值
  • 覆蓋接收任務的通知值
  • 設置接收任務通知值的某些位
  • 增長接收任務的通知值

      相對於用前必須分別建立隊列、二進制信號量、計數信號量或事件組的狀況,使用任務通知顯然更靈活。更好的是,相比於使用信號量解除任務阻塞,使用任務通知能夠快45%、使用更少的RAM(使用GCC編譯器,-o2優化級別)。

      使用API函數xTaskNotify()和xTaskNotifyGive()(中斷保護等價函數爲xTaskNotifyFromISR()和vTaskNotifyGiveFromISR())發送通知,在接收RTOS任務調用API函數xTaskNotifyWait()或ulTaskNotifyTake()以前,這個通知都被保持着。若是接收RTOS任務已經由於等待通知而進入阻塞狀態,則接收到通知後任務解除阻塞並清除通知。

      RTOS任務通知功能默認是使能的,能夠經過在文件FreeRTOSConfig.h中設置宏configUSE_TASK_NOTIFICATIONS爲0來禁止這個功能,禁止後每一個任務節省8字節內存。

      雖然RTOS任務通知速度更快而且佔用內存更少,但它也有一些限制:

  1. 只能有一個任務接收通知事件。
  2. 接收通知的任務能夠由於等待通知而進入阻塞狀態,可是發送通知的任務即使不能當即完成通知發送也不能進入阻塞狀態。

1.發送通知-方法1

1.1函數描述

[objc] view plain copy
  1. BaseType_t xTaskNotify( TaskHandle_txTaskToNotify,  
  2.                 uint32_t ulValue,  
  3.                 eNotifyAction eAction);  

      向指定任務發送指定的通知值。若是打算使用RTOS任務通知實現輕量級的二進制或計數信號量,推薦使用API函數xTaskNotifyGive()來代替本函數。

      此函數不能夠在中斷服務例程中調用,中斷保護等價函數爲xTaskNotifyFromISR()。

1.2參數描述

  • xTaskToNotify:被通知的任務句柄。
  • ulValue:通知更新值
  • eAction:枚舉類型,指明更新通知值的方法

      枚舉變量成員以及做用以下表所示。

1.3返回值

      參數eAction爲eSetValueWithoutOverwrite時,若是被通知任務還沒取走上一個通知,又接收到了一個通知,則此次通知值未能更新並返回pdFALSE,不然返回pdPASS。

2.發送通知-方法2

2.1函數描述

      BaseType_t xTaskNotifyGive(TaskHandle_t xTaskToNotify );

      其實這是一個宏,本質上至關於xTaskNotify( ( xTaskToNotify ), ( 0 ), eIncrement )。能夠使用該API函數代替二進制或計數信號量,但速度更快。在這種狀況下,應該使用API函數ulTaskNotifyTake()來等待通知,而不該該使用API函數xTaskNotifyWait()。

      此函數不能夠在中斷服務例程中調用,中斷保護等價函數爲vTaskNotifyGiveFromISR()。

2.2參數描述

  • xTaskToNotify:被通知的任務句柄。

2.3用法舉例

[objc] view plain copy
  1. staticvoid prvTask1( voidvoid *pvParameters );  
  2. staticvoid prvTask2( voidvoid *pvParameters );  
  3.    
  4. /* 保存任務句柄 */  
  5. staticTaskHandle_t xTask1 = NULL, xTask2 = NULL;  
  6.    
  7. /* 建立兩個任務,它們之間反覆發送通知,啓動RTOS調度器*/  
  8. voidmain( void )  
  9. {  
  10.     xTaskCreate( prvTask1, "Task1",200, NULL, tskIDLE_PRIORITY, &xTask1 );  
  11.     xTaskCreate( prvTask2, "Task2",200, NULL, tskIDLE_PRIORITY, &xTask2 );  
  12.     vTaskStartScheduler();  
  13. }  
  14. /*-----------------------------------------------------------*/  
  15.    
  16. staticvoid prvTask1( voidvoid *pvParameters )  
  17. {  
  18.     for( ;; )  
  19.     {  
  20.         /*向prvTask2(),發送通知,使其解除阻塞狀態 */  
  21.         xTaskNotifyGive( xTask2 );  
  22.    
  23.         /* 等待prvTask2()的通知,進入阻塞 */  
  24.         ulTaskNotifyTake( pdTRUE, portMAX_DELAY);  
  25.     }  
  26. }  
  27. /*-----------------------------------------------------------*/  
  28.    
  29. staticvoid prvTask2( voidvoid *pvParameters )  
  30. {  
  31.     for( ;; )  
  32.     {  
  33.         /* 等待prvTask1()的通知,進入阻塞 */  
  34.         ulTaskNotifyTake( pdTRUE, portMAX_DELAY);  
  35.    
  36.         /*向prvTask1(),發送通知,使其解除阻塞狀態 */  
  37.         xTaskNotifyGive( xTask1 );  
  38.     }  
  39. }  

3.獲取通知

3.1函數描述

[objc] view plain copy
  1. uint32_t ulTaskNotifyTake( BaseType_txClearCountOnExit,  
  2.                    TickType_txTicksToWait );  

      ulTaskNotifyTake()是專門爲使用更輕量級更快的方法來代替二進制或計數信號量而量身打造的。FreeRTOS獲取信號量的API函數爲xSemaphoreTake(),能夠使用ulTaskNotifyTake()函數等價代替。

      當一個任務使用通知值來實現二進制或計數信號量時,其它任務或者中斷要使用API函數xTaskNotifyGive()或者使用參數eAction爲eIncrementAPI函數xTaskNotify()。推薦使用xTaskNotifyGive()函數(實際上是個宏,咱們這裏把它看做一個API函數)。另外須要注意的是,若是在中斷中使用,要使用它們的中斷保護等價函數:vTaskNotifyGiveFromISR()和xTaskNotifyFromISR()。

      API函數xTaskNotifyTake()有兩種方法處理任務的通知值,一種方法是在函數退出時將通知值清零,這種方法適用於實現二進制信號量;另一種方法是在函數退出時將通知值減1,這種方法適用於實現計數信號量。

      若是RTOS任務的通知值爲0,使用xTaskNotifyTake()能夠可選的使任務進入阻塞狀態,直到該任務的通知值不爲0。進入阻塞的任務不消耗CPU時間。

3.2參數描述

  • xClearCountOnExit:若是該參數設置爲pdFALSE,則API函數xTaskNotifyTake()退出前,將任務的通知值減1;若是該參數設置爲pdTRUE,則API函數xTaskNotifyTake()退出前,將任務通知值清零。
  • xTicksToWait:因等待通知而進入阻塞狀態的最大時間。時間單位爲系統節拍週期。宏pdMS_TO_TICKS用於將指定的毫秒時間轉化爲相應的系統節拍數。

3.3返回值

      返回任務的當前通知值,爲0或者爲調用API函數xTaskNotifyTake()以前的通知值減1。

3.4用法舉例

[objc] view plain copy
  1. /* 中斷處理程序。*/  
  2. voidvANInterruptHandler( void )  
  3. {  
  4. BaseType_txHigherPriorityTaskWoken;  
  5.    
  6.     prvClearInterruptSource();  
  7.    
  8.     /* xHigherPriorityTaskWoken必須被初始化爲pdFALSE。若是調用vTaskNotifyGiveFromISR()會解除vHandlingTask任務的阻塞狀態,而且vHandlingTask任務的優先級高於當前處於運行狀態的任務,則xHigherPriorityTaskWoken將會自動被設置爲pdTRUE。*/  
  9.     xHigherPriorityTaskWoken = pdFALSE;  
  10.    
  11.     /*向一個任務發送通知,xHandlingTask是該任務的句柄。*/  
  12.     vTaskNotifyGiveFromISR( xHandlingTask,&xHigherPriorityTaskWoken );  
  13.    
  14.     /* 若是xHigherPriorityTaskWoken爲pdTRUE,則強制上下文切換。這個宏的實現取決於移植層,可能會調用portEND_SWITCHING_ISR */  
  15.     portYIELD_FROM_ISR(xHigherPriorityTaskWoken );  
  16. }  
  17. /*---------------------------------------------------------------------------------------------------*/  
  18.    
  19. /* 一個由於等待通知而阻塞的任務。*/  
  20. voidvHandlingTask( voidvoid *pvParameters )  
  21. {  
  22. BaseType_txEvent;  
  23.    
  24.     for( ;; )  
  25.     {  
  26.         /*等待通知,無限期阻塞。參數pdTRUE表示函數退出前會清零通知值。這顯然是用於替代二進制信號量的用法。須要注意的是,真實的程序通常不會無限期阻塞。*/  
  27.         ulTaskNotifyTake( pdTRUE, portMAX_DELAY);  
  28.    
  29.         /* 當處理完全部事件後,仍然等待下一個通知*/  
  30.         do  
  31.         {  
  32.             xEvent = xQueryPeripheral();  
  33.    
  34.             if( xEvent != NO_MORE_EVENTS )  
  35.             {  
  36.                 vProcessPeripheralEvent( xEvent);  
  37.             }  
  38.    
  39.         } while( xEvent != NO_MORE_EVENTS );  
  40.     }  
  41. }  

4.等待通知

4.1函數描述

[objc] view plain copy
  1. BaseType_t xTaskNotifyWait( uint32_tulBitsToClearOnEntry,  
  2.                     uint32_tulBitsToClearOnExit,  
  3.                     uint32_t*pulNotificationValue,  
  4.                     TickType_txTicksToWait );  

      若是打算使用RTOS任務通知實現輕量級的二進制或計數信號量,推薦使用API函數ulTaskNotifyTake()來代替本函數。

4.2參數描述

  • ulBitsToClearOnEntry:在使用通知以前,先將任務的通知值與參數ulBitsToClearOnEntry的按位取反值按位與操做。設置參數ulBitsToClearOnEntry爲0xFFFFFFFF(ULONG_MAX),表示清零任務通知值。
  • ulBitsToClearOnExit:在函數xTaskNotifyWait()退出前,將任務的通知值與參數ulBitsToClearOnExit的按位取反值按位與操做。設置參數ulBitsToClearOnExit爲0xFFFFFFFF(ULONG_MAX),表示清零任務通知值。
  • pulNotificationValue:用於向外回傳任務的通知值。這個通知值在參數ulBitsToClearOnExit起做用前將通知值拷貝到*pulNotificationValue中。若是不須要返回任務的通知值,這裏設置成NULL。
  • xTicksToWait:因等待通知而進入阻塞狀態的最大時間。時間單位爲系統節拍週期。宏pdMS_TO_TICKS用於將指定的毫秒時間轉化爲相應的系統節拍數。

4.3返回值

      若是接收到通知,返回pdTRUE,若是API函數xTaskNotifyWait()等待超時,返回pdFALSE。

4.4用法舉例

[objc] view plain copy
  1. /*這個任務使用任務通知值的位來傳遞不一樣的事件,這在某些狀況下能夠代替事件組。*/  
  2. voidvAnEventProcessingTask( voidvoid *pvParameters )  
  3. {  
  4. uint32_tulNotifiedValue;  
  5.    
  6.     for( ;; )  
  7.     {  
  8.         /*等待通知,無限期阻塞(沒有超時,因此不用檢查函數返回值)。其它任務或者中斷設置的通知值中的不一樣位表示不一樣的事件。參數0x00表示使用通知前不清除任務的通知值位,參數ULONG_MAX 表示函數xTaskNotifyWait()退出前將任務通知值設置爲0*/  
  9.         xTaskNotifyWait( 0x00, ULONG_MAX,&ulNotifiedValue, portMAX_DELAY );  
  10.    
  11.         /*根據通知值處理事件*/  
  12.         if( ( ulNotifiedValue & 0x01 ) != 0)  
  13.         {  
  14.             prvProcessBit0Event();  
  15.         }  
  16.    
  17.         if( ( ulNotifiedValue & 0x02 ) != 0)  
  18.         {  
  19.             prvProcessBit1Event();  
  20.         }  
  21.    
  22.         if( ( ulNotifiedValue & 0x04 ) != 0)  
  23.         {  
  24.             prvProcessBit2Event();  
  25.         }  
  26.    
  27.         /* ……*/  
  28.     }  
  29. }  

5.任務通知並查詢

5.1函數描述

[objc] view plain copy
  1. BaseType_t xTaskNotifyAndQuery(TaskHandle_t xTaskToNotify,  
  2.                         uint32_tulValue,  
  3.                         eNotifyActioneAction,  
  4.                         uint32_t*pulPreviousNotifyValue );  

      此函數與任務通知API函數xTaskNotify()很是像,只不過此函數具備一個附加參數,用來回傳任務當前的通知值,而後根據參數ulValue和eAction更新任務的通知值。

      此函數不能在中斷服務例程中使用,在中斷服務例程中使用xTaskNotifyAndQueryFromISR()函數。

5.2參數描述

  • xTaskToNotify:被通知的任務句柄。
  • ulValue:通知更新值
  • eAction:枚舉類型,指明更新通知值的方法,枚舉變量成員以及做用見xTaskNotify()一節。
  • pulPreviousNotifyValue:回傳未被更新的任務通知值。若是不須要回傳未被更新的任務通知值,這裏設置爲NULL,這樣就等價於調用xTaskNotify()函數。

5.3返回值

      參數eAction爲eSetValueWithoutOverwrite時,若是被通知任務還沒取走上一個通知,又接收到了一個通知,則此次通知值未能更新並返回pdFALSE,不然返回pdPASS。

 

 

 

FreeRTOS系列第15篇---使用任務通知實現命令行解釋器

雖然這是介紹FreeRTOS系列的文章,但這篇文章偏重於命令行解釋器的實現。這一方面是由於任務通知使用起來很是簡單,另外一方面也由於對於嵌入式程序來講,使用命令行解釋器來輔助程序調試是很是有用的。程序調試是一門技術,基本上咱們須要兩種調試手段,一種是能夠單步仿真的硬件調試器,另一種是能夠長期監視程序狀態的狀態輸出,能夠經過串口、顯示屏等等手段輸出異常信息或者某些關鍵點。這裏的命令行解釋器就屬於後者。

     本文實現的命令行解釋器具備如下特性:

  • 支持十進制參數,識別負號;
  • 支持十六進制參數,十六進制以‘0x’開始;
  • 命令名長度可定義,默認最大20個字符;
  • 參數數目可定義,默認最多8個參數;
  • 命令名和參數之間以空格隔開,空格個數任意;
  • 整條命令以回車換行符結束;
  • 整條命令最大長度可定義,默認64字節,包括回車換行符;
  • 若是使用SecureCRT串口工具(推薦),支持該軟件的控制字符,好比退格鍵、左移鍵、右移鍵等。

      一個帶參數的命令格式以下所示:

                                      參數名 <參數1> <參數2> … <參數3>[回車換行符]

1.編碼風格

      FreeRTOS的編碼標準及風格見《FreeRTOS系列第4篇---FreeRTOS編碼標準及風格指南》,但我本身的編碼風格跟FreeRTOS並不相同,而且我也不打算改變我當前堅持使用的編碼風格。因此在這篇或者之後的文章中可能會在一個程序中看到兩種不一樣的編碼風格,對於涉及FreeRTOS的代碼,我儘量使用FreeRTOS建議的編碼風格,與FreeRTOS無關的代碼,我仍然使用本身的編碼風格。我能夠保證,兩種編碼風格決不會影響程序的可讀性,編寫良好可讀性的代碼,是我一直注重並堅持的。

2.一些準備工做

2.1串口硬件驅動

      命令行解釋器使用一個硬件串口,須要外部提供兩個串口底層函數:一個是串口初始化函數init_cmd_uart(),用於初始化串口波特率、中斷等事件;另外一個是發送單個字符函數my_putc()。此外,命令行爲串口接收中斷服務程序提供函數fill_rec_buf(),用於保存接收到的字符,當收到回車換行符後,該函數向命令行分析任務發送通知。

2.2一個類printf函數

      類printf函數用來格式化輸出,我通常用來輔助調試,爲了方便的將調試代碼從程序中去除,須要將類printf函數進行封裝。個人文章《編寫優質嵌入式C程序》第5.2節給出了一個完整的類printf函數實現和封裝代碼,最終咱們使用到的類printf函數是以下形式的宏:

[objc] view plain copy
  1. MY_DEBUGF(CMD_LINE_DEBUG,("第%d個參數:%d\n",i+1,arg[i]));      

3.使用任務通知

      咱們將會建立一個任務,用來分析接收到的命令,若是命令有效則調用命令實現函數。這個任務名字爲vTaskCmdAnalyze()。串口接收中斷用於接收命令,若是接收到回車換行符,則向任務vTaskCmdAnalyze()發送任務通知,代表已經接收到一條完整命令,任務能夠去處理了。

       示意框圖如圖3-1所示。

4.數據結構

      命令行解釋器程序須要涉及兩個數據結構:一個與命令有關,包括命令的名字、命令的最大參數數目、命令的回調函數類型、命令幫助信息等;另外一個與分析命令有關,包括接收命令字符緩衝區、存放參數緩衝區等。

4.1與命令有關的數據結構

       定義以下:

[objc] view plain copy
  1. typedef struct {  
  2.      char constconst *cmd_name;                        //命令字符串  
  3.      int32_t max_args;                            //最大參數數目  
  4.      void (*handle)(int argc,voidvoid * cmd_arg);     //命令回調函數  
  5.      char  *help;                                 //幫助信息  
  6.  }cmd_list_struct;  

      須要說明一下命令回調函數的參數,argc保存接收到的參數數目,cmd_arg指向參數緩衝區,目前只支持32位的整形參數,這在絕大多數嵌入式場合是足夠的。

4.2與分析命令有關數據結構

       定義以下:

[objc] view plain copy
  1. #define ARG_NUM     8          //命令中容許的參數個數  
  2. #define CMD_LEN     20         //命令名佔用的最大字符長度  
  3. #define CMD_BUF_LEN 60         //命令緩存的最大長度  
  4.    
  5. typedef struct {  
  6.     char rec_buf[CMD_BUF_LEN];            //接收命令緩衝區  
  7.     char processed_buf[CMD_BUF_LEN];      //存儲加工後的命令(去除控制字符)  
  8.     int32_t cmd_arg[ARG_NUM];             //保存命令的參數  
  9. }cmd_analyze_struct;  

      緩衝區的大小使用宏來定義,經過更改相應的宏定義,能夠設置整條命令的最大長度、命令參數最大數目等。

5.串口接收中斷處理函數

      本文使用的串口軟件是SecureCRT,在這個軟件下敲擊的任何鍵盤字符,都會馬上經過串口硬件發送出去,這與Telnet相似。因此咱們無需使用串口的FIFO,每接收到一個字符就產生一次中斷。串口中斷與硬件關係密切,因此命令行解釋器提供了一個與硬件無關的函數fill_rec_buf(),每當串口中斷接收到一個字符,就以收到的字符爲參數調用這個函數。       fill_rec_buf()函數主要操做變量cmd_analyze,變量的聲明原型爲:

[objc] view plain copy
  1. cmd_analyze_struct cmd_analyze;  

       函數fill_rec_buf()的實現代碼爲:

[objc] view plain copy
  1. /*提供給串口中斷服務程序,保存串口接收到的單個字符*/  
  2. void fill_rec_buf(char data)  
  3. {  
  4.     //接收數據   
  5.     static uint32_t rec_count=0;  
  6.      
  7.    cmd_analyze.rec_buf[rec_count]=data;  
  8.     if(0x0A==cmd_analyze.rec_buf[rec_count] && 0x0D==cmd_analyze.rec_buf[rec_count-1])  
  9.     {  
  10.        BaseType_t xHigherPriorityTaskWoken = pdFALSE;  
  11.        rec_count=0;  
  12.          
  13.        /*收到一幀數據,向命令行解釋器任務發送通知*/  
  14.        vTaskNotifyGiveFromISR (xCmdAnalyzeHandle,&xHigherPriorityTaskWoken);  
  15.          
  16.        /*是否須要強制上下文切換*/  
  17.        portYIELD_FROM_ISR(xHigherPriorityTaskWoken );  
  18.     }  
  19.     else  
  20.     {  
  21.        rec_count++;  
  22.          
  23.        /*防護性代碼,防止數組越界*/  
  24.        if(rec_count>=CMD_BUF_LEN)  
  25.        {  
  26.            rec_count=0;  
  27.        }  
  28.     }      
  29. }  

6.命令行分析任務

      命令行分析任務大部分時間都會由於等待任務通知而處於阻塞狀態。當接收到一個通知後,任務首先去除命令行中的無效字符和控制字符,而後找出命令名並分析參數數目、將參數轉換成十六進制數並保存到參數緩衝區中,最後檢查命令名和參數是否合法,若是合法則調用命令回調函數處理本條命令。

6.1去除無效字符和控制字符

      串口軟件SecureCRT支持控制字符。好比在輸入一串命令的時候,發現某個字符輸入錯誤,就要使用退格鍵或者左右移動鍵定位到錯誤的位置進行修改。這裏的退格鍵和左右移動鍵都屬於控制字符,好比退格鍵的鍵值爲0x0八、左移鍵的鍵值爲0x1B0x5B 0x44。咱們以前也說過,在軟件SecureCRT中輸入字符時,每敲擊一個字符,該字符馬上經過串口發送給咱們的嵌入式設備,也就是全部鍵值都會按照敲擊鍵盤的順序存入到接收緩衝區中,但這裏面可能有咱們不須要的字符,咱們首先須要利用控制字符將不須要的字符刪除掉。這個工做由函數get_true_char_stream()實現,代碼以下所示:

[objc] view plain copy
  1. /** 
  2. * 使用SecureCRT串口收發工具,在發送的字符流中可能帶有不須要的字符以及控制字符, 
  3. * 好比退格鍵,左右移動鍵等等,在使用命令行工具解析字符流以前,須要將這些無用字符以 
  4. * 及控制字符去除掉. 
  5. * 支持的控制字符有: 
  6. *   上移:1B 5B 41 
  7. *   下移:1B 5B 42 
  8. *   右移:1B 5B 43 
  9. *   左移:1B 5B 44 
  10. *   回車換行:0D 0A 
  11. *  Backspace:08 
  12. *  Delete:7F 
  13. */  
  14. static uint32_t get_true_char_stream(charchar *dest,const charchar *src)  
  15. {  
  16.    uint32_t dest_count=0;  
  17.    uint32_t src_count=0;  
  18.      
  19.     while(src[src_count]!=0x0D && src[src_count+1]!=0x0A)  
  20.     {  
  21.        if(isprint(src[src_count]))  
  22.        {  
  23.            dest[dest_count++]=src[src_count++];  
  24.        }  
  25.        else  
  26.        {  
  27.            switch(src[src_count])  
  28.            {  
  29.                 case    0x08:                          //退格鍵鍵值  
  30.                 {  
  31.                     if(dest_count>0)  
  32.                     {  
  33.                         dest_count --;  
  34.                     }  
  35.                     src_count ++;  
  36.                 }break;  
  37.                 case    0x1B:  
  38.                 {  
  39.                     if(src[src_count+1]==0x5B)  
  40.                     {  
  41.                         if(src[src_count+2]==0x41 || src[src_count+2]==0x42)  
  42.                         {  
  43.                             src_count +=3;              //上移和下移鍵鍵值  
  44.                         }  
  45.                         else if(src[src_count+2]==0x43)  
  46.                         {  
  47.                             dest_count++;               //右移鍵鍵值  
  48.                             src_count+=3;  
  49.                         }  
  50.                         else if(src[src_count+2]==0x44)  
  51.                         {  
  52.                             if(dest_count >0)           //左移鍵鍵值  
  53.                             {  
  54.                                 dest_count --;  
  55.                             }  
  56.                            src_count +=3;  
  57.                         }  
  58.                         else  
  59.                         {  
  60.                             src_count +=3;  
  61.                         }  
  62.                     }  
  63.                     else  
  64.                     {  
  65.                         src_count ++;  
  66.                     }  
  67.                 }break;  
  68.                 default:  
  69.                 {  
  70.                     src_count++;  
  71.                 }break;  
  72.            }  
  73.        }  
  74.     }  
  75.    dest[dest_count++]=src[src_count++];  
  76.     dest[dest_count++]=src[src_count++];  
  77.     return dest_count;  
  78. }  

6.2參數分析

      接收到的命令中可能帶有參數,咱們須要知道參數的數目,還須要把字符型的參數轉換成整形數並保存到參數緩衝區(這是由於命令回調函數須要這兩個參數)。這個工做由函數cmd_arg_analyze()實現,代碼以下所示:

[objc] view plain copy
  1. /** 
  2. * 命令參數分析函數,以空格做爲一個參數結束,支持輸入十六進制數(如:0x15),支持輸入負數(如-15) 
  3. * @param rec_buf   命令參數緩存區 
  4. * @param len       命令的最大可能長度 
  5. * @return -1:       參數個數過多,其它:參數個數 
  6. */  
  7. static int32_t cmd_arg_analyze(charchar *rec_buf,unsigned int len)  
  8. {  
  9.    uint32_t i;  
  10.    uint32_t blank_space_flag=0;    //空格標誌  
  11.    uint32_t arg_num=0;             //參數數目  
  12.    uint32_t index[ARG_NUM];        //有效參數首個數字的數組索引  
  13.      
  14.     /*先作一遍分析,找出參數的數目,以及參數段的首個數字所在rec_buf數組中的下標*/  
  15.     for(i=0;i<len;i++)  
  16.     {  
  17.        if(rec_buf[i]==0x20)        //爲空格  
  18.        {  
  19.            blank_space_flag=1;                
  20.            continue;  
  21.        }  
  22.         else if(rec_buf[i]==0x0D)   //換行  
  23.        {  
  24.            break;  
  25.        }  
  26.        else  
  27.        {  
  28.            if(blank_space_flag==1)  
  29.            {  
  30.                 blank_space_flag=0;   
  31.                 if(arg_num < ARG_NUM)  
  32.                 {  
  33.                    index[arg_num]=i;  
  34.                     arg_num++;           
  35.                 }  
  36.                 else  
  37.                 {  
  38.                     return -1;      //參數個數太多  
  39.                 }  
  40.            }  
  41.        }  
  42.     }  
  43.      
  44.     for(i=0;i<arg_num;i++)  
  45.     {  
  46.         cmd_analyze.cmd_arg[i]=string_to_dec((unsigned charchar *)(rec_buf+index[i]),len-index[i]);  
  47.     }  
  48.     return arg_num;  
  49. }  

      在這個函數cmd_arg_analyze()中,調用了字符轉整形函數string_to_dec()。咱們只支持整形參數,這裏給出一個字符轉整形函數的簡單實現,能夠識別負號和十六進制的前綴’0x’。在這個函數中調用了三個C庫函數,分別是isdigit()、isxdigit()和tolower(),所以須要包含頭文件#include <ctype.h>。函數string_to_dec()實現代碼以下:

[objc] view plain copy
  1. /*字符串轉10/16進制數*/  
  2. static int32_t string_to_dec(uint8_t *buf,uint32_t len)  
  3. {  
  4.    uint32_t i=0;  
  5.    uint32_t base=10;       //基數  
  6.    int32_t  neg=1;         //表示正負,1=正數  
  7.    int32_t  result=0;  
  8.      
  9.     if((buf[0]=='0')&&(buf[1]=='x'))  
  10.     {  
  11.        base=16;  
  12.        neg=1;  
  13.        i=2;  
  14.     }  
  15.     else if(buf[0]=='-')  
  16.     {  
  17.        base=10;  
  18.        neg=-1;  
  19.        i=1;  
  20.     }  
  21.     for(;i<len;i++)  
  22.     {  
  23.        if(buf[i]==0x20 || buf[i]==0x0D)    //爲空格  
  24.        {  
  25.            break;  
  26.        }  
  27.          
  28.        result *= base;  
  29.        if(isdigit(buf[i]))                 //是否爲0~9  
  30.        {  
  31.            result += buf[i]-'0';  
  32.        }  
  33.        else if(isxdigit(buf[i]))           //是否爲a~f或者A~F  
  34.        {  
  35.             result+=tolower(buf[i])-87;  
  36.        }  
  37.        else  
  38.        {  
  39.            result += buf[i]-'0';  
  40.        }                                          
  41.     }  
  42.    result *= neg;  
  43.      
  44.     return result ;  
  45. }  

6.3定義命令回調函數

      咱們舉兩個例子:第一個是不帶參數的例子,輸入命令後,函數返回一個「Helloworld!」字符串;第二個是帶參數的例子,咱們輸入命令和參數後,函數返回每個參數值。咱們在講數據結構的時候特別提到過命令回調函數的原型,這裏要根據這個函數原型來聲明命令回調函數。

6.3.1不帶參數的命令回調函數舉例
[objc] view plain copy
  1. /*打印字符串:Hello world!*/  
  2. void printf_hello(int32_t argc,voidvoid *cmd_arg)  
  3. {  
  4.    MY_DEBUGF(CMD_LINE_DEBUG,("Hello world!\n"));  
  5. }  
6.3.2帶參數的命令行回調函數舉例
[objc] view plain copy
  1. /*打印每一個參數*/  
  2. void handle_arg(int32_t argc,voidvoid * cmd_arg)  
  3. {  
  4.    uint32_t i;  
  5.    int32_t  *arg=(int32_t *)cmd_arg;  
  6.      
  7.     if(argc==0)  
  8.     {  
  9.        MY_DEBUGF(CMD_LINE_DEBUG,("無參數\n"));  
  10.     }  
  11.     else  
  12.     {  
  13.        for(i=0;i<argc;i++)  
  14.        {  
  15.            MY_DEBUGF(CMD_LINE_DEBUG,("第%d個參數:%d\n",i+1,arg[i]));  
  16.        }  
  17.     }  
  18. }  

6.4定義命令表

      在講數據結構的時候,咱們定義了與命令有關的數據結構。每條命令須要包括命名名、最大參數、命令回調函數、幫助等信息,這裏要將每條命令組織成列表的形式。

[objc] view plain copy
  1. /*命令表*/  
  2. const cmd_list_struct cmd_list[]={  
  3. /*   命令    參數數目    處理函數        幫助信息                         */     
  4. {"hello",   0,      printf_hello,   "hello                      -打印HelloWorld!"},  
  5. {"arg",     8,      handle_arg,      "arg<arg1> <arg2> ...      -測試用,打印輸入的參數"},  
  6. };  

      若是要定義本身的命令,只須要按照6.3節的格式編寫命令回調函數,而後將命令名、參數數目、回調函數和幫助信息按照本節格式加入到命令表中便可。

6.5命令行分析任務實現

      有了上面的基礎,命令行分析任務實現起來就很是輕鬆了,源碼以下:

[objc] view plain copy
  1. /*命令行分析任務*/  
  2. void vTaskCmdAnalyze( voidvoid *pvParameters )  
  3. {  
  4.    uint32_t i;  
  5.    int32_t rec_arg_num;  
  6.     char cmd_buf[CMD_LEN];        
  7.      
  8.     while(1)  
  9.     {  
  10.        uint32_t rec_num;  
  11.          
  12.        ulTaskNotifyTake(pdTRUE,portMAX_DELAY);  
  13.     rec_num=get_true_char_stream(cmd_analyze.processed_buf,cmd_analyze.rec_buf);  
  14.          
  15.        /*從接收數據中提取命令*/  
  16.        for(i=0;i<CMD_LEN;i++)  
  17.        {  
  18.            if((i>0)&&((cmd_analyze.processed_buf[i]==' ')||(cmd_analyze.processed_buf[i]==0x0D)))  
  19.            {  
  20.                 cmd_buf[i]='\0';        //字符串結束符  
  21.                 break;  
  22.            }  
  23.            else  
  24.            {  
  25.                 cmd_buf[i]=cmd_analyze.processed_buf[i];  
  26.            }  
  27.        }  
  28.          
  29.        rec_arg_num=cmd_arg_analyze(&cmd_analyze.processed_buf[i],rec_num);  
  30.          
  31.        for(i=0;i<sizeof(cmd_list)/sizeof(cmd_list[0]);i++)  
  32.        {  
  33.            if(!strcmp(cmd_buf,cmd_list[i].cmd_name))       //字符串相等  
  34.            {  
  35.                 if(rec_arg_num<0 || rec_arg_num>cmd_list[i].max_args)  
  36.                 {  
  37.                     MY_DEBUGF(CMD_LINE_DEBUG,("參數數目過多!\n"));  
  38.                 }  
  39.                 else  
  40.                 {  
  41.                     cmd_list[i].handle(rec_arg_num,(voidvoid *)cmd_analyze.cmd_arg);  
  42.                 }  
  43.                 break;  
  44.            }  
  45.              
  46.        }  
  47.        if(i>=sizeof(cmd_list)/sizeof(cmd_list[0]))  
  48.        {  
  49.            MY_DEBUGF(CMD_LINE_DEBUG,("不支持的指令!\n"));  
  50.        }  
  51.     }  
  52. }  

7.使用的串口工具

      推薦使用SecureCRT軟件,這是我以爲最適合命令行交互的串口工具。此外,這個軟件很是強大,除了支持串口,還支持SSH、Telnet等。對於串口,SecureCRT工具還支持文件發送協議:Xmodem、Ymodem和Zmodem。這在使用串口遠程升級時頗有用,能夠用來發送新的程序二進制文件。我曾經使用Ymodem作過遠程升級,之後有時間再詳細介紹SecureCRT的Ymodem功能細節。

      要用於本文介紹的命令行解釋器,要對SecureCRT軟件作一些設置。

7.1設置串口參數

      選擇Serial功能、設置端口、波特率、校驗等,特別要注意的是不要勾選任何流控制選項,如圖2-1所示。

圖2-1:設置串口參數

7.2設置新行模式

       依次點擊菜單欄的「選項」---「會話選項」,在彈出的「會話選項」界面中,點擊左邊樹形菜單的「終端」---「仿真」---「模式」,在右邊的仿真模式區域選中「換行」和「新行模式」,如圖2-2所示。

圖2-2:設置新行模式

 

7.3設置本地回顯

       依次點擊菜單欄的「選項」---「會話選項」,在彈出的「會話選項」界面中,點擊左邊樹形菜單的「終端」---「仿真」---「高級」,在右邊的「高級仿真」區域,選中「本地回顯」,如圖2-3所示。

圖2-3:設置本地回顯

8.測試

       咱們經過6.3節和6.4接定義了兩個命令,第一條命令的名字爲」hello」,這是一個無參數命令,直接輸出字符串」Hello world!」。第二條命令的名字爲」arg」,是一個帶參數命令,輸出每一個參數的值。下面對這兩個命令進行測試。

8.1無參數命令測試

       設置好SecureCRT軟件,輸入字符」hello」後,按下回車鍵,設備會返回字符串」Hello world!」。如圖8-1所示。

圖8-1:無參數命令測試

8.2帶參數命令測試

       設置好SecureCRT軟件,輸入字符」arg 1 2 -3 0x0a」後,按下回車鍵,設備會返回每一個參數值。如圖8-2所示。

圖8-2:帶參數命令測試

 

FreeRTOS系列第16篇---可視化追蹤調試

使用RTOS編程,爲每一個任務分配多大的堆棧空間就成了一項技術活:分配多了浪費系統資源,分配少了又恐怕會發生堆棧溢出。因爲中斷和搶佔式調度器的存在,咱們要估算出一個任務須要多少堆棧是很是困難的,今天咱們就介紹一種方法,來獲取每一個任務的剩餘堆棧空間。本文以NXP LPC177x_8x系列微控制器爲例。

      咱們將這個功能作成一個命令,添加到《FreeRTOS系列第15篇---使用任務通知實現命令行解釋器》一文介紹的命令解釋列表中。當程序運行一段時間後,咱們在SecureCRT軟件中輸入命令「task」後回車,能看到如圖1-1所示的任務信息。這裏只有兩個任務,其中堆棧一列中的數字,表明對應任務剩餘的堆棧空間,單位是StackType_t類型,這個類型在移植層定義,通常定義爲4字節。

圖1-1:任務信息

1.使能可視化追蹤和運行時間統計功能     

      如圖1-1所示,要實現堆棧使用量信息以及CPU使用率信息,必須將FreeRTOSConfig.h文件中的兩個宏設置爲1:

[objc] view plain copy
  1. #define configUSE_TRACE_FACILITY          1        
  2. #define configGENERATE_RUN_TIME_STATS 1  

      第一個宏用來使能可視化追蹤功能,第二個宏用來使能運行時間統計功能。若是第二個宏設置爲1,則下面兩個宏必須被定義:

  1. portCONFIGURE_TIMER_FOR_RUN_TIME_STATS():用戶程序須要提供一個基準時鐘函數,函數完成初始化基準時鐘功能,這個函數要被define到宏portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()上。這是由於運行時間統計須要一個比系統節拍中斷頻率還要高分辨率的基準定時器,不然,統計可能不精確。基準定時器中斷頻率要比統節拍中斷快10~100倍。基準定時器中斷頻率越快,統計越精準,但能統計的運行時間也越短(好比,基準定時器10ms中斷一次,8位無符號整形變量能夠計到2.55秒,但若是是1秒中斷一次,8位無符號整形變量能夠統計到255秒)。
  2. portGET_RUN_TIME_COUNTER_VALUE():用戶程序須要提供一個返回基準時鐘當前「時間」的函數,這個函數要被define到宏portGET_RUN_TIME_COUNTER_VALUE()上。

         咱們使用定時器1來產生基準時鐘,定時器1初始化函數爲:

[objc] view plain copy
  1. /** 
  2. * 初始化計時定時器1,用於OS任務運行時間統計 
  3. */  
  4. void init_timer1_for_runtime_state(void)  
  5. {  
  6.     TIM_TIMERCFG_Type Timer0CfgType;  
  7.                   
  8.     Timer0CfgType.PrescaleOption=TIM_PRESCALE_USVAL;        //預分頻的單位是微秒  
  9.     Timer0CfgType.PrescaleValue=500;                        //預分頻後爲500微秒,  
  10.     TIM_Init(LPC_TIM1,TIM_TIMER_MODE,&Timer0CfgType);  
  11.      
  12.     LPC_TIM1->TCR=0x01;  
  13. }  

      定時器1被配置成每隔500微秒,TC寄存器值增一。咱們將定時器1的 TC寄存器值做爲基準時鐘當前時間。當TC寄存器值溢出時,大概要通過24.8天,這對於咱們這個應用是足夠的。

      在FreeRTOSConfig.h中,定義初始化基準定時器宏和獲取當前時間宏:

[objc] view plain copy
  1. extern void init_timer1_for_runtime_state(void);  
  2. #define TIMER1_TC         ( * ( ( volatile uint32_t * )0x40008008 ) )  
  3. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() init_timer1_for_runtime_state()  
  4. #define portGET_RUN_TIME_COUNTER_VALUE() TIMER1_TC  

2.獲取任務信息並格式化

      獲取每一個任務的狀態信息使用的是API函數uxTaskGetSystemState(),該函數定義爲:

[objc] view plain copy
  1. UBaseType_tuxTaskGetSystemState(  
  2.                        TaskStatus_t * constpxTaskStatusArray,  
  3.                        const UBaseType_tuxArraySize,  
  4.                        unsigned longlong * constpulTotalRunTime );  

      函數uxTaskGetSystemState()向TaskStatus_t結構體填充相關信息,系統中每個任務的信息均可以填充到TaskStatus_t結構體數組中,數組大小由uxArraySize指定。結構體TaskStatus_t定義以下:

 

[objc] view plain copy
  1. typedef struct xTASK_STATUS  
  2. {  
  3.    /* 任務句柄*/  
  4.    TaskHandle_t xHandle;  
  5.    
  6.    /* 指針,指向任務名*/  
  7.    const signed charchar *pcTaskName;  
  8.    
  9.    /*任務ID,是一個獨一無二的數字*/  
  10.    UBaseType_t xTaskNumber;  
  11.    
  12.    /*填充結構體時,任務當前的狀態(運行、就緒、掛起等等)*/  
  13.    eTaskState eCurrentState;  
  14.    
  15.    /*填充結構體時,任務運行(或繼承)的優先級。*/  
  16.    UBaseType_t uxCurrentPriority;  
  17.    
  18.    /* 當任務因繼承而改變優先級時,該變量保存任務最初的優先級。僅當configUSE_MUTEXES定義爲1有效。*/  
  19.    UBaseType_t uxBasePriority;  
  20.    
  21.    /* 分配給任務的總運行時間。僅當宏configGENERATE_RUN_TIME_STATS爲1時有效。*/  
  22.    unsigned long ulRunTimeCounter;  
  23.    
  24.    /* 從任務建立起,堆棧剩餘的最小數量,這個值越接近0,堆棧溢出的可能越大。 */  
  25.    unsigned short usStackHighWaterMark;  
  26. }TaskStatus_t;  

      注意,這個函數僅用來調試用,調用此函數會掛起全部任務,直到函數結束後才恢復掛起的任務,所以任務可能被掛起很長時間。在文件FreeRTOSConfig.h中,宏configUSE_TRACE_FACILITY必須設置爲1,此函數纔有效。

      因爲咱們不使用動態內存分配策略,因此實現定義了最大任務個數並預先分配好了存儲任務狀態信息的數組:

[objc] view plain copy
  1. #defineMAX_TASK_NUM        5  
  2. TaskStatus_tpxTaskStatusArray[MAX_TASK_NUM];  

      正確調用函數uxTaskGetSystemState()後,任務的信息會被放在TaskStatus_t結構體中,咱們須要將這些信息格式化爲容易閱讀的形式,並共經過串口打印到屏幕。完成這些功能的函數叫作get_task_state(),代碼以下所示:

[objc] view plain copy
  1. /*獲取OS任務信息*/  
  2. voidget_task_state(int32_t argc,voidvoid *cmd_arg)  
  3. {  
  4.     const chartask_state[]={'r','R','B','S','D'};  
  5.     volatile UBaseType_t uxArraySize, x;  
  6.     uint32_t ulTotalRunTime,ulStatsAsPercentage;  
  7.    
  8.     /* 獲取任務總數目 */  
  9.     uxArraySize = uxTaskGetNumberOfTasks();  
  10.    if(uxArraySize>MAX_TASK_NUM)  
  11.     {  
  12.         MY_DEBUGF(CMD_LINE_DEBUG,("當前任務數量過多!\n"));  
  13.     }  
  14.    
  15.     /*獲取每一個任務的狀態信息 */  
  16.     uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, &ulTotalRunTime );  
  17.    
  18.     #if (configGENERATE_RUN_TIME_STATS==1)  
  19.      
  20.     MY_DEBUGF(CMD_LINE_DEBUG,("任務名      狀態  ID    優先級  堆棧    CPU使用率\n"));  
  21.    
  22.     /* 避免除零錯誤 */  
  23.     if( ulTotalRunTime > 0 )  
  24.     {  
  25.         /* 將得到的每個任務狀態信息部分的轉化爲程序員容易識別的字符串格式 */  
  26.         for( x = 0; x < uxArraySize; x++ )  
  27.         {  
  28.             char tmp[128];  
  29.              
  30.             /* 計算任務運行時間與總運行時間的百分比。*/  
  31.             ulStatsAsPercentage =(uint64_t)(pxTaskStatusArray[ x ].ulRunTimeCounter)*100 / ulTotalRunTime;  
  32.    
  33.             if( ulStatsAsPercentage > 0UL )  
  34.             {  
  35.    
  36.                sprintf(tmp,"%-12s%-6c%-6d%-8d%-8d%d%%",pxTaskStatusArray[ x].pcTaskName,task_state[pxTaskStatusArray[ x ].eCurrentState],  
  37.                                                                        pxTaskStatusArray[ x ].xTaskNumber,pxTaskStatusArray[ x].uxCurrentPriority,  
  38.                                                                        pxTaskStatusArray[ x ].usStackHighWaterMark,ulStatsAsPercentage);  
  39.             }  
  40.             else  
  41.             {  
  42.                 /* 任務運行時間不足總運行時間的1%*/  
  43.                 sprintf(tmp,"%-12s%-6c%-6d%-8d%-8dt<1%%",pxTaskStatusArray[x ].pcTaskName,task_state[pxTaskStatusArray[ x ].eCurrentState],  
  44.                                                                        pxTaskStatusArray[ x ].xTaskNumber,pxTaskStatusArray[ x].uxCurrentPriority,  
  45.                                                                        pxTaskStatusArray[ x ].usStackHighWaterMark);                 
  46.             }  
  47.            MY_DEBUGF(CMD_LINE_DEBUG,("%s\n",tmp));  
  48.         }  
  49.     }  
  50.     MY_DEBUGF(CMD_LINE_DEBUG,("任務狀態:   r-運行  R-就緒  B-阻塞  S-掛起  D-刪除\n"));  
  51.     #endif //#if (configGENERATE_RUN_TIME_STATS==1)  
  52. }  

3.添加到命令解釋列表

      在《FreeRTOS系列第15篇---使用任務通知實現命令行解釋器》一文咱們講過了命令表,這裏只須要將get_task_state()函數添加到命令列表中,命令設置爲」task」,代碼以下所示:

[objc] view plain copy
  1. /*命令表*/  
  2. const cmd_list_structcmd_list[]={  
  3. /*   命令    參數數目    處理函數        幫助信息                                  */     
  4.     {"?",       0,     handle_help,     "?                                  -打印幫助信息"},                   
  5.     {"reset",   0,     handle_reset,    "reset                              -重啓控制器"},  
  6.     {"arg",     8,     handle_arg,      "arg<arg1> <arg2> ...               -測試用,打印輸入的參數"},  
  7.     {"hello",   0,     printf_hello,    "hello                              -打印HelloWorld!"},  
  8.     {"task",    0,     get_task_state,  "task                               -獲取任務信息"},  
  9. }; 

FreeRTOS系列第17篇---FreeRTOS隊列

本文介紹隊列的基本知識,詳細源碼分析見《FreeRTOS高級篇5---FreeRTOS隊列分析

1.FreeRTOS隊列

      隊列是主要的任務間通信方式。能夠在任務與任務間、中斷和任務間傳送信息。大多數狀況下,隊列用於具備線程保護的FIFO(先進先出)緩衝區:新數據放在隊列的後面。固然,數據也能夠放在隊列的前面,在下一篇講隊列API函數時,會涉及到數據的存放位置。

圖1-1:讀寫隊列

      圖1-1所示的隊列中,最多能保存5個項目,而且假設隊列永遠不會滿。任務A使用API函數xQueueSendToBack()向隊列發送數據,每次發送一個數據,新入隊的數據置於上一次入隊數據的後面。任務B使用API函數xQueueReceive()將數據從隊列取出,先入隊的數據先出隊。

2.使用模型:最簡單、最靈活

      一般狀況下,魚和熊掌是不可兼得的,但FreeRTOS的隊列用戶模型管理卻兼顧簡單和靈活。發送到隊列的消息是經過拷貝實現的,這意味着隊列存儲的數據是原數據,而不是原數據的引用。FreeRTOS隊列具備如下特性:

  • C變量(整形、簡單結構體等等)中的簡單信息能夠直接傳送到隊列。這樣就不須要爲信息分配緩存也不須要再進行什麼拷貝工做。一樣的,信息能夠直接從隊列讀取到C變量中。用直接拷貝的方法入隊,能夠容許任務當即覆寫已經入隊的變量或者緩存,實際上隊列中已經保存了這些變量或緩衝區攜帶的信息。由於變量中的數據內容是以拷貝的方式入隊的,因此變量自身是容許重複使用的。發送信息的任務和接收信息的任務並不須要就哪一個任務擁有信息、哪一個任務釋放信息(當信息再也不使用時)而達成一致。
  • 隊列是經過拷貝傳遞數據的,但這並不妨礙隊列經過引用來傳遞數據。當信息的大小到達一個臨界點後,逐字節拷貝整個信息是不實際的,能夠定義一個指針隊列,只拷貝指向消息的指針來代替整個信息拷貝。FreeRTOS+UDP IP棧例程正是使用這種方法向FreeRTOS協議棧傳遞大量網絡數據的。
  • 隊列內存區域分配由內核完成。
  • 變長消息能夠經過定義保存一個結構體變量的隊列實現,結構體一個成員指向要入隊的緩存,另外一個成員保存緩存數據的大小。
  • 單個隊列能夠接收不一樣類型信息,而且信息能夠來自不一樣的位置。經過定義保存一個結構體變量的隊列來實現,結構體的一個成員保存信息類型,另外一個成員保存信息數據(或者指向信息數據的指針)。數據如何解讀取決於信息類型。管理FreeRTOS+UDP IP棧的任務正是使用單個隊列接收ARP定時器時間通知、以太網硬件傳送來的數據包、從應用層傳送來的數據包、網絡關閉事件等等。
  • 天生適用於那些內存保護(MPU)場合。一個具備內存區域保護的任務能夠向另外一個具備內存區域保護的任務傳遞數據,由於調用隊列發送函數會引發RTOS提高微控制器特權級別。只有RTOS(具備全部特權)才能夠訪問隊列存儲區域。
  • 在中斷函數中使用獨立的API。將RTOS任務API和中斷服務例程API分來實現意味着能夠避免執行時的上下文調用檢查開銷,還意味着在大多數狀況下,與其它RTOS產品相比,用戶建立中斷服務例程會更簡單。
  • API函數很簡單。

3.隊列阻塞

      API函數容許指定阻塞時間。

      每當任務企圖從一個空的隊列讀取數據時,任務會進入阻塞狀態(這樣任務不會消耗任何CPU時間而且另外一個任務能夠運行)直到隊列中出現有效數據或者阻塞時間到期。

      每當任務企圖向一個滿的隊列寫數據時,任務會進入阻塞狀態,直到隊列中出現有效空間或者阻塞時間到期。

      若是多個任務阻塞在一個隊列上,那麼最高優先級別的任務會第一個解除阻塞。

      注:中斷程序中毫不能夠使用不帶「FromISR」結尾的API函數!

      總結一下隊列的基本用法:

  1. 定義一個隊列句柄變量,用於保存建立的隊列:xQueueHandle xQueue1;
  2. 使用API函數xQueueCreate()建立一個隊列。
  3. 若是但願使用先進先出隊列,使用API函數xQueueSend()或xQueueSendToBack()向隊列投遞隊列項。若是但願使用後進先出隊列,使用API函數xQueueSendToFront()向隊列投遞隊列項。若是在中斷服務程序中,切記使用它們的帶中斷保護版本。
  4. 使用API函數xQueueReceive()從隊列讀取隊列項,若是在中斷服務程序中,切記使用它們的帶中斷保護版本。
      以上使用的API函數將在下一篇文章中介紹。
 

FreeRTOS系列第18篇---FreeRTOS隊列API函數

 FreeRTOS爲操做隊列提供了很是豐富的API函數,包括隊列的建立、刪除,靈活的入隊和出隊方式、帶中斷保護的入隊和出隊等等。下面就來詳細講述這些API函數。

1.獲取隊列入隊信息數目

1.1函數描述

      UBaseType_t uxQueueMessagesWaiting( QueueHandle_t xQueue );

      返回隊列中存儲的信息數目。具備中斷保護的版本爲uxQueueMessagesWaitingFromISR(),原型爲:UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue )。

1.2參數描述

  •  xQueue:隊列句柄

2.獲取隊列的空閒數目

2.1函數描述

      UBaseType_t uxQueueSpacesAvailable( QueueHandle_t xQueue );

      返回隊列的空閒數目。

2.2參數描述

  •  xQueue:隊列句柄

3.刪除隊列

3.1函數描述

      void vQueueDelete( QueueHandle_t xQueue );

      刪除隊列並釋放全部分配給隊列的內存。

3.2參數描述

  •  xQueue:隊列句柄

4.復位隊列

4.1函數描述

      BaseType_t xQueueReset( QueueHandle_t xQueue );

      將隊列復位到初始狀態。

4.2參數描述

  •  xQueue:隊列句柄

4.3返回值

      FreeRTOSV7.2.0以及之後的版本老是返回pdPASS。

5.建立隊列

5.1函數描述

     QueueHandle_t xQueueCreate (UBaseType_t uxQueueLength, UBaseType_t uxItemSize);

     建立新隊列。爲新隊列分配指定的存儲空間並返回隊列句柄。

5.2參數描述

  •  usQueueLength:隊列項數目
  •  uxItemSize:每一個隊列項大小,單位是字節。隊列項經過拷貝入隊而不是經過引用入隊,所以須要隊列項的大小。每一個隊列項的大小必須相同。

5.3返回值

      成功建立隊列返回隊列句柄,否自返回0。

5.4用法舉例

[objc] view plain copy
  1. struct AMessage  
  2. {  
  3.     portCHAR ucMessageID;  
  4.     portCHAR ucData[ 20 ];  
  5. };  
  6. void vATask( void*pvParameters )  
  7. {  
  8.     xQueueHandle xQueue1, xQueue2;  
  9.     // 建立一個隊列,隊列能包含10個unsigned long類型的值。  
  10.     xQueue1 = xQueueCreate( 10, sizeof( unsigned portLONG ));  
  11.     if( xQueue1 ==0 )  
  12.     {  
  13.         // 隊列建立失敗,不能夠使用  
  14.     }  
  15.     // 建立一個隊列,隊列能包含10個 Amessage結構體指針類型的值。  
  16.     // 這樣能夠經過傳遞指針變量來包含大量數據。  
  17.     xQueue2 =xQueueCreate( 10, sizeof( struct AMessage * ) );  
  18.     if( xQueue2 ==0 )  
  19.     {  
  20.         // 隊列建立失敗,不能夠使用  
  21.     }  
  22.     // ... 任務的其它代碼.  
  23. }  

6.向隊列投遞隊列項

6.1 函數描述

      BaseType_t xQueueSend(QueueHandle_t xQueue,  const void * pvItemToQueue,  TickType_t xTicksToWait );

      實際上是一個宏,真正被調用的函數是xQueueGenericSend()。定義這個宏是爲了向後兼容那些不包含函數xQueueSendToFront()和xQueueSendToBack()宏的FreeRTOS版本。它與xQueueSendToBack()等同。

      這個宏向隊列尾部投遞一個隊列項。項目以拷貝的形式入隊,而不是引用形式入隊。毫不能夠在中斷服務例程中調用這個宏,使用帶有中斷保護的版本xQueueSendFromISR()來完成相同的功能。

6.2參數描述

  • xQueue:隊列句柄。
  • pvItemToQueue:指針,指向要入隊的項目。要保存到隊列中的項目字節數在隊列建立時就已肯定。所以要從指針pvItemToQueue指向的區域拷貝到隊列存儲區域的字節數,也已肯定。
  • xTicksToWait:若是隊列滿,任務等待隊列空閒的最大時間。若是隊列滿而且xTicksToWait被設置成0,函數馬上返回。時間單位爲系統節拍時鐘週期,所以宏portTICK_PERIOD_MS能夠用來輔助計算真實延時值。若是INCLUDE_vTaskSuspend設置成1,而且指定延時爲portMAX_DELAY將引發任務無限阻塞(沒有超時)。

6.3返回值

      隊列項入隊成功返回pdTRUE,不然返回errQUEUE_FULL。

6.4用法舉例

[objc] view plain copy
  1. struct AMessage  
  2.  {  
  3.       portCHAR ucMessageID;  
  4.       portCHAR ucData[ 20 ];  
  5.  }xMessage;  
  6.  unsigned portLONG ulVar = 10UL;  
  7.  void vATask( voidvoid *pvParameters )  
  8.  {  
  9.      xQueueHandle xQueue1, xQueue2;  
  10.      struct AMessage *pxMessage;  
  11.      /*建立一個隊列,隊列能包含10個unsigned long類型的值。*/  
  12.      xQueue1 = xQueueCreate( 10, sizeof( unsigned portLONG ) );  
  13.      /* 建立一個隊列,隊列能包含10個 Amessage結構體指針類型的值。 
  14.        這樣能夠經過傳遞指針變量來包含大量數據。*/  
  15.      xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );  
  16.      // ...  
  17.      if( xQueue1 != 0 )  
  18.      {  
  19.           /*1個unsigned long型數據入隊.若是須要等待隊列空間變的有效, 
  20.          會最多等待10個系統節拍週期*/  
  21.           if( xQueueSend( xQueue1, ( voidvoid * ) &ulVar, ( portTickType ) 10 ) !=pdPASS )  
  22.           {  
  23.                /*消息入隊失敗*/  
  24.           }  
  25.     }  
  26.     if( xQueue2 != 0 )  
  27.     {  
  28.          /* 發送一個指向結構體Amessage的對象,若是隊列滿也不等待 */  
  29.          pxMessage = & xMessage;  
  30.          xQueueSend( xQueue2, ( voidvoid * ) &pxMessage, ( portTickType ) 0 );  
  31.     }  
  32.          //... 任務其他代碼.  
  33.  }  

7.向隊列投遞隊列項(帶中斷保護)

7.1函數描述

[objc] view plain copy
  1. BaseType_t xQueueSendFromISR (QueueHandle_t xQueue,  
  2.           const voidvoid *pvItemToQueue,  BaseType_t *pxHigherPriorityTaskWoken);  

      實際上是一個宏,真正被調用的函數是xQueueGenericSendFromISR()。這個宏是xQueueSend()的中斷保護版本,用於中斷服務程序,等價於xQueueSendToBackFromISR()

    在中斷服務例程中向隊列尾部投遞一個隊列項。

7.2參數描述

  • xQueue:隊列句柄。
  • pvItemToQueue:指針,指向要入隊的項目。要保存到隊列中的項目字節數在隊列建立時就已肯定。所以要從指針pvItemToQueue指向的區域拷貝到隊列存儲區域的字節數,也已肯定。
  • pxHigherPriorityTaskWoken:若是入隊致使一個任務解鎖,而且解鎖的任務優先級高於當前運行的任務,則該函數將*pxHigherPriorityTaskWoken設置成pdTRUE。若是xQueueSendFromISR()設置這個值爲pdTRUE,則中斷退出前須要一次上下文切換。從FreeRTOS V7.3.0起,pxHigherPriorityTaskWoken稱爲一個可選參數,並能夠設置爲NULL。

7.3返回值

      列項入隊成功返回pdTRUE,不然返回errQUEUE_FULL。

7.4用法舉例

[objc] view plain copy
  1. void vBufferISR( void )  
  2. {  
  3.     portCHARcIn;  
  4.     portBASE_TYPE xHigherPriorityTaskWoken;  
  5.    
  6.     /* 初始化,沒有喚醒任務*/  
  7.     xHigherPriorityTaskWoken = pdFALSE;  
  8.    
  9.     /* 直到緩衝區爲空 */  
  10.     do  
  11.     {  
  12.         /* 從緩衝區得到一個字節數據 */  
  13.         cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );                                                        
  14.    
  15.         /* 投遞這個數據 */  
  16.         xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );  
  17.     }while( portINPUT_BYTE( BUFFER_COUNT ) );  
  18.    
  19.     /* 這裏緩衝區已空,若是須要進行一個上下文切換*/  
  20.     /*根據不一樣移植平臺,這個函數也不一樣*/  
  21.     portYIELD_FROM_ISR(xHigherPriorityTaskWoken);  
  22. }  

8.向隊列尾部投遞隊列項

8.1函數描述

[objc] view plain copy
  1. BaseType_t xQueueSendToBack(QueueHandle_t xQueue,  
  2.                    const voidvoid * pvItemToQueue, TickType_t xTicksToWait );  

      實際上是一個宏,真正被調用的函數是xQueueGenericSend()。這個宏等價於xQueueSend()。

      向隊列尾投遞一個隊列項。毫不能夠在中斷中調用這個宏,能夠使用帶有中斷保護的版本xQueueSendToBackFromISR ()來完成相同功能。

8.2參數描述

      同xQueueSend()。

8.3返回值

      同xQueueSend()。

8.4用法舉例

      同xQueueSend()。

9.向隊列尾部投遞隊列項(帶中斷保護)

9.1函數描述

[objc] view plain copy
  1. BaseType_t xQueueSendToBackFromISR (QueueHandle_t xQueue,  
  2.       const voidvoid *pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken );  

      實際上是一個宏,真正被調用的函數是xQueueGenericSendFromISR()。這個宏是xQueueSendToBack()的中斷保護版本,用於中斷服務程序,等價於xQueueSendFromISR()

    在中斷服務例程中向隊列尾部投遞一個隊列項。

9.2參數描述

      同QueueSendFromISR()。

9.3返回值

      同QueueSendFromISR()。

9.4用法舉例

      同QueueSendFromISR()。

10.向隊列首部投遞隊列項

10.1函數描述

[objc] view plain copy
  1. BaseType_t xQueueSendToFront(QueueHandle_t xQueue,  
  2.          const voidvoid * pvItemToQueue,TickType_t xTicksToWait);  

      實際上是一個宏,真正被調用的函數是xQueueGenericSend()。

      這個宏向隊列首部投遞一個隊列項。毫不能夠在中斷服務例程中調用這個宏,能夠使用帶有中斷保護的版本xQueueSendToFrontFromISR ()來完成相同功能。

10.2參數描述

  • xQueue:隊列句柄。
  • pvItemToQueue:指針,指向要入隊的項目。要保存到隊列中的項目字節數在隊列建立時就已肯定。所以要從指針pvItemToQueue指向的區域拷貝到隊列存儲區域的字節數,也已肯定。
  • xTicksToWait:若是隊列滿,任務等待隊列空閒的最大時間。若是隊列滿而且xTicksToWait被設置成0,函數馬上返回。時間單位爲系統節拍時鐘週期,所以宏portTICK_PERIOD_MS能夠用來輔助計算真實延時值。若是INCLUDE_vTaskSuspend設置成1,而且指定延時爲portMAX_DELAY將引發任務無限阻塞(沒有超時)。

10.3返回值

      隊列項入隊成功返回pdTRUE,不然返回errQUEUE_FULL。

11.向隊列首部投遞隊列項(帶中斷保護)

11.1函數描述

[objc] view plain copy
  1. BaseType_t xQueueSendToFrontFromISR (QueueHandle_t xQueue,  
  2.         const voidvoid *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);  

      實際上是一個宏,真正被調用的函數是xQueueGenericSendFromISR()。這個宏是xQueueSendToFront ()的中斷保護版本,用於中斷服務程序。

11.2參數描述

  • xQueue:隊列句柄。
  • pvItemToQueue:指針,指向要入隊的項目。要保存到隊列中的項目字節數在隊列建立時就已肯定。所以要從指針pvItemToQueue指向的區域拷貝到隊列存儲區域的字節數,也已肯定。
  • pxHigherPriorityTaskWoken:若是入隊致使一個任務解鎖,而且解鎖的任務優先級高於當前運行的任務,則該函數將*pxHigherPriorityTaskWoken設置成pdTRUE。若是xQueueSendFromISR()設置這個值爲pdTRUE,則中斷退出前須要一次上下文切換。從FreeRTOS V7.3.0起,pxHigherPriorityTaskWoken稱爲一個可選參數,並能夠設置爲NULL。

11.3返回值

      列項入隊成功返回pdTRUE,不然返回errQUEUE_FULL。

12.讀取並移除隊列項

12.1函數描述

[objc] view plain copy
  1. BaseType_t xQueueReceive(QueueHandle_t xQueue,  
  2.                      voidvoid *pvBuffer,TickType_t xTicksToWait);  

      實際上是一個宏,真正被調用的函數是xQueueGenericReceive()

      這個宏從隊列中讀取一個隊列項並把該隊列項從隊列中刪除。讀取隊列項是以拷貝的形式完成,而不是以引用的形式,所以必須提供足夠大的緩衝區以便容納隊列項。參數pvBuffer指向這個緩衝區。

      毫不能夠在中斷服務例程中調用這個宏,能夠使用使用帶有中斷保護的版本xQueueReceiveFromISR來完成相同功能。

12.2參數描述

  • pxQueue:隊列句柄。
  • pvBuffer:指向一個緩衝區,用於拷貝接收到的列表項。
  • xTicksToWait:要接收的項目隊列爲空時,容許任務最大阻塞時間。若是設置該參數爲0,則表示即隊列爲空也當即返回。阻塞時間的單位是系統節拍週期,宏portTICK_RATE_MS可輔助計算真實阻塞時間。若是INCLUDE_vTaskSuspend設置成1,而且阻塞時間設置成portMAX_DELAY,將會引發任務無限阻塞(不會有超時)。

12.3返回值

      成功接收到列表項返回pdTRUE,不然返回pdFALSE。

12.4用法舉例

[objc] view plain copy
  1. struct AMessage  
  2. {  
  3.     portCHAR ucMessageID;  
  4.     portCHAR ucData[ 20 ];  
  5. } xMessage;  
  6.    
  7. xQueueHandle xQueue;  
  8.    
  9. // 建立一個隊列並投遞一個值  
  10. void vATask( voidvoid *pvParameters )  
  11. {  
  12.      struct AMessage *pxMessage;  
  13.    
  14.      // 建立一個隊列,隊列能包含10個 Amessage結構體指針類型的值。  
  15.      // 這樣能夠經過傳遞指針變量來包含大量數據。  
  16.      xQueue =xQueueCreate( 10, sizeof( struct AMessage * ) );  
  17.      if( xQueue == 0)  
  18.      {  
  19.           // 建立隊列失敗  
  20.     }  
  21.     // ...  
  22.     // 向隊列發送一個指向結構體對象Amessage的指針,若是隊列滿不等待  
  23.     pxMessage = & xMessage;  
  24.     xQueueSend(xQueue, ( voidvoid * ) &pxMessage, ( portTickType ) 0 );  
  25.     // ... 其它代碼  
  26. }  
  27.    
  28. // 該任務從隊列中接收一個隊列項  
  29. voidvADifferentTask( voidvoid *pvParameters )  
  30. {  
  31.     struct AMessage *pxRxedMessage;  
  32.    
  33.     if( xQueue != 0)  
  34.     {  
  35.         // 從建立的隊列中接收一個消息,若是消息無效,最多阻塞10個系統節拍週期  
  36.         if(xQueueReceive( xQueue, &( pxRxedMessage ), ( portTickType ) 10 ) )  
  37.         {  
  38.             // 如今pcRxedMessage 指向由vATask任務投遞進來的結構體Amessage變量  
  39.         }  
  40.     }  
  41.    // ... 其它代碼  
  42.  }  

13讀取並移除隊列項(帶中斷保護)

13.1函數描述

[objc] view plain copy
  1. BaseType_t xQueueReceiveFromISR (QueueHandle_t xQueue,  
  2.         voidvoid *pvBuffer, BaseType_t *pxHigherPriorityTaskWoken);  

      從隊列中讀取一個隊列項並把該隊列項從隊列中刪除。功能與xQueueReceive()相同,用於中斷服務函數。

13.2參數描述

  • pxQueue:隊列句柄。
  • pvBuffer:指向一個緩衝區,用於拷貝接收到的列表項。
  • pxHigherPriorityTaskWoken:若是入隊致使一個任務解鎖,而且解鎖的任務優先級高於當前運行的任務,則該函數將*pxHigherPriorityTaskWoken設置成pdTRUE。若是xQueueSendFromISR()設置這個值爲pdTRUE,則中斷退出前須要一次上下文切換。從FreeRTOS V7.3.0起,pxHigherPriorityTaskWoken稱爲一個可選參數,並能夠設置爲NULL。

13.3返回值

      成功接收到列表項返回pdTRUE,不然返回pdFALSE。

13.4用法舉例

[objc] view plain copy
  1. xQueueHandle xQueue;  
  2.    
  3. /* 該函數建立一個隊列並投遞一些值 */  
  4. voidvAFunction( voidvoid *pvParameters )  
  5. {  
  6.      portCHAR cValueToPost;  
  7.      const portTickType xBlockTime = (portTickType )0xff;  
  8.    
  9.      /*建立一個隊列,能夠容納10個portCHAR型變量 */  
  10.      xQueue = xQueueCreate( 10, sizeof( portCHAR ) );  
  11.      if( xQueue == 0 )  
  12.      {  
  13.           /* 隊列建立失敗 */  
  14.      }  
  15.     /*…... */  
  16.     /* 投遞一些字符,在ISR中使用。若是隊列滿,任務將會阻塞xBlockTime 個系統節拍週期 */  
  17.     cValueToPost = 'a';  
  18.     xQueueSend( xQueue, ( voidvoid * ) &cValueToPost, xBlockTime );  
  19.     cValueToPost = 'b';  
  20.     xQueueSend( xQueue, ( voidvoid * ) &cValueToPost, xBlockTime );  
  21.     /*... 繼續投遞字符 ... 當隊列滿時,這個任務會阻塞*/  
  22.     cValueToPost = 'c';  
  23.     xQueueSend( xQueue, ( voidvoid * ) &cValueToPost, xBlockTime );  
  24. }  
  25.    
  26. /* ISR:輸出從隊列接收到的全部字符 */  
  27. voidvISR_Routine( void )  
  28. {  
  29.      portBASE_TYPE xTaskWokenByReceive = pdFALSE;  
  30.      portCHAR cRxedChar;  
  31.    
  32.      while( xQueueReceiveFromISR( xQueue, ( voidvoid *) &cRxedChar, &xTaskWokenByReceive) )  
  33.     {  
  34.        /* 接收到一個字符串,輸出.*/  
  35.        vOutputCharacter( cRxedChar );  
  36.        /* 若是從隊列移除一個字符串後喚醒了向此隊列投遞字符的任務,那麼參數xTaskWokenByReceive將會設置成pdTRUE,這個循環不管重複多少次,僅會 
  37.           有一個任務被喚醒。*/  
  38.     }  
  39.     /*這裏緩衝區已空,若是須要進行一個上下文切換根據不一樣移植平臺,這個函數也不一樣 */  
  40.     portYIELD_FROM_ISR(xTaskWokenByReceive);  
  41. }  

14.讀取但不移除隊列項

14.1函數描述

[objc] view plain copy
  1. BaseType_t xQueuePeek(QueueHandle_t xQueue,  
  2.         voidvoid *pvBuffer, TickType_t xTicksToWait);  

      實際上是一個宏,真正被調用的函數是xQueueGenericReceive()

      這個宏從隊列中讀取一個隊列項,但不會把該隊列項從隊列中移除。這個宏毫不能夠用在中斷服務例程中,能夠使用使用帶有中斷保護的版本xQueuePeekFromIS()來完成相同功能。

14.2參數描述

      同xQueueReceive()。

14.3返回值

      同xQueueReceive()。

14.4用法舉例

      同xQueueReceive()。

15.讀取但不移除隊列項(帶中斷保護)

15.1函數描述

      BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue, void *pvBuffer,);

      功能與xQueuePeek()相同,用於中斷服務程序。

15.2參數描述

  • pxQueue:隊列句柄。
  • pvBuffer:指向一個緩衝區,用於拷貝接收到的列表項。

15.3返回值

      成功接收到列表項返回pdTRUE,不然返回pdFALSE。

16.隊列註冊

16.1函數描述

      void vQueueAddToRegistry(QueueHandle_t xQueue, char *pcQueueName,);

      爲隊列分配名字並進行註冊。

16.2參數描述

  • l  xQueue:隊列句柄
  • l  pcQueueName:分配給隊列的名字。這僅是一個有助於調試的字符串。隊列註冊僅存儲指向隊列名字符串的指針,所以這個字符串必須是靜態的(全局變量活着存儲在ROM/Flash中),不能夠定義到堆棧中。

      隊列註冊有兩個目的,這兩個目的都是爲了調試RTOS內核:

  1. 它容許隊列具備一個相關的文本名字,在GUI調試中能夠容易的標識隊列;
  2. 包含調試器用於定位每個已經註冊的隊列和信號量時所需的信息。

       隊列註冊僅用於調試器。

       宏configQUEUE_REGISTRY_SIZE定義了能夠註冊的隊列和信號量的最大數量。僅當你想使用可視化調試內核時,才進行隊列和信號量註冊。

16.3用法舉例

[objc] view plain copy
  1. void vAFunction( void )  
  2. {  
  3.     xQueueHandle xQueue;  
  4.    
  5.    /*建立一個隊列,能夠容納10個char類型數值 */  
  6.    xQueue = xQueueCreate( 10, sizeof( portCHAR ) );  
  7.    
  8.    /* 咱們想可視化調試,因此註冊它*/  
  9.    vQueueAddToRegistry( xQueue, "AMeaningfulName" );  
  10. }  

17.解除註冊

17.1函數描述

      void vQueueUnregisterQueue(QueueHandle_t xQueue);

      從隊列註冊表中移除指定的隊列。

17.2參數描述

  • xQueue:隊列句柄

18.查詢隊列是否爲空(僅用於中斷服務程序)

18.1函數描述

      BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue );

      查詢隊列是否爲空。這個函數僅用於ISR。

18.2參數描述

  • xQueue:隊列句柄

18.3返回值

      隊列非空返回pdFALSE,其它值表示隊列爲空。

19.查詢隊列是否滿(僅用於中斷服務程序)

19.1函數描述

      BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue );

      查詢隊列是否滿,僅用於ISR。

19.2參數描述

  • xQueue:隊列句柄

19.3返回值

      隊列沒有滿返回pdFALSE,其它值表示隊列滿。

20.向隊列尾部覆蓋式投遞隊列項

20.1函數描述

      BaseType_t xQueueOverwrite(QueueHandle_t xQueue, const void * pvItemToQueue);

      實際上是個宏,真正被調用的函數是xQueueGenericSend()。這個宏是xQueueSendToBack()的另外一個版本,向隊列尾投遞一個隊列項,若是隊列已滿,則覆蓋以前的隊列項。通常用於只有一個隊列項的隊列中,若是隊列的隊列項超過1個,使用這個宏會觸發一個斷言(已經正肯定義configASSERT()的狀況下)。這個宏毫不能夠在中斷服務程序中調用,能夠使用使用帶有中斷保護的版本xQueueOverwriteFromISR()來完成相同功能。

20.2參數描述

  • xQueue:隊列句柄。
  • pvItemToQueue:指針,指向要入隊的項目。要保存到隊列中的項目字節數在隊列建立時就已肯定。所以要從指針pvItemToQueue指向的區域拷貝到隊列存儲區域的字節數,也已肯定。

20.3返回值

      老是返回pdPASS。

20.4用法舉例

[objc] view plain copy
  1. void vFunction( voidvoid *pvParameters )  
  2. {  
  3.     QueueHandle_t xQueue;  
  4.     unsigned long ulVarToSend, ulValReceived;  
  5.    
  6.     /*建立隊列,保存一個unsignedlong值。若是一個隊列的隊列項超過1個,強烈建議不要使用xQueueOverwrite(),若是使用xQueueOverwrite()會觸發一個斷言(已經正肯定義configASSERT()的狀況下)。*/  
  7.     xQueue = xQueueCreate( 1, sizeof( unsigned long ) );  
  8.    
  9.     /*使用 xQueueOverwrite().向隊列寫入10*/  
  10.     ulVarToSend = 10;  
  11.     xQueueOverwrite( xQueue, &ulVarToSend );  
  12.    
  13.     /*從隊列讀取值,可是不把這個值從隊列中刪除。*/  
  14.     ulValReceived = 0;  
  15.     xQueuePeek( xQueue, &ulValReceived, 0 );  
  16.    
  17.     if( ulValReceived != 10 )  
  18.      {  
  19.           /* 處理錯誤*/  
  20.      }  
  21.    
  22.      /*到這裏隊列還是滿的。使用xQueueOverwrite()覆寫隊列,寫入值100 */  
  23.      ulVarToSend = 100;  
  24.      xQueueOverwrite( xQueue, &ulVarToSend );  
  25.    
  26.      /* 從隊列中讀取值*/  
  27.      xQueueReceive( xQueue, &ulValReceived, 0 );  
  28.    
  29.      if( ulValReceived != 100 )  
  30.      {  
  31.           /*處理錯誤 */  
  32.      }  
  33.    
  34.       /* ... */  
  35. }  

21向隊列尾部覆蓋式投遞隊列項(帶中斷保護)

21.1函數描述

[objc] view plain copy
  1. BaseType_t xQueueOverwriteFromISR (QueueHandle_t xQueue, const voidvoid * pvItemToQueue,  
  2.                        BaseType_t *pxHigherPriorityTaskWoken);  

      實際上是個宏,真正被調用的函數是xQueueGenericSendFromISR()。這個宏的功能與xQueueOverwrite()相同,用在中斷服務程序中。

21.2參數描述

  • xQueue:隊列句柄。
  • pvItemToQueue:指針,指向要入隊的項目。要保存到隊列中的項目字節數在隊列建立時就已肯定。所以要從指針pvItemToQueue指向的區域拷貝到隊列存儲區域的字節數,也已肯定。
  • pxHigherPriorityTaskWoken:若是入隊致使一個任務解鎖,而且解鎖的任務優先級高於當前運行的任務,則該函數將*pxHigherPriorityTaskWoken設置成pdTRUE。若是xQueueSendFromISR()設置這個值爲pdTRUE,則中斷退出前須要一次上下文切換。從FreeRTOS V7.3.0起,pxHigherPriorityTaskWoken稱爲一個可選參數,並能夠設置爲NULL。

21.3返回值

      老是返回pdPASS。

 

 

FreeRTOS系列第19篇---FreeRTOS信號量

本文介紹信號量的基礎知識,詳細源碼分析見FreeRTOS高級篇6---FreeRTOS信號量分析

1.信號量簡介

      FreeRTOS的信號量包括二進制信號量、計數信號量、互斥信號量(之後簡稱互斥量)和遞歸互斥信號量(之後簡稱遞歸互斥量)。

      咱們能夠把互斥量和遞歸互斥量當作特殊的信號量。互斥量和信號量在用法上不一樣:

  • 信號量用於同步,任務間或者任務和中斷間同步;互斥量用於互鎖,用於保護同時只能有一個任務訪問的資源,爲資源上一把鎖。
  • 信號量用於同步時,通常是一個任務(或中斷)給出信號,另外一個任務獲取信號;互斥量必須在同一個任務中獲取信號、同一個任務給出信號。
  • 互斥量具備優先級繼承,信號量沒有。
  • 互斥量不能用在中斷服務程序中,信號量能夠。
  • 建立互斥量和建立信號量的API函數不一樣,可是共用獲取和給出信號API函數;

2.二進制信號量

      二進制信號量既能夠用於互斥功能也能夠用於同步功能。

      二進制信號量和互斥量很是類似,可是有一些細微差異:互斥量包含一個優先級繼承機制,二進制信號量則沒有這個機制。這使得二進制信號量更好的用於實現同步(任務間或任務和中斷間),互斥量更好的用於實現簡單互斥。本節僅描述用於同步的二進制信號量。

      信號量API函數容許指定一個阻塞時間。當任務企圖獲取一個無效信號量時,任務進入阻塞狀態,阻塞時間用來肯定任務進入阻塞的最大時間,阻塞時間單位爲系統節拍週期時間。若是有多個任務阻塞在同一個信號量上,那麼當信號量有效時,具備最高優先級別的任務最早解除阻塞。

      能夠將二進制信號量看做只有一個項目(item)的隊列,所以這個隊列只能爲空或滿(所以稱爲二進制)。任務和中斷使用隊列無需關注誰控制隊列---只須要知道隊列是空仍是滿。利用這個機制能夠在任務和中斷之間同步。

      考慮這樣一種狀況,一個任務用來維護外設。使用輪詢的方法會浪費CPU資源而且妨礙其它任務執行。更好的作法是任務的大部分時間處於阻塞狀態(容許其它任務執行),直到某些事件發生該任務才執行。能夠使用二進制信號量實現這種應用:當任務取信號量時,由於此時還沒有發生特定事件,信號量爲空,任務會進入阻塞狀態;當外設須要維護時,觸發一箇中斷服務例程,該中斷服務僅僅給出信號量(向隊列寫數據)。任務只是取信號,並不須要歸還,中斷服務只是給信號。

      任務的優先級能夠用於確保外設及時得到維護。還能夠使用隊列來代替二進制信號量。中斷例程能夠捕獲與外設事件相關的數據並將它發往任務的隊列。任務發現隊列數據有效時解除阻塞,若是須要,則進行數據處理。第二種方案使得中斷執行儘量的短,其它處理過程能夠在任務中實現。

      注:中斷程序中決不可以使用無「FromISR」結尾的API函數。

      注:在大部分應用場合,任務通知均可以代替二進制信號量,而且速度更快、生成的代碼更少。

圖1-1:中斷和任務之間同步---使用信號量

      如圖1-1所示,程序開始運行時,信號量無效,所以任務阻塞在這個信號量下。一段時間後,一箇中斷髮生,在中斷服務程序中使用API函數xSemaphoreGiveFromISR()給出了一個信號,信號量變得有效。當退出中斷服務程序後,執行上下文切換,任務解除阻塞,使用API函數xSemaphoreTake()取走信號量並執行任務。以後信號量變得無效,任務再次進入阻塞。

3.計數信號量

      二進制信號量能夠被認爲是長度爲1的隊列,計數信號量則能夠被認爲長度大於1的隊列。此外,信號量使用者沒必要關心存儲在隊列中的數據,只需關心隊列是否爲空。

      一般計數信號量用於下面兩種事件:

  1. 計數事件:在這種場合下,每當事件發生,事件處理程序將給出一個信號(信號量計數值增1),當處理事件時,處理程序會取走信號量(信號量計數值減1)。所以,計數值是事件發生的數量和事件處理的數量差值。在這種狀況下,計數信號量在建立時其值爲0。
  2. 資源管理:這種用法下,計數值表示有效的資源數目。任務必須先獲取信號量才能獲取資源控制權。當計數值減爲零時表示沒有的資源。當任務完成後,它會返還信號量---信號量計數值增長。在這種狀況下,信號量建立時,計數值等於最大資源數目。

      注:中斷程序中決不可以使用無「FromISR」結尾的API函數。

      注:在大部分應用場合,任務通知均可以代替計數信號量,而且速度更快、生成的代碼更少。

4.互斥量

      互斥量是一個包含優先級繼承機制的二進制信號量。用於實現同步(任務之間或者任務與中斷之間)的話,二進制信號量是更好的選擇,互斥量用於簡單的互鎖。

      用於互鎖的互斥量能夠充當保護資源的令牌。當一個任務但願訪問某個資源時,它必須先獲取令牌。當任務使用完資源後,必須還回令牌,以便其它任務能夠訪問同一資源。

      互斥量和信號量使用相同的API函數,所以互斥量也容許指定一個阻塞時間。阻塞時間單位爲系統節拍週期時間,數目表示獲取互斥量無效時最多處於阻塞狀態的系統節拍週期個數。

      互斥量與二進制信號量最大的不一樣是:互斥量具備優先級繼承機制。也就是說,若是一個互斥量(令牌)正在被一個低優先級任務使用,此時一個高優先級企圖獲取這個互斥量,高優先級任務會由於得不到互斥量而進入阻塞狀態,正在使用互斥量的低優先級任務會臨時將本身的優先級提高,提高後的優先級與與進入阻塞狀態的高優先級任務相同。這個優先級提高的過程叫作優先級繼承。這個機制用於確保高優先級任務進入阻塞狀態的時間儘量短,以及將已經出現的「優先級翻轉」影響下降到最小。

      在不少場合中,某個硬件資源只有一個,當低優先級任務佔用該資源的時候,即使高優先級任務也只能乖乖的等待低優先級任務釋放資源。這裏高優先級任務沒法運行而低優先級任務能夠運行的現象稱爲「優先級翻轉」。

      爲何優先級繼承可以下降優先級翻轉的影響呢?舉個例子,如今有任務A、任務B和任務C,三個任務的優先級順序爲任務C>任務B>任務A。任務A和任務C都要使用某一個硬件資源,而且當前任務A佔有該資源。

      先看沒有優先級繼承的狀況:任務C也要使用該資源,可是此時任務A正在使用這個資源,所以任務C進入阻塞,此時三個任務的優先級順序沒有發生變化。在任務C進入阻塞以後,某硬件產生了一次中斷,喚醒了一個事件,該事件能夠解除任務B的阻塞狀態。在中斷結束後,由於任務B的優先級是大於任務A的,因此任務B搶佔任務A的CPU權限。那麼任務C的阻塞時間就至少爲:中斷處理時間+任務B的運行時間+任務A的運行時間。

      再看有優先級繼承的狀況:任務C也要使用該資源,可是此時任務A正在使用這個資源,所以任務C進入阻塞,此時因爲優先級A會繼承任務C的優先級,三個任務的優先級順序發生了變化,新的優先級順序爲:任務C=任務A>任務B。在任務C進入阻塞以後,某硬件產生了一次中斷,喚醒了一個事件,該事件能夠解除任務B的阻塞狀態。在中斷結束後,由於任務A的優先級臨時被提升,大於任務B的優先級,因此任務A繼續得到CPU權限。任務A完成後,處於高優先級的任務C會接管CPU。因此任務C的阻塞時間爲:中斷處理時間+任務A的運行時間。看,任務C的阻塞時間變小了,這就是優先級繼承的優點。

      優先級繼承不能解決優先級反轉,只能將這種狀況的影響下降到最小。硬實時系統在一開始設計時就要避免優先級反轉發生。

圖4-1 互斥量用於保護資源

      如圖4-1所示,互斥量用來保護資源。爲了訪問資源,任務必須先獲取互斥量。任務A想獲取資源,首先它使用API函數xSemaphoreTake()獲取信號量,成功獲取到信號量後,任務A就持有了互斥量,能夠安全的訪問資源。期間任務B開始執行,它也想訪問資源,任務B也要先得到信號量,可是信號量此時是無效的,任務B進入阻塞狀態。當任務A執行完成後,使用API函數xSemaphoreGive()釋放信號量。以後任務B解除阻塞,任務B使用API函數xSemaphoreTake()獲取並獲得信號量,任務B能夠訪問資源。

5.遞歸互斥量

      已經獲取遞歸互斥量的任務能夠重複獲取該遞歸互斥量。使用xSemaphoreTakeRecursive() 函數成功獲取幾回遞歸互斥量,就要使用xSemaphoreGiveRecursive()函數返還幾回,在此以前遞歸互斥量都處於無效狀態。好比,某個任務成功獲取5次遞歸互斥量,那麼在它沒有返還5次該遞歸互斥量以前,這個互斥量對別的任務無效。

      遞歸互斥量能夠當作帶有優先級繼承機制的信號量,獲取遞歸互斥量的任務在用完後必須返還。

      互斥量不能夠用在中斷服務程序中,這是由於:

互斥量具備優先級繼承機制,只有在任務中獲取或給出互斥纔有意義。

中斷不能由於等待互斥量而阻塞。

FreeRTOS系列第20篇---FreeRTOS信號量API函數

FreeRTOS的信號量包括二進制信號量、計數信號量、互斥信號量(之後簡稱互斥量)和遞歸互斥信號量(之後簡稱遞歸互斥量)。咱們能夠把互斥量和遞歸互斥量當作特殊的信號量。

      信號量API函數實際上都是宏,它使用現有的隊列機制。這些宏定義在semphr.h文件中。若是使用信號量或者互斥量,須要包含semphr.h頭文件。

      二進制信號量、計數信號量和互斥量信號量的建立API函數是獨立的,可是獲取和釋放API函數都是相同的;遞歸互斥信號量的建立、獲取和釋放API函數都是獨立的。

1建立二進制信號量

1.1函數描述

       SemaphoreHandle_t  xSemaphoreCreateBinary( void );

       這個函數用於建立一個二進制信號量。二進制信號量要麼有效要麼無效,這也是爲何叫作二進制的緣由。

       新建立的信號量處於無效狀態,這意味着使用API函數xSemaphoreTake()獲取信號以前,須要先給出信號。

       二進制信號量和互斥量很是類似,但也有細微的區別:互斥量具備優先級繼承機制,二進制信號量沒有這個機制。這使得二進制信號量更適合用於同步(任務之間或者任務和中斷之間),互斥量更適合互鎖。

       一旦得到二進制信號量後不須要恢復,一個任務或中斷不斷的產生信號,而另外一個任務不斷的取走這個信號,經過這樣的方式來實現同步。

       低優先級任務擁有互斥量的時候,若是另外一個高優先級任務也企圖獲取這個信號量,則低優先級任務的優先級會被臨時提升,提升到和高優先級任務相同的優先級。這意味着互斥量必需要釋放,不然高優先級任務將不能獲取這個互斥量,而且那個擁有互斥量的低優先級任務也永遠不會被剝奪,這就是操做系統中的優先級翻轉。

       互斥量和二進制信號量都是SemaphoreHandle_t類型,而且能夠用於任何具備這類參數的API函數中。

1.1.2返回值

  •  NULL:建立信號量失敗,由於FreeRTOS堆棧不足。
  •  其它值:信號量建立成功。這個返回值存儲着信號量句柄。

1.1.3用法舉例

[objc] view plain copy
  1. SemaphoreHandle_t xSemaphore;  
  2.    
  3. void vATask( voidvoid * pvParameters )  
  4. {  
  5.     /* 建立信號量 */  
  6.    xSemaphore = xSemaphoreCreateBinary();  
  7.    
  8.    if( xSemaphore == NULL )  
  9.    {  
  10.        /* 因堆棧不足,信號量建立失敗,這裏進行失敗處理*/  
  11.    }  
  12.    else  
  13.    {  
  14.        /* 信號量能夠使用。信號量句柄存儲在變量xSemahore中。 
  15.           若是在這裏調用API函數xSemahoreTake()來獲取信號量, 
  16.           則必然是失敗的,由於建立的信號量初始是無效(空)的。*/  
  17.    }  
  18. }  

2建立計數信號量

2.1函數描述

[objc] view plain copy
  1. SemaphoreHandle_t xSemaphoreCreateCounting ( UBaseType_t uxMaxCount,  
  2.                                  UBaseType_t uxInitialCount )  

       建立計數信號量,計數信號量一般用於如下兩種狀況:

  1. 事件計數:在這種應用場合,每當事件發生,事件處理程序會「產生」一個信號量(信號量計數值會遞增),每當處理任務處理事件,會取走一個信號量(信號量計數值會遞減)。所以,事件發生或者事件被處理後,計數值是會變化的。
  2. 資源管理:在這種應用場合下,計數值表示有效資源的數目。爲了得到資源,任務首先要得到一個信號量---遞減信號量計數值。當計數值爲0時,表示沒有可用的資源。當佔有資源的任務完成,它會釋放這個資源,相應的信號量計數值會增一。計數值達到初始值(最大值)表示全部資源均可用。

2.2參數描述

  •  uxMaxCount:最大計數值,當信號到達這個值後,就再也不增加了。
  •  uxInitialCount:建立信號量時的初始值。

2.3返回值

       NULL表示信號量建立失敗,不然返回信號量句柄。

2.4用法舉例

[objc] view plain copy
  1. void vATask( voidvoid * pvParameters )  
  2.  {  
  3.      xSemaphoreHandle xSemaphore;  
  4.    
  5.      // 必須先建立信號量,才能使用它  
  6.      // 信號量能夠計數的最大值爲10,計數初始值爲0.  
  7.      xSemaphore = xSemaphoreCreateCounting( 10, 0 );  
  8.    
  9.      if( xSemaphore != NULL )  
  10.      {  
  11.          // 信號量建立成功  
  12.          // 如今能夠使用信號量了。  
  13.      }  
  14.  }  

3建立互斥量

3.1函數描述

       SemaphoreHandle_t xSemaphoreCreateMutex( void )

       建立互斥量。能夠使用API函數xSemaphoreTake()和xSemaphoreGive()訪問互斥量,可是毫不能夠用xSemaphoreTakeRecursive()和xSemaphoreGiveRecursive()訪問。

       二進制信號量和互斥量很是類似,但也有細微的區別:互斥量具備優先級繼承機制,二進制信號量沒有這個機制。這使得二進制信號量更適合用於同步(任務之間或者任務和中斷之間),互斥量更適合互鎖。

       一旦得到二進制信號量後不須要恢復,一個任務或中斷不斷的產生信號,而另外一個任務不斷的取走這個信號,經過這樣的方式來實現同步。

       低優先級任務擁有互斥量的時候,若是另外一個高優先級任務也企圖獲取這個信號量,則低優先級任務的優先級會被臨時提升,提升到和高優先級任務相同的優先級。這意味着互斥量必需要釋放,不然高優先級任務將不能獲取這個互斥量,而且那個擁有互斥量的低優先級任務也永遠不會被剝奪,這就是操做系統中的優先級翻轉。

       互斥量和二進制信號量都是SemaphoreHandle_t類型,而且能夠用於任何具備這類參數的API函數中。

3.2返回值

       NULL表示信號量建立失敗,不然返回信號量句柄。

3.3用法舉例

[objc] view plain copy
  1. xSemaphoreHandle xSemaphore;  
  2.    
  3. voidvATask( voidvoid * pvParameters )  
  4. {  
  5.     // 互斥量在未建立以前是不可用的  
  6.     xSemaphore = xSemaphoreCreateMutex();  
  7.     if( xSemaphore != NULL )  
  8.     {  
  9.         // 建立成功  
  10.         // 在這裏能夠使用這個互斥量了   
  11.     }  
  12. }  

4建立遞歸互斥量

4.1函數描述

       SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )

       用於建立遞歸互斥量。被建立的互斥量能夠被API函數xSemaphoreTakeRecursive()和xSemaphoreGiveRecursive()使用,但不能夠被API函數xSemaphoreTake()和xSemaphoreGive()使用。

       遞歸類型的互斥量能夠被擁有者重複獲取。擁有互斥量的任務必須調用API函數xSemaphoreGiveRecursive()將擁有的遞歸互斥量所有釋放後,該信號量才真正被釋放。好比,一個任務成功獲取同一個互斥量5次,那麼這個任務要將這個互斥量釋放5次以後,其它任務才能獲取到它。

       遞歸互斥量具備優先級繼承機制,所以任務得到一次信號後必須在使用完後作一個釋放操做。

       互斥量類型信號不能夠用在中斷服務例程中。

4.2返回值

       NULL表示互斥量建立失敗,不然返回互斥量句柄。

4.3用法舉例

[objc] view plain copy
  1. xSemaphoreHandle xMutex;  
  2.    
  3. void vATask( voidvoid * pvParameters )  
  4. {  
  5.     // 互斥量未建立前是不能被使用的  
  6.     xMutex = xSemaphoreCreateRecursiveMutex();  
  7.    
  8.     if( xMutex != NULL )  
  9.     {  
  10.         // 建立成功  
  11.         // 在這裏建立互斥量  
  12.     }  
  13. }  

5刪除信號量

5.1函數描述

       void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );

       刪除信號量。若是有任務阻塞在這個信號量上,則這個信號量不要刪除。

5.2參數描述

       xSemaphore:信號量句柄

6獲取信號量

6.1函數描述

       xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait)

       獲取信號量。信號量必須是經過API函數xSemaphoreCreateBinary()、xSemaphoreCreateCounting()和xSemaphoreCreateMutex()預先建立過的。注意,遞歸互斥量類型信號量不能使用該函數、不用在中斷服務程序中使用該函數。

6.2參數描述

  •  xSemaphore:信號量句柄
  •  xTickToWait:信號量無效時,任務最多等待的時間,單位是系統節拍週期個數。使用宏portTICK_PERIOD_MS能夠輔助將系統節拍個數轉化爲實際時間(以毫秒爲單位)。若是設置爲0,表示不是設置等待時間。若是INCLUDE_vTaskSuspend設置爲1,而且參數xTickToWaitportMAX_DELAY則能夠無限等待。

6.3返回值

       成功獲取到信號量返回pdTRUE,不然返回pdFALSE

6.4用法舉例

[objc] view plain copy
  1. SemaphoreHandle_t xSemaphore = NULL;  
  2.    
  3. /*這個任務建立信號量 */  
  4. void vATask( voidvoid * pvParameters )  
  5. {  
  6.     /*建立互斥型信號量,用於保護共享資源。*/  
  7.     xSemaphore = xSemaphoreCreateMutex();  
  8. }  
  9.    
  10. /* 這個任務使用信號量 */  
  11. void vAnotherTask( voidvoid * pvParameters )  
  12. {  
  13.     /* ... 作其它事情. */  
  14.    
  15.     if( xSemaphore != NULL )  
  16.     {  
  17.         /*若是信號量無效,則最多等待10個系統節拍週期。*/  
  18.         if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )  
  19.         {  
  20.             /*到這裏咱們獲取到信號量,如今能夠訪問共享資源了*/  
  21.    
  22.             /* ... */  
  23.    
  24.             /* 完成訪問共享資源後,必須釋放信號量*/  
  25.             xSemaphoreGive( xSemaphore );  
  26.         }  
  27.         else  
  28.         {  
  29.             /* 沒有獲取到信號量,這裏處理異常狀況。*/  
  30.         }  
  31.     }  
  32. }  

7獲取信號量(帶中斷保護)

7.1函數描述

[objc] view plain copy
  1. xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,  
  2.                 signedBaseType_t *pxHigherPriorityTaskWoken)  

       API函數xSemaphoreTake()的另外一版本,用於中斷服務程序。

7.2參數描述

  •  xSemaphore:信號量句柄
  •  pxHigherPriorityTaskWoken:若是*pxHigherPriorityTaskWoken爲pdTRUE,則須要在中斷退出前手動進行一次上下文切換。從FreeRTOS V7.3.0開始,該參數爲可選參數,並能夠設置爲NULL。

7.3返回值

       信號量成功獲取返回pdTRUE,不然返回pdFALSE。

8獲取遞歸互斥量

8.1函數描述

       xSemaphoreTakeRecursive(SemaphoreHandle_t xMutex, TickType_t xTicksToWait );

       獲取遞歸互斥信號量。互斥量必須是經過API函數xSemaphoreCreateRecursiveMutex()建立的類型。

       文件FreeRTOSConfig.h中的宏configUSE_RECURSIVE_MUTEXES必須設置成1,此函數纔有效。

      已經獲取遞歸互斥量的任務能夠重複獲取該遞歸互斥量。使用xSemaphoreTakeRecursive() 函數成功獲取幾回遞歸互斥量,就要使用xSemaphoreGiveRecursive()函數返還幾回,在此以前遞歸互斥量都處於無效狀態。好比,某個任務成功獲取5次遞歸互斥量,那麼在它沒有返還5次該遞歸互斥量以前,這個互斥量對別的任務無效。

8.2參數描述

  •  xMutex:互斥量句柄,必須是使用API函數xSemaphoreCreateRecursiveMutex()返回的。
  •  xTickToWait:互斥量無效時,任務最多等待的時間,單位是系統節拍週期個數。使用宏portTICK_PERIOD_MS能夠輔助將系統節拍個數轉化爲實際時間(以毫秒爲單位)。若是設置爲0,表示不是設置等待時間。若是任務已經擁有信號量則xSemaphoreTakeRecursive()當即返回,無論xTickToWait是什麼值。

8.3返回值

     成功獲取遞歸互斥量返回pdTURE,不然返回pdFALSE

8.4用法舉例

[objc] view plain copy
  1. SemaphoreHandle_t xMutex = NULL;  
  2.    
  3. // 這個任務建立互斥量  
  4. void vATask( voidvoid *pvParameters )  
  5. {  
  6.     // 這個互斥量用於保護共享資源  
  7.     xMutex =xSemaphoreCreateRecursiveMutex();  
  8. }  
  9.    
  10. //這個任務使用互斥量  
  11. void vAnotherTask( voidvoid *pvParameters )  
  12. {  
  13.     // ... 作其它事情.  
  14.    
  15.     if( xMutex != NULL )  
  16.     {  
  17.         // 若是互斥量無效,則最多等待10系統時鐘節拍週期.     
  18.         if(xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE )  
  19.         {  
  20.             // 到這裏咱們成功獲取互斥量並能夠訪問共享資源了  
  21.    
  22.             // ...  
  23.             // 因爲某種緣由,某些代碼須要在一個任務中屢次調用API函數  
  24.             // xSemaphoreTakeRecursive()。固然不會像本例中這樣連續式  
  25.             //調用,實際代碼會有更加複雜的結構  
  26.             xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );  
  27.             xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );  
  28.    
  29.             // 咱們獲取一個互斥量三次,因此咱們要將這個互斥量釋放三次  
  30.             //它纔會變得有效。再一次說明,實際代碼可能會更加複雜。  
  31.             xSemaphoreGiveRecursive( xMutex );  
  32.             xSemaphoreGiveRecursive( xMutex );  
  33.             xSemaphoreGiveRecursive( xMutex );  
  34.    
  35.             // 到這裏,這個共享資源能夠被其它任務使用了.  
  36.         }  
  37.         else  
  38.         {  
  39.             // 處理異常狀況  
  40.         }  
  41.     }  
  42. }  

9釋放信號量

9.1函數描述

       xSemaphoreGive(SemaphoreHandle_t xSemaphore )

       用於釋放一個信號量。信號量必須是API函數xSemaphoreCreateBinary()xSemaphoreCreateCounting()xSemaphoreCreateMutex() 建立的。必須使用API函數xSemaphoreTake()獲取這個信號量。

       這個函數毫不能夠在中斷服務例程中使用,能夠使用帶中斷保護版本的API函數xSemaphoreGiveFromISR()來實現相同功能。

       這個函數不能用於使用API函數xSemaphoreCreateRecursiveMutex()所建立的遞歸互斥量。

9.2參數描述

  •  xSemaphore:信號量句柄。

9.3返回值

       信號量釋放成功返回pdTRUE,不然返回pdFALSE。

9.4用法舉例

[objc] view plain copy
  1. SemaphoreHandle_t xSemaphore = NULL;  
  2.    
  3. voidvATask( voidvoid * pvParameters )  
  4. {  
  5.    // 建立一個互斥量,用來保護共享資源  
  6.    xSemaphore = xSemaphoreCreateMutex();  
  7.    
  8.     if( xSemaphore != NULL )  
  9.     {  
  10.          if( xSemaphoreGive( xSemaphore ) != pdTRUE )  
  11.          {  
  12.               //咱們但願這個函數調用失敗,由於首先要獲取互斥量  
  13.          }  
  14.          // 獲取信號量,不等待  
  15.          if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) )  
  16.          {  
  17.               // 如今咱們擁有互斥量,能夠安全的訪問共享資源  
  18.               if( xSemaphoreGive( xSemaphore ) != pdTRUE )  
  19.               {  
  20.                    //咱們不但願這個函數調用失敗,由於咱們必須  
  21.                    //要釋放已獲取的互斥量  
  22.               }  
  23.          }  
  24.      }  
  25. }  

10釋放信號量(帶中斷保護)

10.1函數描述

[objc] view plain copy
  1. xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,  
  2.                          signed BaseType_t *pxHigherPriorityTaskWoken )  

       釋放信號量。是API函數xSemaphoreGive()的另個版本,用於中斷服務程序。信號量必須是經過API函數xSemaphoreCreateBinary()或xSemaphoreCreateCounting()建立的。這裏沒有互斥量,是由於互斥量不能夠用在中斷服務程序中。

10.2參數描述

       xSemaphore:信號量句柄

       pxHigherPriorityTaskWoken:若是*pxHigherPriorityTaskWoken爲pdTRUE,則須要在中斷退出前人爲的經行一次上下文切換。從FreeRTOS V7.3.0開始,該參數爲可選參數,並能夠設置爲NULL。

10.3返回值

       成功釋放信號量返回pdTURE,不然返回errQUEUE_FULL。

10.4用法舉例

[objc] view plain copy
  1. #define LONG_TIME 0xffff  
  2. #define TICKS_TO_WAIT    10  
  3.    
  4. SemaphoreHandle_t xSemaphore = NULL;  
  5.    
  6. /* Repetitive task. */  
  7. void vATask( voidvoid * pvParameters )  
  8. {  
  9.     /* 咱們使用信號量同步,因此先建立一個二進制信號量.必須確保 
  10.        在建立這個二進制信號量以前,中斷不會訪問它。*/  
  11.     xSemaphore = xSemaphoreCreateBinary();  
  12.    
  13.     for( ;; )  
  14.     {  
  15.         /* 咱們但願每產生10次定時器中斷,任務運行一次。*/  
  16.         if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE )  
  17.         {  
  18.             /* 到這裏成功獲取到信號量*/  
  19.    
  20.             ...  
  21.    
  22.            /* 咱們成功執行完一次,因爲這是個死循環,因此任務仍會 
  23.                阻塞在等待信號量上。信號量由ISR釋放。*/  
  24.         }  
  25.     }  
  26. }  
  27.    
  28. /* 定時器 ISR */  
  29. void vTimerISR( voidvoid * pvParameters )  
  30. {  
  31.     static unsigned char ucLocalTickCount = 0;  
  32.     static signed BaseType_txHigherPriorityTaskWoken;  
  33.    
  34.     /*定時器中斷髮生 */  
  35.    
  36.       ...執行其它代碼  
  37.    
  38.     /*須要vATask() 運行嗎? */  
  39.     xHigherPriorityTaskWoken = pdFALSE;  
  40.     ucLocalTickCount++;  
  41.     if( ucLocalTickCount >= TICKS_TO_WAIT )  
  42.     {  
  43.         /* 釋放信號量,解除vATask任務阻塞狀態 */  
  44.         xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );  
  45.    
  46.         /* 復位計數器 */  
  47.         ucLocalTickCount = 0;  
  48.     }  
  49.    
  50.     /* 若是 xHigherPriorityTaskWoken 表達式爲真,須要執行一次上下文切換*/  
  51.     portYIELD_FROM_ISR( xHigherPriorityTaskWoken );  
  52. }  

11釋放遞歸互斥量

11.1函數描述

       xSemaphoreGiveRecursive(SemaphoreHandle_t xMutex )

       釋放一個遞歸互斥量。互斥量必須是使用 API函數xSemaphoreCreateRecursiveMutex()建立的。文件FreeRTOSConfig.h中宏configUSE_RECURSIVE_MUTEXES必須設置成1本函數纔有效。

11.2參數描述

  •  xMutex:互斥量句柄。必須是函數xSemaphoreCreateRecursiveMutex()返回的值。

11.3返回值

       若是遞歸互斥量釋放成功,返回pdTRUE。

11.4用法舉例

       見「8 獲取遞歸互斥量」。

12獲取互斥量持有任務的句柄

12.1函數描述

       TaskHandle_t xSemaphoreGetMutexHolder( SemaphoreHandle_t xMutex );

       返回互斥量持有任務的句柄(若是有的話),互斥量由參數xMutex指定。

       若是調用此函數的任務持有互斥量,那麼能夠可靠的返回任務句柄,可是若是是別的任務持有互斥量,則不總可靠。

       文件FreeRTOSConfig.h中宏configUSE_MUTEXES必須設置成1本函數纔有效。

12.2參數描述

  •  xMutex:互斥量句柄

12.3返回值

       返回互斥量持有任務的句柄。若是參數xMutex不是互斥類型信號量或者雖然互斥量有效但這個互斥量不被任何任務持有則返回NULL。

 

      這是FreeRTOS基礎篇的最後一篇博文,到這裏咱們已經能夠移植、熟練使用FreeRTOS了。但若是想知道FreeRTOS背後的運行機制,這些是遠遠不夠的,下面要走的路還會很長。要不要了解FreeRTOS背後運行機制,全憑各位的興趣,畢竟咱們即便不清楚汽車的構造細節,但只要掌握駕駛技巧也能夠很好的開車的。使用RTOS也與之類似,只要咱們掌握了基礎篇的那些知識,咱們已經能夠很好的使用FreeRTOS了。探索FreeRTOS背後運行的機制,是咱們對未知事件的好奇,也是咱們相信理解了FreeRTOS運行機制,可讓咱們更優雅、更少犯錯、更舉重若輕的的使用RTOS。

      FreeRTOS高級篇已經開始寫了,能夠點擊這裏擦看最新的文章列表

 

 

FreeRTOS歷史版本更新記錄

FreeRTOS V9.0.0相對於FreeRTOS V9.0.0rc2更新記錄:

        FreeRTOSV9.0.0發行於2016.05.25,推薦官方網站下載。考慮到官方網站的服務器在美國,下載速度較慢,下面給出CSDN下載地址:點擊下載,下載須要1個資源分,請原諒博主須要資源分增長上傳權限。

         內核更新:

  • 修改新API函數xTaskCreateStatic()的原型,去除一個參數而且改善和其它"CreateStatic()" API函數的兼容性。點擊此處查看更多信息。

        在FreeRTOS V9.0.0rc2版本中,xTaskCreateStatic()原型爲:

 

[objc] view plain copy
  1. BaseType_txTaskCreateStatic( TaskFunction_t pxTaskCode,  
  2.                         const charchar * const pcName,  
  3.                         constuint32_t ulStackDepth,  
  4.                         void* const pvParameters,  
  5.                         UBaseType_t uxPriority,  
  6.                         TaskHandle_t*pxCreatedTask,  
  7.                         StackType_t * const puxStackBuffer,  
  8.                         StaticTask_t * const pxTaskBuffer );  

        在FreeRTOS V9.0.0正式版中,xTaskCreateStatic()原型爲:

 

[objc] view plain copy
  1. TaskHandle_txTaskCreateStatic( TaskFunction_t pxTaskCode,  
  2.                        const charchar * const pcName,  
  3.                        const uint32_t ulStackDepth,  
  4.                        voidvoid * const pvParameters,  
  5.                        UBaseType_t uxPriority,  
  6.                        StackType_t * const puxStackBuffer,  
  7.                     StaticTask_t * const pxTaskBuffer );  

        注:博主看過FreeRTOS V9.0.0rc2版,也看過V9.0.0正式版,發現兩個版本函數xTaskCreateStatic()是相同的。

  • GCC ARM Cortex-A移植層接口:增長configUSE_TASK_FPU_SUPPORT配置宏。當這個宏設置爲2時,每個任務自動使用浮點(FPU)環境。
  • GCC ARM Cortex-A移植層接口:使用vApplicationFPUSafeIRQHandler()來代替vApplicationIRQHandler()能夠在任意中斷嵌套入口處自動保存和恢復全部浮點寄存器。
  • 全部ARMCortex-M3/4F/7移植層接口:當使用ARM Cortex-M3/4/7架構文件而要求嚴格一致的建立任務時,清除位於任務堆棧中的任務入口地址的最低有效位(一般不會有什麼明顯做用,除非使用QMEU仿真器)。
  • 增長GCC和Keil ARMCortex-M4F MPU移植層接口,以前僅ARM Cortex-M3支持使用MPU。
  • ARM Cortex-M3/4F MPU移植層接口:如今徹底支持FreeRTOS V9.0.0 API,增長一個演示例程,展現如何使用更新後的MPU移植層接口。演示例程位於:FreeRTOS/Demo/CORTEX_MPU_Simulator_Keil_GCC。
  • 全部ARMCortex-M3/4F/7移植層接口:在默認低功耗Tickless實現中增長同步指令。
  • 全部ARMCortex-M0移植層接口:防止第一個運行任務堆棧的項目被丟棄。
  • Win32移植層接口:減小堆棧的使用數目,改變刪除Windows線程方法,增長最大執行時間。
  • 增長用於MikroC編譯器的ARMCortex-M4F移植層接口。
  • MPS430X IAR移植層接口:更新至和最新的EW430工具一致。
  • IAR32 GCC移植層接口:糾正當onfigMAX_API_CALL_INTERRUPT_PRIORITY == portMAX_PRIORITY時,函數vPortExitCritical()的錯誤。
  • 更新一致性:函數vTaskGetTaskInfo()如今有一個別名爲vTaskGetInfo()、函數xTaskGetTaskHandle()如今有一個別名爲xTaskGetHandle()、函數pcQueueGetQueueName()如今有一個別名爲pcQueueGetName()。
  • 修改其它註釋錯誤和編譯器警告。

        演示例程更新:

  • 更新AtmelStudio工程,如今可以使用Atmel Studio 7編譯。
  • 更新Xilinx SDK版本,使用2016.01月版本。
  • PIC32演示例程中,刪除依賴IO庫的部分。
  • 將XilinxUltraScale Cortex-R5演示例程移動到主目錄下。
  • l  MSP432庫更新到最新版本。
  • l  增長使用GCC、Keil和MikroC編譯器的MicrochipCEC1302 (ARM Cortex-M4F)演示例程。
  • l  將AtmelSAMA5D2演示例程移動到主目錄下。

 

FreeRTOS V9.0.0rc2相對於FreeRTOS V9.0.0rc1更新記錄:

         FreeRTOSV9.0.0rc2發行於2016.03.30,推薦官方網站下載。考慮到官方網站的服務器在美國,下載速度較慢,下面給出CSDN下載地址:點擊下載,下載須要1個資源分,請原諒博主須要資源分增長上傳權限。

  • 使用靜態內存分配方法建立RTOS對象更簡單了,若是傳遞的緩衝區參數是NULL,則不能轉成動態內存分配,而是返回錯誤信息。
  • 增長configSUPPORT_DYNAMIC_ALLOCATION配置宏,容許應用程序不使用堆空間(設置爲0)。
  • 小幅優化運行時間。
  • 新增兩個基於SiliconLabs EFM32微控制器的Tickless低功耗實現例子。
  • 增長API函數xTimerGetPeriod()和xTimerGetExpireTime()。

 

FreeRTOS V9.0.0rc1相對於FreeRTOS V8.2.3更新記錄:

         FreeRTOSV9.0.0rc1發行於2016.02.19,推薦官方網站下載。考慮到官方網站的服務器在美國,下載速度較慢,下面給出CSDN下載地址:點擊下載,下載須要1個資源分,請原諒博主須要資源分增長上傳權限。

         RTOS內核更新:

  • 主要新功能:如今能夠使用靜態分配內存建立任務、信號量、隊列、定時器和事件組,所以能夠徹底不須要調用pvPortMalloc()函數。
  • 主要新功能:增長API函數xTaskAbortDelay(),容許一個任務強制另外一個任務當即離開阻塞狀態,即便被阻塞的任務等待的事件還沒發生,或者被阻塞的任務超時定時器尚未超時。
  • 一些必要更新,容許FreeRTOS運行在64位架構上。
  • 增長函數vApplicationDaemonTaskStartupHook()。當RTOS守護進程(被定時器服務任務調用)啓動運行時執行。若是應用程序但願啓動調度器後再執行某些初始化代碼,這個函數是比較有用。
  • 增長API函數xTaskGetTaskHandle(),用於根據任務名稱獲取任務句柄。這個函數使用多重字符串比較操做,比較費時,所以建議一個任務只使用一次,將獲取的任務句柄保存到局部變量中,下次使用這個局部變量中保存的值。
  • 增長API函數pcQueueGetQueueName(),用於根據隊列句柄獲取隊列名字。
  • 如今,即便configUSE_PREEMPTION配置爲0,也能夠使用Tickless空閒(用於低功耗應用,經過下降系統節拍定時器中斷頻率使CPU長期處於低功耗狀態)了。
  • 如今,若是一個任務刪除另一個任務,被刪除任務的堆棧和TCB當即釋放。若是一個任務刪除本身,則任務的堆棧和TCB和之前同樣,經過空閒任務刪除。
  • 在中斷服務程序中,若是使用任務通知解除任務阻塞狀態,可是參數xHigherPriorityTaskWoken並無使用(正確狀況下,使用這個參數來人爲判斷是否要進行上下文切換),則下次系統節拍時鐘中斷將會掛起一個上下文切換請求。
  • heap_1.c和heap_2.c如今能夠使用configAPPLICATION_ALLOCATED_HEAP設置選項,之前這個選項僅用在heap_4.c。configAPPLICATION_ALLOCATED_HEAP容許應用程序本身定義一個數組來代替FreeRTOS的堆,數組必須爲:uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];這樣作的好處是程序能夠指定堆的存儲位置,好比內部RAM或外擴RAM等。
  • 如今,TaskStatus_t結構體包含任務堆棧的基地址了。TaskStatus_t結構體用於獲取任務的詳細信息,通常用在可視化追蹤調試中。
  • 增長API函數vTaskGetTaskInfo(),用於返回一個TaskStatus_t結構體,結構體包含單個任務的信息。之前這個信息只能經過調用一個獲取全部任務信息的API函數(uxTaskGetSystemState())間接獲得(使用TaskStatus_t結構體數組存儲這些信息)。
  • 增長API函數uxSemaphoreGetCount(),用於計數信號量,返回當前的計數值。
  • 優化Cortex-M3移植層接口

         演示例程更新:

  • 更新SAM4L AtmelStudio工程,Atmel Studio 7能夠使用該工程了
  • 增長ARMCortex-A53 64位移植層接口
  • 增長用於Xilinx公司產品UltrascaleMPSoC的移植層接口和演示例程
  • 增長Cortex-M7SAME70 GCC演示例程
  • 增長EFM32 Giant和Wonder Gecko演示例程

 FreeRTOS V8.2.3相對於FreeRTOS V8.2.2更新記錄:

FreeRTOSV8.2.3發行於2015.10.16,推薦官方網站下載。考慮到官方網站的服務器在美國,下載速度較慢,下面給出CSDN下載地址:點擊下載 ,下載須要1個資源分,請原諒博主須要資源分增長上傳權限。

內核更新:

  • 修正被肯定的BUG,當使用軟件定時器時,V8.2.2版本修改的軟件定時器代碼,會致使tickless低功耗應用無限期睡眠。
  • 簡化並改善堆棧溢出檢查效率
  • 增長API函數xTaskNotifyStateClear()
  • 新增IAR和GCC編譯器下的Cortex-R微處理器移植層接口,可是不能使用ARM通用中斷控制器。
  • 新增PIC32MEC14xx移植層接口。
  • PIC32MZ 移植層接口中增長對PIC32MZ EF(使用浮點)的支持。
  • Zynq7000移植層接口如今使用弱符號(好比啓動文件中常常出現的「[WEAK]」)聲明設置和清除系統節拍時鐘中斷函數,所以它們能夠被應用程序覆寫。
  • 增長configUSE_TASK_FPU_SUPPORT配置宏,儘管當前只有PIC32MZ EF移植層使用這個宏。
  • 更新RL78和78K0 IAR移植層接口,改善對組合內存模型的支持。
  • 小幅更新heap_5.c,移除引發某些編譯器產生警告的代碼。
  • 簡化許可,見官方程序包/FreeRTOS/License/license.txt

FreeRTOS+ 更新:

  • 使用目錄名WolfSSL代替CyaSSL
  • 更新爲最新的WolfSSL代碼
  • 更新爲最新的跟蹤記錄代碼
  • 增長用於串流追蹤(streaming trace)的FreeRTOS+追蹤記錄庫。

       演示例程更新:

  • 增長Renesas RZ/T (Cortex-R)、PIC32MZ EF(帶浮點硬件的PIC32)、PIC32MEC14xx、RX71M、RX113和RX231演示例程。
  • 整理拼寫和編譯器警告。


FreeRTOS V8.2.2相對於FreeRTOS V8.2.1更新記錄:

FreeRTOSV8.2.2發行於2015.08.12,推薦官方網站下載。考慮到官方網站的服務器在美國,下載速度較慢,下面給出CSDN下載地址:點擊下載 ,下載須要1個資源分,請原諒博主須要資源分增長上傳權限。

內核更新:

  • 增長英特爾IA32/x86 32位處理器移植層接口
  • 通常性維護
  • 增長PRIVILEGED_FUNCTION和PRIVILEGED_DATA宏,用於內存受保護的系統。
  • 在projdefs.h文件中增長errno定義,用於FreeRTOS+組件。
  • 刪除當使用軟件定時器時,避免tickless無限期等待的限制代碼
  • 增長xTaskNotifyAndQueryFromISR(),這是API函數xTaskNotifyAndQuery()的帶中斷保護版本。
  • MSP430X移植層接口中增長額外的NOP指令,確保和硬件文檔嚴格一致
  • Microblaze移植層接口:增長用於移植層接口優化任務的選項。
  • Microblaze移植層接口:以前在任務建立時任務繼承異常使能狀態。如今,只要Microblaze設計支持異常,則全部任務建立時都默認異常使能。
  • Windows移植層接口:增長額外的安全措施,確保正確的啓動序列和線程切換時間。
  • Windows移植層接口:改善移植層接口實現代碼,優化任務選擇彙編代碼。
  • 更新heap_4.c和heap_5.c,容許在64位處理器上使用。
  • 簡化建立隊列代碼
  • 改善tickless空閒表現
  • 確保公共內核文件中的變量沒有初始化爲非零值。
  • 改正heap_4.c和heap_5.c中的xHeapStructSize計算

       演示例程更新:

  • 增長用於IA32/x86移植層接口的演示例程工程,使用Galileo硬件。
  • 增長MSP430FR5969演示例程(之前須要單獨下載)
  • 增長FreeRTOS BSP,用於Xilinx SDK自動建立FreeRTOS應用程序。
  • 增長基於SAMV71 (ARM Cortex-M7)硬件的Atmel Studio / GCC工程
  • 更新Xilinx SDK工程,SDK使用2015.02月版本。
  • 移除Microblaze演示例程
  • 增長基於MSP43FR5969硬件的IAR和CCS演示例程。

       FreeRTOS+更新:

  • 更新FreeRTOS+跟蹤記錄庫
  • 增長Reliance Edge源碼和演示例程。Reliance Edge是一個故障保護事務性文件系統,特別適用於那些要求高可靠性場合的文件存儲。
  • 增長configAPPLICATION_PROVIDES_cOutputBuffer配置宏,用於FreeRTOS+CLI使用者向一個固定內存地址放置輸出緩存區。
  • 改善NetworkInterface.c文件,該文件用於FreeRTOS+UDP的Windows移植層接口。
 

FreeRTOS V8.2.1相對於FreeRTOS V8.2.0更新記錄:

FreeRTOSV8.2.1發行於2015.03.24,推薦官方網站下載。考慮到官方網站的服務器在美國,下載速度較慢,下面給出CSDN下載地址:點擊下載 ,下載須要1個資源分,請原諒博主須要資源分增長上傳權限。

內核更新:

  • 增長用戶可定義的、靈活的本地存儲設備
  • 增長API函數vTimerSetTimerID(),與API函數pvTimerGetTimerID()互補,容許將定時器的ID用做定時器局部存儲器。
  • 修正一個在中斷服務程序中使用隊列有關的潛在問題。
  • 更新一些Xilinx Microblaze GCC有關的移植層接口代碼
  • 增長ARM Cortex-M4F移植層接口,使用TI公司CCS編譯器。
  • 增長ARM Cortex-M7 r0p1移植層接口,使用IAR、GCC和Keil編譯器
  • 若是宏configUSE_CO_ROUTINES設置爲0,將整個croutine.c文件代碼屏蔽
  • 改變一些數據類型:將uint32_t改成size_t。爲64位Windows移植層作準備。
  • 更新PIC32移植層接口,移除使用最新XC32編譯器棄用的警告輸出。

       演示例程更新:

  • 增長TI公司的MSP432微控制器(ARM Cortex-M4F內核)演示例程,使用IAR、Keil和CCS編譯器。
  • 增長STM32F微控制器(ARM Cortex-M7)演示例程,使用IAR和Keil編譯器。
  • 增長Atmel公司的SAMV71微處理器(ARM Cortex-M7)演示例程,使用IAR和Keil編譯器。
  • 增長Microblaze演示例程,使用2014.04月Xilinx SDK版本、運行在KC705開發板平臺。

FreeRTOS V8.2.0相對於FreeRTOS V8.1.2更新記錄:

FreeRTOSV8.2.0發行於2015.01.16,推薦官方網站下載。考慮到官方網站的服務器在美國,下載速度較慢,下面給出CSDN下載地址:點擊下載 ,下載須要1個資源分,請原諒博主須要資源分增長上傳權限。

內核更新:

  • 重要新特性! 任務通知。任務通知能夠在大多數狀況下代替二進制信號量、計數信號量、事件組,而且任務通知具備更快的運行速度和更少的RAM需求。
  • 增長一個新的頭文件,必須包含在編譯文件中。將過期的定義分離成單獨一個新頭文件叫FreeRTOS/Source/include/deprecated_definitions.h。注:這些過期的定義在一些上年頭的演示例程中仍會使用。
  • 將xSemaphoreGiveFromISR()設計成一個函數,以前版本是宏定義,真正被調用的函數是xQueueGenericSendFromISR()。作成函數後,會增長編譯後的代碼大小,可是會提高性能。
  • 更改TCB分配方式,如今任務堆棧增加老是遠離分配的TCB空間(這會改善堆棧溢出調試)。
  • 使用GCC、IAR和Keil編譯器的Cortex-M4F移植層接口代碼使用更對的內聯(inline),這能夠改善性能,可是以增長代碼大小爲代價。
  • 建立隊列如今只須要調用一次pvPortMalloc()就能夠分配隊列結構體和隊列項存儲空間。
  • 引進新的臨界區宏,用於讀取系統節拍(tick)計數器
  • 在heap_4.c中引入configAPPLICATION_ALLOCATED_HEAP配置宏,容許應用程序提供堆數組空間。
  • 引入configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES配置宏,若是設置爲1,則在列表和列表項結構體中包含一個已知值。打算使用這個已知值輔助調試。若是這個值被覆寫,則極可能是應用程序代碼改寫了內核使用的RAM。
  • 全部Cortex-M移植層接口都包含configASSERT(),用於測試中斷控制寄存器最低5位,用來防止在中斷服務程序中調用taskENTER_CRITICAL(),如今,改成測試中斷控制寄存器的最低8位。
  • 新增API函數uxTaskPriorityGetFromISR()。
  • Cortex-A5 GIC-less移植層接口再也不向中斷處理服務函數傳遞中斷外設地址。
  • 修復一個問題。問題出如今FreeRTOS-MPU中,當刪除任務時,會試圖釋放屬於任務的堆棧,即便這個堆棧是使用靜態方法分配的。
  • 任務狀態信息格式化函數如今會使用空格填充任務名字,確保正確的列對齊,即便任務名字很長也會得體的對齊。
  • 更新FreeRTOS+跟蹤記錄庫的版本爲2.7.0

       演示例程更新:

  • 增長兩個演示例程:IntSemTest和TaskNotify。前者演示並測試在中斷中使用互斥量,後者測試任務通知。
  • 增長Atmel SAMA5D4(Cortex-A5 MPU)移植層接口和演示例程。
  • 增長Altera Cyclone V(Cortex-A9 MPU)演示例程。
  • 更新Zynq演示例程,使用2014.04月的Xilinx's SDK版本,而且將RTOS新特性增長到演示例程任務。
  • 更新Atmel SAM4E和SAM4S演示例程,包含不少額外的測試和演示任務。
  • 修復一個極端狀況下出現的問題。該問題會致使Atmel SAM4L執行低功耗tickless代碼。
  • 修改中斷隊列測試,使之更能容忍CPU負載變化。
  • 更新MSVC FreeRTOS模擬器演示例程,包含最新標準的測試和演示任務。
  • 更新MingW/Eclipse FreeRTOS模擬器演示例程,使之匹配FreeRTOS MSVC模擬器演示例程。
  • 更新全部使用FreeRTOS+跟蹤代碼的演示例程,使用最新的跟蹤記錄代碼。

FreeRTOS V8.1.2相對於FreeRTOS V8.1.1更新記錄:

FreeRTOSV8.1.2發行於2014.09.02,推薦官方網站下載。考慮到官方網站的服務器在美國,下載速度較慢,下面給出CSDN下載地址:點擊下載 ,下載須要1個資源分,請原諒博主須要資源分增長上傳權限。

  • 在個別移植層接口代碼中,默認打開configUSE_PORT_OPTIMISED_TASK_SELECTION,以容許使用硬件特殊指令加快任務切換(好比Cortex-M硬件接口)。

FreeRTOS V8.1.1相對於FreeRTOS V8.1.0更新記錄:

FreeRTOSV8.1.1發行於2014.08.29,推薦官方網站下載。考慮到官方網站的服務器在美國,下載速度較慢,下面給出CSDN下載地址:點擊下載 ,下載須要1個資源分,請原諒博主須要資源分增長上傳權限。

  • 按照你們要求,小幅修改V8.1.0代碼,使得在中斷服務程序中能夠釋放互斥類型信號量(帶有優先級繼承)。
相關文章
相關標籤/搜索