FreeRTOS学习记录
FreeRTOS學(xué)習(xí)記錄
- 前言
- FreeRTOS學(xué)習(xí)記錄
- 在STM32CubeMX中配置FreeRTOS
前言
本人小白,最近學(xué)習(xí)了FreeRTOS操作系統(tǒng),打算做一點(diǎn)記錄。
學(xué)習(xí)的過(guò)程中雖然做了點(diǎn)練習(xí),不過(guò)都是跟著例程來(lái)的,大部分沒(méi)什么記錄必要,本文更多的是零零散散地記錄我在學(xué)習(xí)過(guò)程中的一些疑惑與個(gè)人見(jiàn)解,可能會(huì)有錯(cuò)誤還請(qǐng)指出。
這只是一個(gè)初學(xué)者的簡(jiǎn)單學(xué)習(xí)記錄,并沒(méi)有全面系統(tǒng)的知識(shí)介紹,學(xué)習(xí)的話還是要看對(duì)應(yīng)的教程啦。
使用到的工具及版本:
FreeRTOS:V9.0.0
STM32CubeMX版本:6.3.0
HAL庫(kù):STM32CubeF4 Firmware Package V1.26.2
MDK-ARM:V5.32.0.0
開(kāi)發(fā)板:野火的霸天虎開(kāi)發(fā)板V2(主控芯片是STM32F407ZGT6)
參考的資料:
野火的《FreeRTOS內(nèi)核實(shí)現(xiàn)與應(yīng)用開(kāi)發(fā)實(shí)戰(zhàn)》
FreeRTOS入門(mén)手冊(cè)_中文
FreeRTOS官網(wǎng)提供的介紹。
FreeRTOS學(xué)習(xí)記錄
本文對(duì)port部分(移植層面)源碼的探究查看的是FreeRTOSv9.0.0\FreeRTOS\Source\portable\RVDS\ARM_CM4_MPU,這是我所使用的開(kāi)發(fā)工具和硬件決定的。其他工具或者硬件上的實(shí)現(xiàn)可能不一樣。
任務(wù)會(huì)在什么情況下切換?
1、 我們自己編寫(xiě)的程序所引起的任務(wù)切換。比如我們自己調(diào)用taskYIELD(),或者我們調(diào)用的API函數(shù)里面包含了任務(wù)切換(這種時(shí)候往往是當(dāng)前任務(wù)進(jìn)入阻塞/掛起,又或者是有更高優(yōu)先級(jí)的任務(wù)加入了就緒列表,API函數(shù)里面會(huì)在需要的時(shí)候去調(diào)用任務(wù)切換)。
2、 tick中斷里面會(huì)觸發(fā)任務(wù)切換。tick由內(nèi)核的Systick產(chǎn)生,屬于內(nèi)核的定時(shí)器/計(jì)數(shù)器(其設(shè)計(jì)的本意可能就是供操作系統(tǒng)使用的,為操作系統(tǒng)提供時(shí)間度量,我猜的)。
注意: 搶占式調(diào)度上面兩種情況都存在。協(xié)作式調(diào)度不存在第2種情況的切換(tick中斷的切換),協(xié)作式調(diào)度中所有的切換都是“顯式的”。
顯而易見(jiàn)協(xié)作式調(diào)度比搶占式調(diào)度有著更高的CPU效率,畢竟不需要每個(gè)tick中斷就跑去看看是不是要切換任務(wù)。但是它存在著高優(yōu)先級(jí)中斷可能得不到快速響應(yīng)的風(fēng)險(xiǎn),這取決于我們開(kāi)發(fā)者的程序設(shè)計(jì),得看我們是不是在合適的時(shí)機(jī)調(diào)用了任務(wù)切換,如果程序結(jié)構(gòu)復(fù)雜的話我們可能得時(shí)時(shí)刻刻考慮是不是該在什么地方進(jìn)行一次任務(wù)切換,這可太麻煩了,有的時(shí)候甚至不現(xiàn)實(shí)。
通過(guò)FreeRTOSConfig.h里面的configUSE_PREEMPTION(0是協(xié)作式調(diào)度,非0是搶占式調(diào)度)決定調(diào)度類型。
tick中斷源碼與任務(wù)切換源碼
系統(tǒng)tick依賴于Cotex-M4的SysTick硬件中斷資源,任務(wù)切換依賴于PendSV硬件中斷資源(注:下圖的SysTick及之前的中斷是屬于內(nèi)核的,后面的是芯片廠商的)
來(lái)看看SysTick_Handler(systick中斷函數(shù))。
如果調(diào)度器此時(shí)壓根還沒(méi)開(kāi)啟,或者調(diào)用了vTaskEndScheduler( void ),那么這里的xPortSysTickHandler()并不會(huì)執(zhí)行,系統(tǒng)節(jié)拍則不會(huì)增加(當(dāng)然前提是宏INCLUDE_xTaskGetSchedulerState定義為1)。調(diào)度器如果掛起,系統(tǒng)時(shí)間依然會(huì)增加。
接下來(lái)看看xPortSysTickHandler()里面是啥。
首先調(diào)用了vPortRaiseBASEPRI()把中斷屏蔽,它的實(shí)現(xiàn)利用了Cotex-M4內(nèi)核的BASEPRI register(這張圖是從《STM32F4xx-Cortex_-M4內(nèi)核參考手冊(cè)》截的)。
然后調(diào)用的xTaskIncrementTick() 則是實(shí)現(xiàn)了:
1、系統(tǒng)tick加1(無(wú)論調(diào)度器是否掛起)
2、如果tick溢出,把延時(shí)列表和延時(shí)溢出列表交換
3、處理延時(shí)列表的事宜
4、如果是搶占式調(diào)度,會(huì)返回pdTRUE,這樣接下來(lái)就會(huì)把PendSV中斷標(biāo)志位置位,進(jìn)入PendSV中斷服務(wù)完成任務(wù)切換。如果是協(xié)作式調(diào)度則返回pdFALSE,不會(huì)置位PendSV中斷標(biāo)志。
接下來(lái)看看PendSV_Handler(PendSV中斷函數(shù),用于任務(wù)切換)。
有關(guān)R14與R15(PC)寄存器的描述(我在網(wǎng)上找的)
R14稱為子程序鏈接寄存器LR(Link Register),當(dāng)執(zhí)行子程序調(diào)用指令(BL)時(shí),R14可得到R15(程序計(jì)數(shù)器PC)的備份。在每一種運(yùn)行模式下,都可用R14保存子程序的返回地址,當(dāng)用BL或BLX指令調(diào)用子程序時(shí),將PC的當(dāng)前值復(fù)制給R14,執(zhí)行完子程序后,又將R14的值復(fù)制回PC,即可完成子程序的調(diào)用返回。
寄存器R15用作程序計(jì)數(shù)器(PC),在ARM狀態(tài)下,位[1:0]為0,位[31:2]用于保存PC,在Thumb狀態(tài)下,位[0]為0,位[31:1]用于保存PC。由于ARM體系結(jié)構(gòu)采用了多級(jí)流水線技術(shù),對(duì)于ARM指令集而言,PC總是指向當(dāng)前指令的下兩條指令的地址,即PC的值為當(dāng)前指令的地址值加8個(gè)字節(jié)程序狀態(tài)寄存器。
一開(kāi)始我只找了R14的描述,然后覺(jué)得很奇怪,上圖中的程序怎么會(huì)跳到R14(PC的內(nèi)容)所指的地址呢,不應(yīng)該是PC+1嗎(我只了解一點(diǎn)51內(nèi)核,cortex-M內(nèi)核還沒(méi)怎么了解,以后有時(shí)間得看看它的內(nèi)容),所以才查了PC寄存器的內(nèi)容。
閱讀這些匯編代碼需要對(duì)照ARM與Thumb指令集說(shuō)明(除非特殊需要,不然應(yīng)該沒(méi)什么人會(huì)去背這個(gè)吧)。可以在keil提供的“幫助”窗口查閱。
任務(wù)優(yōu)先級(jí)與中斷優(yōu)先級(jí)
中斷優(yōu)先級(jí)用于硬件中斷管理,屬于微處理器內(nèi)核的內(nèi)容。cortex-M4的中斷優(yōu)先級(jí)數(shù)字越大優(yōu)先級(jí)越低。
任務(wù)優(yōu)先級(jí)用于調(diào)度器的任務(wù)管理,屬于操作系統(tǒng)的內(nèi)容。FreeRTOS中任務(wù)優(yōu)先級(jí)數(shù)字越大優(yōu)先級(jí)越高。
vTaskDelay相對(duì)延時(shí)與vTaskDelayUntil絕對(duì)延時(shí)
我試著寫(xiě)了一段代碼來(lái)進(jìn)行說(shuō)明
我畫(huà)了個(gè)非常潦草的圖來(lái)說(shuō)明Task_1的運(yùn)行情況(這里假設(shè)Task_1處于運(yùn)行狀態(tài)的時(shí)間非常短,沒(méi)有畫(huà)出來(lái))
可以看到用相對(duì)延時(shí)的話任務(wù)每次被喚醒的時(shí)間間隔可能是55個(gè)tick,也可能是50個(gè)tick,絕對(duì)延時(shí)則會(huì)讓任務(wù)每次進(jìn)入就緒的時(shí)間是確定的。
關(guān)于消息隊(duì)列、信號(hào)量與互斥量
用于進(jìn)程間通信或同步的手段。信號(hào)量與互斥量的創(chuàng)建利用了消息隊(duì)列的數(shù)據(jù)結(jié)構(gòu)。它們可以實(shí)現(xiàn)任務(wù)間的通信,本質(zhì)上就是函數(shù)利用全局變量來(lái)對(duì)其他函數(shù)產(chǎn)生影響(進(jìn)行通信)。它們可以讓等待它們的任務(wù)進(jìn)入阻塞狀態(tài)。
事件位
相比起消息隊(duì)列,它的結(jié)構(gòu)更加簡(jiǎn)單。不能傳遞值,只傳遞事件標(biāo)志。
任務(wù)通知
相比起上述的時(shí)間和消息隊(duì)列,它更更簡(jiǎn)單,不需要?jiǎng)?chuàng)建該對(duì)象,也就不需要額外花費(fèi)RAM空間,因?yàn)樗窃谌蝿?wù)控制塊(TCB)里定義了uint32_t類型的變量(用來(lái)傳遞值)和uint8_t類型的變量(用來(lái)傳遞狀態(tài))來(lái)實(shí)現(xiàn)的,它的功能函數(shù)的實(shí)現(xiàn)也更加簡(jiǎn)單。(FreeRTOS V8.2.0 才開(kāi)始支持的)
內(nèi)存管理方案
heap_1.c:最簡(jiǎn)單的,只能申請(qǐng)內(nèi)存,不能進(jìn)行內(nèi)存釋放,申請(qǐng)內(nèi)存的時(shí)間是一個(gè)常量。
heap_2.c:可刪除內(nèi)存。采用了一種最佳匹配算法,但是會(huì)產(chǎn)生內(nèi)存碎片。
heap_3.c:簡(jiǎn)單地封裝了標(biāo)準(zhǔn) C 庫(kù)中的 malloc()和 free()函數(shù)。此時(shí)FreeRTOSConfig.h中的configTOTAL_HEAP_SIZE 宏定義不起作用,使用啟動(dòng)文件里劃分的堆空間。
heap_4.c:在heap_2.c的基礎(chǔ)上多了合并相鄰內(nèi)存空間的功能。
heap_5.c:在heap_4.c的基礎(chǔ)上把外擴(kuò)RAM也納入分配空間。
關(guān)于“stdint.readme”文件
在C語(yǔ)言的發(fā)展過(guò)程中會(huì)添加新的標(biāo)準(zhǔn),所以會(huì)有C89、C99、C11標(biāo)準(zhǔn)。不同的編譯器對(duì)標(biāo)準(zhǔn)的支持程度不一樣,有的會(huì)支持新出的標(biāo)準(zhǔn)而有的則不會(huì)。FreeRTOS希望在不同的編譯器下都可以編譯,所以它沒(méi)有用C99及C99之后的內(nèi)容,除了C99引進(jìn)的stdint.h。這個(gè)文件定義了一些整數(shù)類型和宏。
如果編譯器不提供stdint.h的話,就把FreeRTOS/Source/include路徑下的stdint.readme改成stdint.h并放到工程指定的頭文件路徑下吧。
關(guān)于協(xié)程
和任務(wù)(task)相似的東西,但是對(duì)RAM的要求很低,是為存儲(chǔ)非常小的情況設(shè)計(jì)的,但是使用起來(lái)有更多的限制,現(xiàn)在用的很少,FreeRTOS官方雖然沒(méi)有刪除協(xié)程,但并不再打算更新這部分內(nèi)容。
關(guān)于互斥量的優(yōu)先級(jí)繼承機(jī)制
一個(gè)用來(lái)減輕優(yōu)先級(jí)反轉(zhuǎn)危害的措施。當(dāng)更高優(yōu)先級(jí)的任務(wù)(A)要獲取一個(gè)被低優(yōu)先級(jí)任務(wù)(B)占用的互斥量時(shí),A不可避免的要等待B,如果B的優(yōu)先級(jí)臨時(shí)提高到與A一樣,可以避免優(yōu)先級(jí)介于兩者的任務(wù)這時(shí)候摻一腳(假設(shè)有任務(wù)C,優(yōu)先級(jí)順序A>C>B,沒(méi)有優(yōu)先級(jí)繼承的話C可能會(huì)搶占B,導(dǎo)致A又要等B又要等C執(zhí)行)。如果不想出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn),要硬實(shí)時(shí)之類的,應(yīng)該是在一開(kāi)始的設(shè)計(jì)階段就要考慮。
在STM32CubeMX中配置FreeRTOS
原本我是計(jì)劃學(xué)習(xí)μC/OS的,因?yàn)椴閷W(xué)習(xí)路徑的時(shí)候網(wǎng)上都推薦μC/OS,不過(guò)在使用STM32CubeMX的時(shí)候發(fā)現(xiàn)它提供了FreeRTOS中間件并可以快速配置,所以選擇了先學(xué)習(xí)FreeRTOS。
STM32CubeMX中FreeRTOS的配置界面。
CMSIS是什么
CMSIS是ARM和一眾廠商一起決定的 Cortex-M 處理器系列的通用接口,相當(dāng)于大家圍在一起制定了標(biāo)準(zhǔn)并提供了各種外設(shè)函數(shù)、實(shí)時(shí)操作系統(tǒng)和中間設(shè)備等在該標(biāo)準(zhǔn)下的通用接口的實(shí)現(xiàn),使用通用接口的話,應(yīng)用程序可以更簡(jiǎn)單地在不同廠商的Cortex-M處理器之間反復(fù)橫跳(移植更方便),V2表示版本2。
版本選擇不了,STM32CubeMX里面好像只能用新版本的FreeRTOS。
點(diǎn)擊選項(xiàng)可以看見(jiàn)參數(shù)說(shuō)明等。
創(chuàng)建任務(wù)
任務(wù)、隊(duì)列等直接在STM32CubeMX里面就可以創(chuàng)建好,不必在編輯器里面寫(xiě)。
STM32CubeMX默認(rèn)創(chuàng)建了一個(gè)defaultTask,這是一個(gè)啥都不做的任務(wù),每次執(zhí)行就阻塞1個(gè)tick。
我創(chuàng)建了兩個(gè)任務(wù)
關(guān)于優(yōu)先級(jí)
單從配置的名字看不出具體的優(yōu)先級(jí)數(shù)字,只能看出優(yōu)先級(jí)先后。我在cmsis_os2.h里面找到它的定義。
優(yōu)先級(jí)個(gè)數(shù)超過(guò)32個(gè),可見(jiàn)沒(méi)有使用硬件優(yōu)化任務(wù)查找(Cortex-M處理器提供了一個(gè)計(jì)算前導(dǎo)零的指令‘CLZ’可以優(yōu)化優(yōu)先級(jí)查找)。
關(guān)于Timebase Source
Timebase Source如果使用SysTick,那么在生成代碼前會(huì)有這樣的警告。
原因是使用HAL庫(kù)的一些函數(shù)(比如延時(shí))會(huì)依賴一個(gè)時(shí)鐘源提供周期的節(jié)拍,這和操作系統(tǒng)的tick(心跳)是一樣的道理,這里的Timebase Source是設(shè)定HA庫(kù)函數(shù)L所依賴的時(shí)鐘源,如果把時(shí)鐘源設(shè)為內(nèi)核的SysTick,相當(dāng)于HAL庫(kù)和操作系統(tǒng)共用一個(gè)“心臟”,如果HAL庫(kù)的某個(gè)功能把“心臟”給停了那么操作系統(tǒng)會(huì)受到影響。
給HAL庫(kù)換一個(gè)時(shí)鐘源就好了,我這里用的TIM1。
順便一提,HAL庫(kù)用來(lái)記錄節(jié)拍數(shù)的全局變量叫uwTick,FreeRTOS用來(lái)記錄節(jié)拍數(shù)的全局變量叫xTickCount。
編寫(xiě)任務(wù)函數(shù)
創(chuàng)建好任務(wù)后產(chǎn)生代碼,接下來(lái)寫(xiě)任務(wù)功能就好了,我這里就是簡(jiǎn)單的點(diǎn)燈,把freetros.c里的任務(wù)函數(shù)補(bǔ)充好。
現(xiàn)象
燈光顏色周期性地變化。
總結(jié)
以上是生活随笔為你收集整理的FreeRTOS学习记录的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 记录从零学习LaTeX的点滴
- 下一篇: 华师2017高等工程数学期末试题