小猫爪:动手笔记01-FreeRTOS移植

小猫爪:动手笔记01-FreeRTOS移植

  • 1 前言
  • 2 准备
    • 2.1 下载FreeRTOS源代码
    • 2.2 准备一个RT1050的普通工程
  • 3 移植
    • 3.1 添加文件
    • 3.2 编译
    • 3.3 实现FreeRTOS接口
  • 4 运行
  • 5 总结
    • 5.1 SVC_Handler
    • 5.2 PendSV_Handler
    • 5.3 SysTick_Handler
  • END

1 前言

学习FreeRTOS移植对于一个新手来说还是非常有必要的,关于FreeRTOS的基本知识我这里就不再多做阐述了,网上一搜一大堆,这里我给大家推荐一个大神的专题文章:1.《FreeRTOS基础篇》2. 《FreeRTOS高级篇》。接下来就开始移植它吧。

2 准备

我这里是用的KEIL,毕竟学生时代接触到的神,虽然它不是非常完美,但是对它还是情有独钟。我移植的FreeRTOS版本是V10.4.0,其他版本可能适不适用,我也不是很清楚。至于MCU平台则选择我最近学习的RT1050。

2.1 下载FreeRTOS源代码

下载地址:https://www.freertos.org/a00104.html
解压打开如下:
小猫爪:动手笔记01-FreeRTOS移植_第1张图片对我这个穷人来说要用的就是FreeRTOS这个文件夹,懂得人自然懂,留着备用。打开文件夹FreeRTOS如下:
小猫爪:动手笔记01-FreeRTOS移植_第2张图片
可以看到里面就几个文件夹,每一个文件夹的作用可以参考里面的README.md,里面对每个文件夹描述的很清晰。

接下来打开Source文件夹,我们需要的源代码就在里面了:
小猫爪:动手笔记01-FreeRTOS移植_第3张图片FreeRTOS实时操作系统内核仅包含三个必要文件,此外还有三个可选文件。必要文件分别是tasks.c、queue.c和list.c,是整个RTOS的主体。可选文件分别是timers.c、event_groups.c和croutine.c,负责用于软件定时器、事件组和协程。小孩子才做选择,我全都要。

细心的小伙伴可能发现了,漏掉了一个stream_buffer.c,这个文件其实是后来FreeRTOS v10版本升级的功能,用来做任务间的数据传输用的,在V10版本以前则是没有这个文件,这些都不重要,来者不拒。

2.2 准备一个RT1050的普通工程

实不相瞒,我已经偷偷准备好了。接下来就要开始干活了。

3 移植

3.1 添加文件

(1)复制整个Source文件夹到RT1050工程目录下,并改名FreeRTOS如下,然后打开工程,在KEIL中新建分组FreeRTOS,然后再把上文提到的FreeRTOS的三个核心文件以及可选文件加入该分组,一顿操作猛如虎,一看战绩0杠5,如下:
小猫爪:动手笔记01-FreeRTOS移植_第4张图片
(2)将FreeRTOS的头文件路径包含进工程,如下:
在这里插入图片描述
(3)这一步就是所以代码移植的重点了,接口移植,也是所有协议代码移植的重点所在,是程序与硬件的连接方式,只有完成接口的正确配置,FreeRTOS才能在MCU上跑起来。那么对于FreeRTOS的接口在哪里呢,你猜的没错,就在Source文件夹剩下的portable的文件夹中,打开一看:
小猫爪:动手笔记01-FreeRTOS移植_第5张图片我反正一眼就看到KEIL的文件夹,很开心,结果打开了一个寂寞:
小猫爪:动手笔记01-FreeRTOS移植_第6张图片
但是这个文件标题给了一个很重要的信息,see also RVDS directory,让我们看看RVDS目录,于是我们在找RVDS文件夹,结果发现果然有一个RVDS,打开:
小猫爪:动手笔记01-FreeRTOS移植_第7张图片打开我需要的M7核的文件夹:
小猫爪:动手笔记01-FreeRTOS移植_第8张图片
要养成看到readMe就打开的良好习惯,看完之后直呼内行,总结一下:

M7的r0p1版本有bug,所以你有两个选择,如果MCU的M7内核版本不是r0p1,你就可以选择使用ARM_CM7,或者最好选择使用ARM_CM4F,但是如果是r0p1,只能选择这个。

那该怎么选择呢,所以我又去数据手册搜索了一下r0p1,结果确实搜到了:
小猫爪:动手笔记01-FreeRTOS移植_第9张图片但是只有ETM(ETM是M7核里的一个一般用于trace的组件,这里就不多做介绍了),所以我们果断选择ARM_CM4F,找到ARM_CM4F,果断将其中的port.c也加入到工程分组中,看到portmacro.h, 就果断把该路径加入到工程。一切都这么流畅丝滑。

在这里插入图片描述

完成这些还差最后一个部分,那就是FreeRTOS的内存管理部件,这个部件在Source\portable\MemMang中:
小猫爪:动手笔记01-FreeRTOS移植_第10张图片看到ReadMe,果断点开,结果WTF,English filled my eyes,但是,傻子才全看,夸张的我一下子就看到了重点:
小猫爪:动手笔记01-FreeRTOS移植_第11张图片看完,果断将heap_4.c放入了工程。OK,最重要的接口部分已经被我们搞定了,不过还没有完,还差最后一步。

(4)加入FreeRTOS配置头文件,这个文件就相当于FreeRTOS的功能设置,可以通过这个文件对FreeRTOS的一些功能进行裁剪和增加。

那这个文件在哪里能找到呢,好像Source里面的文件夹都被霍霍完了,心机之蛙一直摸你肚子,在demo文件夹里找一个例子复制过来呗,所以我们在demo里找到同为M7核的兄弟CORTEX_M7_STM32F7_STM32756G-EVAL_IAR_Keil, 复制FreeRTOSConfig.h到工程文件夹中,并将其文件夹路径加入到工程路径,因为这个文件经常被修改,所以最好直接将其加入到分组。至此,所有文件添加完毕。
小猫爪:动手笔记01-FreeRTOS移植_第12张图片

3.2 编译

既然所有文件都加入好了,咱们屁话不多说,直接compile。

  1. 出错:./FreeRTOS\FreeRTOSConfig.h(138): error: ‘stm32f7xx_hal.h’ file not found
    原因:由于FreeRTOSConfig.h是我们偷来的,所以要把其烙印给换成RT1050的烙印。
    修改:直接删除#include “stm32f7xx_hal.h”。

屁话不多说,再次compile。

  1. 出错:我草,直接爆炸。一大堆错误,不过错误都集中在port.c和portmacro.h中(估计小伙伴不会出现错误,这估计跟KEIL版本有关)。贴出一个错误:
    . …/FreeRTOS_Source/portable/GCC/ARM_CM4F\portmacro.h(169): error: unknown type name ‘__forceinline’
    原因:莫慌,小问题。经过大量的实验加查找资料,听说是KEIL编译器后来升级了,所以在上文中我选择的文件夹RVDS就不再适用KEIL了(鬼知道我试了多少次,卧槽!!!)。
    修改:将port.c和portmacro.h文件替换为GCC文件夹中的ARMM4F中的port.c和portmacro.h。

屁话不多说,再次compile。

  1. 出错:flexspi_nor_debug\iled_blinky.out: Error: L6200E: Symbol SysTick_Handler multiply defined (by port.o and led_blinky.o).
    原因:SysTick_Handler函数重新定义,这个函数相当于FreeRTOS的计时器
    修改:注释掉原有的 SysTick_Handler函数。

屁话不多说,再次compile。

  1. 出错:flexspi_nor_debug\iled_blinky.out: Error: L6218E: Undefined symbol vAssertCalled (referred from event_groups.o).
    在这里插入图片描述原因:因为有些回调函数还没有实现,所以我们需要找到这些函数,将其实现或者将其删除。
    ①vAssertCalled
    全局搜索,结果发现它在复制的FreeRTOSConfig.h中,删掉。②vApplicationStackOverflowHook
    全局搜索,结果发现它需要configCHECK_FOR_STACK_OVERFLOW这个宏大于0才能使能,找到这个宏,给它改成0。
    ③vApplicationTickHook
    全局搜索,结果发现它需要configUSE_TICK_HOOK这个宏大于0才能使能,找到这个宏,给它改成0。
    ④vApplicationMallocFailedHook
    全局搜索,结果发现它需要configUSE_MALLOC_FAILED_HOOK这个宏等于1才能使能,找到这个宏,给它改成0。

屁话不多说,再次compile。

终于,它没有任何错误了。

3.3 实现FreeRTOS接口

上面我们只是实现了编译通过,接下来则是需要完善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的移植了。是骡子是拉出来溜溜。

4 运行

在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);
	}	
}

至此,大功告成。。。。。。。

5 总结

从上面的移植过程其实可以看出来移植非常简单,FreeRTOS的接口说白了就是M7核的三个异常,分别是PendSV_Handler,SVC_Handler,SysTick_Handler,在这里不妨说一下这三个函数对于FreeRTOS的意义。

5.1 SVC_Handler

它是SVC异常的服务函数,SVC名字叫做系统服务调用,这个东西其实说白了就是一个M核为用户提供的一个软件触发中断,而且优先级极高,需要内核瞬间去响应,可以打断其他除了reset、NMI、错误等异常外的任何或者中断。啥意思呢,就是说你软件执行一段激发SVC异常操作,这样就可以产生SVC异常,CPU就会抛弃其他操作立即响应该异常。

5.2 PendSV_Handler

这个呢,叫做可悬起的系统服务调用,看名字就没有SVC厉害,跟SVC一样也不一样,一样就是它也是一个通过软件操作触发的异常,不一样的是它的优先级不仅没有SVC高,而且还可以设置它的优先级,而FreeRTOS任务之间的切换,就是所谓的任务调度器,则就是靠这个异常去实现的。

5.3 SysTick_Handler

这个滴答定时器在前面已经提过了,它为FreeRTOS提供了一个计时器,定时器的中断间隔就是FreeRTOS的最小时间片,而且在滴答定时器中断里还干了一件事情就是触发PendSV异常来进行任务调度器的执行。

(想了解更多FreeRTOS的知识还是去多读读更加深入的帖子吧。。。。)

END

你可能感兴趣的:(动手笔记,嵌入式,freertos)