前言
学习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" #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).*
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了。