1
FreeRTOS移植及配置
在程序中,移植了正點原子的基于STM32的FreeRTOS程序,編寫了自己的內(nèi)存管理程序malloc.c,程序主要結(jié)構(gòu)如下:
FreeRTOS_CORE中是FreeRTOS的核心文件,包括與協(xié)程有關(guān)的croutine.c,與事件組有關(guān)的event_groups.c,與列表有關(guān)的list.c,與隊列有關(guān)的queue.c,與任務(wù)有關(guān)的tasks.c,與定時器時鐘有關(guān)的timers.c。
FreeRTOS_PORTABLE中是與FreeRTOS內(nèi)存管理有關(guān)的文件,包括port.c和heap_4.c,port.c中主要包含一些與中斷有關(guān)的函數(shù),heap_4.c上一篇文章有詳細(xì)介紹,與內(nèi)存分配釋放有關(guān)。
在FreeRTOSConfig.h文件中,對FreeRTOS的很多參數(shù)進行了配置,用戶可以修改其中的文件對程序參量進行配置并對程序功能進行選擇使用。
先聲明了一個斷言,方便提示用戶,當(dāng)程序出錯時在FreeRTOS的那個程序哪一行出錯。
//斷言
#define vAssertCalled(char,int) printf("Error:%s,%drn",char,int)
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)
然后是與任務(wù)調(diào)度算法相關(guān)的配置選項,主要的是configUSE_PREEMPTION和configUSE_TIME_SLICING,不過一般都開啟,置1就行。
/***************************************************************************************************************/
/* 調(diào)度算法配置選項 */
/***************************************************************************************************************/
#define configUSE_PREEMPTION 1 //1使用搶占式內(nèi)核,0使用協(xié)程
#define configUSE_TIME_SLICING 1 //1使能時間片調(diào)度(默認(rèn)是使能的),同等優(yōu)先級是否交替執(zhí)行
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 //1啟用特殊方法(使用硬件方法)來選擇下一個要運行的任務(wù)
//一般是硬件計算前導(dǎo)零指令,如果所使用的
//MCU沒有這些硬件指令的話此宏應(yīng)該設(shè)置為0!
#define configUSE_TICKLESS_IDLE 0 //1啟用低功耗tickless模式
然后是基礎(chǔ)配置選項,重要的幾個是configUSE_QUEUE_SETS、configCPU_CLOCK_HZ、configTICK_RATE_HZ、configMAX_TASK_NAME_LEN、configUSE_16_BIT_TICKS,其他保持默認(rèn),configCPU_CLOCK_HZ要改成我們單片機的時鐘頻率,這里使用了代碼定義的量SystemCoreClock,configTICK_RATE_HZ時鐘節(jié)拍頻率設(shè)置為1000,也就是周期為1ms,任務(wù)名字字符串長度configMAX_TASK_NAME_LEN如果你創(chuàng)建任務(wù)時的命名很長,需要改一下,configUSE_16_BIT_TICKS使用的時鐘位數(shù),需要和單片機一致,STM32為32位,這里需要將這個宏定義為0。
/***************************************************************************************************************/
/* FreeRTOS基礎(chǔ)配置配置選項 */
/***************************************************************************************************************/
#define configUSE_QUEUE_SETS 1 //為1時啟用隊列
#define configCPU_CLOCK_HZ (SystemCoreClock) //CPU頻率
#define configTICK_RATE_HZ (1000) //時鐘節(jié)拍頻率,這里設(shè)置為1000,周期就是1ms
#define configMAX_PRIORITIES (32) //可使用的最大優(yōu)先級
#define configMINIMAL_STACK_SIZE ((unsigned short)130) //空閑任務(wù)使用的堆棧大小
#define configMAX_TASK_NAME_LEN (20) //任務(wù)名字字符串長度
#define configUSE_16_BIT_TICKS 0 //系統(tǒng)節(jié)拍計數(shù)器變量數(shù)據(jù)類型,
//1表示為16位無符號整形,0表示為32位無符號整形
#define configIDLE_SHOULD_YIELD 1 //為1時空閑任務(wù)放棄CPU使用權(quán)給其他同優(yōu)先級的用戶任務(wù)
#define configUSE_TASK_NOTIFICATIONS 1 //為1時開啟任務(wù)通知功能,默認(rèn)開啟
#define configUSE_MUTEXES 1 //為1時使用互斥信號量
#define configQUEUE_REGISTRY_SIZE 8 //不為0時表示啟用隊列記錄,具體的值是可以
//記錄的隊列和信號量最大數(shù)目。
#define configCHECK_FOR_STACK_OVERFLOW 0 //大于0時啟用堆棧溢出檢測功能,如果使用此功能
//用戶必須提供一個棧溢出鉤子函數(shù),如果使用的話
//此值可以為1或者2,因為有兩種棧溢出檢測方法。
#define configUSE_RECURSIVE_MUTEXES 1 //為1時使用遞歸互斥信號量
#define configUSE_MALLOC_FAILED_HOOK 0 //1使用內(nèi)存申請失敗鉤子函數(shù)
#define configUSE_APPLICATION_TASK_TAG 0 //1為每個任務(wù)分配一個“標(biāo)簽”值,標(biāo)簽鉤子函數(shù)
#define configUSE_COUNTING_SEMAPHORES 1 //為1時使用計數(shù)信號量
然后是與內(nèi)存申請有關(guān)配置選項,一般保持默認(rèn)就行,系統(tǒng)總堆大小可能需要微調(diào)。
/***************************************************************************************************************/
#define configSUPPORT_DYNAMIC_ALLOCATION 1 //支持動態(tài)內(nèi)存申請
#define configTOTAL_HEAP_SIZE ((size_t)(20*1024)) //系統(tǒng)所有總的堆大小
然后是與鉤子函數(shù)有關(guān)的配置選項,我們不需要使用鉤子函數(shù),也不建議初學(xué)者用,因為空閑任務(wù)的鉤子函數(shù)需要寫的很高效率盡量不阻塞。
#define configUSE_IDLE_HOOK 0 //1,使用空閑鉤子;0,不使用
#define configUSE_TICK_HOOK 0 //1,使用時間片鉤子;0,不使用
與運行時間和任務(wù)狀態(tài)收集有關(guān)的配置選項 ,這部分主要與調(diào)試代碼有關(guān),測一下代碼運行時間,觀察效率等。
/***************************************************************************************************************/
/* FreeRTOS與運行時間和任務(wù)狀態(tài)收集有關(guān)的配置選項 */
/***************************************************************************************************************/
#define configGENERATE_RUN_TIME_STATS 0 //為1時啟用運行時間統(tǒng)計功能
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ConfigureTimeForRunTimeStats() //定時器3提供時間統(tǒng)計的時基,頻率為10K,即周期為100us
#define portGET_RUN_TIME_COUNTER_VALUE() FreeRTOSRunTimeTicks //獲取時間統(tǒng)計時間值
#define configUSE_TRACE_FACILITY 1 //為1啟用可視化跟蹤調(diào)試
#define configUSE_STATS_FORMATTING_FUNCTIONS 1 //與宏configUSE_TRACE_FACILITY同時為1時會編譯下面3個函數(shù)
與協(xié)程有關(guān)的配置選項,保持默認(rèn),我們用不到協(xié)程。
/***************************************************************************************************************/
/* FreeRTOS與協(xié)程有關(guān)的配置選項 */
/***************************************************************************************************************/
#define configUSE_CO_ROUTINES 0 //為1時啟用協(xié)程,啟用協(xié)程以后必須添加文件croutine.c
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) //協(xié)程的有效優(yōu)先級數(shù)目
與軟件定時器有關(guān)的配置選項,F(xiàn)reeRTOS自帶軟件定時器,但是我們一般也用不到,STM32中有硬件定時器-滴答定時器,用這個就行。
/***************************************************************************************************************/
/* FreeRTOS與軟件定時器有關(guān)的配置選項 */
/***************************************************************************************************************/
#define configUSE_TIMERS 1 //為1時啟用軟件定時器
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1) //軟件定時器優(yōu)先級
#define configTIMER_QUEUE_LENGTH 5 //軟件定時器隊列長度
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2) //軟件定時器任務(wù)堆棧大小
FreeRTOS可選函數(shù)配置選項,這些以INCLUDE_開頭的宏定義,主要是一些函數(shù)的開關(guān),比如第一個INCLUDE_xTaskGetSchedulerState,設(shè)置為1意味著,使用TaskGetSchedulerState這個函數(shù)。
/***************************************************************************************************************/
/* FreeRTOS可選函數(shù)配置選項 */
/***************************************************************************************************************/
#define INCLUDE_xTaskGetSchedulerState 1 //獲取任務(wù)調(diào)度狀態(tài)
#define INCLUDE_vTaskPrioritySet 1 //任務(wù)優(yōu)先級設(shè)置
#define INCLUDE_uxTaskPriorityGet 1 //獲取任務(wù)優(yōu)先級
#define INCLUDE_vTaskDelete 1 //刪除任務(wù)
#define INCLUDE_vTaskCleanUpResources 1 //清理任務(wù)占用資源
#define INCLUDE_vTaskSuspend 1 //暫停任務(wù)
#define INCLUDE_vTaskDelayUntil 1 //掛起任務(wù),固定時間周期
#define INCLUDE_vTaskDelay 1 //掛起任務(wù),延時一段時間
#define INCLUDE_eTaskGetState 1 //獲取任務(wù)狀態(tài)
#define INCLUDE_xTimerPendFunctionCall 1 //暫停狀態(tài)喚起
FreeRTOS與中斷有關(guān)的配置選項,用不著,也不用深究。
/***************************************************************************************************************/
/* FreeRTOS與中斷有關(guān)的配置選項 */
/***************************************************************************************************************/
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 //中斷最低優(yōu)先級
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 //系統(tǒng)可管理的最高中斷優(yōu)先級
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY < < (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY < < (8 - configPRIO_BITS) )
最后是與中斷服務(wù)函數(shù)有關(guān)的配置選項,只不過做了兩個重命名。
/***************************************************************************************************************/
/* FreeRTOS與中斷服務(wù)函數(shù)有關(guān)的配置選項 */
/***************************************************************************************************************/
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
2
使用隊列進行任務(wù)間的同步與互斥
原來的程序是STM32裸機開發(fā),程序是順序執(zhí)行,可能使用中斷,對于電池電量收集、IMU數(shù)據(jù)采集、超聲波檢測等功能都是順序執(zhí)行,采用頻率控制各個“進程”,即在while循環(huán)中使用函數(shù)獲取程序當(dāng)前時間,減去上一時刻時間,當(dāng)差值大于某一值時,運行相應(yīng)函數(shù),如下圖所示。
而在改進后的程序中,采用實時系統(tǒng),使用多線程的方式管理這些功能,為每個功能創(chuàng)建一個任務(wù)(線程),根據(jù)任務(wù)調(diào)度機制運行,如下圖所示。
并為各個任務(wù)設(shè)置優(yōu)先級,move_base是最基本的移動任務(wù)優(yōu)先級最高。
各個任務(wù)之間通過隊列進行溝通,不然move_base任務(wù)的優(yōu)先級最高,則會一直執(zhí)行,其他任務(wù)得不到執(zhí)行時間。
在move_base任務(wù)中以電池電量采集任務(wù)的執(zhí)行為例:
代碼邏輯:當(dāng)執(zhí)行到這部分代碼時,判斷電池隊列的句柄是否為NULL(之前創(chuàng)建了,所以不為空),然后使用xQueueReceive函數(shù)進行隊列數(shù)據(jù)采集,從Battery_Queue隊列中將數(shù)據(jù)采集到Receive_vattery_volt變量中,剛開始采集時,隊列中肯定是沒數(shù)據(jù)的,這是就會進入隊列阻塞,阻塞時間為portMAX_DELAY直到隊列中有數(shù)據(jù),這時move_base任務(wù)則會掛起,低優(yōu)先級的任務(wù)會執(zhí)行,當(dāng)執(zhí)行到電池電量采集任務(wù)時,向隊列中放入了數(shù)據(jù),move_base任務(wù)則會搶占,繼續(xù)執(zhí)行,然后將采集到的數(shù)據(jù)發(fā)給上位機。
其他任務(wù)都是這個邏輯,使用這種方法的好處就是提高了我們程序的效率,使得STM32程序多線程執(zhí)行,并且提供了很多接口,方便用戶修改和閱讀。
3
總結(jié)
FreeRTOS在STM32F103上的移植完結(jié),整理來說FreeRTOS還是挺簡單的,不過很多知識沒用到我也就沒仔細(xì)學(xué),任務(wù)之間的同步互斥與溝通有很多方式,除了隊列還有信號量、互斥量等等,我只是選擇了最常用的一種。
-
定時器
+關(guān)注
關(guān)注
23文章
3271瀏覽量
116575 -
FreeRTOS
+關(guān)注
關(guān)注
12文章
485瀏覽量
63400 -
上位機
+關(guān)注
關(guān)注
27文章
952瀏覽量
55432 -
STM32F103
+關(guān)注
關(guān)注
33文章
481瀏覽量
64802 -
定時中斷
+關(guān)注
關(guān)注
0文章
19瀏覽量
8658
發(fā)布評論請先 登錄
相關(guān)推薦
STM32F103控制ad7606采集程序分享
將FreeRTOS移植到STM32F103中的步驟
如何去完成STM32F103的FreeRTOS操作系統(tǒng)的移植呢
基于STM32F103的FreeRTOS移植其代碼該如何去實現(xiàn)呢
基于STM32F103的振動監(jiān)測系統(tǒng)設(shè)計
在GD32F103移植STM32F103代碼

華為LiteOS系統(tǒng)移植到STM32F103開發(fā)板(基于MDK環(huán)境)

FreeRTOS移植到STM32F103步驟與注意事項

評論