FreeRTOS从代码层面进行原理分析(4 移植)
从前 3 篇博客中我们已经搞清楚了最开始对 FreeRTOS 有疑问的前 2 个问题。
1. FreeRTOS 是如何建立任务的呢?
2. FreeRTOS 是调度和切换任务的呢?
3. FreeRTOS 是如何保证实时性呢?
以下就是前三篇博客的连接,如果要开始移植首先真的是要搞清楚原理,这样就可以明确移植的过程中那些是重点。让自己的行动更明确。
FreeRTOS从代码层面进行原理分析(1 任务的建立)
FreeRTOS从代码层面进行原理分析(2 任务的启动)
FreeRTOS从代码层面进行原理分析(3 任务的切换)
在能实际的通过让代码跑起来的方式分析我们的第三个问题 FreeRTOS 是如何保证实时性
时,首先就是要把 FreeRTOS 移植到我自己的板子上。然后再通过调试(在之前的博客也有过介绍如何调试)或者代码设置等方式来搞清楚这个问题。
我做本次实验的设备:
1.翻出大学时候买的 STM32 开发板。MCU 型号 STM32F103RCT6
2. 同时期买的 ST-Link
移植前的准备工作
书接上文!既然知道原理了,那么用起来还会难吗? 接下来然我们开始把 FreeRTOS 移植到板子上吧~ 目前网上很多都是使用 Keil 作为环境进行 FreeRTOS 的移植,在这篇博客中我们将会使用GCC交叉编译的方式来移植 FreeRTOS。其实原理上也是相似的。废话不多说,让我们继续。
首先使用使用 CubeMX 随便生成一个简单的小项目。对于 CubeMX 的安装和简单的配置,详细的文章太多了,这里面就不仔细讲了。
为了能和其他代码隔离开,咱们在这个 CubeMX 生成的的项目内建立一个叫做 freertos 的文件夹,专门存放 FreeRTOS 的代码。
然后咱们再在 freertos 文件夹中建立三个文件夹,用于存放 FreeRTOS 不同部分的文件。
根据之前咱们对 FreeRTOS 的理解,一个项目首先是需要任务相关的全部代码。在下载的 FreeRTOS 源码的 source 目录下的文件(不包括文件夹)全部复制到 src 文件夹中。
然后把 source 下面的 include 目录中的内容复制到咱们的 include 目录中。
在前面的学习中,我们知道对于任务栈的初始化,切换任务的过程中需要针对不同架构的 MCU 对应实现的汇编函数。
在源码的 portable\GCC\ARM_CM3
文件夹中将 port.c
文件复制到咱们自己的 portable 文件夹中。
在 FreeRTOS 创建任务的时候还需要为任务栈申请对应的栈空间。所以我们要找到内存申请相关的函数,这些函数实现在源码的 \FreeRTOS\Source\portable\MemMang
目录中,我们先选择 heap4.c (一般都是用这个,各个heapX.c文件的区别可以看官方文档)也同样复制到我们自己的 portable 目录中。
我们知道还有一个叫做 FreeRTOSConfig.h
的头文件是用于配置 FreeRTOS 的。这个文件我们可以在对应架构的 Demo 中找到。比如我使用的就是从 FreeRTOS\Demo\CORTEX_STM32F103_Primer_GCC
找到的。 把这个 FreeRTOSConfig.h
也复制到咱们自己的 include 目录中。
至此,我们已经完成了 FreeRTOS 文件的移植工作。但是既然添加了文件就势必要改动 Makefile 文件,把新添加的文件也能够编译到项目中。
Makefile 的修改
原本的 Makfile 是 CubeMX 自己生成的,咱们只要把自己添加的文件添加到 Makfile 文件中就可以了。
首先,先添加头文件也就 include 目录。
然后咱们把其他所有的 C 文件也添加进去。
wildcard 是使用通配符,意思就是找到这个文件夹下所有的同类型文件。
到这里我们可以在 main.c 中添加上 FreeRTOS 最常用的头文件,然后编译一下看看有没有错误。
#include <FreeRTOS.h>
#include <task.h>
下图已经看到编译出来了可执行文件,说明编译成功了。
但是到这里还有其他问题不能运行~ 大家能想到是什么吗? 没错, FreeRTOS 的核心就是通过 systick 中断来处理任务的。所以我们要确定这些中断都能被正确触发。
设置中断向量
在 CubeMX 生成的项目中也是有启动文件的,叫做 startup_stm32f103xe.s
。但是这个里面的中断向量的名称和 FreeRTOS 里面 Cortex-m3 的中断函数名称有点不匹配。因为这些中断函数全部定义在 port.c 中,所以在文件开始部分加入下面的定义来保证名称对应:
#define xPortSysTickHandler SysTick_Handler
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
然后不要忘记把项目本身中断的设置文件(stm32f1xx_it.c
)中的这三个函数注释掉。下图仅展示一个,要不图片太大了。
这个时候已经把 FreeRTOS 和咱们 CubeMX 生成的项目融合在一起了。别忘了再编译一下检查有没有错误。
继续解决其他遇到的问题
这个时候编译并烧录以下,发现没有任何反应。 调试一下,方法可以参考以下两篇之前的博客:
在Windows上交叉编译STM32(环境搭建)
使用 OpenOCD 来调试 STM32
使用 GDB where 命令确认当前代码位置, 使用 info stack 命令打印栈的调用顺序,确认哪里出了问题。
一看这咱们都没把 FreeRTOS 的代码添加进去,怎么就能进 Systick 中断呢? 估计是 CubeMX 生成的代码处理中断的时候会打开 Systick 中断。但是完全咱们不需要呀~ 而且在 FreeRTOS 中 Cortex-m3 的代码中也会设置和打开 Systick 中断。所以这里我们写寄存器把它直接关掉。
加入任务相关的代码
好了到这里让我们加入 FreeRTOS 相关的代码吧~ 我在这里是建立了两个任务,已不同的时间对小灯进行关闭和打开,用于观察代码在 FreeRTOS 下“并行”现象。
在 main 函数中把任务给调用起来~ 任务栈的深度设置的小一点。
编译,烧录~ 成功!
总结
通过之前的几篇博客,我们已经知道了 FreeRTOS 创建任务,任务切换的原理。这个时候再进行移植,就会变得非常的轻松。
接下我们就有设备可以用来探究第三个问题啦~ FreeRTOS 是如何保证实时性呢?以及切换任务的损耗。