日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

FreeRTOS学习记录

發(fā)布時(shí)間:2024/1/1 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 FreeRTOS学习记录 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

FreeRTOS學(xué)習(xí)記錄

  • 前言
  • FreeRTOS學(xué)習(xí)記錄
  • 在STM32CubeMX中配置FreeRTOS

前言

本人小白,最近學(xué)習(xí)了FreeRTOS操作系統(tǒng),打算做一點(diǎn)記錄。
學(xué)習(xí)的過程中雖然做了點(diǎn)練習(xí),不過都是跟著例程來的,大部分沒什么記錄必要,本文更多的是零零散散地記錄我在學(xué)習(xí)過程中的一些疑惑與個(gè)人見解,可能會有錯(cuò)誤還請指出。
這只是一個(gè)初學(xué)者的簡單學(xué)習(xí)記錄,并沒有全面系統(tǒng)的知識介紹,學(xué)習(xí)的話還是要看對應(yīng)的教程啦。

使用到的工具及版本:
FreeRTOS:V9.0.0

STM32CubeMX版本:6.3.0
HAL庫:STM32CubeF4 Firmware Package V1.26.2
MDK-ARM:V5.32.0.0
開發(fā)板:野火的霸天虎開發(fā)板V2(主控芯片是STM32F407ZGT6)

參考的資料:
野火的《FreeRTOS內(nèi)核實(shí)現(xiàn)與應(yīng)用開發(fā)實(shí)戰(zhàn)》
FreeRTOS入門手冊_中文
FreeRTOS官網(wǎng)提供的介紹。

FreeRTOS學(xué)習(xí)記錄

本文對port部分(移植層面)源碼的探究查看的是FreeRTOSv9.0.0\FreeRTOS\Source\portable\RVDS\ARM_CM4_MPU,這是我所使用的開發(fā)工具和硬件決定的。其他工具或者硬件上的實(shí)現(xiàn)可能不一樣。

任務(wù)會在什么情況下切換?
1、 我們自己編寫的程序所引起的任務(wù)切換。比如我們自己調(diào)用taskYIELD(),或者我們調(diào)用的API函數(shù)里面包含了任務(wù)切換(這種時(shí)候往往是當(dāng)前任務(wù)進(jìn)入阻塞/掛起,又或者是有更高優(yōu)先級的任務(wù)加入了就緒列表,API函數(shù)里面會在需要的時(shí)候去調(diào)用任務(wù)切換)。
2、 tick中斷里面會觸發(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)度中所有的切換都是“顯式的”。
顯而易見協(xié)作式調(diào)度比搶占式調(diào)度有著更高的CPU效率,畢竟不需要每個(gè)tick中斷就跑去看看是不是要切換任務(wù)。但是它存在著高優(yōu)先級中斷可能得不到快速響應(yīng)的風(fēng)險(xiǎn),這取決于我們開發(fā)者的程序設(shè)計(jì),得看我們是不是在合適的時(shí)機(jī)調(diào)用了任務(wù)切換,如果程序結(jié)構(gòu)復(fù)雜的話我們可能得時(shí)時(shí)刻刻考慮是不是該在什么地方進(jìn)行一次任務(wù)切換,這可太麻煩了,有的時(shí)候甚至不現(xiàn)實(shí)。
通過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)核的,后面的是芯片廠商的)

來看看SysTick_Handler(systick中斷函數(shù))。

void SysTick_Handler(void) { #if (INCLUDE_xTaskGetSchedulerState == 1 )if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED){#endif /* INCLUDE_xTaskGetSchedulerState */ xPortSysTickHandler();#if (INCLUDE_xTaskGetSchedulerState == 1 )}#endif /* INCLUDE_xTaskGetSchedulerState */ }

如果調(diào)度器此時(shí)壓根還沒開啟,或者調(diào)用了vTaskEndScheduler( void ),那么這里的xPortSysTickHandler()并不會執(zhí)行,系統(tǒng)節(jié)拍則不會增加(當(dāng)然前提是宏INCLUDE_xTaskGetSchedulerState定義為1)。調(diào)度器如果掛起,系統(tǒng)時(shí)間依然會增加。
接下來看看xPortSysTickHandler()里面是啥。

void xPortSysTickHandler( void ) {/* The SysTick runs at the lowest interrupt priority, so when this interruptexecutes all interrupts must be unmasked. There is therefore no need tosave and then restore the interrupt mask value as its value is alreadyknown - therefore the slightly faster vPortRaiseBASEPRI() function is usedin place of portSET_INTERRUPT_MASK_FROM_ISR(). */vPortRaiseBASEPRI();{/* Increment the RTOS tick. */if( xTaskIncrementTick() != pdFALSE ){/* A context switch is required. Context switching is performed inthe PendSV interrupt. Pend the PendSV interrupt. */portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;}}vPortClearBASEPRIFromISR(); }

首先調(diào)用了vPortRaiseBASEPRI()把中斷屏蔽,它的實(shí)現(xiàn)利用了Cotex-M4內(nèi)核的BASEPRI register(這張圖是從《STM32F4xx-Cortex_-M4內(nèi)核參考手冊》截的)。

然后調(diào)用的xTaskIncrementTick() 則是實(shí)現(xiàn)了:
1、系統(tǒng)tick加1(無論調(diào)度器是否掛起)
2、如果tick溢出,把延時(shí)列表和延時(shí)溢出列表交換
3、處理延時(shí)列表的事宜
4、如果是搶占式調(diào)度,會返回pdTRUE,這樣接下來就會把PendSV中斷標(biāo)志位置位,進(jìn)入PendSV中斷服務(wù)完成任務(wù)切換。如果是協(xié)作式調(diào)度則返回pdFALSE,不會置位PendSV中斷標(biāo)志。

接下來看看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)采用了多級流水線技術(shù),對于ARM指令集而言,PC總是指向當(dāng)前指令的下兩條指令的地址,即PC的值為當(dāng)前指令的地址值加8個(gè)字節(jié)程序狀態(tài)寄存器。

一開始我只找了R14的描述,然后覺得很奇怪,上圖中的程序怎么會跳到R14(PC的內(nèi)容)所指的地址呢,不應(yīng)該是PC+1嗎(我只了解一點(diǎn)51內(nèi)核,cortex-M內(nèi)核還沒怎么了解,以后有時(shí)間得看看它的內(nèi)容),所以才查了PC寄存器的內(nèi)容。
閱讀這些匯編代碼需要對照ARM與Thumb指令集說明(除非特殊需要,不然應(yīng)該沒什么人會去背這個(gè)吧)。可以在keil提供的“幫助”窗口查閱。


任務(wù)優(yōu)先級與中斷優(yōu)先級
中斷優(yōu)先級用于硬件中斷管理,屬于微處理器內(nèi)核的內(nèi)容。cortex-M4的中斷優(yōu)先級數(shù)字越大優(yōu)先級越低。
任務(wù)優(yōu)先級用于調(diào)度器的任務(wù)管理,屬于操作系統(tǒng)的內(nèi)容。FreeRTOS中任務(wù)優(yōu)先級數(shù)字越大優(yōu)先級越高。
vTaskDelay相對延時(shí)與vTaskDelayUntil絕對延時(shí)
我試著寫了一段代碼來進(jìn)行說明

void Task_1( void* parameter ) {/* 用于保存上次時(shí)間。調(diào)用后自動更新 */static portTickType PreviousWakeTime;...PreviousWakeTime = xTaskGetTickCount(); for( ;; ){... /*假設(shè)此處可能切換成了更高優(yōu)先級的任務(wù)并去執(zhí)行,并假設(shè)執(zhí)行該任務(wù)要花費(fèi)5個(gè)tick才會把CPU釋放給Task_1*/vTaskDelay(50) or vTaskDelayUntil(&PreviousWakeTime,50) } }

我畫了個(gè)非常潦草的圖來說明Task_1的運(yùn)行情況(這里假設(shè)Task_1處于運(yùn)行狀態(tài)的時(shí)間非常短,沒有畫出來)

可以看到用相對延時(shí)的話任務(wù)每次被喚醒的時(shí)間間隔可能是55個(gè)tick,也可能是50個(gè)tick,絕對延時(shí)則會讓任務(wù)每次進(jìn)入就緒的時(shí)間是確定的。
關(guān)于消息隊(duì)列、信號量與互斥量
用于進(jìn)程間通信或同步的手段。信號量與互斥量的創(chuàng)建利用了消息隊(duì)列的數(shù)據(jù)結(jié)構(gòu)。它們可以實(shí)現(xiàn)任務(wù)間的通信,本質(zhì)上就是函數(shù)利用全局變量來對其他函數(shù)產(chǎn)生影響(進(jìn)行通信)。它們可以讓等待它們的任務(wù)進(jìn)入阻塞狀態(tài)。
事件位
相比起消息隊(duì)列,它的結(jié)構(gòu)更加簡單。不能傳遞值,只傳遞事件標(biāo)志。
任務(wù)通知
相比起上述的時(shí)間和消息隊(duì)列,它更更簡單,不需要?jiǎng)?chuàng)建該對象,也就不需要額外花費(fèi)RAM空間,因?yàn)樗窃谌蝿?wù)控制塊(TCB)里定義了uint32_t類型的變量(用來傳遞值)和uint8_t類型的變量(用來傳遞狀態(tài))來實(shí)現(xiàn)的,它的功能函數(shù)的實(shí)現(xiàn)也更加簡單。(FreeRTOS V8.2.0 才開始支持的)
內(nèi)存管理方案
heap_1.c:最簡單的,只能申請內(nèi)存,不能進(jìn)行內(nèi)存釋放,申請內(nèi)存的時(shí)間是一個(gè)常量。
heap_2.c:可刪除內(nèi)存。采用了一種最佳匹配算法,但是會產(chǎn)生內(nèi)存碎片。
heap_3.c:簡單地封裝了標(biāo)準(zhǔn) C 庫中的 malloc()和 free()函數(shù)。此時(shí)FreeRTOSConfig.h中的configTOTAL_HEAP_SIZE 宏定義不起作用,使用啟動文件里劃分的堆空間。
heap_4.c:在heap_2.c的基礎(chǔ)上多了合并相鄰內(nèi)存空間的功能。
heap_5.c:在heap_4.c的基礎(chǔ)上把外擴(kuò)RAM也納入分配空間。
關(guān)于“stdint.readme”文件
在C語言的發(fā)展過程中會添加新的標(biāo)準(zhǔn),所以會有C89、C99、C11標(biāo)準(zhǔn)。不同的編譯器對標(biāo)準(zhǔn)的支持程度不一樣,有的會支持新出的標(biāo)準(zhǔn)而有的則不會。FreeRTOS希望在不同的編譯器下都可以編譯,所以它沒有用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)相似的東西,但是對RAM的要求很低,是為存儲非常小的情況設(shè)計(jì)的,但是使用起來有更多的限制,現(xiàn)在用的很少,FreeRTOS官方雖然沒有刪除協(xié)程,但并不再打算更新這部分內(nèi)容。
關(guān)于互斥量的優(yōu)先級繼承機(jī)制
一個(gè)用來減輕優(yōu)先級反轉(zhuǎn)危害的措施。當(dāng)更高優(yōu)先級的任務(wù)(A)要獲取一個(gè)被低優(yōu)先級任務(wù)(B)占用的互斥量時(shí),A不可避免的要等待B,如果B的優(yōu)先級臨時(shí)提高到與A一樣,可以避免優(yōu)先級介于兩者的任務(wù)這時(shí)候摻一腳(假設(shè)有任務(wù)C,優(yōu)先級順序A>C>B,沒有優(yōu)先級繼承的話C可能會搶占B,導(dǎo)致A又要等B又要等C執(zhí)行)。如果不想出現(xiàn)優(yōu)先級反轉(zhuǎn),要硬實(shí)時(shí)之類的,應(yīng)該是在一開始的設(shè)計(jì)階段就要考慮。

在STM32CubeMX中配置FreeRTOS

原本我是計(jì)劃學(xué)習(xí)μC/OS的,因?yàn)椴閷W(xué)習(xí)路徑的時(shí)候網(wǎng)上都推薦μC/OS,不過在使用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)用程序可以更簡單地在不同廠商的Cortex-M處理器之間反復(fù)橫跳(移植更方便),V2表示版本2。
版本選擇不了,STM32CubeMX里面好像只能用新版本的FreeRTOS。
點(diǎn)擊選項(xiàng)可以看見參數(shù)說明等。

創(chuàng)建任務(wù)
任務(wù)、隊(duì)列等直接在STM32CubeMX里面就可以創(chuàng)建好,不必在編輯器里面寫。
STM32CubeMX默認(rèn)創(chuàng)建了一個(gè)defaultTask,這是一個(gè)啥都不做的任務(wù),每次執(zhí)行就阻塞1個(gè)tick。
我創(chuàng)建了兩個(gè)任務(wù)


關(guān)于優(yōu)先級
單從配置的名字看不出具體的優(yōu)先級數(shù)字,只能看出優(yōu)先級先后。我在cmsis_os2.h里面找到它的定義。

/// Priority values. typedef enum {osPriorityNone = 0, ///< No priority (not initialized).osPriorityIdle = 1, ///< Reserved for Idle thread.osPriorityLow = 8, ///< Priority: lowosPriorityLow1 = 8+1, ///< Priority: low + 1osPriorityLow2 = 8+2, ///< Priority: low + 2osPriorityLow3 = 8+3, ///< Priority: low + 3osPriorityLow4 = 8+4, ///< Priority: low + 4osPriorityLow5 = 8+5, ///< Priority: low + 5osPriorityLow6 = 8+6, ///< Priority: low + 6osPriorityLow7 = 8+7, ///< Priority: low + 7osPriorityBelowNormal = 16, ///< Priority: below normalosPriorityBelowNormal1 = 16+1, ///< Priority: below normal + 1osPriorityBelowNormal2 = 16+2, ///< Priority: below normal + 2osPriorityBelowNormal3 = 16+3, ///< Priority: below normal + 3osPriorityBelowNormal4 = 16+4, ///< Priority: below normal + 4osPriorityBelowNormal5 = 16+5, ///< Priority: below normal + 5osPriorityBelowNormal6 = 16+6, ///< Priority: below normal + 6osPriorityBelowNormal7 = 16+7, ///< Priority: below normal + 7osPriorityNormal = 24, ///< Priority: normalosPriorityNormal1 = 24+1, ///< Priority: normal + 1osPriorityNormal2 = 24+2, ///< Priority: normal + 2osPriorityNormal3 = 24+3, ///< Priority: normal + 3osPriorityNormal4 = 24+4, ///< Priority: normal + 4osPriorityNormal5 = 24+5, ///< Priority: normal + 5osPriorityNormal6 = 24+6, ///< Priority: normal + 6osPriorityNormal7 = 24+7, ///< Priority: normal + 7osPriorityAboveNormal = 32, ///< Priority: above normalosPriorityAboveNormal1 = 32+1, ///< Priority: above normal + 1osPriorityAboveNormal2 = 32+2, ///< Priority: above normal + 2osPriorityAboveNormal3 = 32+3, ///< Priority: above normal + 3osPriorityAboveNormal4 = 32+4, ///< Priority: above normal + 4osPriorityAboveNormal5 = 32+5, ///< Priority: above normal + 5osPriorityAboveNormal6 = 32+6, ///< Priority: above normal + 6osPriorityAboveNormal7 = 32+7, ///< Priority: above normal + 7osPriorityHigh = 40, ///< Priority: highosPriorityHigh1 = 40+1, ///< Priority: high + 1osPriorityHigh2 = 40+2, ///< Priority: high + 2osPriorityHigh3 = 40+3, ///< Priority: high + 3osPriorityHigh4 = 40+4, ///< Priority: high + 4osPriorityHigh5 = 40+5, ///< Priority: high + 5osPriorityHigh6 = 40+6, ///< Priority: high + 6osPriorityHigh7 = 40+7, ///< Priority: high + 7osPriorityRealtime = 48, ///< Priority: realtimeosPriorityRealtime1 = 48+1, ///< Priority: realtime + 1osPriorityRealtime2 = 48+2, ///< Priority: realtime + 2osPriorityRealtime3 = 48+3, ///< Priority: realtime + 3osPriorityRealtime4 = 48+4, ///< Priority: realtime + 4osPriorityRealtime5 = 48+5, ///< Priority: realtime + 5osPriorityRealtime6 = 48+6, ///< Priority: realtime + 6osPriorityRealtime7 = 48+7, ///< Priority: realtime + 7osPriorityISR = 56, ///< Reserved for ISR deferred thread.osPriorityError = -1, ///< System cannot determine priority or illegal priority.osPriorityReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization. } osPriority_t;

優(yōu)先級個(gè)數(shù)超過32個(gè),可見沒有使用硬件優(yōu)化任務(wù)查找(Cortex-M處理器提供了一個(gè)計(jì)算前導(dǎo)零的指令‘CLZ’可以優(yōu)化優(yōu)先級查找)。

關(guān)于Timebase Source
Timebase Source如果使用SysTick,那么在生成代碼前會有這樣的警告。

原因是使用HAL庫的一些函數(shù)(比如延時(shí))會依賴一個(gè)時(shí)鐘源提供周期的節(jié)拍,這和操作系統(tǒng)的tick(心跳)是一樣的道理,這里的Timebase Source是設(shè)定HA庫函數(shù)L所依賴的時(shí)鐘源,如果把時(shí)鐘源設(shè)為內(nèi)核的SysTick,相當(dāng)于HAL庫和操作系統(tǒng)共用一個(gè)“心臟”,如果HAL庫的某個(gè)功能把“心臟”給停了那么操作系統(tǒng)會受到影響。
給HAL庫換一個(gè)時(shí)鐘源就好了,我這里用的TIM1。

順便一提,HAL庫用來記錄節(jié)拍數(shù)的全局變量叫uwTick,FreeRTOS用來記錄節(jié)拍數(shù)的全局變量叫xTickCount。
編寫任務(wù)函數(shù)
創(chuàng)建好任務(wù)后產(chǎn)生代碼,接下來寫任務(wù)功能就好了,我這里就是簡單的點(diǎn)燈,把freetros.c里的任務(wù)函數(shù)補(bǔ)充好。

/* USER CODE BEGIN Header_Task_LED_Red */ /*** @brief Function implementing the TASK_LED_RED thread.* @param argument: Not used* @retval None*/ /* USER CODE END Header_Task_LED_Red */ void Task_LED_Red(void *argument) {/* USER CODE BEGIN Task_LED_Red *//* Infinite loop */for(;;){HAL_GPIO_TogglePin(LED_RED_GPIO_Port,LED_RED_Pin);osDelay(3000);}/* USER CODE END Task_LED_Red */ }/* USER CODE BEGIN Header_Task_LED_Green */ /** * @brief Function implementing the TASK_LED_GREEN thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_Task_LED_Green */ void Task_LED_Green(void *argument) {/* USER CODE BEGIN Task_LED_Green *//* Infinite loop */for(;;){HAL_GPIO_TogglePin(LED_GREEN_GPIO_Port,LED_GREEN_Pin);osDelay(1000);}/* USER CODE END Task_LED_Green */ }

現(xiàn)象
燈光顏色周期性地變化。

總結(jié)

以上是生活随笔為你收集整理的FreeRTOS学习记录的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。