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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【STM32】FreeRTOS移植

發布時間:2024/4/24 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【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 undefined

SystemCoreClock未定義,因為在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; #endif

3.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 //定義系統文件夾是否支持OS

4.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移植的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。