FreeRTOS系统移植

前言

学习RTOS之前最重要的就是要学会将系统移植到单片机中,这里可以直接使用cubemx生成移植好的工程,也可以下载库来进行自己移植,这里我选择下载库来自己移植,因为这样可以配合Linux开发stm32单片机程序。

一、下载系统代码

首先打开FreeRTOS的官网这个是中文站点,所以速度很快,然后点击下载:

在下载里面不要最新的系统,这里我需要V9.0.0就可以了,因为高版本的增加了很多物联网相关的内容,对于我们现在的系统开发不太需要,所以这里点击更多下载

然后选择下面不带LTS的,LTS没有示例项目,有一些文件我们需要在示例工程中拿,然后需要转到github中去下载

来到GitHub中是最新版本,我们要找老版本需要点击Code转去代码那里,然后找到V9.0.0的代码

来到了后在标签这找到V9.0.0

然后点击下载zip格式的文件

下载完成后就可以解压,解压完成就是下面这几个软件

可以看到有两个文件夹,一个是FreeRTOS,另一个是FreeRTOS-Plus,这两个是有区别的,第一个是内核,是我们需要的,第二个Plus的是在内核的基础上增加了一系列扩展组件和工具,提供了更丰富的功能和更高层次的抽象,例如,包含文件系统、TCP/IP 协议栈、USB 主机 / 设备栈、安全功能(如加密库)、图形用户界面(GUI)库等。如果做一些高级的东西可以用这个,但我们现在只是学习,选择第一个基础的就可以了。

打开第一个文件夹后会有三个文件夹,我们先看Source文件夹,在Source文件夹中存放的是内核源代码和对应的头文件

Source文件夹中有一个portable这个是内核文件,其中内核文件是分平台的,这个平台是编译器的平台,比如我们用Keil,那就在这个文件夹中找到Keil进行移植就可以了,但是这里不能直接把Keil文件夹直接拿过去用,原因在移植的时候会说

然后在include文件夹中存放的是对应的头文件

回到上一层,有一个Demo文件夹,这个文件夹就是每个芯片所对应的示例工程,有一些文件我们需要在这个示例文件中

然后我们就可以开始工程的移植了。

二、移植工程

这里需要拿之前做好的标准库工程来进行改,只需要在做好的工程来新增文件就可以了,打开工程后点击这个三个正方形设置文件

在这里面新建一个freeRTOS文件夹,在里面添加一下的.c文件,我这为了工程的干净,不增加.h文件了

然后点击Options中点击C/C++点击Include Paths中的三个点,增加一下编译文件目录

需要添加的文件目录如下:

然后在main.c中增加两个头文件,并且添加一个测试的内容,这里使用的是最小系统上的测试灯,所以需要进行一下初始化

然后编写一下控制函数

编写一下任务函数,记得这个任务函数的格式是void TaskName(void*)

然后在main.c中创建任务并且进行任务的调度:

1
2
3
4
5
6
7
8
9
int main(){
NVIC_SetPriorityGrouping(NVIC_PriorityGroup_4); // 设置中断优先级分组为第四组 这个是为了后面的定时器需要增加的
Init_Led();
xTaskCreate(LED_Test, "LED_Test", 128, NULL, 1, NULL); // 创建任务
vTaskStartScheduler(); // 调度任务

while(1){
}
}

作为上面的操作后还不能进行测试,如果这样测试的话会导致在vTaskStartScheduler()函数的位置卡死,因为还没有移植成功,需要先在freeRTOSConfig.h文件中的最后面添加这三个宏定义:

1
2
3
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
#define vPortSVCHandler SVC_Handler

然后在stm32f10x_it.c中需要注释一下这三个回调函数,因为这三个回调函数在系统中是进行了一下实现的,如果还没有,那会重复定义:

这样就可以编译并且运行测试了,可以看到LED灯在闪烁。

三、完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include "stm32f10x.h"                  // Device header
#include "freertos.h"
#include "task.h"

void Init_Led(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
}

void LED_Cmd(uint8_t bit)
{
if (bit == 1)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_5);
}
else if (bit == 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_5);
}
}

void LED_Test(void* param)
{
while(1)
{
LED_Cmd(1);
vTaskDelay(500);
LED_Cmd(0);
vTaskDelay(500);
}
}

int main(){
NVIC_SetPriorityGrouping(NVIC_PriorityGroup_4); // 设置中断优先级分组为第四组
Init_Led();
xTaskCreate(LED_Test, "LED_Test", 128, NULL, 1, NULL); // 创建任务
vTaskStartScheduler(); // 调度任务

while(1){
}
}

四、Linux下的MarkDown文件

这里上面的工程一模一样,只不过就是需要在那个编译器工程那选择GCC的文件,因为使用的是arm-gcc进行编译,所以这里要选择的是GCC的文件夹

然后其它的内容不改变,然后开始编辑makefile文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
TARGET=test	# 这里是项目名,改就可以
CC=arm-none-eabi-gcc
OBJCOPY=arm-none-eabi-objcopy
RM=rm -f
CORE=3
CPUFLAGS=-mthumb -mcpu=cortex-m$(CORE)
INCFLAGS=-I $(shell pwd)/lib/CMSIS -I $(shell pwd)/lib/ -I $(shell pwd)/freeRTOS/include -I $(shell pwd)/freeRTOS/portable/GCC -I $(shell pwd)/freeRTOS/portable/GCC/MemMang FWlib/inc -I $(shell pwd)/freeRTOS/portable/GCC/ARM_CM3 -I $(shell pwd)/user
LDFLAGS = -T stm32_flash.ld -Wl,-cref,-u,Reset_Handler -Wl,-Map=$(TARGET).map -Wl,--gc-sections -Wl,--defsym=malloc_getpagesize_P=0x80 -Wl,--start-group -lc -lm -Wl,--end-group
CFLAGS=$(INCFLAGS) -D STM32F10X_HD -D USE_STDPERIPH_DRIVER -Wall -g
START_SRC=$(shell find ./ -name 'startup_stm32f10x_hd.s')
START_OBJ=$(START_SRC:%.s=%.o)
C_SRC=$(shell find ./ -name '*.c')
C_OBJ=$(C_SRC:%.c=%.o)
INTERRFACE_CFG=/usr/local/share/openocd/scripts/interface/stlink-v2.cfg # 这里就是下载器的配置文件,可以在前面提到的文件夹中找到对应下载器的配置文件
TARGET_CFG=/usr/local/share/openocd/scripts/target/stm32f1x.cfg
$(TARGET):$(START_OBJ) $(C_SRC)
$(CC) $^ $(CPUFLAGS) $(LDFLAGS) $(CFLAGS) -o $(TARGET).elf
$(OBJCOPY) $(TARGET).elf $(TARGET).bin
$(OBJCOPY) $(TARGET).elf -Oihex $(TARGET).hex
$(START_OBJ):$(START_SRC)
$(CC) -c $^ $(CPUFLAGS) $(CFLAGS) -o $@
$(C_OBJ):%.o:%.c
$(CC) -c $^ $(CPUFLAGS) $(CFLAGS) -o $@
clear:
$(RM) $(shell find ./ -name '*.o') $(TARGET).*

# 这个是一键下载,如果不是stlink下载器可以改对应的变量
download:
openocd -f $(INTERRFACE_CFG) -f $(TARGET_CFG) -c init -c halt -c "flash write_image erase $(PWD)/$(TARGET).bin" -c reset -c shutdown

其实最主要的是增加编译的头文件,然后剩下的一样。

总结

这是一小步,但是系统学习和高级功能实现的一大步,好好搞一下就ok了。