FreeRTOS版本:202212.01
STM32型号:STM32H743VIT6
使用工具:Clion
其实在STM32cubeMX中就可以直接配置FreeRTOS,rtthread等操作系统,而且使用接口封装过的,很方便。
但我还是想手动一直一遍,肯定有不一样的收获。
1.下载FreeRTOS源码
FreeRTOS官网https://www.freertos.org/
源码如下
FreeRTOS文件夹
FreeRTOS是内核源码还有一些Demo
FreeRTOS-Plus文件夹
FreeRTOS-Plus是一个包含扩展功能的文件夹
它提供了一些额外的组件和库,这些组件和库并非FreeRTOS内核的一部分,但可以增强系统的功能或提供额外的支持。具体内容包括但不限于:
FreeRTOS-Plus-CLI:一个命令行接口库,允许用户通过串口或其他通信接口与系统进行交互,执行命令和获取信息。
FreeRTOS-Plus-TCP:一个TCP/IP协议栈,为嵌入式系统提供网络通信能力,支持标准的网络协议如TCP、UDP、ICMP等。
FreeRTOS-Plus-FAT:一个文件系统库,支持FAT文件系统,使得嵌入式设备能够读写标准的FAT格式的存储介质,如SD卡或USB闪存驱动器。
FreeRTOS-Plus-IO:一个输入输出库,提供了一套统一的接口来管理不同硬件平台的输入输出设备,简化了设备驱动程序的开发。
FreeRTOS-Plus-Trace:一个跟踪和调试工具,帮助开发者监控和分析系统的运行状态,特别是任务调度和资源使用情况。
【参考文章:二、FreeRTOS目录结构与配置选项讲解】
tools文件夹
tools文件夹是一些工具脚本
所以我们移植FreeRTOS与学习它的内核的话,只看FreeRTOS这个文件夹即可。
2.FreeRTOS源码文件
Demo文件夹
FreeRTOS/Demo文件夹中,包含各种演示应用程序,展示了如何在不同的硬件平台上使用 FreeRTOS。每个子文件夹对应一个特定的硬件平台或开发板
移植时要参考和你的芯片相同内核或者类似的Demo,用得到其中的配置文件FreeRTOSConfig.h。
比如我的芯片类型是STM32H743,是cortex M7内核的,但是Demo中并没有STM32H743的例程,那我就找同样cortex M7内核的例程,发现有个CORTEX_M7_STM32F7_STM32756G-EVAL_IAR_Keil,那就它了。
Source文件夹
FreeRTOS/Source是 FreeRTOS 内核源码。
include是头文件
portable 是移植文件:包含与具体硬件平台相关的移植代码。
内核主要文件包括:
tasks.c:任务管理。
queue.c:队列管理。
list.c:链表管理。
timers.c:软件定时器管理。
event_groups.c:事件组管理。
croutine.c:协程(几乎不用)
stream_buffer.c:缓冲区
Portable 文件夹
包含与具体硬件平台相关的移植代码。FreeRTOS 支持多种不同的处理器架构和编译器,因此需要针对不同的平台进行移植。
主要子文件夹包括:
MemMang:内存管理相关代码。
GCC:GNU 编译器集合相关的移植代码。
RVDS:ARM RealView 编译工具链相关的移植代码。
IAR:IAR 编译器相关的移植代码。
因为Clion是使用GCC来编译,所以只关注GCC这个文件夹,如下:
ARM_CM7文件内容如下:
遇见readMe就读一读,肯定没有坏处。
机翻一下:
在ARM Cortex-M7微控制器上运行FreeRTOS有两种选择。最佳选择取决于所使用的ARM Cortex-M7内核的版本。这个revision由一个’r’数字和一个’p’数字指定,所以看起来会有些不同比如“r0p1”。检查正在使用的微控制器的文档,以找到该微控制器中使用的Cortex-M7内核的修订版。如有疑问,请使用FreeRTOS端口是专门为r0p1版本提供的,因为它可以使用所有核心修订。
第一种选择是使用ARM Cortex-M4F端口,第二种选择是使用Cortex-M7 r0p1端口,后者包含一个小的勘误解决方法。
如果ARM Cortex-M7内核的版本不是r0p1,则可以选择已使用,但建议使用FreeRTOS ARM Cortex-M4F接口
/FreeRTOS/Source/portable/GCC/ARM_CM4F目录。
如果ARM Cortex-M7内核的版本为r0p1,则使用FreeRTOS ARM Cortex-M7 r0p1接口位于
/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1中目录。
就是说r0p1是内核版本,好像这个内核版本有个小bug?如果内核版本是r0p1就使用CM7接口,不是r0p1版本就使用CM4F接口。
老实讲,我并没有找到STM32H743的内核版本是啥。(如有大佬知道请求告知或指点)
算了,先移植一个试试。就CM7的吧
3.拷贝FreeRTOS必要文件到工程
首先有一个可正常运行的裸机工程,比如点灯工程。
在此基础上进行移植。
工程目录下新建文件夹(自己任意起名字)
我建的是
My_Middlewares/FreeRTOS。
(如果使用cubeMX配置其他中间件的话,会自动生成一个文件夹Middlewares,为了与它避免因为名字冲突所带来的麻烦,所以我建的是My_Middlewares)
文件结构如下:
inc是头文件,就是FreeRTOS源码中include文件夹中的内容,还有把Demo中的FreeRTOSConfig.h文件也拷贝过来。
src是源文件
port是接口文件
MemMang是内存管理文件
4.修改CMakelists_temaplate
修改cmakelists的模版文件,不要直接修改cmakelists文件,因为每次生成代码都会重新覆盖这个文件。所以修改CMakelists_temaplate
增加头文件路径
增加编译文件
将21到23取消注释,即开启硬件浮点单元FPU
然后更新cmakelists
5.修改FreeRTOSConfig.h
这个文件毕竟是从人家那拷贝过来的,手动修改一下
大家的每个FreeRTOSConfig.h可能都不一样,修改也可能不一样。
这里仅以我拷贝的这个文件进行说明
这里是#ifndef IAR_SYSTEMS_ASM,反正我不是使用IAR,那这个就不删了。
将#include "stm32F7xx_hal.h"修改为#include “stm32h7xx_hal.h”
将configASSERT注释掉,没注释掉一直报错。
或者可以自己加一个断言
比如这样(前提是串口可以正常使用):
#define vAssertCalled(char,int) printf(“Error:%s,%d\r\n”,char,int)
#define configASSERT(x) if((x)==0) vAssertCalled(FILE,LINE)
我这里先不加了直接注释掉了。
将一些钩子函数hook都置为0
configCPU_CLOCK_HZ默认为系统时钟
configTICK_RATE_HZ滴答频率默认1ms
这个不用修改
6.修改stm32h7xx_it.c
在stm32中,基础时钟默认使用的是Systick定时器,用于滴答计数,就是用于HAL_Delay这些。
但是在FreeRTOS中,因为所有的 Cortex M内核的单片机都有Systick定时器,所以FreeRTOS使用的是Systick产生滴答信号。
在FreeRTOSConfig.h中使用宏定义了这几个函数,与我们在stm32h7xx_it.c中的函数冲突,所以将stm32h7xx_it.c中将这三个修改为弱定义
7.验证
/*************************************FreeRTOS includes*************************************/
#include "FreeRTOS.h"
#include "task.h"
static TaskHandle_t xHandleTaskLED1 = NULL;
static TaskHandle_t xHandleTaskuart1 = NULL;
static void vTaskLED1(void *pvParameters);
static void vTaskUART1(void *pvParameters);
void vTaskLED1(void *pvParameters)
{
while(1)
{
bsp_LedToggle();
vTaskDelay(100);
}
}
void vTaskUART1(void *pvParameters)
{
while(1)
{
printf("vTaskUART1: Hello World!\r\n");
vTaskDelay(500);
}
}
void AppTaskCreate (void) {
xTaskCreate(vTaskLED1, /* 任务函数 */
"vTaskLED1", /* 任务名 */
100, /* 任务栈大小,单位word,也就是4字节 */
NULL, /* 任务参数 */
1, /* 任务优先级*/
&xHandleTaskLED1); /* 任务句柄 */
xTaskCreate(vTaskUART1, /* 任务函数 */
"vTaskUART1", /* 任务名 */
100, /* 任务栈大小,单位word,也就是4字节 */
NULL, /* 任务参数 */
1, /* 任务优先级*/
&xHandleTaskuart1); /* 任务句柄 */
}
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MPU Configuration--------------------------------------------------------*/
MPU_Config();
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/******** 省略********/
/*创建任务*/
AppTaskCreate();
/* 启动调度,开始执行任务 */
vTaskStartScheduler();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
同时led灯也在闪烁
OK!移植完成
8.修改时钟源
为了避免HAL的基础时钟和FreeRTOS的时钟冲突,可以选用其他定时器作为HAL的基础时钟,比如基本定时器TIM6
时钟这一块的内容向大佬学习的【11.2】FreeRTOS源码精讲② HAL和FreeRTOS基础时钟——Kevin带你读《STM32Cube高效开发教程高级篇》