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