@[TOC](3.1 FreeRTOS详细移植步骤(自己的实操))
自己使用阿波罗F767的内存管理实验和定时器实验,进行复刻。
FreeRTOS源码版本是FreeRTOS 202212.01。官网和Github都有下载。
按照STM32F767FreeRTOS开发手册V1.1进行移植复刻。
注:这个开发手册不是开发指南。跟视频里用的教程(开发手册)有出入。
新建FreeRTOS的工程方法可以分为:
1、CubeMX生成带FreeRTOS的HAL库工程。(CSDN有一篇文章介绍,简单)
2、已有HAL库的基础工程,将FreeRTOS移植进该工程。(步骤复杂)
3、已有标准固件库的基础工程,将FreeRTOS移植进该工程。(步骤复杂)
本文记录"已有HAL库的基础工程,将FreeRTOS移植进该工程"的方法。
计划完成FreeRTOS基础工程移植后
再完成"任务创建与恢复"实验,看看代码的含义。
完成后续视频中的实验。
由于没有开发板,实验只编程,没有报错即可,不看实验现象。
(虽然是一种不好的学习方法)
1 准备
1 阿波罗F767的内存管理实验
作为基础工程
2 阿波罗F767的定时器实验
用到里面的设备驱动代码
3 FreeRTOS源码
官网或github下载。2.24.5.24,下载的是V202212.01版本。
4(可选) 正点原子的"FreeRTOS移植实验",这个是移植完成的工程,可当作参考。
没找到HAL的FreeRTOS工程。只有固件库的FreeRTOS工程,可按照教程修改,使之支持HAL
库。
2 移植
2.1 STM32F7xx_DFP
keil5编辑STM32F系列的工程时,需要添加STM32F7xx_DFP的pack,CSDN上已记录添加步骤。
2.2 建立基础工程
使用内存管理实验作为基础工程。
将内存管理实验复制一份,并命名为"FreeRTOS_Demo
"。
修改名字(非必须),并编译。
会发现0error0warning。
2.3 FreeRTOS源码文件处理
2.3.1 源码文件解压
略
2.3.2 源码文件简化
源码解压后的文件夹是FreeRTOSv202212.01
,找到其中的Source
文件夹是各源码文件,有一些文件用不到,所以就删了。
有以上绿色下划线的五个文件,FreeRTOS就可以用起来了。
不用其他功能,对应的.c文件,可不添加。
这里就全部保留了。
再将portable
文件夹中的文件进行删除。
只留下下面三个文件夹(用其他编辑器或MCU,根据实际情况选择留下的文件夹)
2.4 添加FreeRTOS源码文件
将FreeRTOS源码文件添加至工程。过程简单,不再展示图片。
第一步:在基础工程中新建一个名为 FreeRTOS 的文件夹。
第二步:将Source
文件夹下的所有源码文件,复制进第一步新建的文件夹中。
第三步:在keil中新建两个"分组",分别是 FreeRTOS_CORE
和FreeRTOS_PORTABLE
。
新建分组,不等于新建文件夹。
第四步:在keil中为 FreeRTOS_CORE
添加c文件
添加完成后
第五步:在keil中为FreeRTOS_PORTABLE
添加c文件
这个分组中添加两个文件,port.c
和heap_4.c
。
port.c
与所用的芯片有关,这个选用了STM32F767,所以要选择RVDS文件夹下的ARM_CM7中的port.c
文件。
heap_4.c
是内存管理算法,选择MemMang文件夹下的heap_4.c
文件。
添加完成后如下图。
第六步:添加头文件路径
在上两步中添加了c文件,还要添加FreeRTOS的头文件路径,要不keil找不到头文件会报错。
完成以上六步后,点击编译,会报错,提示没有"FreeRTOSConfig.h"配置文件。
所以进行2.5小节。
2.5 添加FreeRTOS配置文件
FreeRTOSConfig.h是一个配置文件,在这个文件中,通过宏定义完成对系统功能的配置和裁剪。共有三个方法。推荐第三种。
方法一:自己创建并编写。
略
方法二:从官方demo中移植。
在官方demo中找到针对F7的KEIL工程。
上面的文件夹中,即可找到FreeRTOSConfig.h配置文件。
Cortex就是ARM公司一个系列处理器的名称。
比如英特尔旗下处理器有酷睿,奔腾,赛扬。
ARM在最初的处理器型号都用数字命名,最后一个是ARM11系列,在应用ARMv7架构后,推出了Cortex这一系列,老式的则命名为Classic系列。
其中:"A"系列面向尖端的基于虚拟内存的操作系统和用户应用;"R"系列针对实时系统;"M"系列对微控制器。
方法三:从"FreeRTOS移植实验"中获取(推荐)
从正点原子的"FreeRTOS移植实验"中获取带注释 的配置文件,更方便学习。
在扩展例程中找到该实验。
找到后将文件复制,粘贴到移植工程中FreeRTOS文件夹下的Include中(放在USER下也行,灵活点),这样就不需要再添加"头文件路径"了。
此时再编译工程,仍会报错。
按照下面的步骤继续进行修改,逐渐的消除错误信息,完成移植。
2.6 修改system文件
修改其中的sys.h,delay.c,uart.c三个文件。
这三个文件大部分是针对UCOS的,所以要进行修改。
使之支持FreeRTOS。
2.6.1 修改sys.h文件
打开sys.h文件
使用了FreeRTOS,所以将SYSTEM_SUPPORT_OS修改为1。
c
//0,不支持os
//1,支持os
#define SYSTEM_SUPPORT_OS 1 //定义系统文件夹是否支持OS
2.6.2 修改uart.c文件
有两部分需要进行修改
第一部分:添加FreeRTOSConfig.h头文件
默认添加的是UCOS的"includes.h"头文件,将其删除,添加为"FreeRTOSConfig.h"的头文件。
c
//如果使用os,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
//#include "includes.h" //UCOS 使用
#include "FreeRTOSConfig.h" //FreeRTOS使用
#endif
第二部分:修改USART1的中断服务函数
uart.c文件默认支持UCOS的,UCOS进出中断时需要添加OSIntEnter()和 OSIntExit(),使用 FreeRTOS 的话就不需要了,所以将这两行代码删除掉。
看了下,本工程的uart.c文件中,该代码用了条件编译。如下。
c
#if SYSTEM_SUPPORT_OS //使用OS
OSIntEnter();
#endif
所以直接删除也行,宏前面加个叹号也行。
这里直接删除。
重新学习一下宏定义,看看#if和#ifdef的区别是什么?
删除后的代码为
c
//串口1中断服务程序 FreeRTOS
void USART1_IRQHandler(void)
{
u32 timeout=0;
u32 maxDelay=0x1FFFF;
HAL_UART_IRQHandler(&UART1_Handler); //调用HAL库中断处理公用函数
timeout=0;
while (HAL_UART_GetState(&UART1_Handler)!=HAL_UART_STATE_READY)//等待就绪
{
timeout++;超时处理
if(timeout>maxDelay) break;
}
timeout=0;
while(HAL_UART_Receive_IT(&UART1_Handler,(u8 *)aRxBuffer, RXBUFFERSIZE)!=HAL_OK)//一次处理完成之后,重新开启中断并设置RxXferCount为1
{
timeout++; //超时处理
if(timeout>maxDelay) break;
}
}
注意,在我这里用的这个例程中,USART1的中断服务函数是针对UCOS的,即使删除了OSIntEnter()和 OSIntExit()也可能 不对。
但在下方有一个被注释的USART1中断服务函数,这个是针对FreeRTOS的。就是上面删除后的代码。(这段代码也是跟指南中的代码一致的)
所以一定要跟上面的代码对应。
还可以将上面的代码复制,对"USART1的中断服务函数"进行替换。
2.6.3 修改delay.c文件
同理,delay.c文件中的程序是支持UCOS的,经过对比,发现不适合将程序修改后适配FreeRTOS,所以将delay.c文件的代码全部删除,然后将固件库的"FreeRTOS移植实验"delay.c文件代码复制过来。复制过来之后,再稍作修改,使之支持HAL库。
因为delay.c文件涉及到 FreeRTOS 的系统时钟,所以修改比较大。
delay.c文件中有5个函数,将所以的代码复制,经过修改后,支持HAL的FreeRTOS中的delay.c代码如下。
以后使用时,直接将下面的代码复制到delay.c中即可。
注意,倒数第二个函数void delay_ms(u32 nms),形参改变了,需要在.h中重新声明一下。
c
#include "delay.h"
#include "sys.h"
//
//如果使用OS,则包括下面的头文件即可
#if SYSTEM_SUPPORT_OS
#include "FreeRTOS.h" //FreeRTOS使用
#include "task.h"
#endif
//
//使用SysTick的普通计数模式对延迟进行管理(支持OS)
//包括delay_us,delay_ms
//********************************************************************************
static u8 fac_us=0; //us延时倍乘数
static u16 fac_ms=0; //ms延时倍乘数,在os下,代表每个节拍的ms数
extern void xPortSysTickHandler(void);
//systick中断服务函数,使用OS时用到
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
xPortSysTickHandler();
}
HAL_IncTick();
}
//初始化延迟函数
//当使用FreeRTOS时,此函数会初始化FreeRTOS的时钟节拍。
//SYSTICK的时钟固定为AHB时钟,基础例程里面SYSTICK时钟频率为AHB/8
//这里为了兼容FreeRTOS,所以将SYSTICK的时钟频率改为AHB的频率!
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{
u32 reload;
//SysTick 频率为 HCLK
HAL_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);
}
修改完成后,点击编译。
2.7 修改中断相关文件
编译后会报错
这三个函数,在port.c和stm32f7xx_it.c中被多次定义了。
所以选在将stm32f7xx_it.c中的这三个函数注释掉。
用条件编译注释掉,如下
c
#if (!SYSTEM_SUPPORT_OS)
void SVC_Handler(void)
{
}
#endif
注意,SYSTEM_SUPPORT_OS这个宏在sys.h中,所以要在stm32f7xx_it.c包含这个头文件。
完成后再点击编译。
2.8 小节
编译后没有错误。
但是还需要编写应用程序进行验证。
移植步骤大体就是这样。
看开发手册中,不同芯片还会有其他错误,等实际遇到时再去改吧。
2.9 验证移植是否成功
需要编写应用程序。