【STM32】FreeRTOS移植
00. 目錄
文章目錄
- 00. 目錄
- 01. 簡介
- 02. 預備工作
- 03. FreeRTOS移植
- 04. SYSTEM文件修改
- 05. 測試程序設計
- 06. 附錄
- 07. 參考
01. 簡介
我們已經了解了FreeRTOS,接下來我們就把FreeRTOS移植到我們所使用的平臺上,我們使用的板子是STM32F407。
02. 預備工作
要移植FreeRTOS,肯定需要一個基礎工程,基礎工程越簡單越好,我們就用之前的跑馬燈實驗作為基礎工程。
下載好FreeRTOS源碼
03. FreeRTOS移植
3.1 創建FreeRTOS文件夾
3.2 將FreeRTOS的源碼添加到FreeRTOS目錄中
FreeRTOSv10.4.1\FreeRTOS\Source
3.3 刪除portable文件夾中相關的文件和目錄
我們只需要留下keil、MemMang和RVDS這三個文件夾,其余的都可以刪除的。
3.4 向工程分組中添加文件
打開基礎工程,新建分組FreeRTOS_CORE和FreeRTOS_PORTABLE,然后向這兩個分組中添加文件。
FreeRTOS_CORE中的文件就在FreeRTOS目錄中。FreeRTOS_PORTABLE分組中的port.c和heap_4.c是怎么來的呢?port.c是RVDS文件夾下的ARM_CM4F中的文件,因為STM32F407是Cortex-M4內核并且帶有FPU,因此要選擇ARM_CM4F中的port.c文件。heap_4.c是MemMang文件夾中的,里面有5個c文件:heap_1.c,heap_2.c,heap_3.c,heap_4.c,heap_5.c。這5個c文件是五種不同的內存管理方法。
這5個文件都可以用來作為FreeRTOS的內存管理文件,只是它們的實現原理不同,各有利弊。后面我們再來討論FreeRTOS的內存管理。
3.5 添加響應的頭文件路徑
3.6 找不到頭文件的錯誤
..\FreeRTOS\croutine.c(27): error: #5: cannot open source input file "FreeRTOS.h": No such file or directory解決辦法
FreeRTOS.h頭文件在FreeRTOS\include目錄中。
再次編譯發現又有頭文件找不到FreeRTOSConfig.h文件
..\FreeRTOS\include\FreeRTOS.h(57): error: #5: cannot open source input file "FreeRTOSConfig.h": No such file or directory在項目文件中搜索該頭文件發現找不到,這個時候我們可以找找FreeRTOS的官方移植工程匯總會不會有這個文件,打開FreeRTOS針對STM32F407的移植工程文件,文件夾是H:\02_stm32\其它資料\FreeRTOS\FreeRTOSv10.4.1\FreeRTOS\Demo\CORTEX_M4F_STM32F407ZG-SK。
找到該文件之后,我們直接將它復制到FreeRTOS源碼中的include文件夾下。FreeRTOSConfig.h文件是配置文件,一般的操作系統都有裁剪、配置功能,而這些裁剪及配置都是通過一個文件來完成的,基本都是通過宏定義來完成對系統的配置和裁剪的。
3.7 SystemCoreClock沒有定義的錯誤
..\FreeRTOS\portable\RVDS\ARM_CM4F\port.c(732): error: #20: identifier "SystemCoreClock" is undefinedSystemCoreClock未定義,因為在FreeRTOSConfig.h中使用了SystemCoreClock來標記MCU的頻率。
這是一個條件編譯,只有定義了__ICCARM__以后下邊的代碼才有效。
/* Ensure stdint is only used by the compiler, and not the assembler. */ #ifdef __ICCARM__#include <stdint.h>extern uint32_t SystemCoreClock; #endif修改這個條件編譯,修改后的代碼如下:
/* Ensure stdint is only used by the compiler, and not the assembler. */ #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)#include <stdint.h>extern uint32_t SystemCoreClock; #endif3.8 重復定義的錯誤
..\OBJ\01Test.axf: Error: L6200E: Symbol PendSV_Handler multiply defined (by port.o and stm32f4xx_it.o). ..\OBJ\01Test.axf: Error: L6200E: Symbol SVC_Handler multiply defined (by port.o and stm32f4xx_it.o). ..\OBJ\01Test.axf: Error: L6200E: Symbol SysTick_Handler multiply defined (by port.o and stm32f4xx_it.o).port.c和stm32f4xx_it.c兩個文件中有重復定義的函數:PendSV_Handler(), SVC_Handler(), SysTick_Handler()。屏蔽掉stm32f4xx_it.c中的PendSV_Handler(), SVC_Handler(), SysTick_Handler()三個函數。再次編譯,發現如下未定義錯誤。
3.9 未定義錯誤
Build started: Project: 01Test *** Using Compiler 'V5.06 update 6 (build 750)', folder: 'D:\Keil_v5\ARM\ARMCC\Bin' Build target '01Test' compiling stm32f4xx_it.c... linking... ..\OBJ\01Test.axf: Error: L6218E: Undefined symbol vApplicationIdleHook (referred from tasks.o). ..\OBJ\01Test.axf: Error: L6218E: Undefined symbol vApplicationStackOverflowHook (referred from tasks.o). ..\OBJ\01Test.axf: Error: L6218E: Undefined symbol vApplicationTickHook (referred from tasks.o). ..\OBJ\01Test.axf: Error: L6218E: Undefined symbol vApplicationMallocFailedHook (referred from heap_4.o). Not enough information to list image symbols. Not enough information to list load addresses in the image map. Finished: 2 information, 0 warning and 4 error messages. "..\OBJ\01Test.axf" - 4 Error(s), 0 Warning(s). Target not created. Build Time Elapsed: 00:00:05發現這些未定義的函數,它們都是Hook結尾的,這些函數有個共同的名稱:鉤子函數,這是因為在FreeRTOSConfig.h中開啟了這些鉤子函數,但是沒有定義這些鉤子函數導致的。我們在FreeRTOSConfig.h中關閉這些鉤子函數就行了,關閉的方法很簡單,將相應的宏定義改為0即可。這里將宏configUSE_IDLE_HOOK、configUSE_TICK_HOOK、configUSE_MALLOC_FAILED_HOOK和configCHECK_FOR_STACK_OVERFLOW定義為0。
最后編譯一下,應該就沒有錯誤了,如果還有錯誤的話大家自行根據錯誤類型查找和修改錯誤。
04. SYSTEM文件修改
4.1 修改sys.h文件
使用FreeRTOS,我們應該將宏SYSTEM_SUPPORT_OS改為1。
//0,不支持ucos //1,支持ucos #define SYSTEM_SUPPORT_OS 1 //定義系統文件夾是否支持OS4.2 修改usart.c文件
usart.c文件修改也很簡單,usart.c文件有兩部分要修改,一個是添加FreeRTOS.h頭文件,默認是添加的UCOS中的includes.h頭文件。修改之后內容如下:
//如果使用ucos,則包括下面的頭文件即可. #if SYSTEM_SUPPORT_OS #include "FreeRTOS.h" //os使用 #endif修改USART1中的中斷服務函數,在使用UCOS的時候需要進出中斷的時候添加OSIntEnter()和OSIntExit()。使用FreeRTOS的話就不需要了,所以將這兩行代碼刪除即可。
void USART1_IRQHandler(void) //串口1中斷服務程序 {u8 Res;if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中斷(接收到的數據必須是0x0d 0x0a結尾){Res =USART_ReceiveData(USART1);//(USART1->DR); //讀取接收到的數據if((USART_RX_STA&0x8000)==0)//接收未完成{if(USART_RX_STA&0x4000)//接收到了0x0d{if(Res!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始else USART_RX_STA|=0x8000; //接收完成了 }else //還沒收到0X0D{ if(Res==0x0d)USART_RX_STA|=0x4000;else{USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;USART_RX_STA++;if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收數據錯誤,重新開始接收 } }} } }4.3 修改delay.c文件
delay.c文件修改的就比較大了,因為涉及到FreeRTOS的系統時鐘,delay.c文件里面有4個函數。
首先來看看SysTick_Handler(),次函數是滴答定時器的中斷服務函數,代碼如下:
extern void xPortSysTickHandler(void);//systick中斷服務函數,使用OS時用到 void SysTick_Handler(void) { if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系統已經運行{xPortSysTickHandler(); } }delay_init()是用來初始化滴答定時器和延時函數,代碼如下:
//初始化延遲函數 //SYSTICK的時鐘固定為AHB時鐘,基礎例程里面SYSTICK時鐘頻率為AHB/8 //這里為了兼容FreeRTOS,所以將SYSTICK的時鐘頻率改為AHB的頻率! //SYSCLK:系統時鐘頻率 void delay_init(u8 SYSCLK) {u32 reload;SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK); fac_us=SYSCLK; //不論是否使用OS,fac_us都需要使用reload=SYSCLK; //每秒鐘的計數次數 單位為M reload*=1000000/configTICK_RATE_HZ; //根據configTICK_RATE_HZ設定溢出時間//reload為24位寄存器,最大值:16777216,在168M下,約合0.0998s左右 fac_ms=1000/configTICK_RATE_HZ; //代表OS可以延時的最少單位 SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//開啟SYSTICK中斷SysTick->LOAD=reload; //每1/configTICK_RATE_HZ斷一次 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //開啟SYSTICK }延時函數定義如下:
//延時nus //nus:要延時的us數. //nus:0~204522252(最大值即2^32/fac_us@fac_us=168) void delay_us(u32 nus) { u32 ticks;u32 told,tnow,tcnt=0;u32 reload=SysTick->LOAD; //LOAD的值 ticks=nus*fac_us; //需要的節拍數 told=SysTick->VAL; //剛進入時的計數器值while(1){tnow=SysTick->VAL; if(tnow!=told){ if(tnow<told)tcnt+=told-tnow; //這里注意一下SYSTICK是一個遞減的計數器就可以了.else tcnt+=reload-tnow+told; told=tnow;if(tcnt>=ticks)break; //時間超過/等于要延遲的時間,則退出.} }; } //延時nms //nms:要延時的ms數 //nms:0~65535 void delay_ms(u32 nms) { if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系統已經運行{ if(nms>=fac_ms) //延時的時間大于OS的最少時間周期 { vTaskDelay(nms/fac_ms); //FreeRTOS延時}nms%=fac_ms; //OS已經無法提供這么小的延時了,采用普通方式延時 }delay_us((u32)(nms*1000)); //普通方式延時 }//延時nms,不會引起任務調度 //nms:要延時的ms數 void delay_xms(u32 nms) {u32 i;for(i=0;i<nms;i++) delay_us(1000); }編譯之后出現如下錯誤
..\OBJ\01Test.axf: Error: L6200E: Symbol SysTick_Handler multiply defined (by port.o and delay.o).在port.c和delay.c中有重復定義的函數,SysTick_Handler只能二選一,很明顯delay.c中的SysTick_Handler()保留下來,打開FreeRTOSConfig.h文件,找到如下宏定義:
#define xPortSysTickHandler SysTick_Handler注釋上面的宏定義。
05. 測試程序設計
main.c文件
#include "sys.h" #include "delay.h" #include "usart.h" #include "led.h" #include "FreeRTOS.h" #include "task.h"//任務優先級 #define START_TASK_PRIO 1 //任務堆棧大小 #define START_STK_SIZE 128 //任務句柄 TaskHandle_t StartTask_Handler; //任務函數 void start_task(void *pvParameters);//任務優先級 #define LED0_TASK_PRIO 2 //任務堆棧大小 #define LED0_STK_SIZE 50 //任務句柄 TaskHandle_t LED0Task_Handler; //任務函數 void led0_task(void *pvParameters);//任務優先級 #define LED1_TASK_PRIO 3 //任務堆棧大小 #define LED1_STK_SIZE 50 //任務句柄 TaskHandle_t LED1Task_Handler; //任務函數 void led1_task(void *pvParameters);//任務優先級 #define FLOAT_TASK_PRIO 4 //任務堆棧大小 #define FLOAT_STK_SIZE 128 //任務句柄 TaskHandle_t FLOATTask_Handler; //任務函數 void float_task(void *pvParameters);int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//設置系統中斷優先級分組4delay_init(168); //初始化延時函數uart_init(115200); //初始化串口LED_Init(); //初始化LED端口//創建開始任務xTaskCreate((TaskFunction_t )start_task, //任務函數(const char* )"start_task", //任務名稱(uint16_t )START_STK_SIZE, //任務堆棧大小(void* )NULL, //傳遞給任務函數的參數(UBaseType_t )START_TASK_PRIO, //任務優先級(TaskHandle_t* )&StartTask_Handler); //任務句柄 vTaskStartScheduler(); //開啟任務調度 }//開始任務任務函數 void start_task(void *pvParameters) {taskENTER_CRITICAL(); //進入臨界區//創建LED0任務xTaskCreate((TaskFunction_t )led0_task, (const char* )"led0_task", (uint16_t )LED0_STK_SIZE, (void* )NULL, (UBaseType_t )LED0_TASK_PRIO, (TaskHandle_t* )&LED0Task_Handler); //創建LED1任務xTaskCreate((TaskFunction_t )led1_task, (const char* )"led1_task", (uint16_t )LED1_STK_SIZE, (void* )NULL,(UBaseType_t )LED1_TASK_PRIO,(TaskHandle_t* )&LED1Task_Handler); //浮點測試任務xTaskCreate((TaskFunction_t )float_task, (const char* )"float_task", (uint16_t )FLOAT_STK_SIZE, (void* )NULL,(UBaseType_t )FLOAT_TASK_PRIO,(TaskHandle_t* )&FLOATTask_Handler); vTaskDelete(StartTask_Handler); //刪除開始任務taskEXIT_CRITICAL(); //退出臨界區 }//LED0任務函數 void led0_task(void *pvParameters) {while(1){LED0=~LED0;vTaskDelay(500);} } //LED1任務函數 void led1_task(void *pvParameters) {while(1){LED1=0;vTaskDelay(200);LED1=1;vTaskDelay(800);} }//浮點測試任務 void float_task(void *pvParameters) {static float float_num=0.00;while(1){float_num+=0.01f;printf("float_num的值為: %.4f\r\n",float_num);vTaskDelay(1000);} }實驗結果:
LED0和LED1開始閃爍,LED0均勻閃爍,LED1亮的時間短,滅的時間長。然后串口不斷有數據輸出。
06. 附錄
6.1 【STM32】STM32系列教程匯總
網址:【STM32】STM32系列教程匯總
07. 參考
《FreeRTOS Reference Manual》
《Using the FreeRTOS Real Time Kernel -A Practical Guide》
《The Definitive Guide to ARM Cortex-M3 and Cortex-M4 Processors,3rd Edition》
總結
以上是生活随笔為你收集整理的【STM32】FreeRTOS移植的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【STM32】FreeRTOS下载和介绍
- 下一篇: 【STM32】FreeRTOS编码风格