www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 公眾號精選 > wenzi嵌入式軟件
[導(dǎo)讀]嵌入式系統(tǒng)不只是ARM+Linux,不是只有安卓,凡是電子產(chǎn)品都可稱為嵌入式系統(tǒng)。物聯(lián)網(wǎng)行業(yè)的興起,也提升了FreeRTOS市場占有率。本文就是介紹FreeRTOS基礎(chǔ)及其應(yīng)用,只是個(gè)人整理,可能存在問題,其目的只是簡要介紹系統(tǒng)的基礎(chǔ),只能作為入門資料。

微信公眾號:嵌入式系統(tǒng)

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

目錄

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

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

三、 初識 FreeRTOS

四、 任務(wù)

五、 隊(duì)列 

六、 軟件定時(shí)器

七、 信號量

八、 事件 

九、 任務(wù)通知

十、 內(nèi)存管理

十一、 通用接口

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

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

1.1 前后臺系統(tǒng)

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

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

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

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

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

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

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

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

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

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

2.1 鏈表

鏈表作為 C 語言中一種基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu),在平時(shí)寫程序的時(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)勢是占用的內(nèi)存空間可以隨著需求擴(kuò)大或縮小,動(dòng)態(tài)調(diào)整。實(shí)際FreeRTOS中各種任務(wù)的記錄都是依靠鏈表動(dòng)態(tài)管理,具體的可以參考源碼的任務(wù)控制塊tskTCB。任務(wù)切換狀態(tài),就是將對應(yīng)的鏈表進(jìn)行操作,鏈表操作涉及創(chuàng)建和插入、刪除和查找。

2.2 隊(duì)列

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

2.3 任務(wù)

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

? 運(yùn)行(Running):該狀態(tài)表明任務(wù)正在執(zhí)行,此時(shí)它占用處理器,調(diào)度器選擇運(yùn)行的永遠(yuǎn)是處于最高優(yōu)先級的就緒態(tài)任務(wù)。 

? 阻塞(Blocked):任務(wù)當(dāng)前正在等待某個(gè)事件,比如信號量或外部中斷。 

? 掛起態(tài)(Suspended):處于掛起態(tài)的任務(wù)對調(diào)度器而言是不可見的。

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

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

2.4 臨界區(qū)

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

三、 初識 FreeRTOS

3.1 FreeRTOS源碼

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

FreeRTOS 是開源免費(fèi)的,可從官網(wǎng) www.freertos.org 下載源碼和說明手冊。例如展銳的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)合編寫。通常難度比較高,不過一般芯片原廠提供移植好的接口文件。這里不介紹移植的方法,因?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 為了推廣自己,會給針對不同半導(dǎo)體廠商的評估板實(shí)現(xiàn)基礎(chǔ)功能范例, Demo下就是參考范例。

3.1.3 FreeRTOSConfig.h配置

FreeRTOSConfig.h頭文件對FreeRTOS 所需的功能的宏均做了定義,需要根據(jù)應(yīng)用情況配置合適的參數(shù),其作用類似MTK功能機(jī)平臺的主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),特殊情況下咨詢原廠再調(diào)整。

3.2 FreeRTOS 編碼規(guī)范

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

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

FreeRTOS針對不同的處理器,對標(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í)候往往會把變量的類型當(dāng)作前綴,好處看到就知道其類型。 

char 型變量的前綴是 c 

short 型變量的前綴是 s 

long 型變量的前綴是 l 

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

變量是無符號型的再加前綴 u,是指針變量則加前綴 p

3.2.3 函數(shù)名

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

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

3.2.4 宏

宏內(nèi)容是由大寫字母表示,前綴是小寫字母,表示該宏在哪個(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)用開發(fā)

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

特別說明,有些功能的實(shí)現(xiàn)方式有多種形式,只針對常用方式進(jìn)行說明,例如task的創(chuàng)建,只說明動(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)先級 */(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ù)名字,字符串形式,最大長度由 FreeRTOSConfig.h 中定義的 configMAX_TASK_NAME_LEN 宏指定,多余部分會被自動(dòng)截掉,只是方便調(diào)試。

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

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


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

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

4.2 開啟調(diào)度

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

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

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

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

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

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

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

4.3 啟動(dòng)方式

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

第一種:main 函數(shù)中將硬件初始化, RTOS 系統(tǒng)初始化,所有任務(wù)的創(chuàng)建完成,最后一步開啟調(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. }

申請任務(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í)間溢出 的問題,實(shí)際開發(fā)單片機(jī)項(xiàng)目計(jì)時(shí)超過24h的可以借鑒。其中pxReadyTasksLists鏈表數(shù)組,其下標(biāo)就是任務(wù)的優(yōu)先級。4.5 任務(wù)調(diào)度源碼分析

創(chuàng)建完任務(wù)的時(shí)候,vTaskStartScheduler開啟調(diào)度器,空閑任務(wù)、定時(shí)器任務(wù)也是在開啟調(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)先級是最低的,以便系統(tǒng)中其他任務(wù)能隨時(shí)搶占空閑任務(wù)的 CPU 使用權(quán)。這些都是系統(tǒng)必要的東西,也無需自己實(shí)現(xiàn)。

1. void vTaskStartSchedulervoid )  
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)先級為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,接收開始、結(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)開始運(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ù)

vTaskSuspend()

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

vTaskSuspendAll()

將所有的任務(wù)都掛起

? 任務(wù)恢復(fù)函數(shù)

vTaskResume()
vTaskResume()
xTaskResumeFromISR()

任務(wù)恢復(fù)就是讓掛起的任務(wù)重新進(jìn)入就緒狀態(tài),恢復(fù)的任務(wù)會保留掛起前的狀態(tài)信息,在恢復(fù)的時(shí)候根據(jù)掛起時(shí)的狀態(tài)繼續(xù)運(yùn)行。xTaskResumeFromISR() 專門用在中斷服務(wù)程序中。無論通過調(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)用任何會阻塞運(yùn)行的接口。一般在中斷服務(wù)函數(shù)中只做標(biāo)記事件的發(fā)生,然后通知任務(wù),讓對應(yīng)任務(wù)去執(zhí)行相關(guān)處理 。

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

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

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

五、 隊(duì)列

5.1 隊(duì)列的概念

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

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

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

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

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

申請內(nèi)存后,xQueueGenericReset再對其進(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ì)列寫數(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ì)列的所有信息都會被系統(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 會將消息拷貝到消息隊(duì)列隊(duì)尾,否則,會根據(jù)用戶指定的超時(shí)時(shí)間進(jìn)行阻塞,消息發(fā)送接口很多,最簡單的是 xQueueSend(),用于向隊(duì)列尾部發(fā)送一個(gè)隊(duì)列消息。消息以拷貝的形式入隊(duì),該函數(shù)絕對不能在中斷服務(wù)程序里面被調(diào)用,中斷中必須使用帶有中斷保護(hù)功能的 xQueueSendFromISR()來代替。

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ù)掛起(沒有超時(shí))。 

返回值

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

xQueueSendToBack與xQueueSend完全相同, xQueueSendFromISR()與 xQueueSendToBackFromISR(),帶FromISR表示只能在中斷中使用,freeRTOS所以帶這個(gè)后綴的都是這個(gè)含義。xQueueSendToFront()和QueueSendToFrontFromISR()用于向隊(duì)列隊(duì)首發(fā)送一個(gè)消息。這些在任務(wù)中發(fā)送消息的函數(shù)都是 xQueueGenericSend()展開的宏定義。

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ì)列中寫入了數(shù)據(jù),該任務(wù)將自動(dòng)由阻塞態(tài)轉(zhuǎn)為就緒態(tài)。當(dāng)任務(wù)等待的時(shí)間超過了指定的阻塞時(shí)間,即使隊(duì)列中尚無有效數(shù)據(jù),任務(wù)也會自動(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)目沒有使用。

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

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

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

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

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

2、發(fā)送或者是接收消息都是以拷貝的方式進(jìn)行,如果消息內(nèi)容過于龐大,可以將消息的地址作為消息進(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)申請內(nèi)存保存,隊(duì)列只傳遞指針  
12. } track_task_message_struct_t;   

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

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

5、隊(duì)列存在一定限制,在隊(duì)頭沒有取出來之前,是無法取出第二個(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)開銷也將越大,因?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)的,沒有開始計(jì)時(shí)運(yùn)行。FreeRTOS的軟件定時(shí)器支持單次模式和周期模式。

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

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

1. TimerHandle_t xTimerCreateconst 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í)器開啟

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

xTimerStart()、
xTimerReset()、
xTimerStartFromISR() 、xTimerResetFromISR() 
xTimerChangePeriod()、xTimerChangePeriodFromISR()

這些函數(shù)將其狀態(tài)轉(zhuǎn)換為活躍態(tài),開始運(yùn)行。區(qū)別:如果定時(shí)器設(shè)定60秒間隔,已經(jīng)運(yùn)行了30秒,reset是將定時(shí)器重置為原來設(shè)定的時(shí)間間隔,也就是重新開始延時(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)開始調(diào)度的時(shí)候就被創(chuàng)建:vTaskStartScheduler()—xTimerCreateTimerTask。

1. BaseType_t xTimerCreateTimerTaskvoid )  
2. 
{  
3.     BaseType_t xReturn = pdFAIL;  
4.   
5.     prvCheckForValidListAndQueue();  //創(chuàng)建定時(shí)器任務(wù)的隊(duì)列
6.   
7.     if( xTimerQueue != NULL )  
8.     {  
9.         #if ( configSUPPORT_STATIC_ALLOCATION == 1 )  
10.             {  
11.                       /**/
12.             }  
13.         #else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */  
14.             {  
15.                  //創(chuàng)建定時(shí)器任務(wù)
16.                 xReturn = xTaskCreate( prvTimerTask,  
17.                                        configTIMER_SERVICE_TASK_NAME,  
18.                                        configTIMER_TASK_STACK_DEPTH,  
19.                                        NULL,  
20.                                        ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,  
21.                                        &xTimerTaskHandle );  
22.             }  
23.         #endif /* configSUPPORT_STATIC_ALLOCATION */  
24.     }  
25.      /**/
26.     return xReturn;  
27. }  

任務(wù)創(chuàng)建后,等候命令執(zhí)行

1.static portTASK_FUNCTION( prvTimerTask, pvParameters )  
2. 
{  
3.      /**/
4.   
5.     for( ; ; )  
6.     {  
7.         //最近即將超時(shí)的定時(shí)器還有多長時(shí)間溢出
8.         xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty );  
9.   
10.         //阻塞等待,定時(shí)器溢出或受到命令,進(jìn)入下一步(原因不明)
11.         prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty );  
12.   
13.         //接收命令并處理,見下面
14.         prvProcessReceivedCommands();  
15.     }  
16. }  

所有定時(shí)器接口,都是使用xTimerGenericCommand向隊(duì)列發(fā)送控制命令,命令如下:

1. #define tmrCOMMAND_START_DONT_TRACE             ( ( BaseType_t ) 0 )  
2. #define tmrCOMMAND_START                        ( ( BaseType_t ) 1 )  
3. #define tmrCOMMAND_RESET                        ( ( BaseType_t ) 2 )  
4. #define tmrCOMMAND_STOP                         ( ( BaseType_t ) 3 )  
5. #define tmrCOMMAND_CHANGE_PERIOD                ( ( BaseType_t ) 4 )  
6. #define tmrCOMMAND_DELETE                       ( ( BaseType_t ) 5 )  

6.7 軟件定時(shí)器使用注意點(diǎn)

1、查看其他開源代碼,對定時(shí)器的使用并不多,但實(shí)際項(xiàng)目中過多依賴定時(shí)器,導(dǎo)致應(yīng)用邏輯混亂。 

2、freeRTOS 的定時(shí)器不是無限制的,其根源是接收定時(shí)器控制命令消息的隊(duì)列,默認(rèn)只有10個(gè)單元。

1. xTimerQueue = xQueueCreate( ( UBaseType_t ) configTIMER_QUEUE_LENGTH, sizeof( DaemonTaskMessage_t ) );  

定時(shí)器過多,可能出現(xiàn)發(fā)起定時(shí)器命令失敗,原因是隊(duì)列已滿??梢詫⒛J(rèn)的10擴(kuò)大為15,后續(xù)盡量使用信號量來優(yōu)化代碼。 

4、軟件定時(shí)器的回調(diào)函數(shù)要快進(jìn)快出,而且不能有任何阻塞任務(wù)運(yùn)行的情況,不能有vTaskDelay() 以及其它能阻塞任務(wù)運(yùn)行的函數(shù)。特別說明,其回調(diào)函數(shù)是在定時(shí)器任務(wù)執(zhí)行的,并不是開啟定時(shí)器的任務(wù)。

七、 信號量

7.1 信號量的概念

信號量(Semaphore)是一種實(shí)現(xiàn)任務(wù)間通信的機(jī)制,可以實(shí)現(xiàn)任務(wù)之間同步或臨界資源的互斥訪問,常用于協(xié)助一組相互競爭的任務(wù)來訪問臨界資源。在多任務(wù)系統(tǒng)中,各任務(wù)之間需要同步或互斥實(shí)現(xiàn)臨界資源的保護(hù),信號量功能可以為用戶提供這方面的支持??梢院唵握J(rèn)為是為支持多任務(wù)同時(shí)操作的全局變量(個(gè)人理解)。

7.1.1 二值信號量

比如有一個(gè)停車位,多個(gè)人都想占用停車,這種情況就可以使用一個(gè)變量標(biāo)記車位狀態(tài),它只有兩種情況,被占用或者沒被占用。在多任務(wù)中使用二值信號量表示,用于任務(wù)與任務(wù)、任務(wù)與中斷的同步。在freeRTOS中,二值信號量看作只有一個(gè)消息的隊(duì)列,因此這個(gè)隊(duì)列只能為空或滿。

7.1.2 計(jì)數(shù)信號量

如果有100個(gè)停車位,可以停100輛車,每進(jìn)去一輛車,車位的數(shù)量就要減一,當(dāng)停車場停滿了 100 輛車的時(shí)候,再來的車就不能停進(jìn)去了。這種場景就需要計(jì)數(shù)信號量來表示多個(gè)狀態(tài)。二進(jìn)制信號量可以被認(rèn)為是長度為 1 的隊(duì)列,而計(jì)數(shù)信號量則可以被認(rèn)為長度大于 1 的隊(duì)列,信號量使用者依然不必關(guān)心存儲在隊(duì)列中的消息,只需關(guān)心隊(duì)列是否有消息即可。

7.1.3 互斥信號量

還是前面車位問題,只剩一個(gè)空車位,雖然員工車離得近,但是領(lǐng)導(dǎo)車來了,要優(yōu)先安排給領(lǐng)導(dǎo)使用,這就是由地位決定?;コ庑盘柫科鋵?shí)是特殊的二值信號量,由于其特有的優(yōu)先級繼承機(jī)制從而使它更適用于簡單互鎖,也就是保護(hù)臨界資源。

優(yōu)先級翻轉(zhuǎn)問題:假設(shè)有任務(wù)H,任務(wù)M和任務(wù)L三個(gè)任務(wù),優(yōu)先級逐次降低。低優(yōu)先級的任務(wù)L搶先占有資源,導(dǎo)致高優(yōu)先級的任務(wù)H阻塞等待,此時(shí)再有中等優(yōu)先級的任務(wù)M,它不需要該資源,且優(yōu)先級高于任務(wù)L,它優(yōu)先執(zhí)行;之后再執(zhí)行任務(wù)L,最后才執(zhí)行任務(wù)H??雌饋砭褪歉邇?yōu)先級的任務(wù)反而不如低優(yōu)先級的任務(wù),即優(yōu)先級翻轉(zhuǎn)。

改進(jìn)型的互斥信號量具有優(yōu)先級繼承機(jī)制,操作系統(tǒng)對獲取到臨界資源的任務(wù)提高其優(yōu)先級為所有等待該資源的任務(wù)中的最高優(yōu)先級。一旦任務(wù)釋放了該資源,就恢復(fù)到原來的優(yōu)先級。

任務(wù)L先占用資源,任務(wù)H申請不到資源會進(jìn)入阻塞態(tài),同時(shí)系統(tǒng)就會把當(dāng)前正在使用資源的任務(wù)L的優(yōu)先級臨時(shí)提高到與任務(wù)H優(yōu)先級相同,即使任務(wù)M被喚醒了,因?yàn)樗膬?yōu)先級比任務(wù)H低,所以無法打斷任務(wù)L,因?yàn)槿蝿?wù)L的優(yōu)先級被臨時(shí)提升到 H;任務(wù)L使用完該資源,任務(wù)H優(yōu)先級最高,將接著搶占 CPU 的使用權(quán),這樣保證任務(wù)H在任務(wù)M前優(yōu)先執(zhí)行。

上面的這些就是為了說明,二值信號量因?yàn)閮?yōu)先級翻轉(zhuǎn),不能用于對臨界區(qū)的訪問。

7.1.4 遞歸互斥信號量

信號量是每獲取一次,可用信號量個(gè)數(shù)就會減少一個(gè),釋放一次就增加一個(gè)。但是遞歸信號量則不同。對于已經(jīng)獲取遞歸互斥量的任務(wù)可以重復(fù)獲取該遞歸互斥量,該任務(wù)擁有遞歸信號量的所有權(quán)。任務(wù)成功獲取幾次遞歸互斥量,就要返還幾次,在此之前遞歸互斥量都處于無效狀態(tài),其他任務(wù)無法獲取,只有持有遞歸信號量的任務(wù)才能獲取與釋放。類似棧的效果。

7.2 二值信號量的應(yīng)用

二值信號量是任務(wù)與任務(wù)間、任務(wù)與中斷間同步的重要手段。例如,任務(wù)A使用串口發(fā)出AT數(shù)據(jù)后,獲取二值信號量無效進(jìn)入阻塞;

某個(gè)時(shí)間后,任務(wù)B中串口收到正確的回復(fù),釋放二值信號量。

任務(wù)A就立即從阻塞態(tài)中解除,進(jìn)入就緒態(tài),等待運(yùn)行。這種機(jī)制用在模塊AT交互很合適。

7.3 計(jì)數(shù)信號量的應(yīng)用

計(jì)數(shù)信號量可以用于資源管理,允許多個(gè)任務(wù)獲取信號量訪問共享資源。例如有公共資源車位3個(gè),但是有多個(gè)任務(wù)要使用,這種場景就必須使用計(jì)數(shù)信號量。三個(gè)資源最多支持 3 個(gè)任務(wù)訪問,那么第 4 個(gè)任務(wù)訪問的時(shí)候,會因?yàn)楂@取不到信號量而進(jìn)入阻塞。也就是第4個(gè)人無法占用車位,必須前面有車離開。等到其中一個(gè)有任務(wù)(比如任務(wù) 1) 釋放掉該資源的時(shí)候,第 4 個(gè)任務(wù)才能獲取到信號量從而進(jìn)行資源的訪問。其運(yùn)作的機(jī)制類似下圖。

在這里插入圖片描述

7.4 互斥信號量的應(yīng)用

多任務(wù)環(huán)境下往往存在多個(gè)任務(wù)競爭同一臨界資源的應(yīng)用場景,互斥量可被用于對臨界資源的保護(hù)從而實(shí)現(xiàn)獨(dú)占式訪問。互斥量可以降低信號量存在的優(yōu)先級翻轉(zhuǎn)問題帶來的影響。

比如有兩個(gè)任務(wù)需要對串口進(jìn)行發(fā)送數(shù)據(jù),其硬件資源只有一個(gè),那么兩個(gè)任務(wù)肯定不能同時(shí)發(fā)送,不然導(dǎo)致數(shù)據(jù)錯(cuò)誤,那么就可以用互斥量對串口資源進(jìn)行保護(hù),當(dāng)一個(gè)任務(wù)正在使用串口的時(shí)候,另一個(gè)任務(wù)則無法使用串口,等到前一個(gè)任務(wù)使用串口完成后, 另外一個(gè)任務(wù)才能獲得串口的使用權(quán)。

另外需要注意的是互斥量不能在中斷服務(wù)函數(shù)中使用,因?yàn)槠涮赜械膬?yōu)先級繼承機(jī)制只在任務(wù)起作用,在中斷的上下文環(huán)境毫無意義。

互斥信號量可以在多個(gè)任務(wù)之間進(jìn)行資源保護(hù),而臨界區(qū)只能是在同一個(gè)任務(wù)進(jìn)行,但是其速度快。(個(gè)人理解)

7.5 信號量接口

所有信號量semaphore使用套路相近,都是創(chuàng)建creat、刪除delete、釋放give和獲取take四種;釋放和獲取支持任務(wù)級和中斷級FromISR,其中互斥量和遞歸互斥量不支持中斷。使用對應(yīng)的信號量,需要在FreeRTOSConfig.h開啟對應(yīng)的功能。

7.5.1 信號量創(chuàng)建

xSemaphoreCreateBinary()用于創(chuàng)建一個(gè)二值信號量,并返回一個(gè)句柄,默認(rèn)二值信號量為空,在使用函數(shù) xSemaphoreTake()獲取之前必須 先 調(diào) 用 函 數(shù) xSemaphoreGive() 釋放后才可以獲取。

xSemaphoreCreateCounting()創(chuàng)建計(jì)數(shù)信號量。

1. #define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount )   

uxMaxCount 計(jì)數(shù)信號量的最大值,當(dāng)達(dá)到這個(gè)值的時(shí)候,信號量不能再被釋放。uxInitialCount 創(chuàng)建計(jì)數(shù)信號量的初始值。

xSemaphoreCreateMutex()用于創(chuàng)建一個(gè)互斥量,并返回一個(gè)互斥量句柄,只能被同一個(gè)任務(wù)獲取一次,如果同一個(gè)任務(wù)想再次獲取則會失敗。

xSemaphoreCreateRecursiveMutex()用于創(chuàng)建一個(gè)遞歸互斥量,遞歸信號量可以被同一個(gè)任務(wù)獲取很多次,獲取多少次就需要釋放多少次。遞歸信號量與互斥量一樣,都實(shí)現(xiàn)了優(yōu)先級繼承機(jī)制,可以減少優(yōu)先級反轉(zhuǎn)的反生。

7.5.2 信號量刪除

vSemaphoreDelete()用于刪除一個(gè)信號量,包括二值信號量,計(jì)數(shù)信號量,互斥量和遞 歸互斥量。如果有任務(wù)阻塞在該信號量上,暫時(shí)不要?jiǎng)h除該信號量。傳入的參數(shù)為創(chuàng)建時(shí)返回的句柄。

7.5.3 信號量釋放

當(dāng)信號量有效的時(shí)候,任務(wù)才能獲取信號量,信號量變得有效就是釋放信號量。每調(diào)用一次該函數(shù)就釋放一個(gè)信號量,注意釋放的次數(shù),尤其是計(jì)數(shù)信號量。

xSemaphoreGive()是任務(wù)中釋放信號量的宏,可以用于二值信號量、計(jì)數(shù)信號量、互斥量的釋放,但不能釋放由函數(shù)xSemaphoreCreateRecursiveMutex()創(chuàng)建的遞歸互斥量,遞歸互斥信號量用xSemaphoreGiveRecursive()釋放。xSemaphoreGiveFromISR()帶中斷保護(hù)釋放一個(gè)信號量,被釋放的信號量可以是二值信號量和計(jì)數(shù)信號量,不能釋放互斥量和遞歸互斥量,因?yàn)榛コ饬亢瓦f歸互斥量不可在中斷中使用,互斥量的優(yōu)先級繼承機(jī)制只能在任務(wù)中起作用。

7.5.4 信號量獲取

與釋放信號量對應(yīng)的是獲取信號量,當(dāng)信號量有效的時(shí)候,任務(wù)才能獲取信號量,當(dāng)任務(wù)獲取了某個(gè)信號量的時(shí)候,該信號量的可用個(gè)數(shù)就減一,當(dāng)它減到0 的時(shí)候,任務(wù)就無法再獲取了,并且獲取的任務(wù)會進(jìn)入阻塞態(tài)(如果設(shè)定了阻塞超時(shí)時(shí)間)。

xSemaphoreTake()函數(shù)用于獲取信號量,不帶中斷保護(hù)。獲取的信號量對象可以是二值信號量、計(jì)數(shù)信號量和互斥量,但是遞歸互斥量并不能使用它。

1. #define xSemaphoreTake( xSemaphore, xBlockTime )  

xSemaphore 信號量句柄 

xBlockTime 等待信號量可用的最大超時(shí)時(shí)間,單位為 tick 

獲取 成 功 則 返 回 pdTRUE ,在 指定的 超時(shí) 時(shí)間 中 沒 有 獲 取 成 功 則 返 回errQUEUE_EMPTY。

使用xSemaphoreTakeRecursive()獲取遞歸互斥量。xSemaphoreTakeFromISR()是獲取信號量的中斷版本,是一個(gè)不帶阻塞機(jī)制獲取信號量的函數(shù),獲取對象必須由是已經(jīng)創(chuàng)建的信號量,信號量類型可以是二值信號量和計(jì)數(shù)信號量,它與 xSemaphoreTake()函數(shù)不同,它不能用于獲取互斥量,因?yàn)榛コ饬坎豢梢栽谥袛嘀惺褂?,并且互斥量特有的?yōu)先級繼承機(jī)制只能在任務(wù)中起作用,而在中斷中毫無意義。

7.6 信號量使用注意點(diǎn)

1、建議合理使用信號量進(jìn)行事件同步處理,減少對定時(shí)器的依賴。

2、使用前合理設(shè)定超時(shí)時(shí)間和依賴關(guān)系,避免多個(gè)任務(wù)互相等待對方釋放的信號量而死鎖。

八、 事件

8.1 事件的概念

信號量用于單個(gè)任務(wù)與任務(wù)或任務(wù)與中斷之間的同步,但有些任務(wù)可能與多個(gè)任務(wù)由關(guān)聯(lián),此時(shí)信號量實(shí)現(xiàn)就比較麻煩,可以使用事件機(jī)制。

事件是一種實(shí)現(xiàn)任務(wù)間通信的機(jī)制,多任務(wù)環(huán)境下,任務(wù)、中斷之間往往需要同步操作,一個(gè)事件發(fā)生會告知等待中的任務(wù),即形成一個(gè)任務(wù)與任務(wù)、中斷與任務(wù)間的同步。事件可以提供一對多、多對多的同步操作。一對多同步模型:一個(gè)任務(wù)等待多個(gè)事件的觸發(fā),這種情況是比較常見的。

任務(wù)可以通過設(shè)置事件位來實(shí)現(xiàn)事件的觸發(fā)和等待操作。FreeRTOS 的事件僅用于同步,不提供數(shù)據(jù)傳輸功能。

8.2 事件的應(yīng)用

在某些場合,可能需要多個(gè)事件發(fā)生了才能進(jìn)行下一步操作。各個(gè)事件可分別發(fā)送或一起操作事件標(biāo)志組,而任務(wù)可以等待多個(gè)事件,任務(wù)僅對感興趣的事件進(jìn)行關(guān)注。當(dāng)有感興趣的事件發(fā)生時(shí)并且符合感興趣的條件,任務(wù)將被喚醒并進(jìn)行后續(xù)的處理動(dòng)作。

其機(jī)制類似一個(gè)全局變量,子任務(wù)使用特殊的接口函數(shù)對指定的位進(jìn)行寫1或者清零,主任務(wù)阻塞等待該變量滿足設(shè)定的規(guī)則,則返回運(yùn)行。

例如項(xiàng)目中的喂狗機(jī)制,多個(gè)任務(wù),只要有一個(gè)任務(wù)發(fā)生異常,則主任務(wù)停止喂狗,等待被重啟。不使用事件機(jī)制,則3個(gè)任務(wù)定時(shí)向主master task發(fā)送消息,表明自身任務(wù)運(yùn)行正常;同時(shí)master task定時(shí)查詢,是否收到3個(gè)任務(wù)的消息,如果全都收到表示正常,清除進(jìn)入下一個(gè)定時(shí)檢查周期;如果其中一個(gè)未收到則表示對應(yīng)任務(wù)異常,故意停止喂狗等待被重啟。

使用事件機(jī)制,則相對容易,3個(gè)任務(wù)定時(shí)設(shè)置對應(yīng)的標(biāo)志位,master task只需要等待指定的事件位,超時(shí)就表示異常;不需要自身定時(shí)查詢,也省去了定時(shí)發(fā)消息。當(dāng)然缺點(diǎn)是master task只能阻塞等待事件不能執(zhí)行其他業(yè)務(wù)邏輯。

8.3 事件接口

xEventGroupCreate()用于創(chuàng)建一個(gè)事件組,vEventGroupDelete()刪除事件對象控制塊來釋放系統(tǒng)資源。

事件組置位,任務(wù)中使用 xEventGroupSetBits(),中斷中使用xEventGroupSetBitsFromISR();

xEventGroup 事件句柄。uxBitsToSet 指定事件中的事件標(biāo)志位。如設(shè)置 uxBitsToSet 為 0x09 則位 3和位 0 都需要被置位。返回調(diào)用 xEventGroupSetBits() 時(shí)事件組中的值。

事件組清除位,任務(wù)中使用xEventGroupClearBits(),中斷中使用 xEventGroupClearBitsFromISR(),都是用于清除事件組指定的位,如果在獲取事件的時(shí)候沒有將對應(yīng)的標(biāo)志位清除,那么就需要用這個(gè)函數(shù)來進(jìn)行顯式清除。

xEventGroup 事件句柄。uxBitsToClear 指定事件組中的哪個(gè)位需要清除。如設(shè)置 uxBitsToSet 為 0x09則位 3和位 0 都需要被清除。

讀取事件標(biāo)志,任務(wù)中使用 xEventGroupGetBits(),中斷中使用xEventGroupGetBitsFromISR()。

重點(diǎn)是等待事件函數(shù) xEventGroupWaitBits(),獲取任務(wù)感興趣的事件且支持等待超時(shí)機(jī)制,當(dāng)且僅當(dāng)任務(wù)等待的事件發(fā)生時(shí),任務(wù)才能獲取到事件信息。否則任務(wù)將保持阻塞狀態(tài)以等待事件發(fā)生。當(dāng)其它任務(wù)或中斷服務(wù)程序往其等待的事件設(shè)置對應(yīng)的標(biāo)志位,該任務(wù)將自動(dòng)由阻塞態(tài)轉(zhuǎn)為就緒態(tài)。

EventGroupWaitBits()用于獲取事件組中的一個(gè)或多個(gè)事件發(fā)生標(biāo)志,當(dāng)要讀取的事件標(biāo)志位沒有被置位時(shí),任務(wù)將進(jìn)入阻塞等待狀態(tài)。要想使用該函數(shù)必 須 把FreeRTOS/source/event_groups.c 這個(gè) C 文件添加到工程中。

1. EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,  
2.                                  const EventBits_t uxBitsToWaitFor,  
3.                                  const BaseType_t xClearOnExit,  
4.                                  const BaseType_t xWaitForAllBits,  
5.                                  TickType_t xTicksToWait )
  

參數(shù)

xEventGroup 事件句柄。 

  uxBitsToWaitFor 一個(gè)按位或的值,指定需要等待事件組中的哪些位置1。如需要等待 bits 0 and/or bit 1 and/or bit 2則 uxBitsToWaitFor 配置為 0x07(0111b)。

xClearOnExit pdTRUE:xEventGroupWaitBits() 等待到滿足任務(wù)喚醒的事件時(shí),系統(tǒng)將清除由形參 uxBitsToWaitFor 指定的事件標(biāo)志位。pdFALSE:不會清除由形參 uxBitsToWaitFor 指定的事件標(biāo)志位。

xWaitForAllBits pdTRUE :當(dāng)形參 uxBitsToWaitFor 指定的位都置位的時(shí)候,xEventGroupWaitBits()才滿足任務(wù)喚醒的條件,這也是“邏輯與”等待事件,并且在沒有超時(shí)的情況下返回對應(yīng)的事件標(biāo)志位的值。pdFALSE:當(dāng)形參 uxBitsToWaitFor 指定的位有其中任意一個(gè)置位的時(shí)候,這也是常說的“邏輯或”等待事件,在沒有超時(shí)的情況下 函數(shù)返回對應(yīng)的事件標(biāo)志位的值。xTicksToWait 最大超時(shí)時(shí)間,單位為系統(tǒng)節(jié)拍周期

返回值

返回事件中的哪些事件標(biāo)志位被置位,返回值很可能并不是用戶指定的事件位,需要對返回值進(jìn)行 判斷再處理 。

其應(yīng)用類似某個(gè)全局變量,等待事件的任務(wù)在設(shè)定的時(shí)間內(nèi),監(jiān)控該變量某些位的值;該值由其他任務(wù)或中斷修改。

九、 任務(wù)通知

FreeRTOS 從 V8.2.0 版本開始提供任務(wù)通知這個(gè)功能,可以在一定場合下替代 FreeRTOS 的信號量,隊(duì)列、事件組等,但是使用也有局限性。將宏定義 configUSE_TASK_NOTIFICATIONS 設(shè)置為 1才能開啟開功能。但該功能并不常用。

十、 內(nèi)存管理

10.1 內(nèi)存管理的概念

FreeRTOS 內(nèi)存管理模塊管理用于系統(tǒng)中內(nèi)存資源,它是操作系統(tǒng)的核心模塊之一。主要包括內(nèi)存的初始化、分配以及釋放。一般不同的平臺移植代碼,內(nèi)存的動(dòng)態(tài)申請和釋放接口需要替換。嵌入式實(shí)時(shí)操作系統(tǒng)中,一般不支持標(biāo)準(zhǔn)C庫中的 malloc()和 free(),其內(nèi)存有限,隨著內(nèi)存不斷被分配和釋放,整個(gè)系統(tǒng)內(nèi)存區(qū)域會產(chǎn)生越來越多的碎片。

FreeRTOS提供了 5 種內(nèi)存管理算法,源文件在Source\portable\MemMang 路徑下,使用的時(shí)候選擇其中一個(gè)。heap_1.c、heap_2.c 和 heap_4.c 這三種內(nèi)存管理方案,內(nèi)存堆實(shí)際上是一個(gè)很大的 數(shù) 組ucHeap。

heap_1.c內(nèi)存管理方案簡單,它只能申請內(nèi)存而不能進(jìn)行內(nèi)存釋放。有些嵌入式系統(tǒng)并不會經(jīng)常動(dòng)態(tài)申請與釋放內(nèi)存,一般都是在系統(tǒng)啟動(dòng)后就一直使用下去,永不刪除,適合這種方式。

heap_2.c 方案支持釋放申請的內(nèi)存,但是它不能把相鄰的兩個(gè)小的內(nèi)存塊合成一個(gè)大的內(nèi)存塊,對于每次申請內(nèi)存大小都比較固定的;但每次申請并不是固定內(nèi)存大小的則會造成內(nèi)存碎片。如下圖,隨著不斷的申請釋放,空閑空間會變成很多小片段。

heap_3.c 方案只是封裝了標(biāo)準(zhǔn) C 庫中的 malloc()和 free()函數(shù),由編譯器提供,需要通過編譯器或者啟動(dòng)文件設(shè)置堆空間。

heap_4.c 方案是在heap_2.c 基礎(chǔ)上,對內(nèi)存碎片進(jìn)行了改進(jìn),能把相鄰的空閑的內(nèi)存塊合并成一個(gè)更大的塊,這樣可以減少內(nèi)存碎片。

heap_5.c 方案在實(shí)現(xiàn)動(dòng)態(tài)內(nèi)存分配時(shí)與 heap4.c 方案一樣,采用最佳匹配算法和合并算法,并且允許內(nèi)存堆跨越多個(gè)非連續(xù)的內(nèi)存區(qū),也就是允許在不連續(xù)的內(nèi)存堆中實(shí)現(xiàn)內(nèi)存分配,比如做圖形顯示,可能芯片內(nèi)部的 RAM 不足,額外擴(kuò)展SDRAM,那這種內(nèi)存管理方案則比較合適。

一般物聯(lián)網(wǎng)平臺使用的是heap_4.c。

10.2 內(nèi)存管理接口

不管其內(nèi)部的管理如何實(shí)現(xiàn)的,對上層應(yīng)用層的接口都是一樣的。

1. void *pvPortMallocsize_t xSize )//內(nèi)存申請函數(shù)   
2. void vPortFreevoid *pv );          //內(nèi)存釋放函數(shù)   
3. void vPortInitialiseBlocksvoid )//初始化內(nèi)存堆函數(shù)   
4. size_t xPortGetFreeHeapSizevoid );    //獲取當(dāng)前未分配的內(nèi)存堆大小   
5. size_t xPortGetMinimumEverFreeHeapSizevoid )//獲取未分配的內(nèi)存堆歷史最小值  

一般主要是使用內(nèi)存申請和釋放兩個(gè)接口,用法和注意事項(xiàng)同malloc/free一樣,成對使用。內(nèi)存釋放后盡量將指針設(shè)為NULL。

十一、 通用接口

一些常用接口進(jìn)行說明。

11.1 臨界段

進(jìn)入和退出臨界段的宏在 task.h 中定義,進(jìn)入和退出臨界段的宏分中斷保護(hù)版本和非中斷版本,但最終都是通過開/關(guān)中斷來實(shí)現(xiàn)。主要用于對全局變量的控制,系統(tǒng)使用非常多,但實(shí)際項(xiàng)目中沒使用,因?yàn)槿肿兞康漠惓TL問時(shí)小概率問題,只是測試沒發(fā)現(xiàn),理論上是存在問題的。

1. /* 在中斷場合*/  {   
2.     uint32_t ulReturn;   
3.     
4.     ulReturn = taskENTER_CRITICAL_FROM_ISR(); /* 進(jìn)入臨界段,臨界段可以嵌套 */   
5.     
6.     /* 臨界段代碼 */      
7.        
8.     taskEXIT_CRITICAL_FROM_ISR( ulReturn );  }   /* 退出臨界段 */

1.  /* 在非中斷場合 */  {   
2.       
3.     taskENTER_CRITICAL();     /* 進(jìn)入臨界段 */ 
4. 
5.     /* 臨界段代碼 */    
6.     
7.     taskEXIT_CRITICAL();  }   /* 退出臨界段*/  

11.2 任務(wù)阻塞延時(shí)

vTaskDelay ()阻塞延時(shí),任務(wù)調(diào)用該延時(shí)函數(shù)后會被剝離 CPU 使用權(quán),進(jìn)入阻塞狀態(tài),直到延時(shí)結(jié)束。但是該函數(shù)不能用在中斷服務(wù)和定時(shí)回調(diào)函數(shù)。延時(shí)單位是tick。

11.3 獲取系統(tǒng)時(shí)鐘計(jì)數(shù)值

1. TickType_t xTaskGetTickCountvoid )  
2. TickType_t xTaskGetTickCountFromISRvoid )  

注意該接口分任務(wù)版和中斷版,該接口獲取的是tick計(jì)數(shù)值,需要結(jié)合系統(tǒng)時(shí)鐘頻率轉(zhuǎn)換成時(shí)間。

11.4 中斷回調(diào)函數(shù)

和其它平臺不同,中斷回調(diào)中釋放中斷標(biāo)記即可,freeRTOS中,中斷觸發(fā)后,可能某些阻塞的任務(wù)獲取了相關(guān)信號,需要立刻執(zhí)行,因此中斷服務(wù)發(fā)送消息后,需要主動(dòng)查詢阻塞任務(wù)的情況,執(zhí)行任務(wù)切換動(dòng)作。

1. static uint32_t ulExampleInterruptHandlervoid )  
2. 
{  
3.     BaseType_t xHigherPriorityTaskWoken;  
4. 
5.     xQueueSendToBackFromISR (xQueueRx,&cChar,&xHigherPriorityTaskWoken);  
6.     portYIELD_FROM_ISR(xHigherPriorityTaskWoken);  
7. }  


免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動(dòng)現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動(dòng)力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉