• 正文
  • 相關推薦
申請入駐 產業(yè)圖譜

移植FreeRTOS到小熊派開發(fā)板

2021/03/29
408
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

一、移植準備

1. 硬件準備

本文中使用的開發(fā)板為小熊派IoT開發(fā)板,主控為STM32L431RCT6:

 

 

2. 下載FreeRTOS源碼

FreeRTOS源碼分為兩種,一種是FreeRTOS包,另一種是FreeRTOS LTS Release包。

2.1. FreeRTOS版本

這種版本包含FreeRTOS內核的源碼和示例工程,還有FreeRTOS+的一些擴展庫??梢酝ㄟ^下面的兩種方式下載:

  • 官方下載鏈接:下載官方發(fā)布的包,截至發(fā)文時間,最新發(fā)布的版本為FreeRTOSv202012.00.zip。Github倉庫地址:master分支為官方不斷更新修改的包。

這里我從官方下載,下載解壓之后如圖:

 

 

2.2. FreeRTOS LTS版本

LTS即long term support(長期支持包),這種版本包含FreeRTOS LTS發(fā)布版本的源碼,包括內核、TCP/IP、MQTT、OTA和更多支持的庫。也可以通過下面的兩種方式下載:

  • 官方下載鏈接:下載官方發(fā)布的包,截至發(fā)文時間,最新發(fā)布的版本為FreeRTOSv202012.01-LTS.zip。Github倉庫地址:master分支為官方不斷更新修改的包。

這里我從官方下載,解壓之后如圖:

 

 

3. 裸機工程準備

請準備一份可以正常使用printf串口輸出的裸機工程,本文中我使用cubemx生成。

二、添加源碼到工程中

1. 復制文件

在工程目錄下新建FreeRTOS文件夾,將FreeRTOS官方源碼復制過來,如圖:

接著將portable文件夾下面的文件夾部分刪除,只保留以下幾個文件夾,如圖。其中Gcc、IAR、RVDS(Keil)是分別適配這三種編譯器的,MemMang是FreeRTOS提供的內存管理算法。

 

2. 添加文件到MDK工程

一個RTOS無非就三類文件:底層移植文件、內核實現文件、配置文件,所以在MDK分組中我們按照如下來管理。

2.1. 添加底層移植文件

新建 FreeRTOS/port 分組,因為這里我們是MDK移植環(huán)境,STM32L431RCT6屬于帶FPU的Cortex-M4內核,所以添加位于 FreeRTOSportableRVDSARM_CM4F 下的 port.c 文件:

再添加位于 FreeRTOSportableMemMang 下的 heap_4.c 文件,為FreeRTOS提供一種動態(tài)內存管理算法:

 

2.2. 添加FreeRTOS內核源碼

新建 FreeRTOS/kernel 分組,添加位于 FreeRTOS 文件夾下的所有c文件:

 

 

2.3. 添加FreeRTOS配置文件

FreeRTOS的配置文件屬于和實際硬件相關的文件,在我們復制過來的文件中并沒有,所以要去FreeRTOS源碼中提供的demo工程下找份最相關的文件,復制過來:

為了便于修改,添加到MDK分組中:

 

3. 添加頭文件路徑

此時編譯,檢查是否有錯誤:

 

可以看到編譯器提示 INCLUDE_xTaskGetCurrentTaskHandle 函數沒有實現,全局搜索檢查一下該函數的定義:

 

可以看到只有定義了這兩個宏定義中的任意一個,該函數才會定義,所以在配置文件中添加宏定義,開啟使用互斥鎖

再次編譯,編譯成功。

 

三、修改FreeRTOS配置文件

之前我們添加的配置文件 FreeRTOSConfig.h 文件是從官方提供給STM32F103的demo中復制過來的,本實驗中用的是STM32L431RCT6,需要進行修改。

1. 修改內核基本配置

因為STM32 HAL中定義了芯片的時鐘(SystemCoreClock),所以此處使用一個c語言extern聲明此變量在外部,但這是頭文件,為了不被匯編器所匯編,可以使用如下宏定義:

/* Ensure definitions are only used by the compiler, and not by the assembler. */
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  #include 
  extern uint32_t SystemCoreClock;
#endif

接著修改內核基本時鐘配置:

 

修改一些內核的API功能是否提供:

 

 

2. 修改中斷配置

這部分是FreeRTOS的一個特色,將中斷部分修改為如下配置:

/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
 /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
 #define configPRIO_BITS         __NVIC_PRIO_BITS
#else
 #define configPRIO_BITS         4
#endif

/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY   15

/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5

/* Interrupt priorities used by the kernel port layer itself.  These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY   ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY  ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

/* This is the value being used as per the ST library which permits 16
priority values, 0 to 15.  This must correspond to the
configKERNEL_INTERRUPT_PRIORITY setting.  Here 15 corresponds to the lowest
NVIC value of 255. */
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15

 

3. 定義斷言

/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for( ;; );} 

 

4. 配置中斷接口

RTOS需要配置的中斷有兩個:一個是用于任務切換的pendSV中斷(或者SVC中斷),另一個是用于提供時鐘節(jié)拍的Systick中斷。

/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
#define vPortSVCHandler    SVC_Handler
#define xPortPendSVHandler PendSV_Handler

/* IMPORTANT: This define is commented when used with STM32Cube firmware, when the timebase source is SysTick,
              to prevent overwriting SysTick_Handler defined within STM32Cube HAL */
 
/* #define xPortSysTickHandler SysTick_Handler */

剛剛這兩個宏設置了pendSV和SVC中斷處理程序的名稱,將這兩個處理程序交由FreeRTOS實現,但這會與stm32l4xx_it.c中默認的中斷處理程序沖突,將其屏蔽:

 

最后處理Systick中斷函數,因為Systick中斷處理函數中還有HAL庫的時鐘節(jié)拍處理,所以并沒有交由FreeRTOS實現,而是選擇在Systick的中斷處理函數中調用FreeRTOS的節(jié)拍處理函數。

首先在stm32l4xx_it.c的開始包含FreeRTOS頭文件:

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "FreeRTOS.h"
#include "task.h"
/* USER CODE END Includes */

接著修改Systick中斷處理程序:

/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */
extern void xPortSysTickHandler(void);
  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
#if (INCLUDE_xTaskGetSchedulerState == 1 )
  if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
  {
#endif /* INCLUDE_xTaskGetSchedulerState */
  xPortSysTickHandler();
#if (INCLUDE_xTaskGetSchedulerState == 1 )
  }
#endif /* INCLUDE_xTaskGetSchedulerState */
  /* USER CODE BEGIN SysTick_IRQn 1 */

  /* USER CODE END SysTick_IRQn 1 */
}

 

添加之后會發(fā)現INCLUDE_xTaskGetSchedulerState這個宏沒有開,導致在中斷處理程序中不會檢測調度器狀態(tài),所以在配置文件中配置開啟該API:

 

 

至此,移植全部完成。

 

四、測試內核是否可以正常運行

1. 開啟支持靜態(tài)內存分配

創(chuàng)建一個靜態(tài)任務需要內核開啟靜態(tài)內存分配支持,在配置文件中添加如下宏定義:

#define configSUPPORT_STATIC_ALLOCATION    1

 

當這個宏開啟之后,需要用戶實現 vApplicationGetIdleTaskMemory 函數,來提供一塊靜態(tài)內存空間作為IDLE任務的內存空間,這里我在main.c中實現,如下:

/* GetIdleTaskMemory prototype (linked to static allocation support) */
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];
  
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
  *ppxIdleTaskStackBuffer = &xIdleStack[0];
  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}   

 

2. 創(chuàng)建兩個測試任務

首先在main.c中創(chuàng)建任務1和任務2的TCB控制塊內存空間、任務??臻g,并創(chuàng)建兩個任務的任務入口函數:

#define TASK1_STACK_SIZE    512
#define TASK2_STACK_SIZE    512

StaticTask_t task1_tcb_buffer;
StaticTask_t task2_tcb_buffer;

StackType_t task1_stack[TASK1_STACK_SIZE];
StackType_t task2_stack[TASK2_STACK_SIZE];

void task1_entry(void *args)
{
  /* Enter into a forever loop. */
  while(1)
  {
    printf("task 1 application running...rn");

    vTaskDelay(1000);
  }
}

void task2_entry(void *args)
{
  /* Enter into a forever loop. */
  while(1)
  {
    printf("task 2 application running...rn");
    
    vTaskDelay(1000);
  }
}

接下來在main函數中創(chuàng)建這兩個任務,并啟動內核:

/* USER CODE BEGIN 2 */
printf("FreeRTOS port on BearPi board by mculover666rn");

task1_handle = xTaskCreateStatic(task1_entry,"task1", TASK1_STACK_SIZE, NULL, 1, task1_stack, &task1_tcb_buffer);
            
task2_handle = xTaskCreateStatic(task2_entry,"task2", TASK2_STACK_SIZE, NULL, 2, task2_stack, &task2_tcb_buffer);          

vTaskStartScheduler();
/* USER CODE END 2 */

 

編譯、下載,在串口助手中查看結果,可以看到兩個任務交替運行,每隔1s打印一次日志:

 

 

有意思的一點是,我設置的task1優(yōu)先級是1,task2優(yōu)先級是2,從日志里明顯是task2先跑,難道移植出了問題???

實則不然,FreeRTOS中優(yōu)先級數值越低,優(yōu)先級等級越低,空閑任務的優(yōu)先級為0,這一點和很多RTOS都不相同,需要特別注意!

除此之外還有:

移植uc/OS-III最新版到小熊派開發(fā)板

在小熊派上移植threadX操作系統(tǒng)

STM32標準庫工程中移植TencentOS-tiny

FreeRTOS

FreeRTOS

FreeRTOS 專職開發(fā)人員一直與芯片公司緊密合作, 為客戶提供市場領先external_link以及免費的商用級、高品質 RTOS和工具。

FreeRTOS 專職開發(fā)人員一直與芯片公司緊密合作, 為客戶提供市場領先external_link以及免費的商用級、高品質 RTOS和工具。收起

查看更多

相關推薦

登錄即可解鎖
  • 海量技術文章
  • 設計資源下載
  • 產業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

憑著與生俱來的熱愛,專注于嵌入式物聯網領域。目前包含STM32CubeMX開發(fā)實戰(zhàn)、開源項目進階、RTOS內功修煉記、等系列專欄教程。