在單片機(jī)的Helloworld程序中(讓LED實(shí)現(xiàn)閃爍)的過(guò)程中,常常用到了延時(shí),在這個(gè)延時(shí)過(guò)程中CPU相當(dāng)于什么事情都沒(méi)干,當(dāng)然這只是個(gè)實(shí)現(xiàn)電平翻轉(zhuǎn)的簡(jiǎn)單任務(wù)。但是一旦任務(wù)增多后CPU的算力就大打折扣,因?yàn)樵谘舆t的過(guò)程中,CPU這個(gè)“勞動(dòng)者”在等待時(shí)間消息,幾乎不再工作,這就需要一種方法去最大化“壓榨”CPU的算力,使得CPU全時(shí)間滿負(fù)荷運(yùn)行。
嵌入式設(shè)備一般分為以下幾種:
裸機(jī)系統(tǒng)、輪詢(xún)系統(tǒng)、前后臺(tái)系統(tǒng)、多任務(wù)系統(tǒng) 。輪詢(xún)系統(tǒng)就是一個(gè)主程序中不斷運(yùn)行無(wú)限循環(huán)里邊的程序內(nèi)容;前后臺(tái)系統(tǒng)就是在輪詢(xún)系統(tǒng)中加入了中斷,中斷事件的處理是在中斷函數(shù)進(jìn)行處理的,中斷是前臺(tái),主程序是后臺(tái);多任務(wù)系統(tǒng)是事件的中斷處理在主函數(shù),中斷函數(shù)負(fù)責(zé)傳遞中斷標(biāo)志位。
不同系統(tǒng)模型的對(duì)比如下圖:
常見(jiàn)的操作系統(tǒng)如下:常用的 RTOS 有國(guó)外的 FreeRTOS、μC/OS、RTX 和國(guó)內(nèi)的 FreeRTOS、Huawei LiteOS 和 AliOS-Things 等,其中尤以國(guó)外開(kāi)源且免費(fèi)的FreeRTOS 的市場(chǎng)占有率最高。操作系統(tǒng)其實(shí)就是把硬件資源虛擬化,把單核處理虛擬成“多核”提高“并發(fā)”感。這種思想非常重要,類(lèi)似的有虛擬機(jī),還有當(dāng)下大量應(yīng)用于物聯(lián)網(wǎng)的Docker技術(shù)。
對(duì)于單核ARM單片機(jī)來(lái)說(shuō),要達(dá)到兩個(gè)進(jìn)程任務(wù)的幾乎同步態(tài),那是不可能的,所以對(duì)于多任務(wù)系統(tǒng),要限制每個(gè)任務(wù)的運(yùn)行時(shí)間,所以就需要分時(shí)切片處理,如下圖,運(yùn)行兩個(gè)任務(wù)進(jìn)程,每個(gè)任務(wù)進(jìn)程的運(yùn)行時(shí)間所占據(jù)的時(shí)間片一樣的,只要時(shí)間片劃分足夠小,例如在毫秒級(jí)別上進(jìn)行任務(wù)切換可以滿足大部分系統(tǒng)的穩(wěn)定運(yùn)行需求。時(shí)間片上運(yùn)行過(guò)程如下圖 運(yùn)行結(jié)果如下下圖:
TWO
在操作系統(tǒng),每一個(gè)要執(zhí)行的任務(wù),也就是一段程序的運(yùn)行過(guò)程被稱(chēng)為一個(gè) 進(jìn)程 。進(jìn)程包含著動(dòng)態(tài)的概念,它是一個(gè)程序的運(yùn)行過(guò)程,而不是一個(gè)靜態(tài)的程序。進(jìn)程體現(xiàn)在程序中形式實(shí)際上就是一段循環(huán)執(zhí)行的代碼,使用操作系統(tǒng)的任務(wù)創(chuàng)建函數(shù)創(chuàng)建了這個(gè)進(jìn)程之后,操作系統(tǒng)就自動(dòng)找到這段代碼并執(zhí)行。一段程序執(zhí)行時(shí),一般劃分成三個(gè)階段,開(kāi)始執(zhí)行--->執(zhí)行中--->執(zhí)行完成。這也恰好對(duì)應(yīng)了進(jìn)程的工作狀態(tài): 就緒態(tài) (Ready)---> 運(yùn)行態(tài) (Running---> 終止態(tài) (Blocked)。如果在一個(gè)進(jìn)程執(zhí)行的過(guò)程中,調(diào)用了將進(jìn)程掛起的功能函數(shù),或者是進(jìn)程執(zhí)行時(shí)有更高優(yōu)先級(jí)的 任務(wù)就緒了,則進(jìn)程會(huì)進(jìn)入 掛起狀態(tài) (Suspended)。容易把阻塞態(tài)與掛起態(tài)相混,我的理解是處于阻塞態(tài)的時(shí)候進(jìn)程在等待一個(gè)信號(hào)條件滿足后繼續(xù)運(yùn)行,該信號(hào)條件可以是中斷可以是信號(hào)量等等。而處于掛起態(tài)的時(shí)候該程序根本沒(méi)運(yùn)行,就像一個(gè)人被掛起,那他根本不可能完成任何任務(wù)。
由于使用時(shí)間切片引起任務(wù)進(jìn)程的暫停容易導(dǎo)致正在處理的數(shù)據(jù)丟失,所以操作系統(tǒng)需要給每個(gè)任務(wù)進(jìn)程分配一個(gè)內(nèi)存,存儲(chǔ)上個(gè)時(shí)間片尚未處理的任務(wù)數(shù)據(jù)。
同時(shí)任務(wù)進(jìn)程的工作并不是完全獨(dú)立的而是互相交替訪問(wèn)的,各個(gè)進(jìn)程的通信也需要容易導(dǎo)致對(duì)某個(gè)變量的的訪問(wèn)極易出現(xiàn)沖突,所以需要一個(gè)信息傳輸?shù)慕橘|(zhì)- 存儲(chǔ)隊(duì)列 。該消息隊(duì)列既能寫(xiě)入數(shù)據(jù)也能讀取數(shù)據(jù),消息隊(duì)列對(duì)象一般以全局變量進(jìn)行聲明。消息緩存空間采用先進(jìn)先出FIFO方式進(jìn)行存儲(chǔ),實(shí)現(xiàn)效果如下圖。
有限的硬件資源可能會(huì)被多個(gè)進(jìn)程同時(shí)訪問(wèn),并且有的進(jìn)程對(duì)被控制實(shí)體的作用是相反的,極短時(shí)間內(nèi)互斥任務(wù)進(jìn)程的切換極易使得被控對(duì)象產(chǎn)生震蕩,所以需要對(duì)互斥進(jìn)程做一個(gè)規(guī)約,這個(gè)規(guī)約的實(shí)現(xiàn)就是信號(hào)量,F(xiàn)reeRTOS中可以實(shí)現(xiàn)任務(wù)之間同步或臨界資源的互斥訪問(wèn),常用于協(xié)助一組相互競(jìng)爭(zhēng)的任務(wù)來(lái)訪問(wèn)臨界資源。也可以用作進(jìn)程順序執(zhí)行或者同步運(yùn)行的保證。
具體來(lái)說(shuō)信號(hào)量是一個(gè)非負(fù)整數(shù),由臨界資源釋放,當(dāng)信號(hào)量為0時(shí),剩余任務(wù)進(jìn)程失去資源訪問(wèn)的機(jī)會(huì),只有當(dāng)另一個(gè)任務(wù)停止訪問(wèn)臨界資源時(shí),才會(huì)釋放出信號(hào)量。采用信號(hào)量的實(shí)現(xiàn)過(guò)程如下圖所示。
單片機(jī)開(kāi)發(fā)中用的最多就是二值信號(hào)量了,可以理解為一個(gè)位0和1,也可以理解為true或false如下圖為二值信號(hào)量的實(shí)現(xiàn)過(guò)程。訪問(wèn)的任務(wù)進(jìn)程只能有一個(gè),所以就能避免訪問(wèn)資源引起的競(jìng)爭(zhēng)與及規(guī)避控制效果的互斥。
在一個(gè)復(fù)雜控制系統(tǒng)中一個(gè)控制量常常需要多種事件的出現(xiàn)才能觸發(fā),事件的發(fā)生與消失用1和0來(lái)表示。多事件的狀態(tài)可以通過(guò)FreeRTOS中的事件庫(kù)進(jìn)行事件狀態(tài)的統(tǒng)一管理,F(xiàn)reeRTOS中一個(gè)事件組為32位,可通過(guò)配置文件修改其長(zhǎng)度。
以32位為例其中高九位不可用,一個(gè)位代表一個(gè)事件,例如0x00000092就表示,因?yàn)?x00001188轉(zhuǎn)換為二進(jìn)制0b 0000 0000 0000 0000 0000 0000 1001 0010,然后通過(guò)查詢(xún)事件句柄查詢(xún)對(duì)應(yīng)事件的發(fā)生與否。
THREE
要看懂FreeRTOS源碼了解其原理,那么必須要學(xué)會(huì)c語(yǔ)言中常用的數(shù)據(jù)結(jié)構(gòu)--- 鏈表結(jié)構(gòu) ,鏈表的源文件在list.c文件中,一般分為單向鏈表和雙向鏈表,單向鏈表有頭尾之分,雙向鏈表則構(gòu)成了一個(gè)環(huán),分別指針?lè)謩e指向上個(gè)鏈表節(jié)點(diǎn)地址和下個(gè)鏈表節(jié)點(diǎn)地址。
鏈表的節(jié)點(diǎn)成員以及成員作用如下圖所示:
可以看出鏈表本身就不包含大量數(shù)據(jù)的存儲(chǔ),鏈表是一個(gè)動(dòng)態(tài)的數(shù)據(jù)結(jié)構(gòu),將不連續(xù)的離散的硬件地址通過(guò)鏈表映射形成虛擬的連續(xù)存儲(chǔ)地址,面試中也常常問(wèn)到鏈表和數(shù)組的區(qū)別,數(shù)組是開(kāi)辟了一個(gè)連續(xù)的存儲(chǔ)地址,位置固定。
用最基礎(chǔ)的部分的理解就到此結(jié)束,當(dāng)然考慮到不同任務(wù)進(jìn)程的優(yōu)先級(jí)以及不同任務(wù)執(zhí)行的時(shí)間長(zhǎng)短,和kernel等事件的觸發(fā)中斷,因此FreeRTOS有著更為復(fù)雜的調(diào)度機(jī)制,比如本文開(kāi)頭提到的時(shí)間片輪轉(zhuǎn)法,在考慮到任務(wù)優(yōu)先級(jí)的時(shí)候,該算法根本不能保證高優(yōu)先級(jí)的進(jìn)程任務(wù)的執(zhí)行,而且不同任務(wù)在排隊(duì)列表中順序各有差異。排隊(duì)靠前的低優(yōu)先級(jí)任務(wù)和排隊(duì)較后的高優(yōu)先級(jí)任務(wù)怎么取舍等一系列的問(wèn)題。當(dāng)然可以明確的是本系統(tǒng)的調(diào)度是由“ 先來(lái)先服務(wù)(FCFS) ”調(diào)度算法,“ 優(yōu)先級(jí) ”調(diào)度算法,“ 時(shí)間片輪轉(zhuǎn) ”調(diào)度算法三種算法共同作用的。而且還可以通過(guò)設(shè)置宏configUSE_PREEMPTION來(lái)定義是采用搶占式還是合作式操作系統(tǒng)(說(shuō)太多了不解釋了.....)。
其實(shí)把用起來(lái)就知道操作系統(tǒng)的一個(gè)重要工作就是執(zhí)行各個(gè)進(jìn)程的狀態(tài)切換,因?yàn)閷?shí)際上單片機(jī)每次只能運(yùn)行一個(gè)進(jìn)程,而操作系統(tǒng)通過(guò)適當(dāng)?shù)墓芾?,讓每一個(gè)進(jìn)程都可以得到及時(shí)的響應(yīng),讓多個(gè)進(jìn)程呈現(xiàn)出一種同時(shí)運(yùn)行的“并發(fā)”感,和一根信號(hào)上傳輸多種信號(hào)的思想是一致的都是分時(shí)處理,但是速度肯定不及兩根信號(hào)線分別傳輸兩種信號(hào)。
現(xiàn)在的嵌入式入門(mén)門(mén)檻已經(jīng)很低,原有的STM32固件庫(kù)官方不在更新,使用CubeMX生成的HAL庫(kù)正在逐漸替代原有的固件庫(kù),大大降低開(kāi)發(fā)門(mén)檻提高開(kāi)發(fā)效率。當(dāng)然熟悉板子上的常用的寄存器會(huì)讓你得心應(yīng)手。
項(xiàng)目中關(guān)于RTOS問(wèn)題的記錄:
(1)FreeRTOS一定要配置好系統(tǒng)時(shí)鐘,給予一個(gè)單獨(dú)的硬件時(shí)鐘源作為系統(tǒng)時(shí)鐘。否則時(shí)間片的長(zhǎng)度為無(wú)限大..程序只運(yùn)行第一次進(jìn)入的進(jìn)程任務(wù)。
(2)隊(duì)列的大小要設(shè)置得當(dāng),否則容易內(nèi)存溢出,數(shù)據(jù)后移亂碼等情況。
(3)中斷中對(duì)事件組的操作一律使用....FromISR。
(4)任務(wù)進(jìn)程中的延遲要使用操作系統(tǒng)中提供的延遲函數(shù)。
(5)應(yīng)用操作系統(tǒng)是讓CPU算力最大化,所以盡量使用官方提供的外設(shè),GPIO口的通信盡量不要選擇模擬協(xié)議自行編程,因?yàn)橹虚g需要加入非操作系統(tǒng)的微秒級(jí)延遲,降低了CPU的使用效率。
(6)在通信隊(duì)列操作中,讀取完畢應(yīng)將通信隊(duì)列清空。
(7)單片機(jī)硬件系統(tǒng)會(huì)產(chǎn)生各式各樣的中斷,而FreeRTOS可以幫助你去對(duì)管理中斷,由于STM32的內(nèi)核中斷優(yōu)先級(jí)過(guò)高,所以規(guī)定FreeRTOS能使用的中斷級(jí)別大于4.
評(píng)論