準備
在移植之前,我們首先要獲取到FreeRTOS的官方的原始碼包,這里我們提供兩個下載鏈接:
一個是官網:http://www.freertos.org/
另外一個是代碼托管網站:https://sourceforge.net/projects/freertos/files/FreeRTOS/
這里我們演示如何在代碼托管網站里面下載,打開網站鏈接之后,我們選擇FreeRTOS的最新版本V9.0.0(2016年),盡管現在FreeRTOS的版本已經更新到V10.0.1了,但是我們還是選擇V9.0.0,因為內核很穩定,并且網上資料很多,因為V10.0.0版本之后是亞馬遜收購了FreeRTOS之后才出來的版本,主要添加了一些云端組件,我們本書所講的FreeRTOS是實時內核,采用V9.0.0版本足以,
簡單介紹FreeRTOS
FreeRTOS包含Demo例程和內核原始碼(比較重要,我們就需要提取該目錄下的大部分檔案),
Source檔案夾里面包含的是FreeRTOS內核的源代碼,我們移植FreeRTOS的時候就需要這部分源代碼;
Demo 檔案夾里面包含了FreeRTOS官方為各個單片機移植好的工程代碼,FreeRTOS為了推廣自己,會給各種半導體廠商的評估板寫好完整的工程程式,這些程式就放在Demo這個目錄下,這部分Demo非常有參考價值,

Source檔案夾
這里我們再重點分析下FreeRTOS/ Source檔案夾下的檔案,①和③包含的是FreeRTOS的通用的頭檔案和C檔案,這兩部分的檔案試用于各種編譯器和處理器,是通用的,需要移植的頭檔案和C檔案放在②portblle這個檔案夾,

portblle檔案夾,是與編譯器相關的檔案夾,在不同的編譯器中使用不同的支持檔案,①中的KEIL就是我們就是我們使用的編譯器,其實KEIL里面的內容跟RVDS里面的內容一樣,所以我們只需要③RVDS檔案夾里面的內容即可,里面包含了各種處理器相關的檔案夾,從檔案夾的名字我們就非常熟悉了,我們學習的STM32有M0、M3、M4等各種系列,FreeRTOS是一個軟體,單片機是一個硬體,FreeRTOS要想運行在一個單片機上面,它們就必須關聯在一起,MemMang檔案夾下存放的是跟記憶體管理相關的源檔案,

移植程序
提取原始碼
- 首先在我們的STM32裸機工程模板根目錄下新建一個檔案夾,命名為“FreeRTOS”,并且在FreeRTOS檔案夾下新建兩個空檔案夾,分別命名為“src”與“port”,src檔案夾用于保存FreeRTOS中的核心源檔案,也就是我們常說的‘.c檔案’,port檔案夾用于保存記憶體管理以及處理器架構相關代碼,這些代碼FreeRTOS官方已經提供給我們的,直接使用即可,在前面已經說了,FreeRTOS是軟體,我們的開發版是硬體,軟硬體必須有橋梁來連接,這些與處理器架構相關的代碼,可以稱之為RTOS硬體介面層,它們位于FreeRTOS/Source/Portable檔案夾下,
- 打開FreeRTOS V9.0.0原始碼,在“FreeRTOSv9.0.0\FreeRTOS\Source”目錄下找到所有的‘.c檔案’,將它們拷貝到我們新建的src檔案夾中,

- 打開FreeRTOS V9.0.0原始碼,在“FreeRTOSv9.0.0\FreeRTOS\Source\portable”目錄下找到“MemMang”檔案夾與“RVDS”檔案夾,將它們拷貝到我們新建的port檔案夾中

- 打開FreeRTOS V9.0.0原始碼,在“FreeRTOSv9.0.0\ FreeRTOS\Source”目錄下找到“include”檔案夾,它是我們需要用到FreeRTOS的一些頭檔案,將它直接拷貝到我們新建的FreeRTOS檔案夾中,完成這一步之后就可以看到我們新建的FreeRTOS檔案夾已經有3個檔案夾,這3個檔案夾就包含FreeRTOS的核心檔案,至此,FreeRTOS的原始碼就提取完成,

添加到工程
添加FreeRTOSConfig.h檔案
FreeRTOSConfig.h檔案是FreeRTOS的工程組態檔,因為FreeRTOS是可以裁剪的實時操作內核,應用于不同的處理器平臺,用戶可以通過修改這個FreeRTOS內核的配置頭檔案來裁剪FreeRTOS的功能,所以我們把它拷貝一份放在user這個檔案夾下面,
打開FreeRTOSv9.0.0原始碼,在“FreeRTOSv9.0.0\FreeRTOS\Demo”檔案夾下面找到“CORTEX_STM32F103_Keil”這個檔案夾,雙擊打開,在其根目錄下找到這個“FreeRTOSConfig.h”檔案,然后拷貝到我們工程的user檔案夾下即可,等下我們需要對這個檔案進行修改,
創建工程分組
接下來我們在mdk里面新建FreeRTOS/src和FreeRTOS/port兩個組檔案夾,其中FreeRTOS/src用于存放src檔案夾的內容,FreeRTOS/port用于存放port\MemMang檔案夾 與port\RVDS\ARM_CM3檔案夾的內容,
然后我們將工程檔案中FreeRTOS的內容添加到工程中去,按照已經新建的分組添加我們的FreeRTOS工程原始碼,
在FreeRTOS/port分組中添加MemMang檔案夾中的檔案只需選擇其中一個即可,我們選擇“heap_4.c”,這是FreeRTOS的一個記憶體管理原始碼檔案,
添加完成后:

** 添加頭檔案路徑**
FreeRTOS的原始碼已經添加到開發環境的組檔案夾下面,編譯的時候需要為這些源檔案指定頭檔案的路徑,不然編譯會報錯,FreeRTOS的原始碼里面只有FreeRTOS\include和FreeRTOS\port\RVDS\ARM_CM3這兩個檔案夾下面有頭檔案,只需要將這兩個頭檔案的路徑在開發環境里面指定即可,同時我們還將FreeRTOSConfig.h這個頭檔案拷貝到了工程根目錄下的user檔案夾下,所以user的路徑也要加到開發環境里面,

修改FreeRTOSConfig.h
FreeRTOSConfig.h是直接從demo檔案夾下面拷貝過來的,該頭檔案對裁剪整個FreeRTOS所需的功能的宏均做了定義,有些宏定義被使能,有些宏定義被失能,一開始我們只需要配置最簡單的功能即可,要想隨心所欲的配置FreeRTOS的功能,我們必須對這些宏定義的功能有所掌握,下面我們先簡單的介紹下這些宏定義的含義,然后再對這些宏定義進行修改,
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
#include "stm32f10x.h"
#include "bsp_usart.h"
//針對不同的編譯器呼叫不同的stdint.h檔案
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
//斷言
#define vAssertCalled(char,int) printf("Error:%s,%d\r\n",char,int)
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)
/************************************************************************
* FreeRTOS基礎配置配置選項
*********************************************************************/
/* 置1:RTOS使用搶占式調度器;置0:RTOS使用協作式調度器(時間片)
*
* 注:在多任務管理機制上,作業系統可以分為搶占式和協作式兩種,
* 協作式作業系統是任務主動釋放CPU后,切換到下一個任務,
* 任務切換的時機完全取決于正在運行的任務,
*/
#define configUSE_PREEMPTION 1
//1使能時間片調度(默認式使能的)
#define configUSE_TIME_SLICING 1
/* 某些運行FreeRTOS的硬體有兩種方法選擇下一個要執行的任務:
* 通用方法和特定于硬體的方法(以下簡稱“特殊方法”),
*
* 通用方法:
* 1.configUSE_PORT_OPTIMISED_TASK_SELECTION 為 0 或者硬體不支持這種特殊方法,
* 2.可以用于所有FreeRTOS支持的硬體
* 3.完全用C實作,效率略低于特殊方法,
* 4.不強制要求限制最大可用優先級數目
* 特殊方法:
* 1.必須將configUSE_PORT_OPTIMISED_TASK_SELECTION設定為1,
* 2.依賴一個或多個特定架構的匯編指令(一般是類似計算前導零[CLZ]指令),
* 3.比通用方法更高效
* 4.一般強制限定最大可用優先級數目為32
* 一般是硬體計算前導零指令,如果所使用的,MCU沒有這些硬體指令的話此宏應該設定為0!
*/
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
/* 置1:使能低功耗tickless模式;置0:保持系統節拍(tick)中斷一直運行
* 假設開啟低功耗的話可能會導致下載出現問題,因為程式在睡眠中,可用以下辦法解決
*
* 下載方法:
* 1.將開發版正常連接好
* 2.按住復位按鍵,點擊下載瞬間松開復位按鍵
*
* 1.通過跳線帽將 BOOT 0 接高電平(3.3V)
* 2.重新上電,下載
*
* 1.使用FlyMcu擦除一下芯片,然后進行下載
* STMISP -> 清除芯片(z)
*/
#define configUSE_TICKLESS_IDLE 0
/*
* 寫入實際的CPU內核時鐘頻率,也就是CPU指令執行頻率,通常稱為Fclk
* Fclk為供給CPU內核的時鐘信號,我們所說的cpu主頻為 XX MHz,
* 就是指的這個時鐘信號,相應的,1/Fclk即為cpu時鐘周期;
*/
#define configCPU_CLOCK_HZ (SystemCoreClock)
//RTOS系統節拍中斷的頻率,即一秒中斷的次數,每次中斷RTOS都會進行任務調度
#define configTICK_RATE_HZ (( TickType_t )1000)
//可使用的最大優先級
#define configMAX_PRIORITIES (32)
//空閑任務使用的堆疊大小
#define configMINIMAL_STACK_SIZE ((unsigned short)128)
//任務名字字串長度
#define configMAX_TASK_NAME_LEN (16)
//系統節拍計數器變數資料型別,1表示為16位無符號整形,0表示為32位無符號整形
#define configUSE_16_BIT_TICKS 0
//空閑任務放棄CPU使用權給其他同優先級的用戶任務
#define configIDLE_SHOULD_YIELD 1
//啟用佇列
#define configUSE_QUEUE_SETS 1
//開啟任務通知功能,默認開啟
#define configUSE_TASK_NOTIFICATIONS 1
//使用互斥信號量
#define configUSE_MUTEXES 1
//使用遞回互斥信號量
#define configUSE_RECURSIVE_MUTEXES 1
//為1時使用計數信號量
#define configUSE_COUNTING_SEMAPHORES 1
/* 設定可以注冊的信號量和訊息佇列個數 */
#define configQUEUE_REGISTRY_SIZE 10
#define configUSE_APPLICATION_TASK_TAG 0
/*****************************************************************
FreeRTOS與記憶體申請有關配置選項
*****************************************************************/
//支持動態記憶體申請
#define configSUPPORT_DYNAMIC_ALLOCATION 1
//支持靜態記憶體
#define configSUPPORT_STATIC_ALLOCATION 0
//系統所有總的堆大小
#define configTOTAL_HEAP_SIZE ((size_t)(36*1024))
/***************************************************************
FreeRTOS與鉤子函式有關的配置選項
**************************************************************/
/* 置1:使用空閑鉤子(Idle Hook類似于回呼函式);置0:忽略空閑鉤子
*
* 空閑任務鉤子是一個函式,這個函式由用戶來實作,
* FreeRTOS規定了函式的名字和引數:void vApplicationIdleHook(void ),
* 這個函式在每個空閑任務周期都會被呼叫
* 對于已經洗掉的RTOS任務,空閑任務可以釋放分配給它們的堆疊記憶體,
* 因此必須保證空閑任務可以被CPU執行
* 使用空閑鉤子函式設定CPU進入省電模式是很常見的
* 不可以呼叫會引起空閑任務阻塞的API函式
*/
#define configUSE_IDLE_HOOK 0
/* 置1:使用時間片鉤子(Tick Hook);置0:忽略時間片鉤子
*
*
* 時間片鉤子是一個函式,這個函式由用戶來實作,
* FreeRTOS規定了函式的名字和引數:void vApplicationTickHook(void )
* 時間片中斷可以周期性的呼叫
* 函式必須非常短小,不能大量使用堆疊,
* 不能呼叫以”FromISR" 或 "FROM_ISR”結尾的API函式
*/
/*xTaskIncrementTick函式是在xPortSysTickHandler中斷函式中被呼叫的,因此,vApplicationTickHook()函式執行的時間必須很短才行*/
#define configUSE_TICK_HOOK 0
//使用記憶體申請失敗鉤子函式
#define configUSE_MALLOC_FAILED_HOOK 0
/*
* 大于0時啟用堆疊溢位檢測功能,如果使用此功能
* 用戶必須提供一個堆疊溢位鉤子函式,如果使用的話
* 此值可以為1或者2,因為有兩種堆疊溢位檢測方法 */
#define configCHECK_FOR_STACK_OVERFLOW 0
/********************************************************************
FreeRTOS與運行時間和任務狀態收集有關的配置選項
**********************************************************************/
//啟用運行時間統計功能
#define configGENERATE_RUN_TIME_STATS 0
//啟用可視化跟蹤除錯
#define configUSE_TRACE_FACILITY 0
/* 與宏configUSE_TRACE_FACILITY同時為1時會編譯下面3個函式
* prvWriteNameToBuffer()
* vTaskList(),
* vTaskGetRunTimeStats()
*/
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
/********************************************************************
FreeRTOS與協程有關的配置選項
*********************************************************************/
//啟用協程,啟用協程以后必須添加檔案croutine.c
#define configUSE_CO_ROUTINES 0
//協程的有效優先級數目
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
/***********************************************************************
FreeRTOS與軟體定時器有關的配置選項
**********************************************************************/
//啟用軟體定時器
#define configUSE_TIMERS 1
//軟體定時器優先級
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1)
//軟體定時器佇列長度
#define configTIMER_QUEUE_LENGTH 10
//軟體定時器任務堆疊大小
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2)
/************************************************************
FreeRTOS可選函式配置選項
************************************************************/
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_eTaskGetState 1
#define INCLUDE_xTimerPendFunctionCall 1
//#define INCLUDE_xTaskGetCurrentTaskHandle 1
//#define INCLUDE_uxTaskGetStackHighWaterMark 0
//#define INCLUDE_xTaskGetIdleTaskHandle 0
/******************************************************************
FreeRTOS與中斷有關的配置選項
******************************************************************/
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
//中斷最低優先級
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
//系統可管理的最高中斷優先級
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) /* 240 */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/****************************************************************
FreeRTOS與中斷服務函式有關的配置選項
****************************************************************/
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
/* 以下為使用Percepio Tracealyzer需要的東西,不需要時將 configUSE_TRACE_FACILITY 定義為 0 */
#if ( configUSE_TRACE_FACILITY == 1 )
#include "trcRecorder.h"
#define INCLUDE_xTaskGetCurrentTaskHandle 1 // 啟用一個可選函式(該函式被 Trace原始碼使用,默認該值為0 表示不用)
#endif
#endif /* FREERTOS_CONFIG_H */
修改stm32f10x_it.c
SysTick中斷服務函式是一個非常重要的函式,FreeRTOS所有跟時間相關的事情都在里面處理,SysTick就是FreeRTOS的一個心跳時鐘,驅動著FreeRTOS的運行,就像人的心跳一樣,假如沒有心跳,我們就相當于“死了”,同樣的,FreeRTOS沒有了心跳,那么它就會卡死在某個地方,不能進行任務調度,不能運行任何的東西,因此我們需要實作一個FreeRTOS的心跳時鐘,FreeRTOS幫我們實作了SysTick的啟動的配置:在port.c檔案中已經實作vPortSetupTimerInterrupt()函式,并且FreeRTOS通用的SysTick中斷服務函式也實作了:在port.c檔案中已經實作xPortSysTickHandler()函式,所以移植的時候只需要我們在stm32f10x_it.c檔案中實作我們對應(STM32)平臺上的SysTick_Handler()函式即可,FreeRTOS為開發者考慮得特別多,PendSV_Handler()與SVC_Handler()這兩個很重要的函式都幫我們實作了,在在port.c檔案中已經實作xPortPendSVHandler()與vPortSVCHandler()函式,防止我們自己實作不了,那么在stm32f10x_it.c中就需要我們注釋掉PendSV_Handler()與SVC_Handler()這兩個函式了,
//void SVC_Handler(void)
//{
//}
//void PendSV_Handler(void)
//{
//}
extern void xPortSysTickHandler(void);
//systick中斷服務函式
void SysTick_Handler(void)
{
#if (INCLUDE_xTaskGetSchedulerState == 1 )
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
#endif /* INCLUDE_xTaskGetSchedulerState */
xPortSysTickHandler();
#if (INCLUDE_xTaskGetSchedulerState == 1 )
}
#endif /* INCLUDE_xTaskGetSchedulerState */
}
創建任務
這里,我們創建一個單任務,任務使用的堆疊和任務控制塊是在創建任務的時候FreeRTOS動態分配的,
任務必須是一個死回圈,否則任務將通過LR回傳,如果LR指向了非法的記憶體就會產生HardFault_Handler,而FreeRTOS指向一個死回圈,那么任務回傳之后就在死回圈中執行,這樣子的任務是不安全的,所以避免這種情況,任務一般都是死回圈并且無回傳值的,
并且每個任務回圈主體中應該有阻塞任務的函式,否則就會餓死比它優先級更低的任務!!!
/* FreeRTOS頭檔案 */
#include "FreeRTOS.h"
#include "task.h"
/* 開發板硬體bsp頭檔案 */
#include "bsp_led.h"
static void AppTaskCreate(void);/* AppTask任務 */
/* 創建任務句柄 */
static TaskHandle_t AppTask_Handle = NULL;
int main(void)
{
BaseType_t xReturn = pdPASS;/* 定義一個創建資訊回傳值,默認為pdPASS */
/* 開發板硬體初始化 */
BSP_Init();
/* 創建AppTaskCreate任務 */
xReturn = xTaskCreate((TaskFunction_t )AppTask, /* 任務入口函式 */
(const char* )"AppTask",/* 任務名字 */
(uint16_t )512, /* 任務堆疊大小 */
(void* )NULL,/* 任務入口函式引數 */
(UBaseType_t )1, /* 任務的優先級 */
(TaskHandle_t* )&AppTask_Handle);/* 任務控制塊指標 */
/* 啟動任務調度 */
if(pdPASS == xReturn)
vTaskStartScheduler(); /* 啟動任務,開啟調度 */
else
return -1;
while(1); /* 正常不會執行到這里 */
}
static void AppTask(void* parameter)
{
while (1)
{
LED1_ON;
vTaskDelay(500); /* 延時500個tick */
LED1_OFF;
vTaskDelay(500); /* 延時500個tick */
}
}
關注我

更多資料歡迎關注“物聯網IoT開發”公眾號!
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/33414.html
標籤:嵌入式
上一篇:繼續學習freertos訊息佇列
