基于STM32F103 HAL库的FreeRTOS移植

FreeRTOS 移植准备

移植步骤概述

  1. 添加 FreeRTOS 源码到工程。
  2. 配置 Cmake 编译脚本。
  3. 添加配置文件 FreeRTOSConfig.h。
  4. 修改三个中断函数。
  5. 编写程序验证。

工程准备

使用 STM32CubeMX 生成一个 GPIO 控制工程即可,记得修改时钟源。我使用的是 Cmake 工具链,但是无论什么工具链、HAL 库/标准库,移植思想流程是一致的。

FreeRTOS移植实践

下载源码

下载地址FreeRTOS™ - FreeRTOS™

移植内核源码

再 HAL 库工程的根目录新建目录Middlewares\Third_Party\FreeRTOS\Source,将内核源码均移植到该目录下。

FreeRTOS 的源码目录:

移植后的工程:

配置编译工具链

portable文件夹下是对一些常见的工具链的适配,比如 Keil、IAR、GCC 等,其中MemMang是 FreeRTOS 的内存管理文件,均要保留。

我的工程使用的是GCC工具链,芯片是基于Arm cortex-m3的 STM32F103 芯片,所以在 GCC 文件夹下仅保留ARM_CM3;同时我使用heap_4的堆管理,保留MemMang\heap_4.c

删除所以源文件以外的文件,特别是 Cmake 文件。

配置 Cmake 编译脚本

在根目录的CmakeLists.txt文件中添加以下内容:

set(FreeRTOS_Src
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/croutine.c
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/list.c
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/queue.c
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/tasks.c
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/timers.c
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c
)

# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user sources here
    ${FreeRTOS_Src}
)

# Add include paths
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user defined include paths
    Middlewares/Third_Party/FreeRTOS/Source/include
    Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM3
)

  • 添加**FreeRTOSConfig.h**文件

在下载的 FreeRTOS 源码目录FreeRTOS-LTS\FreeRTOS\FreeRTOS-Kernel\examples\template_configuration下有一个模板文件,但需要我们手动修改添加一些内容。

当然也可以使用别人编写好的,或者 STM32CubeMX 生成的(推荐)。

修改中断函数

下面两个函数在stm32f1xx_it.cportable\GCC\ARM_CM3\port.c重复定义了,因此需要把stm32f1xx_it.c下的函数注释掉。

void PendSV_Handler(void);
void SVC_Handler(void);

由于 FreeRTOS 需要使用SysTick做计时,所以推荐将 HAL 库的时钟源修改为其他定时器。

接着,配置stm32f1xx_it.c文件的SysTick_Handler函数供 FreeRTOS 使用,如下所示。

注意看注释添加程序,不然下次生成会被覆盖删除。

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

/* USER CODE BEGIN EV */
extern void xPortSysTickHandler(void);
/* USER CODE END EV */

/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */
    SysTick->CTRL;

    if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
        /* Call tick handler */
        xPortSysTickHandler();
    }
  /* USER CODE END SysTick_IRQn 0 */

  /* USER CODE BEGIN SysTick_IRQn 1 */

  /* USER CODE END SysTick_IRQn 1 */
}

应用程序验证

规范一些的话,就新建一套头文件做多线程管理,我比较懒,就直接在main.c做简单测试:

我这里在做了多余的 LED 亮灭是为了判断线程是否创建成功,若是调度器成功启动最后的点亮是不会执行的。

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

/* USER CODE BEGIN 0 */
void StartDefaultTask(void* argument)
{
    (void)argument;

    while (1)
    {
        HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
        vTaskDelay(100);
        HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
        vTaskDelay(100);
    }
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
    /* 。。。(省略自动生成的程序) */

    /* USER CODE BEGIN 2 */
    HAL_Delay(2000);
    HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
    HAL_Delay(1000);
    HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
    HAL_Delay(1000);
    BaseType_t i = xTaskCreate(StartDefaultTask, "ledTask", 128*4, NULL, 24, NULL);
    if (i == pdPASS)
    {
        HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
    }
    vTaskStartScheduler();
    HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
  /* USER CODE END 2 */
}

CMSIS_V2 移植准备

下载相关源码:

git clone git@github.com:ARM-software/CMSIS_5.git
git clone git@github.com:ARM-software/CMSIS-FreeRTOS.git

CMSIS_V2 移植实践

使用的是 ARM 开源标准版本,与 ST 版本略有不同,但不影响使用。(但推荐使用 ST 版本,对 STM32 支持更好)

若想使用 ST 版本,可以看 ST 的生成模板仓库,直接移植到对应目录做编译配置:https://github.com/STMicroelectronics/STM32CubeF1#

移植

  • Middlewares\Third_Party\FreeRTOS\Source下新建文件夹 CMSIS_RTOS_V2
  • 找到下列文件,全部移植到CMSIS_RTOS_V2文件夹下。
    • CMSIS-FreeRTOS\CMSIS\RTOS2\FreeRTOS\Source\cmsis_os2.c
    • CMSIS-FreeRTOS\CMSIS\RTOS2\FreeRTOS\Include\freertos_mpool.h
    • CMSIS-FreeRTOS\CMSIS\RTOS2\FreeRTOS\Include\freertos_os2.h
    • CMSIS_5\CMSIS\RTOS2\Include\cmsis_os2.h
    • CMSIS_5\CMSIS\RTOS2\Source\os_systick.c
    • CMSIS_5\CMSIS\RTOS2\Source\os_tick.h
  • 打开os_systick.c,找到下面内容注释掉。
// #include "RTE_Components.h"
// #include CMSIS_device_header

编译配置

在根目录的CmakeLists.txt文件中添加以下内容:

set(FreeRTOS_Src
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/croutine.c
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/list.c
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/queue.c
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/tasks.c
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/timers.c
    # 添加内容 start
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.c
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/os_systick.c
    # 添加内容 end
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c
    ${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c
)

# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user sources here
    ${FreeRTOS_Src}
    # 添加内容 start
    Core/Src/freertos.c
    # 添加内容 end
)

# Add include paths
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user defined include paths
    Middlewares/Third_Party/FreeRTOS/Source/include
    Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM3
    # 添加内容 start
    Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2
    # 添加内容 end
)

验证

为了让目录结构与 STM32CubeMX 生成的尽量一致,我们新建目录Core\Src\freertos.c

删除 main.c中多余的程序(除头文件和自动生成的配置),添加如下内容:

/* USER CODE BEGIN Includes */
#include "cmsis_os2.h"
/* USER CODE END Includes */

/* USER CODE BEGIN 0 */
void MX_FREERTOS_Init(void);
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
    /* 。。。(省略自动生成的程序) */
    /* USER CODE BEGIN 2 */
    osKernelInitialize();
    MX_FREERTOS_Init();
    osKernelStart();
    /* USER CODE END 2 */
}

freertos.c中使用 CMSIS_RTOS_V2标准建立线程,命名和 STM32CubeMX 生成的保持一致,并把 FreeRTOS 原生的 API 线程也放在该文件,方便切换添加。

以为我还没有添加其他外设驱动,不进行多任务验证,读者可自行验证。

#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os2.h"

void MX_FREERTOS_Init(void);


osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
    .name = "defaultTask",
    .stack_size = 128 * 4,
    .priority = (osPriority_t) osPriorityNormal,
  };

void StartDefaultTask(void *argument)
{
    (void)argument;
    while (1)
    {
        HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
        vTaskDelay(1000);
        HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
        vTaskDelay(1000);
    }
}

static void LedTask(void* argument)
{
    (void)argument;
    while (1)
    {
        HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
        vTaskDelay(300);
        HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
        vTaskDelay(300);
    }
}

void MX_FREERTOS_Init(void)
{
    // xTaskCreate(LedTask, "ledTask", 128*2, NULL, 24, NULL);
    defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

}

参考资料

兆易创新GD32 (四)FreeRTOS 移植 与 CMSIS OS2_兆易创新带freertos的工程-优快云博客

【经验分享】STM32使用HAL库手动移植FreeRTOS10.4.1 - STM32团队 ST意法半导体中文论坛

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值