日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
FreeRTOS及其應(yīng)用,萬(wàn)字長(zhǎng)文,基礎(chǔ)入門

嵌入式系統(tǒng)不只是ARM+Linux,不是只有安卓,凡是電子產(chǎn)品都可稱為嵌入式系統(tǒng)。物聯(lián)網(wǎng)行業(yè)的興起,也提升了FreeRTOS市場(chǎng)占有率。本文就是介紹FreeRTOS基礎(chǔ)及其應(yīng)用,只是個(gè)人整理,可能存在問(wèn)題,其目的只是簡(jiǎn)要介紹系統(tǒng)的基礎(chǔ),只能作為入門資料。

目錄

  • 一、 為什么要學(xué)習(xí)RTOS
  • 二、 操作系統(tǒng)基礎(chǔ)
  • 三、 初識(shí) FreeRTOS
  • 四、 任務(wù)
  • 五、 隊(duì)列
  • 六、 軟件定時(shí)器
  • 七、 信號(hào)量
  • 八、 事件
  • 九、 任務(wù)通知
  • 十、 內(nèi)存管理
  • 十一、 通用接口

一、 為什么要學(xué)習(xí) RTOS

進(jìn)入嵌入式這個(gè)領(lǐng)域,入門首先接觸的是單片機(jī)編程,尤其是C51 單片機(jī)來(lái),基礎(chǔ)的單片機(jī)編程通常都是指裸機(jī)編程,即不加入任何 RTOS(Real Time Operating System 實(shí)時(shí)操作系統(tǒng))。常用的有國(guó)外的FreeRTOS、μC/OS、RTX 和國(guó)內(nèi)的 RT-thread、Huawei LiteOS 和 AliOS-Things 等,其中開(kāi)源且免費(fèi)的 FreeRTOS 的市場(chǎng)占有率較高。

1.1 前后臺(tái)系統(tǒng)

在裸機(jī)系統(tǒng)中,所有的操作都是在一個(gè)無(wú)限的大循環(huán)里面實(shí)現(xiàn),支持中斷檢測(cè)。外部中斷緊急事件在中斷里面標(biāo)記或者響應(yīng),中斷服務(wù)稱為前臺(tái),main 函數(shù)里面的while(1)無(wú)限循環(huán)稱為后臺(tái),按順序處理業(yè)務(wù)功能,以及中斷標(biāo)記的可執(zhí)行的事件。小型的電子產(chǎn)品用的都是裸機(jī)系統(tǒng),而且也能夠滿足需求。

1.2 多任務(wù)系統(tǒng)

多任務(wù)系統(tǒng)的事件響應(yīng)也是在中斷中完成的,但是事件的處理是在任務(wù)中完成的。如果事件對(duì)應(yīng)的任務(wù)的優(yōu)先級(jí)足夠高,中斷對(duì)應(yīng)的事件會(huì)立刻執(zhí)行。相比前后臺(tái)系統(tǒng),多任務(wù)系統(tǒng)的實(shí)時(shí)性又被提高了。

在多任務(wù)系統(tǒng)中,根據(jù)程序的功能,把這個(gè)程序主體分割成一個(gè)個(gè)獨(dú)立的,無(wú)限循環(huán)且不能返回的子程序,稱之為任務(wù)。每個(gè)任務(wù)都是獨(dú)立的,互不干擾的,且具備自身的優(yōu)先級(jí),它由操作系統(tǒng)調(diào)度管理。加入操作系統(tǒng)后,開(kāi)發(fā)人員不需要關(guān)注每個(gè)功能模塊之間的沖突,重心放在子程序的實(shí)現(xiàn)。缺點(diǎn)是整個(gè)系統(tǒng)隨之帶來(lái)的額外RAM開(kāi)銷,但對(duì)目前的單片機(jī)的來(lái)影響不大。

1.3 學(xué)習(xí)RTOS的意義

學(xué)習(xí) RTOS,一是項(xiàng)目需要,隨著產(chǎn)品要實(shí)現(xiàn)的功能越來(lái)越多,單純的裸機(jī)系統(tǒng)已經(jīng)不能完美地解決問(wèn)題,反而會(huì)使編程變得更加復(fù)雜,如果想降低編程的難度,就必須引入 RTOS實(shí)現(xiàn)多任務(wù)管理。二是技能需要,掌握操作系統(tǒng),和基于RTOS的編程,實(shí)現(xiàn)更好的職業(yè)規(guī)劃,對(duì)個(gè)人發(fā)展尤其是錢途是必不可少的。

以前一直覺(jué)得學(xué)操作系統(tǒng)就必須是linux,實(shí)際每個(gè)系統(tǒng)都有其應(yīng)用場(chǎng)景,對(duì)于物聯(lián)網(wǎng)行業(yè),殺雞焉用牛刀,小而美,且應(yīng)用廣泛的FreeRTOS 是首選。有一個(gè)操作系統(tǒng)的基礎(chǔ),即使后續(xù)基于其他系統(tǒng)開(kāi)發(fā)軟件,也可觸類旁通,對(duì)新技術(shù)快速入門。目前接觸的幾款芯片都是基于FreeRTOS。

如何學(xué)習(xí)RTOS?最簡(jiǎn)單的就是在別人移植好的系統(tǒng)之上,看看 RTOS 里面的 API 使用說(shuō)明,然后調(diào)用這些 API 實(shí)現(xiàn)自己想要的功能即可。完全不用關(guān)心底層的移植,這是最簡(jiǎn)單快速的入門方法。這種學(xué)習(xí)方式,如果是做產(chǎn)品,可以快速的實(shí)現(xiàn)功能,弊端是當(dāng)程序出現(xiàn)問(wèn)題的時(shí)候,如果對(duì)RTOS不夠了解,會(huì)導(dǎo)致調(diào)試?yán)щy,無(wú)從下手。

各種RTOS內(nèi)核實(shí)現(xiàn)方式都差不多,我們只需要深入學(xué)習(xí)其中一款就行。萬(wàn)變不離其宗,正如掌握了C51基礎(chǔ),后續(xù)換其他型號(hào)或者更高級(jí)的ARM單片機(jī),在原理和方法上,都是有借鑒意義,可以比較快的熟悉并掌握新單片機(jī)的使用。

二、 操作系統(tǒng)基礎(chǔ)

2.1 鏈表

鏈表作為 C 語(yǔ)言中一種基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu),在平時(shí)寫(xiě)程序的時(shí)候用的并不多,但在操作系統(tǒng)里面使用的非常多。FreeRTOS 中存在著大量的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)鏈表和鏈表項(xiàng)的操作(list 和 list item)。FreeRTOS 中與鏈表相關(guān)的操作均在 list.h 和 list.c 這兩個(gè)文件中實(shí)現(xiàn)。

鏈表比數(shù)組,最大優(yōu)勢(shì)是占用的內(nèi)存空間可以隨著需求擴(kuò)大或縮小,動(dòng)態(tài)調(diào)整。實(shí)際FreeRTOS中各種任務(wù)的記錄都是依靠鏈表動(dòng)態(tài)管理,具體的可以參考源碼的任務(wù)控制塊tskTCB。任務(wù)切換狀態(tài),就是將對(duì)應(yīng)的鏈表進(jìn)行操作,鏈表操作涉及創(chuàng)建和插入、刪除和查找。

2.2 隊(duì)列

隊(duì)列是一種只允許在表的前端(front)進(jìn)行刪除操作,而在表的后端(rear)進(jìn)行插入操作。隊(duì)尾放入數(shù)據(jù),對(duì)頭擠出。先進(jìn)先出,稱為FIFO

2.3 任務(wù)

在裸機(jī)系統(tǒng)中,系統(tǒng)的主體就是 main 函數(shù)里面順序執(zhí)行的無(wú)限循環(huán),這個(gè)無(wú)限循環(huán)里面 CPU 按照順序完成各種事情。在多任務(wù)系統(tǒng)中,根據(jù)功能的不同,把整個(gè)系統(tǒng)分割成一個(gè)個(gè)獨(dú)立的且無(wú)法返回的函數(shù),這個(gè)函數(shù)我們稱為任務(wù)。系統(tǒng)中的每一任務(wù)都有多種運(yùn)行狀態(tài)。系統(tǒng)初始化完成后,創(chuàng)建的任務(wù)就可以在系統(tǒng)中競(jìng)爭(zhēng)一定的資源,由內(nèi)核進(jìn)行調(diào)度。

  • ? 就緒(Ready):該任務(wù)在就緒列表中,就緒的任務(wù)已經(jīng)具備執(zhí)行的能力,只等待調(diào)度器進(jìn)行調(diào)度,新創(chuàng)建的任務(wù)會(huì)初始化為就緒態(tài)。
  • ? 運(yùn)行(Running):該狀態(tài)表明任務(wù)正在執(zhí)行,此時(shí)它占用處理器,調(diào)度器選擇運(yùn)行的永遠(yuǎn)是處于最高優(yōu)先級(jí)的就緒態(tài)任務(wù)。
  • ? 阻塞(Blocked):任務(wù)當(dāng)前正在等待某個(gè)事件,比如信號(hào)量或外部中斷。
  • ? 掛起態(tài)(Suspended):處于掛起態(tài)的任務(wù)對(duì)調(diào)度器而言是不可見(jiàn)的。

掛起態(tài)與阻塞態(tài)的區(qū)別,當(dāng)任務(wù)有較長(zhǎng)的時(shí)間不允許運(yùn)行的時(shí)候,我們可以掛起任務(wù),這樣子調(diào)度器就不會(huì)管這個(gè)任務(wù)的任何信息,直到調(diào)用恢復(fù)任務(wù)的 接口;而任務(wù)處于阻塞態(tài)的時(shí)候,系統(tǒng)還需要判斷阻塞態(tài)的任務(wù)是否超時(shí),是否可以解除阻塞。

各任務(wù)運(yùn)行時(shí)使用消息、信號(hào)量等方式進(jìn)行通信,不能是全局變量。任務(wù)通常會(huì)運(yùn)行在一個(gè)死循環(huán)中,不會(huì)退出,如果不再需要,可以調(diào)用刪除任務(wù)。

2.4 臨界區(qū)

臨界區(qū)就是一段在執(zhí)行的時(shí)候不能被中斷的代碼段。在多任務(wù)操作系統(tǒng)里面,對(duì)全局變量的操作不能被打斷,不能執(zhí)行到一半就被其他任務(wù)再次操作。一般被打斷,原因就是系統(tǒng)調(diào)度或外部中斷。對(duì)臨界區(qū)的保護(hù)控制,歸根到底就是對(duì)系統(tǒng)中斷的使能控制。在使用臨界區(qū)時(shí),關(guān)閉中斷響應(yīng),對(duì)部分優(yōu)先級(jí)的中斷進(jìn)行屏蔽,因此臨界區(qū)不允許運(yùn)行時(shí)間過(guò)長(zhǎng)。為了對(duì)臨界區(qū)進(jìn)行控制,就需要使用信號(hào)量通信,實(shí)現(xiàn)同步或互斥操作。

三、 初識(shí) FreeRTOS

3.1 FreeRTOS源碼

FreeRTOS 由美國(guó)的 Richard Barry 于 2003 年發(fā)布, 2018 年被亞馬遜收購(gòu),改名為 AWS FreeRTOS,版本號(hào)升級(jí)為 V10,支持MIT開(kāi)源協(xié)議,亞馬遜收購(gòu) FreeRTOS 也是為了進(jìn)入物聯(lián)網(wǎng)和人工智能,新版本增加了物聯(lián)網(wǎng)行業(yè)的網(wǎng)絡(luò)協(xié)議等功能。

FreeRTOS 是開(kāi)源免費(fèi)的,可從官網(wǎng) www.freertos.org 下載源碼和說(shuō)明手冊(cè)。例如展銳的UIS8910使用的是V10。以FreeRTOSv10.4.1為例,包含 Demo 例程,Source內(nèi)核的源碼,License許可文件。

3.1.1 Source 文件夾

FreeRTOS/ Source 文件夾下的文件:

包括FreeRTOS 的通用的頭文件include和 C 文件,包括任務(wù)、隊(duì)列、定時(shí)器等,適用于各種編譯器和處理器,是通用的。

需要特殊處理適配的在portblle文件夾,其下內(nèi)容與編譯器和處理器相關(guān), FreeRTOS 要想運(yùn)行在一個(gè)單片機(jī)上面,它們就必須關(guān)聯(lián)在一起,通常由匯編和 C 聯(lián)合編寫(xiě)。通常難度比較高,不過(guò)一般芯片原廠提供移植好的接口文件。這里不介紹移植的方法,因?yàn)樽约阂膊幻靼住?/p>

Portblle/MemMang 文件夾下存放的是跟內(nèi)存管理相關(guān)的,總共有五個(gè) heap 文件,有5種內(nèi)存動(dòng)態(tài)分配方式,一般物聯(lián)網(wǎng)產(chǎn)品選用 heap4.c 。

3.1.2 Demo 文件夾

里面包含了 FreeRTOS 官方為各個(gè)單片機(jī)移植好的工程代碼,F(xiàn)reeRTOS 為了推廣自己,會(huì)給針對(duì)不同半導(dǎo)體廠商的評(píng)估板實(shí)現(xiàn)基礎(chǔ)功能范例, Demo下就是參考范例。

3.1.3 FreeRTOSConfig.h配置

FreeRTOSConfig.h頭文件對(duì)FreeRTOS 所需的功能的宏均做了定義,需要根據(jù)應(yīng)用情況配置合適的參數(shù),其作用類似MTK功能機(jī)平臺(tái)的主mak文件,部分定義如下:

 
 
 
 
  1.  #define configUSE_PREEMPTION            1   
  2.  #define configUSE_IDLE_HOOK             0   
  3.  #define configUSE_TICK_HOOK             0   
  4. #define configCPU_CLOCK_HZ              ( SystemCoreClock )   
  5. #define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )   

例如系統(tǒng)時(shí)鐘tick等參數(shù)在就這個(gè)文件配置,具體作用可以看注釋。一般情況下使用SDK不需要改動(dòng),特殊情況下咨詢?cè)瓘S再調(diào)整。

3.2 FreeRTOS 編碼規(guī)范

接觸一個(gè)新平臺(tái)或者SDK,明白它的編碼規(guī)范,文件作用,可以提高源碼閱讀效率,快速熟悉其內(nèi)部實(shí)現(xiàn)。

3.2.1 數(shù)據(jù)類型

FreeRTOS針對(duì)不同的處理器,對(duì)標(biāo)準(zhǔn)C的數(shù)據(jù)類型進(jìn)行了重定義。

 
 
 
 
  1. #define portCHAR        char   
  2. #define portFLOAT       float   
  3. #define portDOUBLE      double   
  4. #define portLONG        long   
  5. #define portSHORT       short   
  6. #define portSTACK_TYPE  uint32_t   
  7. #define portBASE_TYPE   long   

應(yīng)用編碼中,推薦使用的是下面這種風(fēng)格。

 
 
 
 
  1.  typedef int int32_t;   
  2.  typedef short int16_t;   
  3.  typedef char int8_t;   
  4.  typedef unsigned int uint32_t;   
  5.  typedef unsigned short uint16_t;   
  6.  typedef unsigned char uint8_t; 

3.2.2 變量名

FreeRTOS 中,定義變量的時(shí)候往往會(huì)把變量的類型當(dāng)作前綴,好處看到就知道其類型。

char 型變量的前綴是 c

short 型變量的前綴是 s

long 型變量的前綴是 l

復(fù)雜的結(jié)構(gòu)體,句柄等定義的變量名的前綴是 x

變量是無(wú)符號(hào)型的再加前綴 u,是指針變量則加前綴 p

3.2.3 函數(shù)名

函數(shù)名包含了函數(shù)返回值的類型、函數(shù)所在的文件名和函數(shù)的功能,如果是私有的函數(shù)則會(huì)加一個(gè) prv(private)的前綴。

例如vTaskPrioritySet()函數(shù)的返回值為 void 型,在 task.c 這個(gè)文件中定義。

3.2.4 宏

宏內(nèi)容是由大寫(xiě)字母表示,前綴是小寫(xiě)字母,表示該宏在哪個(gè)頭文件定義,如:

 
 
 
 
  1. #define taskYIELD()                 portYIELD()   

表示該宏是在task.h。

3.2.5 個(gè)人解讀

1、編碼不缺編碼規(guī)范,但是實(shí)際使用中很難完全依照標(biāo)準(zhǔn)執(zhí)行,即使freeRTOS源碼也是如此。

2、關(guān)于函數(shù)或者宏定義中帶文件名的作用,使用Source Insight 編輯代碼,該前綴的意義不大。

3、規(guī)則是活的,只要所有人都按一個(gè)規(guī)則執(zhí)行,它就是標(biāo)準(zhǔn)。

3.3 FreeRTOS應(yīng)用開(kāi)發(fā)

關(guān)于freeRTOS的應(yīng)用開(kāi)發(fā),主要是任務(wù)的創(chuàng)建和調(diào)度,任務(wù)間的通信與同步,涉及隊(duì)列、信號(hào)量等操作系統(tǒng)通用接口。結(jié)合應(yīng)用需求,涉及定時(shí)器、延時(shí)、中斷控制等接口。

特別說(shuō)明,有些功能的實(shí)現(xiàn)方式有多種形式,只針對(duì)常用方式進(jìn)行說(shuō)明,例如task的創(chuàng)建,只說(shuō)明動(dòng)態(tài)創(chuàng)建方式,因?yàn)楹苌偈褂渺o態(tài)方式。

四、 任務(wù)

4.1 創(chuàng)建任務(wù)

xTaskCreate()使用動(dòng)態(tài)內(nèi)存的方式創(chuàng)建一個(gè)任務(wù)。

 
 
 
 
  1. ret = xTaskCreate((TaskFunction_t) master_task_main,  /* 任務(wù)入口函數(shù) */(1) 
  2.                    “MASTER”,   /* 任務(wù)名字 */(2) 
  3.                    64*1024,   /* 任務(wù)棧大小 */(3) 
  4.                    NULL,    ,/* 任務(wù)入口函數(shù)參數(shù) */(4) 
  5.                    TASK_PRIORITY_NORMAL,  /* 任務(wù)的優(yōu)先級(jí) */(5) 
  6.                    &task_master_handler);  /* 任務(wù)控制塊指針 */(6) 

創(chuàng)建任務(wù)就是軟件運(yùn)行時(shí)的一個(gè)while(1)的入口,一般閱讀其他代碼,找到這個(gè)函數(shù),再跟蹤到任務(wù)入口函數(shù),學(xué)習(xí)基于freeRTOS系統(tǒng)的代碼,首先就是找到main和這個(gè)接口。

(1):任務(wù)入口函數(shù),即任務(wù)函數(shù)的名稱,需要我們自己定義并且實(shí)現(xiàn)。

(2):任務(wù)名字,字符串形式,最大長(zhǎng)度由 FreeRTOSConfig.h 中定義的 configMAX_TASK_NAME_LEN 宏指定,多余部分會(huì)被自動(dòng)截掉,只是方便調(diào)試。

(3):任務(wù)堆棧大小,單位為字, 4 個(gè)字節(jié),這個(gè)要注意,否則系統(tǒng)內(nèi)存緊缺。

(4):任務(wù)入口函數(shù)形參,不用的時(shí)候配置為 0 或者NULL 即可。

(5) :任務(wù)的優(yōu)先級(jí),在 FreeRTOS 中,數(shù)值越大優(yōu)先級(jí)越高,0 代表最低優(yōu)先級(jí)。基于其SDK開(kāi)發(fā),可將自定義的所有業(yè)務(wù)功能task設(shè)為同一個(gè)優(yōu)先級(jí),按時(shí)間片輪詢調(diào)度。

(6):任務(wù)控制塊指針,使用動(dòng)態(tài)內(nèi)存的時(shí)候,任務(wù)創(chuàng)建函數(shù) xTaskCreate()會(huì)返回一個(gè)指針指向任務(wù)控制塊,也可以設(shè)為NULL,因?yàn)槿蝿?wù)句柄后期可以不使用。

4.2 開(kāi)啟調(diào)度

當(dāng)任務(wù)創(chuàng)建成功后處于就緒狀態(tài)(Ready),在就緒態(tài)的任務(wù)可以參與操作系統(tǒng)的調(diào)度。操作系統(tǒng)任務(wù)調(diào)度器只啟動(dòng)一次,之后就不會(huì)再次執(zhí)行了,F(xiàn)reeRTOS 中啟動(dòng)任務(wù)調(diào)度器的函數(shù)是 vTaskStartScheduler(),并且啟動(dòng)任務(wù)調(diào)度器的時(shí)候就不會(huì)返回,從此任務(wù)管理都由FreeRTOS 管理,此時(shí)才是真正進(jìn)入實(shí)時(shí)操作系統(tǒng)中的第一步。

vTaskStartScheduler開(kāi)啟調(diào)度時(shí),順便會(huì)創(chuàng)建空閑任務(wù)和定時(shí)器任務(wù)。

FreeRTOS 為了任務(wù)啟動(dòng)和任務(wù)切換使用了三個(gè)異常:SVC、PendSV 和SysTick。

SVC(系統(tǒng)服務(wù)調(diào)用,亦簡(jiǎn)稱系統(tǒng)調(diào)用)用于任務(wù)啟動(dòng)。

PendSV(可掛起系統(tǒng)調(diào)用)用于完成任務(wù)切換,它是可以像普通的中斷一樣被掛起的,它的最大特性是如果當(dāng)前有優(yōu)先級(jí)比它高的中斷在運(yùn)行,PendSV會(huì)延遲執(zhí)行,直到高優(yōu)先級(jí)中斷執(zhí)行完畢,這樣產(chǎn)生的PendSV 中斷就不會(huì)打斷其他中斷的運(yùn)行。

SysTick 用于產(chǎn)生系統(tǒng)節(jié)拍時(shí)鐘,提供一個(gè)時(shí)間片,如果多個(gè)任務(wù)共享同一個(gè)優(yōu)先級(jí),則每次 SysTick 中斷,下一個(gè)任務(wù)將獲得一個(gè)時(shí)間片。

FreeRTOS 中的任務(wù)是搶占式調(diào)度機(jī)制,高優(yōu)先級(jí)的任務(wù)可打斷低優(yōu)先級(jí)任務(wù),低優(yōu)先級(jí)任務(wù)必須在高優(yōu)先級(jí)任務(wù)阻塞或結(jié)束后才能得到調(diào)度。相同優(yōu)先級(jí)的任務(wù)采用時(shí)間片輪轉(zhuǎn)方式進(jìn)行調(diào)度(也就是分時(shí)調(diào)度),時(shí)間片輪轉(zhuǎn)調(diào)度僅在當(dāng)前系統(tǒng)中無(wú)更高優(yōu)先級(jí)就緒任務(wù)存在的情況下才有效。

4.3 啟動(dòng)方式

FreeRTOS有兩種啟動(dòng)方式,效果一樣,看個(gè)人喜好。

第一種:main 函數(shù)中將硬件初始化, RTOS 系統(tǒng)初始化,所有任務(wù)的創(chuàng)建完成,最后一步開(kāi)啟調(diào)度。目前看到的幾個(gè)芯片SDK都是這種方式。

第二種:main 函數(shù)中將硬件和 RTOS 系統(tǒng)先初始化好,只創(chuàng)建一個(gè)任務(wù)后就啟動(dòng)調(diào)度器,然后在這個(gè)任務(wù)里面創(chuàng)建其它應(yīng)用任務(wù),當(dāng)所有任務(wù)都創(chuàng)建成功后,啟動(dòng)任務(wù)再把自己刪除。

4.4 任務(wù)創(chuàng)建源碼分析

xTaskCreate()創(chuàng)建任務(wù)。

 
 
 
 
  1. BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,   
  2.                         const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */   
  3.                         const configSTACK_DEPTH_TYPE usStackDepth,   
  4.                         void * const pvParameters,   
  5.                         UBaseType_t uxPriority,   
  6.                         TaskHandle_t * const pxCreatedTask )   
  7. {   
  8.     TCB_t * pxNewTCB;   
  9.     BaseType_t xReturn;   
  10.     
  11.      /* If the stack grows down then allocate the stack then the TCB so the stack  
  12.       * does not grow into the TCB.  Likewise if the stack grows up then allocate  
  13.       * the TCB then the stack. */   
  14.      #if ( portSTACK_GROWTH > 0 )   
  15.          {   
  16.              /**/ 
  17.          }   
  18.      #else /* portSTACK_GROWTH */   
  19.          {   
  20.              StackType_t * pxStack;   
  21.     
  22.              /* Allocate space for the stack used by the task being created. */   
  23.              pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation is the stack. */   
  24.     
  25.              if( pxStack != NULL )   
  26.              {   
  27.                  /* Allocate space for the TCB. */   
  28.                  pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of TCB_t is always a pointer to the task's stack. */   
  29.     
  30.                  if( pxNewTCB != NULL )   
  31.                  {   
  32.                      /* Store the stack location in the TCB. */   
  33.                      pxNewTCB->pxStack = pxStack;   
  34.                  }   
  35.                  else   
  36.                  {   
  37.                      /* The stack cannot be used as the TCB was not created.  Free  
  38.                       * it again. */   
  39.                      vPortFree( pxStack );   
  40.                  }   
  41.              }   
  42.              else   
  43.              {   
  44.                  pxNewTCB = NULL;   
  45.              }   
  46.          }   
  47.      #endif /* portSTACK_GROWTH */   
  48.     
  49.      if( pxNewTCB != NULL )   
  50.      {   
  51.          #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e9029 !e731 Macro has been consolidated for readability reasons. */   
  52.              {   
  53.                  /* Tasks can be created statically or dynamically, so note this  
  54.                   * task was created dynamically in case it is later deleted. */   
  55.                  pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;   
  56.              }   
  57.          #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */   
  58.     
  59.          prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );   
  60.          prvAddNewTaskToReadyList( pxNewTCB ); //將新任務(wù)加入到就緒鏈表候著 
  61.          xReturn = pdPASS;   
  62.      }   
  63.      else   
  64.      {   
  65.          xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;   
  66.      }   
  67.     
  68.      return xReturn;   
  69.  } 

申請(qǐng)任務(wù)控制塊內(nèi)存,檢查配置參數(shù),初始化,將任務(wù)信息加入到就緒鏈表,等待調(diào)度。前面鏈表部分提到,freeRTOS的任務(wù)信息都是使用鏈表記錄,在task.c有

 
 
 
 
  1. PRIVILEGED_DATA static List_t pxReadyTasksLists[configMAX_PRIORITIES];//就緒 
  2. PRIVILEGED_DATA static List_t xDelayedTaskList1;    //延時(shí) 
  3. PRIVILEGED_DATA static List_t xDelayedTaskList2;  
  4. PRIVILEGED_DATA static List_t xPendingReadyList;  //掛起 
  5. PRIVILEGED_DATA static List_t xSuspendedTaskList;   //阻塞 

分別記錄就緒態(tài)、阻塞態(tài)和掛起的任務(wù),其中阻塞態(tài)有2個(gè),是因?yàn)樘厥饪紤],時(shí)間溢出 的問(wèn)題,實(shí)際開(kāi)發(fā)單片機(jī)項(xiàng)目計(jì)時(shí)超過(guò)24h的可以借鑒。其中pxReadyTasksLists鏈表數(shù)組,其下標(biāo)就是任務(wù)的優(yōu)先級(jí)。

4.5 任務(wù)調(diào)度源碼分析

創(chuàng)建完任務(wù)的時(shí)候,vTaskStartScheduler開(kāi)啟調(diào)度器,空閑任務(wù)、定時(shí)器任務(wù)也是在開(kāi)啟調(diào)度函數(shù)中實(shí)現(xiàn)的。

為什么要空閑任務(wù)?因?yàn)?FreeRTOS一旦啟動(dòng),就必須要保證系統(tǒng)中每時(shí)每刻都有一個(gè)任務(wù)處于運(yùn)行態(tài)(Runing),并且空閑任務(wù)不可以被掛起與刪除,空閑任務(wù)的優(yōu)先級(jí)是最低的,以便系統(tǒng)中其他任務(wù)能隨時(shí)搶占空閑任務(wù)的 CPU 使用權(quán)。這些都是系統(tǒng)必要的東西,也無(wú)需自己實(shí)現(xiàn)。

 
 
 
 
  1. void vTaskStartScheduler( void )   
  2. {   
  3.     BaseType_t xReturn;   
  4.    
  5.     /* Add the idle task at the lowest priority. */   
  6.     #if ( configSUPPORT_STATIC_ALLOCATION == 1 )   
  7.         {   
  8.      /***/ 
  9.         }   
  10.      #else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */   
  11.          {   
  12.              /*創(chuàng)建空閑任務(wù)*/   
  13.              xReturn = xTaskCreate( prvIdleTask,   
  14.                                     configIDLE_TASK_NAME,   
  15.                                     configMINIMAL_STACK_SIZE,   
  16.                                     ( void * ) NULL,   
  17.                                     portPRIVILEGE_BIT,  //優(yōu)先級(jí)為0 
  18.                                     &xIdleTaskHandle );   
  19.          }   
  20.      #endif /* configSUPPORT_STATIC_ALLOCATION */   
  21.     
  22.      #if ( configUSE_TIMERS == 1 )   
  23.          {   
  24.              if( xReturn == pdPASS )   
  25.              {   
  26.                  //創(chuàng)建定時(shí)器task,接收開(kāi)始、結(jié)束定時(shí)器等命令 
  27.                  xReturn = xTimerCreateTimerTask();  
  28.              }   
  29.              else   
  30.              {   
  31.                  mtCOVERAGE_TEST_MARKER();   
  32.              }   
  33.          }   
  34.      #endif /* configUSE_TIMERS */   
  35.     
  36.      if( xReturn == pdPASS )   
  37.      {   
  38.          /* freertos_tasks_c_additions_init() should only be called if the user  
  39.           * definable macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is  
  40.           * the only macro called by the function. */   
  41.          #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT   
  42.              {   
  43.                  freertos_tasks_c_additions_init();   
  44.              }   
  45.          #endif   
  46.     
  47.          portDISABLE_INTERRUPTS();   
  48.     
  49.          #if ( configUSE_NEWLIB_REENTRANT == 1 )   
  50.              {   
  51.                  _impure_ptr = &( pxCurrentTCB->xNewLib_reent );   
  52.              }   
  53.          #endif /* configUSE_NEWLIB_REENTRANT */   
  54.     
  55.          xNextTaskUnblockTime = portMAX_DELAY;   
  56.          xSchedulerRunning = pdTRUE;   
  57.          xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;   
  58.     
  59.          portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();   
  60.     
  61.          traceTASK_SWITCHED_IN();   
  62.     
  63.          /* Setting up the timer tick is hardware specific and thus in the  
  64.           * portable interface. */   
  65.          if( xPortStartScheduler() != pdFALSE )   
  66.          {   
  67.              /* 系統(tǒng)開(kāi)始運(yùn)行 */   
  68.          }   
  69.          else   
  70.          {   
  71.              /* Should only reach here if a task calls xTaskEndScheduler(). */   
  72.          }   
  73.      }   
  74.      else   
  75.      {   
  76.         /*****/ 
  77.  }  

4.6 任務(wù)狀態(tài)切換

FreeRTOS 系統(tǒng)中的每一個(gè)任務(wù)都有多種運(yùn)行狀態(tài),具體如下:

任務(wù)掛起函數(shù)

 
 
 
 
  1. vTaskSuspend() 

掛起指定任務(wù),被掛起的任務(wù)絕不會(huì)得到 CPU 的使用權(quán)

 
 
 
 
  1. vTaskSuspendAll() 

將所有的任務(wù)都掛起 ? 任務(wù)恢復(fù)函數(shù)

 
 
 
 
  1. vTaskResume() 
  2. vTaskResume() 
  3. xTaskResumeFromISR() 

任務(wù)恢復(fù)就是讓掛起的任務(wù)重新進(jìn)入就緒狀態(tài),恢復(fù)的任務(wù)會(huì)保留掛起前的狀態(tài)信息,在恢復(fù)的時(shí)候根據(jù)掛起時(shí)的狀態(tài)繼續(xù)運(yùn)行。xTaskResumeFromISR() 專門用在中斷服務(wù)程序中。無(wú)論通過(guò)調(diào)用一次或多次vTaskSuspend()函數(shù)而被掛起的任務(wù),也只需調(diào)用一次恢復(fù)即可解掛 。

? 任務(wù)刪除函數(shù) vTaskDelete()用于刪除任務(wù)。當(dāng)一個(gè)任務(wù)可以刪除另外一個(gè)任務(wù),形參為要?jiǎng)h除任 務(wù)創(chuàng)建時(shí)返回的任務(wù)句柄,如果是刪除自身, 則形參為 NULL。

4.7 任務(wù)使用注意點(diǎn)

1、中斷服務(wù)函數(shù)是不允許調(diào)用任何會(huì)阻塞運(yùn)行的接口。一般在中斷服務(wù)函數(shù)中只做標(biāo)記事件的發(fā)生,然后通知任務(wù),讓對(duì)應(yīng)任務(wù)去執(zhí)行相關(guān)處理 。

2、將緊急的處理事件的任務(wù)優(yōu)先級(jí)設(shè)置偏高一些。

3、空閑任務(wù)(idle 任務(wù))是 FreeRTOS 系統(tǒng)中沒(méi)有其他工作進(jìn)行時(shí)自動(dòng)進(jìn)入的系統(tǒng)任務(wù),永遠(yuǎn)不會(huì)掛起空閑任務(wù),不應(yīng)該陷入死循環(huán)。

4、創(chuàng)建任務(wù)使用的內(nèi)存不要過(guò)多,按需申請(qǐng)。如果浪費(fèi)太多,后續(xù)應(yīng)用申請(qǐng)大空間可能提示內(nèi)存不足。

五、 隊(duì)列

5.1 隊(duì)列的概念

隊(duì)列用于任務(wù)間通信的數(shù)據(jù)結(jié)構(gòu),通過(guò)消息隊(duì)列服務(wù),任務(wù)或中斷服務(wù)將消息放入消息隊(duì)列中。其他任務(wù)或者自身從消息隊(duì)列中獲得消息。實(shí)現(xiàn)隊(duì)列可以在任務(wù)與任務(wù)間、中斷和任務(wù)間傳遞信息。隊(duì)列操作支持阻塞等待,向已經(jīng)填滿的隊(duì)列發(fā)送數(shù)據(jù)或者從空隊(duì)列讀出數(shù)據(jù),都會(huì)導(dǎo)致阻塞,時(shí)間自定義。消息隊(duì)列的運(yùn)作過(guò)程具如下:

5.2 隊(duì)列創(chuàng)建

xQueueCreate()用于創(chuàng)建一個(gè)新的隊(duì)列并返回可用于訪問(wèn)這個(gè)隊(duì)列的句柄。隊(duì)列句柄其實(shí)就是一個(gè)指向隊(duì)列數(shù)據(jù)結(jié)構(gòu)類型的指針。

 
 
 
 
  1. master_queue = xQueueCreate(50, sizeof(task_message_struct_t));   

創(chuàng)建隊(duì)列,占用50個(gè)單元,每個(gè)單元為sizeof(task_message_struct_t)字節(jié),和 malloc比較類似。其最終使用的函數(shù)是 xQueueGenericCreate(),后續(xù)信號(hào)量等也是使用它創(chuàng)建,只是最后的隊(duì)列類型不同。

申請(qǐng)內(nèi)存后,xQueueGenericReset再對(duì)其進(jìn)行初始化,隊(duì)列的結(jié)構(gòu)體xQUEUE成員:

 
 
 
 
  1. typedef struct QueueDefinition /* The old naming convention is used to prevent breaking kernel aware debuggers. */   
  2. {   
  3.     int8_t * pcHead;           /*< Points to the beginning of the queue storage area. */   
  4.     int8_t * pcWriteTo;        /*< Points to the free next place in the storage area. */   
  5.     //類型 
  6.     union   
  7.     {   
  8.         QueuePointers_t xQueue;     /*< Data required exclusively when this structure is used as a queue. */   
  9.         SemaphoreData_t xSemaphore; /*< Data required exclusively when this structure is used as a semaphore. */   
  10.      } u;   
  11.     
  12.      //當(dāng)前向隊(duì)列寫(xiě)數(shù)據(jù)阻塞的任務(wù)列表或者從隊(duì)列取數(shù)阻塞的鏈表 
  13.      List_t xTasksWaitingToSend;   
  14.      List_t xTasksWaitingToReceive;    
  15.     
  16.      //隊(duì)列里有多少個(gè)單元被占用,應(yīng)用中需要 
  17.      volatile UBaseType_t uxMessagesWaiting;  
  18.   
  19.      UBaseType_t uxLength;                   /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */   
  20.      UBaseType_t uxItemSize;                 /*< The size of each items that the queue will hold. */   
  21.     
  22.   /******/ 
  23.  } xQUEUE;   

5.3 隊(duì)列刪除

隊(duì)列刪除函數(shù) vQueueDelete()需傳入要?jiǎng)h除的消息隊(duì)列的句柄即可,刪除之后這個(gè)消息隊(duì)列的所有信息都會(huì)被系統(tǒng)回收清空,而且不能再次使用這個(gè)消息隊(duì)列了。實(shí)際應(yīng)用中很少使用。

5.4 向隊(duì)列發(fā)送消息

任務(wù)或者中斷服務(wù)程序都可以給消息隊(duì)列發(fā)送消息,當(dāng)發(fā)送消息時(shí),如果隊(duì)列未滿或者允許覆蓋入隊(duì),F(xiàn)reeRTOS 會(huì)將消息拷貝到消息隊(duì)列隊(duì)尾,否則,會(huì)根據(jù)用戶指定的超時(shí)時(shí)間進(jìn)行阻塞,消息發(fā)送接口很多,最簡(jiǎn)單的是 xQueueSend(),用于向隊(duì)列尾部發(fā)送一個(gè)隊(duì)列消息。消息以拷貝的形式入隊(duì),該函數(shù)絕對(duì)不能在中斷服務(wù)程序里面被調(diào)用,中斷中必須使用帶有中斷保護(hù)功能的 xQueueSendFromISR()來(lái)代替。

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

用于向隊(duì)列尾部發(fā)送一個(gè)隊(duì)列消息。

參數(shù)

xQueue 隊(duì)列句柄

pvItemToQueue 指針,指向要發(fā)送到隊(duì)列尾部的隊(duì)列消息。

xTicksToWait 隊(duì)列滿時(shí),等待隊(duì)列空閑的最大超時(shí)時(shí)間。如果隊(duì)列滿并且xTicksToWait 被設(shè)置成 0,函數(shù)立刻返回。超時(shí)時(shí)間的單位為系統(tǒng)節(jié)拍周期 tick,延時(shí)為 portMAX_DELAY 將導(dǎo)致任務(wù)掛起(沒(méi)有超時(shí))。

返回值

消息發(fā)送成功成功返回 pdTRUE,否則返回 errQUEUE_FULL。

 
 
 
 
  1. BaseType_t xQueueGenericSend( QueueHandle_t xQueue,    
  2.                   const void * const pvItemToQueue,    
  3.                           TickType_t xTicksToWait,    
  4.                   const BaseType_t xCopyPosition )  //發(fā)送數(shù)據(jù)到消息隊(duì)列的位置 

1. BaseType_t xQueueGenericSend( QueueHandle_t xQueue, 2. const void * const pvItemToQueue, 3. TickType_t xTicksToWait, 4. const BaseType_t xCopyPosition ) //發(fā)送數(shù)據(jù)到消息隊(duì)列的位置

一般使用xQueueSend和xQueueSendFromISR,如不確定當(dāng)前運(yùn)行的是系統(tǒng)服務(wù),還是中斷服務(wù),一般ARM都支持查詢中斷狀態(tài)寄存器判斷,可以封裝一層接口,只管發(fā)消息,內(nèi)部判斷是否使用支持中斷嵌套的版本,UIS8910就是如此。特殊情況下,如發(fā)送網(wǎng)絡(luò)數(shù)據(jù)包未收到服務(wù)器響應(yīng),期望立刻入隊(duì)再次發(fā)送它,可以xQueueSendToFront向隊(duì)頭發(fā)消息。

5.5 從隊(duì)列讀取消息

當(dāng)任務(wù)試圖讀隊(duì)列中的消息時(shí),可以指定一個(gè)阻塞超時(shí)時(shí)間,當(dāng)且僅當(dāng)消息隊(duì)列中有消息的時(shí)候,任務(wù)才能讀取到消息。如果隊(duì)列為空,該任務(wù)將保持阻塞狀態(tài)以等待隊(duì)列數(shù)據(jù)有效。當(dāng)其它任務(wù)或中斷服務(wù)程序往其等待的隊(duì)列中寫(xiě)入了數(shù)據(jù),該任務(wù)將自動(dòng)由阻塞態(tài)轉(zhuǎn)為就緒態(tài)。當(dāng)任務(wù)等待的時(shí)間超過(guò)了指定的阻塞時(shí)間,即使隊(duì)列中尚無(wú)有效數(shù)據(jù),任務(wù)也會(huì)自動(dòng)從阻塞態(tài)轉(zhuǎn)移為就緒態(tài)。所有的task主入口while循環(huán)體都是按這個(gè)執(zhí)行。例如:

 
 
 
 
  1. static void track_master_task_main()   
  2. {   
  3.     track_task_message_struct_t queue_item = {0}; 
  4.     /****/ 
  5.    
  6.     while(1)   
  7.     {   
  8.         if(xQueueReceive(master_queue, &queue_item, portMAX_DELAY))//阻塞等待 
  9.         {   
  10.              track_master_task_msg_handler(&queue_item);   
  11.          }   
  12.      }   
  13.  }   

xQueueReceive()用于從一個(gè)隊(duì)列中接收消息并把消息從隊(duì)列中刪除。如果不想刪除消息的話,就調(diào)用 xQueuePeek()函數(shù)。xQueueReceiveFromISR()與xQueuePeekFromISR()是中斷版本,用于在中斷服務(wù)程序中接收一個(gè)隊(duì)列消息并把消息。這兩個(gè)函數(shù)只能用于中斷,是不帶有阻塞機(jī)制的,實(shí)際項(xiàng)目沒(méi)有使用。

5.6 查詢隊(duì)列使用情況

uxQueueMessagesWaiting()查詢隊(duì)列中存儲(chǔ)的信息數(shù)目,具有中斷保護(hù)的版本為uxQueueMessagesWaitingFromISR()。查詢隊(duì)列的空閑數(shù)目uxQueueSpacesAvailable()。

5.7 隊(duì)列使用注意點(diǎn)

使用隊(duì)列函數(shù)需要注意以下幾點(diǎn):

1、中斷中必須使用帶FromISR后綴的接口;

2、發(fā)送或者是接收消息都是以拷貝的方式進(jìn)行,如果消息內(nèi)容過(guò)于龐大,可以將消息的地址作為消息進(jìn)行發(fā)送、接收。

 
 
 
 
  1. typedef struct     
  2. {     
  3.     TaskHandle_t src_mod_id;     
  4.     int message_id;     
  5.     int32_t param;     
  6.     union     
  7.     {     
  8.         int32_t result;     
  9.         int32_t socket_id;     
  10.      };     
  11.      void* pvdata;  //大數(shù)據(jù)使用動(dòng)態(tài)申請(qǐng)內(nèi)存保存,隊(duì)列只傳遞指針   
  12.  } track_task_message_struct_t;    

3、隊(duì)列并不屬于任何任務(wù),所有任務(wù)都可以向同一隊(duì)列寫(xiě)入和讀出,一個(gè)隊(duì)列可以由多任務(wù)或中斷讀寫(xiě)。

4、隊(duì)列的深度要結(jié)合實(shí)際,可以多申請(qǐng)點(diǎn),前提是每個(gè)隊(duì)列單元盡可能小。

5、隊(duì)列存在一定限制,在隊(duì)頭沒(méi)有取出來(lái)之前,是無(wú)法取出第二個(gè),和STL鏈表存在差異。

六、 軟件定時(shí)器

6.1 軟件定時(shí)器的概念

定時(shí)器有硬件定時(shí)器和軟件定時(shí)器之分,硬件定時(shí)器是芯片本身提供的定時(shí)功能精度高,并且是中斷觸發(fā)方式。軟件定時(shí)器是由操作系統(tǒng)封裝的接口,它構(gòu)建在硬件定時(shí)器基礎(chǔ)之上,使系統(tǒng)能夠提供不受硬件定時(shí)器資源限制,其實(shí)現(xiàn)的功能與硬件定時(shí)器也是類似的。

在操作系統(tǒng)中,通常軟件定時(shí)器以系統(tǒng)節(jié)拍周期為計(jì)時(shí)單位。系統(tǒng)節(jié)拍配置為configTICK_RATE_HZ,該宏在 FreeRTOSConfig.h 中,一般是100或者1000。根據(jù)實(shí)際系統(tǒng) CPU 的處理能力和實(shí)時(shí)性需求設(shè)置合適的數(shù)值,系統(tǒng)節(jié)拍周期的值越小,精度越高,但是系統(tǒng)開(kāi)銷也將越大,因?yàn)檫@代表在 1 秒中系統(tǒng)進(jìn)入時(shí)鐘中斷的次數(shù)也就越多。

6.2 軟件定時(shí)器創(chuàng)建

軟件定時(shí)器需先創(chuàng)建才允許使用,動(dòng)態(tài)創(chuàng)建方式是xTimerCreate(),返回一個(gè)句柄。軟件定時(shí)器在創(chuàng)建成功后是處于休眠狀態(tài)的,沒(méi)有開(kāi)始計(jì)時(shí)運(yùn)行。FreeRTOS的軟件定時(shí)器支持單次模式和周期模式。

單次模式:當(dāng)用戶創(chuàng)建了定時(shí)器并啟動(dòng)了定時(shí)器后,定時(shí)時(shí)間到了,只執(zhí)行一次回調(diào)函數(shù),之后不再執(zhí)行。周期模式:定時(shí)器會(huì)按照設(shè)置的定時(shí)時(shí)間循環(huán)執(zhí)行回調(diào)函數(shù),直到用戶將定時(shí)器停止或刪除。

實(shí)際項(xiàng)目中使用這種模式對(duì)單片機(jī)喂狗就比較省事。

 
 
 
 
  1. TimerHandle_t xTimerCreate( const char * const pcTimerName, //定時(shí)器名稱 
  2.                              const TickType_t xTimerPeriodInTicks,  //定時(shí)時(shí)間 
  3.                              const UBaseType_t uxAutoReload,  //是否自動(dòng)重載 
  4.                              void * const pvTimerID,  //回調(diào)函數(shù)的參數(shù) 
  5.                              TimerCallbackFunction_t pxCallbackFunction )  //回調(diào)函數(shù) 

6.3 軟件定時(shí)器開(kāi)啟

新創(chuàng)建的定時(shí)器沒(méi)有開(kāi)始計(jì)時(shí)啟動(dòng),可以使用

 
 
 
 
  1. xTimerStart()、 
  2. xTimerReset()、 
  3. xTimerStartFromISR() 、xTimerResetFromISR()  
  4. xTimerChangePeriod()、xTimerChangePeriodFromISR() 

這些函數(shù)將其狀態(tài)轉(zhuǎn)換為活躍態(tài),開(kāi)始運(yùn)行。區(qū)別:如果定時(shí)器設(shè)定60秒間隔,已經(jīng)運(yùn)行了30秒,reset是將定時(shí)器重置為原來(lái)設(shè)定的時(shí)間間隔,也就是重新開(kāi)始延時(shí)60秒。ChangePeriod重新設(shè)置計(jì)時(shí)周期。

6.4 軟件定時(shí)器停止

xTimerStop() 用于停止一個(gè)已經(jīng)啟動(dòng)的軟件定時(shí)器,xTimerStopFromISR()是中斷版本。

6.5 軟件定時(shí)器刪除

xTimerDelete()用于刪除一個(gè)已經(jīng)被創(chuàng)建成功的軟件定時(shí)器,釋放資源,刪除之后不能再使用。實(shí)際項(xiàng)目中,任務(wù)和隊(duì)列都是按需創(chuàng)建,一直使用,但是定時(shí)器不使用的就應(yīng)該刪除,并且刪除后一定要將句柄置為NULL。

6.6 軟件定時(shí)器源碼分析

軟件定時(shí)器任務(wù)是在系統(tǒng)開(kāi)始調(diào)度的時(shí)候就被創(chuàng)建:vTaskStartScheduler()—xTimerCreateTimerTask。

 
 
 
 
  1. BaseType_t xTimerCreateTimerTask( void )   

  2. 網(wǎng)頁(yè)名稱:FreeRTOS及其應(yīng)用,萬(wàn)字長(zhǎng)文,基礎(chǔ)入門
    標(biāo)題路徑:http://m.5511xx.com/article/djsgejo.html