前言
本文是在Windows10环境下,以VSCode+arm交叉编译工具链为开发环境,将FreeRTOS移植到STM32F103系列单片机的说明。其实不叫移植,叫做拷贝,大家都叫移植,那就这样叫了。
工程基于你自己的一个能运行的裸机工程。并且我用的是标准固件库。
为啥搭建这个环境,首先是喜欢用VSCode编辑,还有一个重要原因是,用到的所有工具都是开源的,不用担心版权问题。没钱买有些正版工具,又不想用盗版的人最后的倔强(笑死)。当然付费的有付费的优势,买了服务嘛!有时候免费的才是最贵的,遇到问题只能自己苦逼到处寻找解决方案哈哈!
先看第七节附录,或许对你有帮助!
1 准备工作
1.1 工具准备
- VSCode
- GCC-ARM-NONE-EABI(交叉编译工具)
- Make(构建工具)
- CMake(Makefile生成工具)
- OpenOCD(调试下载工具)
上述工具中,前3项是必须的。CMake可以帮助我们生成Makefile文件,如果你自己写Makefile可以不用;OpenOCD是片上调试工具,可以下载和调试,当然你也可以生成二进制文件,用串口下载,不是必须。
这里就不再赘述工具的安装,未配置好环境的读者,可以参考其他文章,先将工具安装好。
我所使用的工具的版本:
1.2 FreeRTOS下载
直接在FreeRTOS官网下载最新的即可,我这里下载的普通版,没有下载长期支持版。官网下载地址
1.3 裸机文件准备
一个能跑的裸机工程,最好带串口驱动的。
2 开始移植
2.1 创建FreeRTOS文件夹
在准备好的裸机工程根目录中创建FreeRTOS文件夹,用于存放系统源码。
裸机工程根目录:
新的根目录:
2.2 FreeRTOS源文件拷贝
-
解压刚刚下载的FreeRTOS的压缩包,并将路径
\FreeRTOSv202212.01\FreeRTOS\Source
下的所有文件拷贝到上一步创建的freertos文件夹下,拷贝后,如下图。
-
进入
FreeRTOSv202212.01\FreeRTOS\Demo\CORTEX_STM32F103_Primer_GCC
,将main.c
以及FreeRTOSConfig.h
复制到你自己的用户代码文件夹下面。
-
进入
\Demo\freertos\portable
文件夹,如下图。留下GCC和MemMang,其余的全部删除。
删除后:
-
进入
\Demo\freertos\portable\GCC
文件夹,只留ARM_CM3
,其余全部删除。
-
进入
\Demo\freertos\portable\MemMang
文件夹,留下heap_4.c
其余全部删除。
3 修改文件
3.1 修改CMakeLists.txt或者Makefile
我这里用的CMakeLists.txt,故只给出CMakeLists.txt的修改。就是在CMakeLists.txt中增加源文件以及头文件的包含路径。
根据你自己文件结构的情况修改即可。
以前的:
增加后的:
3.2 修改FreeRTOSConfig.h文件
在FreeRTOSConfig.h文件最后加上如下三句宏定义,意思是使用FreeRTOS给我们写好的这三个中断的处理函数,不用自己写了。这样加的好处是,我们不用去修改启动文件中的中断函数名,直接用裸机的启动文件就可以,有些历程中是将启动文件中对应的三个中断向量名给改了。
二选一,要么改启动文件,就不需要宏定义了,要么宏定义,就不需要改启动文件。我觉得宏定义更方便,所以选择宏定义。
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
还要删除包含的头文件#include "stm32f10x_lib.h"
,并将#define configUSE_TICK_HOOK 0
定义为0,默认为1的。
说明:链接文件也不需要动
3.3 修改stm32f10x_it.c文件
通常标准库的例程下,都配有专门放中断处理函数的一个文件,即stm32f10x_it.c
,我们需要注释掉或者删掉上一步中所提到的三个中断处理函数,避免和FreeRTOS中的冲突,导致重定义。
或者直接删掉存放中断处理函数的源文件和头文件也行。
3.3 修改main.c文件
-
main函数前面,只留如下代码,并且包含
#include "stm32f10x.h"
如图:
-
main函数中,只留如下代码,如图:
-
main函数后面,只留下
static void prvSetupHardware( void );
的定义,其他全删咯。并且删除此定义中的最后四行。删去如下四行:
4 VSCode相关配置
4.1 安装代码识别补全插件
安装如下,两个插件,并配置c_cpp_properties.json
中的编译器路径。全部配置如下:
{
"configurations": [
{
"name": "arm_gcc",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE",
"STM32F10X_HD",
"USE_STDPERIPH_DRIVER"
],
"cStandard": "c99",
"cppStandard": "c++98",
"intelliSenseMode": "gcc-arm",
"compilerPath": "E:\\stm32_toolchain\\gcc-arm-none-eabi-10.3-2021.10\\bin\\arm-none-eabi-gcc.exe",
"configurationProvider": "ms-vscode.cmake-tools"
}
],
"version": 4
}
4.2 配置cmake和make构建任务
当然,你也可以直接用命令行完成,这样配置为VSCode的任务,是更加方便快捷,不一定都得配置。
这些工具使用的命令参数这些,大家就参考着根据自己的文件结构改一下,这里不多说工具的使用。
配置文件task.json
内容如下:
{
"version": "2.0.0",
"tasks": [
{
"label": "cmake",
"type": "shell",
"command":"cmake",
"args": [
"-B",
"build/",
"-G",
"Unix Makefiles",
"-DCMAKE_BUILD_TYPE=Debug"
],
"group": "build"
},
{
"label": "build",
"type": "shell",
"command":"make",
"args": [
"-j4",
"-C",
"build/"
],
"group": "build"
},
{
"label": "download",
"type": "shell",
"command":"openocd",
"args": [
"-f",
"interface/cmsis-dap.cfg",
"-f",
"target/stm32f1x.cfg",
"-c",
"program build/vscodeSTM32.elf verify reset exit"
],
"group": "build"
}
]
}
4.3 配置调试
调试任务的文件是launch.json
,如下:
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug With OpenOCD",
"cwd": "${workspaceRoot}",
"executable": "build/vscodeSTM32.elf",
"request": "launch",
"type": "cortex-debug",
"servertype": "openocd",
"configFiles": [
"interface/cmsis-dap.cfg",
"target/stm32f1x.cfg"
],
"searchDir": [],
"runToEntryPoint": "main",
"showDevDebugOutput": "none",
"svdFile": "./STM32F10x.svd"
}
]
}
5 构建工程
如果没有配置第4步的,可以直接参考其内容中每个命令,用命令行工具构建。
以下使用配置的VSCode任务来快速构建。
在终端中选择运行生成任务,或者用你的快捷键。
-
运行cmake任务
生成成功:
-
运行make任务
构建成功:
到目前为止,编译链接成功,说明我们的工程和配置文件没有问题。
6 大型点灯以及通信工程
我打算用最常见最简单的两个功能来演示,点灯和串口打印。
- 先包含串口头文件,然后GPIO口的配置声明,以及两个任务函数。如图:
#include "bsp_usart.h"
static void GPIO_LED_Conf(void);
void Task1_LED(void *param)
{
GPIO_LED_Conf();
const TickType_t xTickstoWait = pdMS_TO_TICKS(500);
while (1)
{
GPIO_WriteBit(GPIOE, GPIO_Pin_5, Bit_RESET);
vTaskDelay(xTickstoWait);
GPIO_WriteBit(GPIOE, GPIO_Pin_5, Bit_SET);
vTaskDelay(xTickstoWait);
}
}
void Task2_Print(void *param)
{
USART_Conf();
const TickType_t xTickstoWait = pdMS_TO_TICKS(1000);
while (1)
{
printf("Print Task is runing every 1 second...\n");
vTaskDelay(xTickstoWait);
}
}
- 然后在main函数中创建任务。如图:
xTaskCreate(Task1_LED, "Task1_LED", 100, NULL, 1, NULL);
xTaskCreate(Task2_Print, "Task2_Print", 100, NULL, 1, NULL);
- GPIO的配置我放在main函数后面的,这个需要根据自己的板子led的引脚位置来改。
static void GPIO_LED_Conf(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
}
-
再次构建build或者说make
构建成功:
-
然后就是下载了,可以用串口,也可以用刚刚配置的任务download,反正下载的方式非常多,用你之前的只要能下进去就行。
下载进去试试:
LED灯闪烁:
串口打印:
到此为止,FreeRTOS算是成功移植到了STM32F103上面了。收工!