STM32F407 移植 FreeRTOS

0. 实验准备

本实验是基于正点原子 STM32F407ZG 探索者开发板完成的,所以需要一个STM32F407ZG 探索者开发板
用于移植的基础工程(下面会讲)
FreeRTOS源码(下面会讲)

1. FreeRTOS移植

1.1 移植前准备

1.1.1 基础工程

由于后续需要用到 LED、 LCD、定时器、串口、内存管理等外设及功能,因此以正点原子标准例程-HAL库版本的内存管理的实验工程为基础工程进行 FreeRTOS 的移植 。
如下图所示
STM32F407 移植 FreeRTOS_第1张图片
将此工程复制一份到我们需要移植 FreeRTOS 的文件中作为基础工程,同时修改名字,如下图所示
在这里插入图片描述
由于内存管理实验例程的 BSP 文件夹中可能不包含定时器的驱动文件,因此如果内存管理实验的 BSP 文件夹如果不包含 TIMER 文件夹,需要从定时器相关实验的 BSP 文件夹中拷贝一份 TIMER 到 FreeRTOS 移植基础工程当中,如从下列路径中找到 TIMER 文件夹拷贝到基础工程对应目录中,如下图所示。也可以在文章上方提供的代码包中直接找到基础工程。

标准例程-HAL库版本\实验9 通用定时器实验\实验9-1 通用定时中断器实验\Drivers\BSP

STM32F407 移植 FreeRTOS_第2张图片

1.1.2 FreeRTOS 源码

FreeRTOS 源码可以通过官方获取,这里直接从正点原子的资料中获取,可以在下列路径中找到,如下图所示。如果是从官方下载的源码,注意版本为 V10.4.6,最好还是使用正点原子提供的源码,这样不会出现版本问题,也可以在文章上方提供的代码包中直接找到 FreeRTOS 源码。

资料盘(A盘)\6,软件资料\13,FreeRTOS学习资料

STM32F407 移植 FreeRTOS_第3张图片
将此源码复制到要移植的文件夹中,然后解压,如下图所示
在这里插入图片描述

1.2 添加 FreeRTOS 文件

1.2.1 添加 FreeRTOS 源码

打开我们基础工程的 Middlewares 文件夹,然后新建一个 FreeRTOS 子文件夹,如下图所示
STM32F407 移植 FreeRTOS_第4张图片
然后打开 FreeRTOS 源码的下列路径

\FreeRTOSv202112.00\FreeRTOS\Source

将红框中的文件全部复制到基础工程的Middlewares/FreeRTOS 下,如下图所示
STM32F407 移植 FreeRTOS_第5张图片
复制完成后的Middlewares/FreeRTOS内容如下图所示
STM32F407 移植 FreeRTOS_第6张图片

1.2.2 将文件添加到工程

使用 Keil5 打开我们的基础工程,新建两个文件分组,如下图所示
STM32F407 移植 FreeRTOS_第7张图片
下图为添加完成后的目录结构
STM32F407 移植 FreeRTOS_第8张图片
Middlewares/FreeRTOS_CORE 分组用于存放 FreeRTOS 的内核 C 源码文件, 将“上述步骤中的 FreeRTOS 目录下所有的 FreeRTOS 的内核 C 源文件添加到 Middlewares/FreeRTOS_CORE 分组中 ,如下图所示
STM32F407 移植 FreeRTOS_第9张图片
Middlewares/FreeRTOS_PORT分组用于存放 FreeRTOS 内核的移植文件 ,需要添加两个文件到这个分组,分别为 heap_x.cport.c

  • 首先是 heap_x.c 。在路径 FreeRTOS/portable/MemMang下有五个 C 语言源文件,这五个 C 语言源文件对应了五种 FreeRTOS 提供的内存管理算法 ,读者在进行 FreeRTOS 移植的时候可以根据需求选择合适的方法,具体这五种内存管理的算法 ,在后续 会具体分析,这里就先使用 heap_4.c,将 heap_4.c 添加到 Middlewares/FreeRTOS_PORT 分组中。

  • port.c是 FreeRTOS 与 MCU 这个硬件连接的桥梁,因此对于不同系列的 STM32 开发板,所使用的 port.c 文件是不同的。 port.c文件的路径在 FreeRTOS/portable/RVDS 下。进入到 FreeRTOS/portable/RVDS,可以看到 FreeRTOS 针对不同的 MCU 提供了不同的 port.c 文件,具体正点原子的STM32系列开发板与不同 port.c 的对应关系如下表所示:

正点原子的STM32系列开发板类型 port.c所在文件夹
STM32F1 ARM_CM3
STM32F4 ARM_CM4F
STM32F7 ARM_CM7/r0p1
STM32H1 ARM_CM7/r0p1

演示的开发板为 STM32F4 ,所以我是在 ARM_CM4F 中寻找的 port.c 文件,其他开发板以此类推,全部添加完成后如下图所示(红框为本小节添加的所有内容)
STM32F407 移植 FreeRTOS_第10张图片

1.2.3 添加头文件路径

接下来添加 FreeRTOS 源码的头文件路径,需要添加两个头文件路径,一个头文件路径是 FreeRTOS/include,另外一个头文件路径为 port.c 文件的路径,根据不同类型开发板与 port.c 文件的对应关系进行添加即可(和上文中选择的文件保持一致)。 如下图所示
STM32F407 移植 FreeRTOS_第11张图片

1.2.4 添加 FreeRTOSConfig.h 文件

FreeRTOSConfig.h 是 FreeRTOS 操作系统的配置文件, FreeRTOS 操作系统是可裁剪的,用户可以根据需求对 FreeRTOS 进行裁剪,裁剪掉不需要用到的 FreeRTOS 功能,以此来节约 MCU 的内存资源。获取 FreeRTOSConfig.h 有三个途径:

  1. 用户自行编写(不推荐,且对新手用户不友好)
  2. FreeRTOS 内核的演示工程,在 FreeRTOS 源码中,官方提供了 Demo 文件夹, 包含了 FreeRTOS 官方提供的演示工程,在工程当中就包含了每个演示工程对应的 FreeRTOSConfig.h 文件,读者可以在 Demo 文件夹中找到与自己所使用芯片相似的演示工程中的 FreeRTOSConfig.h 文件,并根据自己的需求,稍作修改 。(不推荐,兼容性不太好)
  3. 第三种途径,可以在正点原子资料的下列路径的 User 子文件夹下 找到 FreeRTOSConfig.h 文件

资料盘(A盘)\4,程序源码\3,扩展例程\2,FreeRTOS例程\FreeRTOS实验例程2 FreeRTOS移植实验

这个文件是参考 FreeRTOS 官网中对 FreeRTOSConfig.h 文件的描述,并针对正点原子的 STM32 系列开发板编写的。此文件在 FreeRTOSConfig.h 文件中并没有对 FreeRTOS 的功能作过多的裁剪,大部分的功能都保留了,在后续的部分实验中还需要对 FreeRTOSConfig.h 文件作相应的修改,以满足实验的需求。

本实验采用途径三,将上面路径的 User 子文件夹下的 FreeRTOSConfig.h 文件添加到基础工程的 User 子目录下即可 (正点原子的STM32系列开发板对应的 FreeRTOSConfig.h 文件是不通用的),如下图所示
STM32F407 移植 FreeRTOS_第12张图片

1.3 修改 SYSTEM 文件

SYSTEM 文件夹中的文件一开始是针对 μC/OS 编写的,因此使用 FreeRTOS 的话,就需要作相应的修改。SYSTEM 文件夹中一共需要修改三个文件,分别是 sys.husart.cdelay.c

1.3.1 sys.h文件

sys.h 文件的修改很简单, 在 sys.h 文件中使用了宏 SYS_SUPPORT_OS 来定义是否支持 OS 因为要支持 FreeRTOS,因此应当将宏 SYS_SUPPORT_OS 定义为 1,具体修改步骤如下所示
STM32F407 移植 FreeRTOS_第13张图片
找到下图所示的位置,修改为1
STM32F407 移植 FreeRTOS_第14张图片

1.3.2 usart.c 文件

usart.c 文件的修改也很简单,一共有两个地方需要修改, 首先就是串口的中断服务函数,原本在使用 μC/OS 的时候,进入和退出中断需要添加 OSIntEnter()OSIntExit() 两个函数,这是 μC/OS 对于中断的相关处理机制,而 FreeRTOS 中并没有这种机制,因此将这两行代码删除,修改如下图所示中选中的代码(注意 F1 和 F4 系列的代码不一样,F1 系列下图不具有参考性)
STM32F407 移植 FreeRTOS_第15张图片
删除完成后如下图
STM32F407 移植 FreeRTOS_第16张图片
接下来 usart.c 要修改的第二个地方就是导入的头文件,因为在串口的中断服务函数当中已经删除了 μC/OS 的相关代码,并且也没有使用到 FreeRTOS 的相关代码,因此将 usart.c 中包含的关于 OS 的头文件删除,删除下图选中的代码
STM32F407 移植 FreeRTOS_第17张图片
删除后如下图所示
STM32F407 移植 FreeRTOS_第18张图片

1.3.3 delay.c 文件

接下来修改 SYSTEM 文件夹中的最后一个文件 delay.c 。大致可分为三个步骤:

  1. 删除适用于 μC/OS 但不适用于 FreeRTOS 的相关代码
  2. 添加 FreeRTOS 的相关代码
  3. 修改部分内容
1)删除适用于 μC/OS 但不适用于 FreeRTOS 的相关代码

一共需要删除 1 个全局变量、 6 个宏定义、 3 个函数 ,这些要删除的代码在使用 μC/OS 的时候会使用到,但是在使用 FreeRTOS 的时候无需使用 ,需要删除的代码如下所示
STM32F407 移植 FreeRTOS_第19张图片
删除完成后如下图所示
STM32F407 移植 FreeRTOS_第20张图片

2)添加 FreeRTOS 的相关代码

只需要在 delay.c 文件中使用 extern 关键字导入一个 FreeRTOS 函数 xPortSysTickHandler() 即可,这个函数用于处理 FreeRTOS 系统时钟节拍的,我们使用 SysTick 作为 FreeRTOS 操作系统的心跳,因此需要在 SysTick 的中断服务函数中调用这个函数,因此将代码添加到 SysTick 中断服务函数之前,代码修改如下(标蓝部分为自己添加的代码):

extern void xPortSysTickHandler(void);

STM32F407 移植 FreeRTOS_第21张图片

3)修改部分内容

最后要修改的内容包括两个,分别是包含头文件和 4 个函数。
首先来看需要修改的 4 个函数,分别是 SysTick_Handler()delay_init()delay_us()delay_ms()

SysTick_Handler()

这个函数是 SysTick 的中断服务函数,需要在这个函数中调用上个步骤中导入的函数 xPortSysTickHandler(),代码修改后如下图所示(删掉原来的SysTick_Handler(),改为下面的)

/**
 * @brief     systick中断服务函数,使用OS时用到
 * @param     ticks : 延时的节拍数  
 * @retval    无
 */  
void SysTick_Handler(void)
{
    HAL_IncTick(); /* OS开始跑了,才执行正常的调度处理 */ 
    if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) 
    { 
        xPortSysTickHandler(); 
    }    
}

STM32F407 移植 FreeRTOS_第22张图片

delay_init()

函数 delay_init() 主要用于初始化 SysTick。这里要说明的是, 在后续调用函数 vTaskStartScheduler() 的时候, FreeRTOS 会按照 FreeRTOSConfig.h 文件的配置对 SysTick 进行初始化,因此 delay_init() 函数初始化的 SysTick 主要使用在 FreeRTOS 开始任务调度之前。函数 delay_init() 要修改的部分主要为 SysTick 的重装载值以及删除不用的代码,代码修改后如下图所示(删掉原来的delay_init(),改为下面的)

/**
 * @brief     初始化延迟函数
 * @param     sysclk: 系统时钟频率, 即CPU频率(rcc_c_ck), 168MHz
 * @retval    无
 */  
void delay_init(uint16_t sysclk) { 
    #if SYS_SUPPORT_OS 
        uint32_t reload; 
    #endif 
    
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); 
    g_fac_us = sysclk; 
    
    #if SYS_SUPPORT_OS 
        reload = sysclk; /* 使用configTICK_RATE_HZ计算重装载值 * configTICK_RATE_HZ在FreeRTOSConfig.h中定义 */ 
        reload *= 1000000 / configTICK_RATE_HZ; /* 删除不用的g_fac_ms相关代码 */ 
        SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; 
        SysTick->LOAD = reload; 
        SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; 
    #endif 
}

STM32F407 移植 FreeRTOS_第23张图片
注意:在正点原子 STM32 系列开发板的标准例程源码中, STM32F1 系列的函数 delay_init() 将 SysTick 的时钟频率设置为 ==CPU时钟频率的 1/8 ==,而 STM32F4/F7/H7 系列的函数 delay_init() 则将 SysTick 的时钟频率设置为与 CPU 相同的时钟频率,由于 FreeRTOS在配置 SysTick 时,并不会配置 SysTick 的时钟源,因此这将导致正点原子 STM32F1 系列与正点原 STM32F4/F7/H7系列的 FreeRTOSConfig.h 文件有所差异,并且也只有这一点存在差异,这是读者在使用正点原子提供的 FreeRTOSConfig.h 文件时需要注意的地方。

delay_us()

函数 delay_us() 用于微秒级的 CPU 忙延时 ,原本的函数 delay_us() 延时的前后加入了自定义函数 delay_osschedlock()delay_osschedunlock() 用于锁定和解锁 μC/OS 的任务调度器,以此来让延时更加准确。在 FreeRTOS 中可以不用加入这两个函数,但是要注意的是,这会让函数 delay_us() 的微秒级延时的精度有所下降, 代码修改后如下图所示(删掉原来的delay_us(),改为下面的)

/**
 * @brief     延时nus
 * @param     nus: 要延时的us数
 * @note      nus取值范围 : 0 ~ 190887435us(最大值即 2^32 / fac_us @fac_us = 21)
 * @retval    无
 */ 
void delay_us(uint32_t nus)
{
    uint32_t ticks;
    uint32_t told, tnow, tcnt = 0;
    uint32_t reload = SysTick->LOAD;        /* LOAD的值 */
    ticks = nus * g_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;                      /* 时间超过/等于要延迟的时间,则退出 */
            }
        }
    }
} 

STM32F407 移植 FreeRTOS_第24张图片

delay_ms()

函数 delay_ms() 用于毫秒级的 CPU 忙延时,原本的函数 delay_ms() 会判断 μC/OS 是否运行,如果 μC/OS 正在运行的话,就使用 μC/OS 的 OS 延时进行毫秒级的延时,否则就调用函数 delay_us() 进行毫秒级的 CPU 忙延时。在 FreeRTOS 中,可以将函数 delay_ms() 定义为只进行 CPU 忙延时,当需要 OS 延时的时候,调用 FreeRTOS 提供的 OS 延时函数 vTaskDelay() 进行系统节拍级延时,代码修改后如下图所示(删掉原来的delay_ms(),改为下面的)

/**
 * @brief     延时nms
 * @param     nms: 要延时的ms数 (0< nms <= 65535) 
 * @retval    无
 */
void delay_ms(uint16_t nms) 
{
    uint32_t i; 
    for (i=0; i<nms; i++) 
    { 
        delay_us(1000); 
    } 
}

STM32F407 移植 FreeRTOS_第25张图片

包含头文件

根据上述步骤的修改,delay.c 文件中使用到了 FreeRTOS 的相关函数,因此就需要在 delay.c 文件中包含 FreeRTOS 的相关头文件,并且移除掉原本存在的 μC/OS相关头文件。先看一下修改前 delay.c文件中包含的 μC/OS相关的头文件(标蓝部分):
STM32F407 移植 FreeRTOS_第26张图片
修改成如下内容:

/* 添加公共头文件 (FreeRTOS 需要用到) */ 
#include "FreeRTOS.h" 
#include "task.h"

STM32F407 移植 FreeRTOS_第27张图片
至此,SYSTEM 文件夹针对 FreeRTOS 的修改就完成了。

1.4 修改中断相关文件

在 FreeRTOS 的移植过程中会用到这三个重要的中断,分别是 FreeRTOS 系统时基定时器的中断( SysTick中断)、 SVC中断、 PendSV中断,这三个中断的中断服务函数在 HAL 库提供的文件中都有定义,对于正点原子不同的 STM32 开发板,对应了不同的文件,具体对应关系如下表所示:

正点原子 的 STM32系列开发板类型 中断服务函数所在文件
STM32F1 stm32f1xx_it.c
STM32F4 stm32f4xx_it.c
STM32F7 stm32f7xx_it.c
STM32H7 stm32h7xx_it.c

其中,SysTick 的中断服务函数在 delay.c 文件中已经定义了,并且 FreeRTOS 也提供了 SVC 和 PendSV 的中断服务函数,因此需要将 HAL 库提供的这三个中断服务函数注释掉,这里采用宏开关的方式让 HAL 库中的这三个中断服务函数不加入编译 ,使用的宏在 sys.h 中定义,因此还需要导入 sys.h 头文件。大家可以按照上表找到对应的文件进行修改。下面是详细的修改步骤。
按照上表的文件名,找到和自己开发板对应的文件,如下图所示
STM32F407 移植 FreeRTOS_第28张图片
导入 sys.h 头文件,导入后如下图所示
STM32F407 移植 FreeRTOS_第29张图片
找到 SVC_Handler() ,然后按照下图的方式进行修改

/**
  * @brief  This function handles SVCall exception.
  * @param  None
  * @retval None
  */
#if (!SYS_SUPPORT_OS)
void SVC_Handler(void)
{
}
#endif

STM32F407 移植 FreeRTOS_第30张图片
找到 PendSV_Handler() ,然后按照下图的方式进行修改

/**
  * @brief  This function handles PendSVC exception.
  * @param  None
  * @retval None
  */
#if (!SYS_SUPPORT_OS)
void PendSV_Handler(void)
{
}
#endif

STM32F407 移植 FreeRTOS_第31张图片

找到 SysTick_Handler() ,然后按照下图的方式进行修改

/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
  */
#if (!SYS_SUPPORT_OS)
void SysTick_Handler(void)
{
  HAL_IncTick();
}
#endif

STM32F407 移植 FreeRTOS_第32张图片
最后,也是移植 FreeRTOS 要修改的最后一个地方 FreeRTOSConfig.h 文件中有如下定义:

#define configPRIO_BITS __NVIC_PRIO_BITS

这个宏定义将 configPRIO_BITS 定义成 __NVIC_PRIO_BITS,而 __NVIC_PRIO_BITS 在 HAL 库中有相关定义,对于正点原子不同的 STM32开发板,__NVIC_PRIO_BITS 定义在不同的文件中,具体的对应关系如下表所示:

正点原子 的 STM32系列开发板类型 __NVIC_PRIO_BITS所在文件
STM32F1 stm32f103xe.h
STM32F4 stm32f407xx.h或 stm32f429xx.h
STM32F7 stm32f750xx.h或 stm32f767xx.h
STM32H7 stm32h750xx.h或 stm32h743xx.h

原本的值为

#define __NVIC_PRIO_BITS 4U

这个值是对的,但是在FreeRTOS中会报错,于是改为下图所示的值

#define __NVIC_PRIO_BITS 4

STM32F407 移植 FreeRTOS_第33张图片

到这里 FreeRTOS 已经移植完成了,不过还有一些可选步骤,也建议大家一起完成

1.5 可选步骤

1.5.1 修改工程名字

如下图所示
STM32F407 移植 FreeRTOS_第34张图片

1.5.2 移除 USMART 调试组件

USMART 是正点原子

2. 验证结果

你可能感兴趣的:(stm32,FreeRTOS,stm32,单片机,嵌入式硬件)