学习FreeRTOS移植对于一个新手来说还是非常有必要的,关于FreeRTOS的基本知识我这里就不再多做阐述了,网上一搜一大堆,这里我给大家推荐一个大神的专题文章:1.《FreeRTOS基础篇》2. 《FreeRTOS高级篇》。接下来就开始移植它吧。
我这里是用的KEIL,毕竟学生时代接触到的神,虽然它不是非常完美,但是对它还是情有独钟。我移植的FreeRTOS版本是V10.4.0,其他版本可能适不适用,我也不是很清楚。至于MCU平台则选择我最近学习的RT1050。
下载地址:https://www.freertos.org/a00104.html
解压打开如下:
对我这个穷人来说要用的就是FreeRTOS这个文件夹,懂得人自然懂,留着备用。打开文件夹FreeRTOS如下:
可以看到里面就几个文件夹,每一个文件夹的作用可以参考里面的README.md,里面对每个文件夹描述的很清晰。
接下来打开Source文件夹,我们需要的源代码就在里面了:
FreeRTOS实时操作系统内核仅包含三个必要文件,此外还有三个可选文件。必要文件分别是tasks.c、queue.c和list.c,是整个RTOS的主体。可选文件分别是timers.c、event_groups.c和croutine.c,负责用于软件定时器、事件组和协程。小孩子才做选择,我全都要。
细心的小伙伴可能发现了,漏掉了一个stream_buffer.c,这个文件其实是后来FreeRTOS v10版本升级的功能,用来做任务间的数据传输用的,在V10版本以前则是没有这个文件,这些都不重要,来者不拒。
实不相瞒,我已经偷偷准备好了。接下来就要开始干活了。
(1)复制整个Source文件夹到RT1050工程目录下,并改名FreeRTOS如下,然后打开工程,在KEIL中新建分组FreeRTOS,然后再把上文提到的FreeRTOS的三个核心文件以及可选文件加入该分组,一顿操作猛如虎,一看战绩0杠5,如下:
(2)将FreeRTOS的头文件路径包含进工程,如下:
(3)这一步就是所以代码移植的重点了,接口移植,也是所有协议代码移植的重点所在,是程序与硬件的连接方式,只有完成接口的正确配置,FreeRTOS才能在MCU上跑起来。那么对于FreeRTOS的接口在哪里呢,你猜的没错,就在Source文件夹剩下的portable的文件夹中,打开一看:
我反正一眼就看到KEIL的文件夹,很开心,结果打开了一个寂寞:
但是这个文件标题给了一个很重要的信息,see also RVDS directory,让我们看看RVDS目录,于是我们在找RVDS文件夹,结果发现果然有一个RVDS,打开:
打开我需要的M7核的文件夹:
要养成看到readMe就打开的良好习惯,看完之后直呼内行,总结一下:
M7的r0p1版本有bug,所以你有两个选择,如果MCU的M7内核版本不是r0p1,你就可以选择使用ARM_CM7,或者最好选择使用ARM_CM4F,但是如果是r0p1,只能选择这个。
那该怎么选择呢,所以我又去数据手册搜索了一下r0p1,结果确实搜到了:
但是只有ETM(ETM是M7核里的一个一般用于trace的组件,这里就不多做介绍了),所以我们果断选择ARM_CM4F,找到ARM_CM4F,果断将其中的port.c也加入到工程分组中,看到portmacro.h, 就果断把该路径加入到工程。一切都这么流畅丝滑。
完成这些还差最后一个部分,那就是FreeRTOS的内存管理部件,这个部件在Source\portable\MemMang中:
看到ReadMe,果断点开,结果WTF,English filled my eyes,但是,傻子才全看,夸张的我一下子就看到了重点:
看完,果断将heap_4.c放入了工程。OK,最重要的接口部分已经被我们搞定了,不过还没有完,还差最后一步。
(4)加入FreeRTOS配置头文件,这个文件就相当于FreeRTOS的功能设置,可以通过这个文件对FreeRTOS的一些功能进行裁剪和增加。
那这个文件在哪里能找到呢,好像Source里面的文件夹都被霍霍完了,心机之蛙一直摸你肚子,在demo文件夹里找一个例子复制过来呗,所以我们在demo里找到同为M7核的兄弟CORTEX_M7_STM32F7_STM32756G-EVAL_IAR_Keil, 复制FreeRTOSConfig.h到工程文件夹中,并将其文件夹路径加入到工程路径,因为这个文件经常被修改,所以最好直接将其加入到分组。至此,所有文件添加完毕。
既然所有文件都加入好了,咱们屁话不多说,直接compile。
屁话不多说,再次compile。
屁话不多说,再次compile。
屁话不多说,再次compile。
屁话不多说,再次compile。
终于,它没有任何错误了。
上面我们只是实现了编译通过,接下来则是需要完善FreeRTOS与MCU之间的接口。
(1)有些移植教程可能会替换start.s文件下的PendSV_Handler,SVC_Handler,SysTick_Handler三个中断号的名字,这些可以直接在FreeRTOSConfig.h中加入以下语句实现:
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
#define xPortSysTickHandler SysTick_Handler
可以看到,我偷过来的h文件已经有了。
(2)实现滴答定时器的初始化。
FreeRTOS需要一个定时器为其提供时钟计数功能,每个cortex内核的MCU都有滴答定时器,它是属于内核的一部分,是cortex内核专门为操作系统内核提供专用,所以这个滴答定时器就不用不白用了。滴答定时器可以产生异常,异常说白了就是比中断优先级高的特殊中断。
一般芯片的库文件都会提供滴答定时器的初始化程序,于是我就是给它拿过来了。如下:
/* Set systick reload value to generate 1ms interrupt */
if (SysTick_Config(SystemCoreClock / 1000U))
{
while (1)
{
}
}
重点在于配置滴答定时器的中断时间和使能中断,中断时间也就是FreeRTOS的最小时间片的大小(一般在1ms~100ms之间,我习惯1ms),中断时间一般根据MCU性能和系统需求来定,大家自己斟酌。
(3)接下来就是根据自己的芯片特点修改一下FreeRTOSConfig.h,因为我这个FreeRTOSConfig.h是从它的兄弟偷来的,所以只要修改一下configCPU_CLOCK_HZ和configTICK_RATE_HZ两个参数就好了。
configCPU_CLOCK_HZ是MCU的内核频率,configTICK_RATE_HZ则是滴答定时器的频率
我的设置如下:
#define configCPU_CLOCK_HZ ( 528000000)
#define configTICK_RATE_HZ ( 1000 )
(特别的:对于有些MCU的RAM大小有限,可能需要修改一下FreeRTOS堆大小configTOTAL_HEAP_SIZE参数,不然会造成内存溢出发生错误。)
到这里就已经完成了FreeRTOS的移植了。是骡子是拉出来溜溜。
在main中插入一小段程序,我的main是这样的,伙伴们灵活模仿,请勿抄袭(其实我也是抄袭来的)。
/*
* Copyright 2019 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "stdint.h"
#include "board.h"
#include "pin_mux.h"
#include "system_MIMXRT1062.h"
#include "clock_config.h"
/* FreeRTOS kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "timers.h"
#include "event_groups.h"
static void LED_Blinky_Task(void *pvParameters);
/*!
* @brief Main function
*/
int main(void)
{
/* Board pin init */
BOARD_InitPins();
BOARD_InitBootClocks();
/* Update the core clock */
SystemCoreClockUpdate();
/* Set systick reload value to generate 1ms interrupt */
if (SysTick_Config(SystemCoreClock / 1000U))
{
while (1);
}
if (xTaskCreate(LED_Blinky_Task, "LED_Blinky_Task", 256, NULL, 12, NULL) != pdPASS)
{
while (1);
}
vTaskStartScheduler();
while (1);
}
static void LED_Blinky_Task(void *pvParameters)
{
while(1)
{
GPIO_PinWrite(GPIO1, 9U, 0);
vTaskDelay(1000);
GPIO_PinWrite(GPIO1, 9U, 1);
vTaskDelay(1000);
}
}
至此,大功告成。。。。。。。
从上面的移植过程其实可以看出来移植非常简单,FreeRTOS的接口说白了就是M7核的三个异常,分别是PendSV_Handler,SVC_Handler,SysTick_Handler,在这里不妨说一下这三个函数对于FreeRTOS的意义。
它是SVC异常的服务函数,SVC名字叫做系统服务调用,这个东西其实说白了就是一个M核为用户提供的一个软件触发中断,而且优先级极高,需要内核瞬间去响应,可以打断其他除了reset、NMI、错误等异常外的任何或者中断。啥意思呢,就是说你软件执行一段激发SVC异常操作,这样就可以产生SVC异常,CPU就会抛弃其他操作立即响应该异常。
这个呢,叫做可悬起的系统服务调用,看名字就没有SVC厉害,跟SVC一样也不一样,一样就是它也是一个通过软件操作触发的异常,不一样的是它的优先级不仅没有SVC高,而且还可以设置它的优先级,而FreeRTOS任务之间的切换,就是所谓的任务调度器,则就是靠这个异常去实现的。
这个滴答定时器在前面已经提过了,它为FreeRTOS提供了一个计时器,定时器的中断间隔就是FreeRTOS的最小时间片,而且在滴答定时器中断里还干了一件事情就是触发PendSV异常来进行任务调度器的执行。
(想了解更多FreeRTOS的知识还是去多读读更加深入的帖子吧。。。。)