FREERTOS简介、移植和系统配置(基于STM32F103)

本文基础内容参考的是正点原子的FREERTOS课程。

这是基于HAL库的

正点原子手把手教你学FreeRTOS实时系统

这是基于标准库的

正点原子FreeRTOS手把手教学-基于STM32

基础知识,直接参考正点原子《FreeRTOS开发指南V1.1》基于标准库的,此处不再赘述。

本文主要对不理解的地方进行查缺补漏,并且先用起来,涉及到原理的部分,可以观看上述教学视频或者开发指南。



裸机和RTOS对比

我们知道,裸机系统的实时性比较差,各任务是顺序执行的,如果有紧急任务,就需要放在中断里,可是中断里又不能进行耗时较长的操作,通常是在中断里面置标志位,然后由主循环判断标志位来执行相关动作,可是这样一来,任务的执行又变成了顺序执行,实时性还是比较差。比如:

上面示例中,肚子疼这一任务属于紧急任务,中断里置标志位,然后主循环里判断标志位从而去医院,但是如果此时打游戏时间很长,就一直无法判断标志位,从而没法及时去医院。

因此,裸机的缺陷是很明显的。

如果是RTOS呢,是怎么处理的呢?

RTOS会创建三个任务,其中可以设置去医院的优先级更高,当肚子疼时,就可以立马执行去医院的操作,实时性就比较好了。

有个有意思的地方,就是延时函数,在裸机中,如果有延时函数,那就是CPU死等,但是在RTOS中,如果一个任务中有延时,那么CPU会看有没有其他任务,从而转去执行其他任务,而不会阻塞在延时这里。比如上面的去医院的例子,去医院的路上就类似于一个延时,这期间,我们照样可以玩游戏和发信息,但是裸机没法做到,去医院路上就只能等着去医院,其他什么事情也干不了。

裸机的特点

RTOS的特点

注意:

这里的高中低优先级,都属于软件层面的优先级设定,中断是硬件层面的优先级,永远都是优先于软件优先级来执行的;

每个任务都有自己的栈空间,这就可以用来保存调度之前的数据信息,是实现返回原来任务时继续执行的关键所在;

如果高优先级任务一直运行,低优先级任务就得不到运行,除非高优先级任务进入阻塞态,这是合理的,因为优先级高,所以肯定很紧急,所以必定占用所有的时间片,比如去医院做手术,肯定是全心全意做手术,不可能做手术时还玩游戏回信息,这里的关键是,实际开发时需要合理安排各任务的优先级。

相对于操作系统来说,裸机主循环里的任务就相当于优先级都是一样的,而且,也没有时间片轮转执行,所以需要排队等候,实时性差。

任务的数值越高,优先级越高;中断的数值越高,优先级越低。要注意区分。

 ✔

了解下可重入的概念:可重入函数详解-CSDN博客

重入:同一个函数被不同执行流调用,当前一个执行流还没执行完,就被其他执行流再次进入,我们称之为重入,一个函数在重入的情况下,运行结果不会出现任何不同或任何问题,则该函数被称为可重入函数,否则为不可重入函数。

也就是,不同的线程进入,都不会被其他线程所影响,该函数仍然会执行其本身的流程,不会有任何变化。

多线程的情况下,几乎一定会出现这种情况,所以需要仔细考虑下可重入的问题。

FREERTOS简介

官网首页:

FreeRTOS - Market leading RTOS (Real Time Operating System) for embedded systems with Internet of Things extensions

2024 年官网现在已经更新中文了。

官网参考手册:

官网参考手册:FreeRTOS 内核快速入门指南

注意事项:

官网进入比较慢,API估计都进不去。

这时如果需要查看API,可以查看正点原子给的几个文档

在这几个文档里搜索一下应该就能找到,可以优先看中文文档,没有的话再看英文文档。

freertos特点

由此可见,freertos的代码量并不高,主要是实现的任务调度。

源码简介

首先获取源码,我们可以从官网下载最新版本的源码

我们也可以直接拿正点原子的源码来使用

实际中我们可以直接拿对应MCU移植例程中的源码来用。

下载完成,我们再来看看里面有哪些内容

我们重点关注这里面的FreeRTOS文件夹,也就是我们要移植的源码。如果想了解其他文件夹中的内容,可以直接参考正点的开发手册。

打开FreeRTOS内核文件夹

Demo 文件夹里面就是 FreeRTOS 的相关例程,打开以后如下图所示:

可以看出 FreeRTOS 针对不同的 MCU 提供了非常多的 Demo,其中就有 ST F1F4 和F7 的相关例程。

打开Source文件夹

这里面包含了内核的头文件、移植文件、核心文件,前面说的FreeRTOS的9000多行代码,重点集中在list.c、queue.c、tasks.c这三个文件中。

再来重点来看一下 portable 这个文件夹,我们知道 FreeRTOS 是个系统,归根结底就是个纯软件的东西,它是怎么和硬件 联系在一起的呢?软件到硬件中间必须有一个桥梁,portable 文件夹里面的东西就是 FreeRTOS 系统和具体的硬件之间的连接桥梁!不同的编译环境,不同的 MCU,其桥梁应该是不同的,打 开 portable 文件夹,如下图所示:

从上图中可以看出 FreeRTOS 针对不同的编译环境和 MCU 都有不同的“桥梁”,我们这里就以 MDK 编译环境下的 STM32F103 为例。

MemMang 这个文件夹是跟内存管理相关的, 我们移植的时候是必须的,具体内容我们后面会专门有一章来讲解。里面的内容如下:

这里面有FreeRTOS提供的几种内存管理算法,我们一般使用heap_4.c这个文件。

Keil 文件夹里面的东西肯定也是必须的,但是我们打开Keil文件夹以后里面只有一个文件:See-also-the-RVDS-directory.txt。 这个 txt 文件是什么鬼?别急嘛!看文件名字“See-also-the-RVDS-directory”,意思就是参考 RVDS 文件夹里面的东西!哎,好吧,再打开 RVDS 文件夹,如下图所示:

RVDS 文件夹针对不同的架构的 MCU 做了详细的分类,STM32F103 就参考 ARM_CM3, 打开 ARM_CM3 文件夹,如下图所示:

ARM_CM3 有两个文件,这两个文件就是我们移植的时候所需要的!

开始移植

可以参考正点的移植过程,开发手册第二章 FreeRTOS 移植

我们这里移植过程略有不同。

1

获取源码

我们直接拿正点的移植例程中的源码来使用。

2

向工程分组中添加文件

打开基础工程,新建分组 FreeRTOS_CORE FreeRTOS_PORTABLE,然后向这两个分组中添加文件,如下图所示:

我们把所有的c文件都加入进来,不用的就放着,没关系。

其中,port.c 是 RVDS 文件 夹下的 ARM_CM3 中的文件,因为 STM32F103 是 Cortex-M3 内核的,因此要选择 ARM_CM3 中的 port.c 文件。

heap_4.c 是 MemMang 文件夹中的,前面说了 MemMang 是跟内存管理相关 的,里面有 5 个 c 文件:heap_1.c、heap_2.c、heap_3.c、heap_4.c 和 heap_5.c。这 5 个 c 文件是 五种不同的内存管理方法,就像从北京到上海你可以坐火车、坐飞机,如果心情好的话也可以 走路,反正有很多种方法,只要能到上海就行。这里也一样的,这 5 个文件都可以用来作为 FreeRTOS 的内存管理文件,只是它们的实现原理不同,各有利弊。

这里我们选择 heap_4.c,至 于原因,后面会有一章节专门来讲解 FreeRTOS 的内存管理,到时候大家就知道原因了。这里 就先选择 heap_4.c。

3

添加相应的头文件路径

添加完 FreeRTOS 源码中的 C 文件以后还要添加 FreeRTOS 源码的头文件路径,头文件路 径如下图所示:

4

确认包含FreeRTOSConfig.h文件

我们这里使用的是正点例程中的源码,所以include目录里面已经包含了系统配置文件FreeRTOSConfig.h,此时编译后一般不会有缺文件的问题。

如果没有FreeRTOSConfig.h文件或者FreeRTOSConfig.h文件不适配MCU,我们需要自己去找。比如打开 FreeRTOS目录里的Demo文件夹里的CORTEX_STM32F103_Keil,打开以后如下图所示:

果然!官方的移植工程中有这个文件,我们可以使用这个文件,但是建议大家使用正点例程中的 FreeRTOSConf.h 文件,这个文件是 FreeRTOS 的系统配置文件,不同的平台其配置不同。

不过注意,正点的FreeRTOSConfig.h中,建议删除他们的头文件包含

看名字就知道,他是 FreeRTOS 的配置文件,一般的操作系统都有裁剪、配置功能,而这些裁剪及配置都是通过一个文件来完成的,基本都是通过宏定义来完成对系统的配置和裁剪的,关于FreeRTOS的配置文件FreeRTOSConfig.h后面也会有一 章节来详细讲解。

5

引入FreeRTOSConfig.h配置文件后,编译,会提示以下内容:

这是因为freertos中也定义了SVC 中断服务函数SVC_Handler()和 PendSV 中断服务函数PendSV_Handler(),而我们STM32的中断文件stm32f10x_it.c中也定义了。

这里freertos重新定义了,所以,需要将STM32定义的这两个空中断处理函数给屏蔽掉。

之后再编译就没有错误了。

5。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

(可略去,因为一开始以为需要手动配置,但是后来实践才发现不用我们自行配置。

配置systick时钟

操作系统一般会使用systick时钟来作为时基,也就是freertos的工作心跳,所以需要配置。

这里说明下,正点原子教程里说要改system文件夹里的内容,他们是想让systick既能作为延时,又能用于操作系统时基,但是本来很简单的东西,这样一来就会让操作变得很复杂,完全没必要,如果使用操作系统,我们可以不用systick来延时,而是配置额外的定时器。让systick专心为rtos服务即可。

所以我们根据实际情况,配置systick为1ms的定时中断,此配置可参考这篇文章:

STM32实现延时(Systick+Tim)-CSDN博客

然后在stm32f10x_it.c文件里的systick中断服务函数里调用操作系统的时基心跳。

extern void xPortSysTickHandler(void);

//systick 中断服务函数,使用 OS 时用到
void SysTick_Handler(void)
{ 
    if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
    {
        xPortSysTickHandler();
    }
}

FreeRTOS 的心跳就是由滴答定时器产生的,根据 FreeRTOS 的系统时钟节拍设置好滴答定 时器的周期,这样就会周期触发滴答定时器中断了。在滴答定时器中断服务函数中调用 FreeRTOS 的 API 函数 xPortSysTickHandler()

注意:

这里调用的函数需要导入的头文件如下:

#include "FreeRTOS.h"

#include "task.h"

而且注意,不能只包含#include "task.h",必须先包含#include "FreeRTOS.h"

在task.h中有如下提示:

包含之后,编译不报错,但是keil里有错误提示

提示没有声明,跳转后,发现该函数的声明和定义都在port.c文件里。

没有头文件,咋办?

这种情况下,需要extern,如下:

extern void xPortSysTickHandler(void);

这一步移植好之后,先确认systick能正常运行中断,并且是1ms,可以在systick中断里计数,然后计数10秒看计数值是不是到了10000左右。可以用串口打印,不过串口的波特率要大,推荐460800,要不然太慢打不出来。

或者将初始化时除以1000去掉,改成1秒中断来看看。不过,貌似systick最大计数值只能到2^24次方,没法定时1秒,那就定时100ms试一下吧。

系统自动获取的,一般都不会有问题。

至此,移植完成,我们可以编写任务来验证是否移植成功。

6

验证

//主函数
int main(void)
{
    CustomSysInit();
    
    CreateStartTask();
}

//任务定义
///开始任务
#define START_STK_SIZE 		        128         //任务堆栈大小
#define START_TASK_PRIO		        1           //任务优先级
TaskHandle_t StartTask_Handler;                 //任务句柄
static void StartTask(void *pvParameters);      //任务函数声明

///PrintTask1任务
#define PrintTask1_STK_SIZE 		128         //任务堆栈大小
#define PrintTask1_TASK_PRIO		2           //任务优先级	
TaskHandle_t PrintTask1_Handler;                //任务句柄
static void PrintTask1(void *pvParameters);     //任务函数声明

///PrintTask2任务
#define PrintTask2_STK_SIZE 		128         //任务堆栈大小	
#define PrintTask2_TASK_PRIO		2           //任务优先级
TaskHandle_t PrintTask2_Handler;                //任务句柄
static void PrintTask2(void *pvParameters);     //任务函数声明

//用户系统初始化
void CustomSysInit(void)
{
    SystickInit();
    FourGInit();
}

//创建开始任务
void CreateStartTask(void)
{
    xTaskCreate((TaskFunction_t )StartTask,             //任务函数
                (const char*    )"StartTask",           //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

static void StartTask(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
    //创建PrintTask1任务
    xTaskCreate((TaskFunction_t )PrintTask1,      
                (const char*    )"PrintTask1",      
                (uint16_t       )PrintTask1_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )PrintTask1_TASK_PRIO,     
                (TaskHandle_t*  )&PrintTask1_Handler);
    //创建PrintTask2任务
    xTaskCreate((TaskFunction_t )PrintTask2,
                (const char*    )"PrintTask2",
                (uint16_t       )PrintTask2_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )PrintTask2_TASK_PRIO,
                (TaskHandle_t*  )&PrintTask2_Handler);
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}

//打印任务1
static void PrintTask1(void *pvParameters)
{
    while(1)
    {
        printf("I am PrintTask1\r\n");
    }
}

//打印任务2
static void PrintTask2(void *pvParameters)
{
    while(1)
    {
        printf("I am PrintTask2\r\n");
    }
}

几个疑惑如下:

任务是否可以加static来限制在文件内?

可以

上面给的例子就是这么做的,经验证没什么问题。

任务的参数问题

我看正点的程序中,每个任务都传了个没用到的参数

我们暂且跟着写吧。

FreeRTOS申请的堆栈和STM32启动文件里的堆栈有关系吗?

参考:

STM32内存结构介绍和FreeRTOS内存分配技巧

给出结论:

FreeRTOS中的堆与STM32内存结构中的堆并不占用相同的空间,两个堆同时存在。以下出现的堆(heap)表示FreeRTOS堆,另外在STM32启动文件中定义大小的堆称为系统堆。

FreeRTOS内核主要使用的内存管理函数为:

void *pvPortMalloc( size_t xSize ); //申请内存

void vPortFree( void *pv );//释放内存

以上函数控制的是FreeRTOS堆;系统堆则应使用malloc()和free()来分配和释放。

FreeRTOS有5种heap的实现方式,在STM32中一般使用heap_4.c。这种方式可以满足大部分使用需求,暂时不用关注其实现细节。

因此,移植FreeRTOS时,不必关注STM32的系统堆栈,因为FreeRTOS针对不同的单片机有自己的一套内存管理方法。这一点和LVGL是略有不同的。

FreeRTOS堆和任务栈在运行中具有很强的动态性,其大小很难估计。

我们在实际使用中,可以先把空间调整得大一些。程序正常运行后,再通过一些API查看堆栈剩余的空间大小,估算程序运行中需求内存空间的最大值。最后将这个最大值乘一个安全系数,得到最终应该分配的空间大小。安全系数推荐1.3到1.5。

为什么要创建一个开始任务,然后由开始任务来创建其他任务,而不是直接创建各个任务。

参考:

FreeRTOS使用 — 合理使用内存 “ 任务中创建任务 ”_freertos 任务可以再创建任务吗-CSDN博客

使用FreeRTOS时,好像不用进行freertos本身的初始化。 

进入临界区就会关中断,关了中断就会关闭任务调度器,因为任务调度是通过PendSV中断来实现的。 

临界区管理

同优先级的情况下,一个任务还没执行完,就换到另一个任务去执行了。这种情况下,如果打印信息,就会出现错乱,比如:

也就是说,此时,我是不希望一个任务被打断的。

这时候,就需要将这部分不希望被打断的代码放到临界区中。

结果如下:

具体如何管理临界区呢?

参考:

freeRtos学习笔记(3)临界区管理_freertos 临界区-CSDN博客

总结下来其实就几个主要步骤:

1

从正点原子获取现成的源码,然后导入工程,并添加对应的头文件;

2

注销STM32的SVC 中断服务函数SVC_Handler()和 PendSV 中断服务函数PendSV_Handler();

3(略)

配置systick并调用RTOS系统时基;

4

确认FreeRTOSConfig.h配置没什么问题;

5

创建任务验证。

补充一个:

将中断分组设置为NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

优化框架结构

正点的任务框架有点不好的地方,那就是把所有任务信息的都写一起,包括任务的声明也写一起,但实际中各任务不太可能都在同一个文件中,而是分布在各个业务文件中。正点的这种方式适合学习,但可能不太适合实际开发。另外,开始任务也没啥必要,可能是为了让我们学些临界区的知识以及删除任务的知识吧,实际中可以去掉。

实际中可以使用这种框架来进行开发。

Main函数中初始化完成后,直接创建各任务,创建完成之后,再开启任务调度,不需要再绕一步搞个开始任务了。

Main里面直接创建任务即可。

然后创建各任务

//创建所有任务
void CreateAllTask(void)
{
    xTaskCreate(PrintTask1, "PrintTask1",  64, NULL, 1,&PrintTask1_Handler);//打印任务1 优先级1
    xTaskCreate(PrintTask2,  "PrintTask2",  64, NULL, 1,NULL);//打印任务2 优先级1 
    xTaskCreate(McuLedFlashTask,  "McuLedFlashTask",  64, NULL, 1,NULL);//mcu led闪烁任务 优先级1      
    vTaskStartScheduler();//开启任务调度
}

这里就比正点的简洁多了,直接在任务里填写堆栈大小、优先级,不需要的写NULL。

注意,任务的句柄并不是必须的,只有需要操作某任务时,才需要,如果不需要对任务进行操作,就不用提供任务句柄。这样一来,任务就可以分散到各业务文件中了。

系统配置文件

FreeRTOSConf.h文件是 FreeRTOS 的系统配置文件,不同的平台其配置不同。看名字就知道,它是 FreeRTOS 的配置文件,一般的操作系统都有裁剪、配置功能,而这些裁剪及配置都是通过一个文件来完成的,基本都是通过宏定义来完成对系统的配置和裁剪的,这个文件可以在FreeRTOS目录里的Demo文件夹里的CORTEX_STM32F103_Keil路径下找到

我们将该文件拿来放到我们的移植目录中。

本文,重点介绍该文件中有哪些内容。

参考正点原子《FreeRTOS开发指南V1.1》“第三章 FreeRTOS 系统配置

正点原子例程中将该文件放在了User目录下

把文件拿过来看看

/***************************************************************************************************************/
/*                                        FreeRTOS基础配置配置选项                                              */
/***************************************************************************************************************/
//1使用抢占式内核,0使用协程
#define configUSE_PREEMPTION					1   

//1使能时间片调度(默认式使能的)                    
#define configUSE_TIME_SLICING					1

//1启用特殊方法来选择下一个要运行的任务
//一般是硬件计算前导零指令,如果所使用的
//MCU没有这些硬件指令的话此宏应该设置为0!						
#define configUSE_PORT_OPTIMISED_TASK_SELECTION	1

//1启用低功耗tickless模式                       
#define configUSE_TICKLESS_IDLE					0 

//为1时启用队列                      
#define configUSE_QUEUE_SETS					1 

//CPU频率                      
#define configCPU_CLOCK_HZ						(SystemCoreClock)

//时钟节拍频率,这里设置为1000,周期就是1ms       
#define configTICK_RATE_HZ						(1000)

//可使用的最大优先级                  
#define configMAX_PRIORITIES					(32)

//空闲任务使用的堆栈大小                    
#define configMINIMAL_STACK_SIZE				((unsigned short)130)

//任务名字字符串长度   
#define configMAX_TASK_NAME_LEN					(16)                    

//系统节拍计数器变量数据类型,
//1表示为16位无符号整形,0表示为32位无符号整形
#define configUSE_16_BIT_TICKS					0                       

//为1时空闲任务放弃CPU使用权给其他同优先级的用户任务                                                                        
#define configIDLE_SHOULD_YIELD					1

//为1时开启任务通知功能,默认开启                       
#define configUSE_TASK_NOTIFICATIONS            1

//为1时使用互斥信号量                       
#define configUSE_MUTEXES						1

//不为0时表示启用队列记录,具体的值是可以
//记录的队列和信号量最大数目。                       
#define configQUEUE_REGISTRY_SIZE				8

//大于0时启用堆栈溢出检测功能,如果使用此功能
//用户必须提供一个栈溢出钩子函数,如果使用的话
//此值可以为1或者2,因为有两种栈溢出检测方法。                       
#define configCHECK_FOR_STACK_OVERFLOW			0

//为1时使用递归互斥信号量                       
#define configUSE_RECURSIVE_MUTEXES				1

//1使用内存申请失败钩子函数                       
#define configUSE_MALLOC_FAILED_HOOK			0
                       
#define configUSE_APPLICATION_TASK_TAG			0

//为1时使用计数信号量                       
#define configUSE_COUNTING_SEMAPHORES			1                       

/***************************************************************************************************************/
/*                                FreeRTOS与内存申请有关配置选项                                                */
/***************************************************************************************************************/
//支持动态内存申请
#define configSUPPORT_DYNAMIC_ALLOCATION        1

//系统所有总的堆大小                       
#define configTOTAL_HEAP_SIZE					((size_t)(20*1024))     

/***************************************************************************************************************/
/*                                FreeRTOS与钩子函数有关的配置选项                                              */
/***************************************************************************************************************/
//1,使用空闲钩子;0,不使用
#define configUSE_IDLE_HOOK						0

//1,使用时间片钩子;0,不使用                       
#define configUSE_TICK_HOOK						0                       

/***************************************************************************************************************/
/*                                FreeRTOS与运行时间和任务状态收集有关的配置选项                                 */
/***************************************************************************************************************/
//为1时启用运行时间统计功能
#define configGENERATE_RUN_TIME_STATS	        0

//为1启用可视化跟踪调试                       
#define configUSE_TRACE_FACILITY				1

//与宏configUSE_TRACE_FACILITY同时为1时会编译下面3个函数                       
#define configUSE_STATS_FORMATTING_FUNCTIONS	1                       

//prvWriteNameToBuffer(),vTaskList(),
//vTaskGetRunTimeStats()
                                                                        
/***************************************************************************************************************/
/*                                FreeRTOS与协程有关的配置选项                                                  */
/***************************************************************************************************************/
//为1时启用协程,启用协程以后必须添加文件croutine.c
#define configUSE_CO_ROUTINES 			        0

//协程的有效优先级数目                       
#define configMAX_CO_ROUTINE_PRIORITIES         ( 2 )                   

/***************************************************************************************************************/
/*                                FreeRTOS与软件定时器有关的配置选项                                            */
/***************************************************************************************************************/
//为1时启用软件定时器
#define configUSE_TIMERS				        1

//软件定时器优先级                               
#define configTIMER_TASK_PRIORITY		        (configMAX_PRIORITIES-1) 

//软件定时器队列长度       
#define configTIMER_QUEUE_LENGTH		        5

//软件定时器任务堆栈大小                               
#define configTIMER_TASK_STACK_DEPTH	        (configMINIMAL_STACK_SIZE*2)    

/***************************************************************************************************************/
/*                                FreeRTOS可选函数配置选项                                                      */
/***************************************************************************************************************/
#define INCLUDE_xTaskGetSchedulerState          1                       
#define INCLUDE_vTaskPrioritySet		        1
#define INCLUDE_uxTaskPriorityGet		        1
#define INCLUDE_vTaskDelete				        1
#define INCLUDE_vTaskCleanUpResources	        1
#define INCLUDE_vTaskSuspend			        1
#define INCLUDE_vTaskDelayUntil			        1
#define INCLUDE_vTaskDelay				        1
#define INCLUDE_eTaskGetState			        1
#define INCLUDE_xTimerPendFunctionCall	        1

/***************************************************************************************************************/
/*                                FreeRTOS与中断有关的配置选项                                                  */
/***************************************************************************************************************/
#ifdef __NVIC_PRIO_BITS
	#define configPRIO_BITS       		__NVIC_PRIO_BITS
#else
	#define configPRIO_BITS       		4                  
#endif

//中断最低优先级
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			15

//系统可管理的最高中断优先级                      
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY	5                       
#define configKERNEL_INTERRUPT_PRIORITY 		( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

/***************************************************************************************************************/
/*                                FreeRTOS与中断服务函数有关的配置选项                                          */
/***************************************************************************************************************/
#define xPortPendSVHandler 	PendSV_Handler
#define vPortSVCHandler 	SVC_Handler

/***************************************************************************************************************/
/*                                FreeRTOS断言等有关的配置选项                                          */
/***************************************************************************************************************/
/* 断言 */
#define vAssertCalled(char, int) printf("Error: %s, %d\r\n", char, int)
#define configASSERT( x ) if( ( x ) == 0 ) vAssertCalled( __FILE__, __LINE__ )

/* FreeRTOS MPU 特殊定义 */
//#define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0
//#define configTOTAL_MPU_REGIONS                                8
//#define configTEX_S_C_B_FLASH                                  0x07UL
//#define configTEX_S_C_B_SRAM                                   0x07UL
//#define configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY            1
//#define configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS             1

/* ARMv8-M 安全侧端口相关定义。 */
//#define secureconfigMAX_SECURE_CONTEXTS         5

#endif /* FREERTOS_CONFIG_H */

大部分配置都有注释,可以先看看。

官方的在线文档中也有详细的说明:

FreeRTOS - The Free RTOS configuration constants and configuration options - FREE Open Source RTOS for small real time embedded systems

FreeRTOS 的配置基本是通过在 FreeRTOSConfig.h 中使用“#define”这样的语句来定义宏定义实现的。

“config”开始的宏是用来完成 FreeRTOS 的配置和裁剪的。

具体参考正点原子的开发手册。

使用“INCLUDE_”开头的宏用来表示使能或除能 FreeRTOS 中相应的 API 函数,作用就是用来配置 FreeRTOS 中的可选 API 函数的。比如当宏 INCLUDE_vTaskPrioritySet 设置为 0 的
时候表示不能使用函数 vTaskPrioritySet() , 当设置为1的时候就表示可 以使用函数vTaskPrioritySet()。这个功能其实就是条件编译,在文件 tasks.c 中有如下图所示的代码。

从图可以看出当满足条 件 : NCLUDE_vTaskPrioritySet == 1 的 时 候 , 函 数vTaskPrioritySet()才会被编译,注意,这里为了缩小篇幅将函数 vTaskPrioritySet()的内容进行了折叠。FreeRTOS 中的裁剪和配置就是这种用条件编译的方法来实现的,不止FreeRTOS这么干,很多的协议栈、RTOS 系统和 GUI 库等都是使用条件编译的方法来完成配置和裁剪的。条件编译的好处就是节省空间,不需要的功能就不用编译,这样就可以根据实际需求来减少系统占用的 ROM 和 RAM 大小,根据自己所使用的MCU来调整系统消耗。

……

更多补充说明

每个任务都有自己的堆栈。设置堆栈大小时,单位是字,也就是四个字节。

任务优先级理论上是无限的,但因为和configUSE_PORT_OPTIMISED_TASK_SELECTION这个宏的功能相关,也就是和硬件相关,所以最大只能设置到32。不过一般也不会用到32个优先级。

任务优先级数字越低表示任务的优先级越低,0 的优先级最低,configMAX_PRIORITIES-1 的优先级最高。

空闲任务的优先级最低,为 0。

关于宏configUSE_16_BIT_TICKS

宏configCHECK_FOR_STACK_OVERFLOW一般只在调试阶段打开。

暂且认为钩子函数就是回调函数吧。

heap4.c中实现了动态内存管理函数。

堆的内存池大小也在heap4.c中有使用。

这里总堆大小的单位是字节,定义的是20K,具体情况具体设置。

这些都是跟调试相关的

configUSE_16_BIT_TICKS这个宏定义影响最大阻塞的时间

补充:三个重要中断

在 FreeRTOS 的移植过程中会这遇到三个重要的中断,分别是 FreeRTOS 系统时基定时器的中断(SysTick 中断)、SVC 中断、PendSV 中断。

SysTick 中断是用来提供系统时基的,很好理解。

那么SVC和PendSV是用来干嘛的呢?

参考:怎样去理解异常SVC和PendSV_pendsv svc-CSDN博客

SVC(系统服务调用)和 PendSV( 可悬挂系统调用 )。
它们多用于在操作系统之上的软件开发中。
SVC用于产生系统函数的调用请求。
例如,操作系统不让用户程序直接访问硬件,而是通过提供一些系统服务函数,用户程序使用 SVC 发出对系统服务函数的呼叫请求,以这种方法调用它们来间接访问硬件。因此,当用户程序想要控制特定的硬件时,它就会产生一个 SVC 异常,然后操作系统提供的 SVC 异常服务例程得到执行,它再调用相关的操作系统函数,后者完成用户程序请求的服务。

获取数据有两条路线:

通过SVC。

绕过SVC(但是不允许)。

由此实现软件硬件分离。 

另一个相关的异常是 PendSV(可悬挂的系统调用),它和 SVC 协同使用。SVC异常是必须立即得到响应的(若因优先级不比当前正处理的高, 或是其它原因使之无法立即响应, 将上访成硬 fault),应用程序执行 SVC 时都是希望所需的请求立即得到响应。PendSV 则不同,它是可以像普通的中断一样被悬起的(不像 SVC 那样会上访)。 OS 可以利用它“缓期执行” 一个异常——直到其它重要的任务完成后才执行动作。

悬起 PendSV 的方法是:手工往 NVIC 的 PendSV 悬起寄存器中写 1。 悬起后, 如果优先级不够高,则将缓期等待执行。PendSV是可悬起异常,如果我们把它配置最低优先级,那么如果同时有多个异常被触发,它会在其他异常执行完毕后再执行,而且任何异常都可以中断它。

假设任务A、任务B。

任务A,2ms执行结束后延迟500ms再次执行,那么OS在这段500ms内寻找下一个要执行的任务B,任务B,2ms执行结束延迟600ms再次执行(如果还有任务C、E等依次类推)。但是现在任务A并没有延迟到500ms所以此时OS执行空闲任务,到达500ms后才会执行任务A。

当空闲任务切换到任务A或者任务B时,此时突然中断产生,PendSV执行。

一时半会儿还不太理解,后续可能需要看这本书:

《ARM Cortex-M3 与 Cortex-M4 权威指南(第三版)》

以后再补充。

  • 28
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值