打印
[应用相关]

FreeRTOS及其应用,万字长文,基础入门

[复制链接]
7228|83
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
嵌入式系统不只是ARM+Linux,不是只有安卓,凡是电子产品都可称为嵌入式系统。物联网行业的兴起,也提升了FreeRTOS市场占有率。本文就是介绍FreeRTOS基础及其应用,只是个人整理,可能存在问题,其目的只是简要介绍系统的基础,只能作为入门资料。
原文链接:https://mp.weixin.qq.com/s/MyPBlHXG0iawoUzdDFTBQw

使用特权

评论回复
沙发
我只会加减乘除|  楼主 | 2022-5-1 17:20 | 只看该作者

一、 为什么要学习 RTOS

进入嵌入式这个领域,入门首先接触的是单片机编程,尤其是C51 单片机来,基础的单片机编程通常都是指裸机编程,即不加入任何 RTOS(Real Time Operating System 实时操作系统)。常用的有国外的FreeRTOS、μC/OS、RTX 和国内的 RT-thread、Huawei LiteOS 和 AliOS-Things 等,其中开源且免费的 FreeRTOS 的市场占有率较高。


使用特权

评论回复
板凳
我只会加减乘除|  楼主 | 2022-5-1 17:20 | 只看该作者
1.1 前后台系统

在裸机系统中,所有的操作都是在一个无限的大循环里面实现,支持中断检测。外部中断紧急事件在中断里面标记或者响应,中断服务称为前台,main 函数里面的while(1)无限循环称为后台,按顺序处理业务功能,以及中断标记的可执行的事件。小型的电子产品用的都是裸机系统,而且也能够满足需求。


使用特权

评论回复
地板
我只会加减乘除|  楼主 | 2022-5-1 17:24 | 只看该作者
1.2 多任务系统

多任务系统的事件响应也是在中断中完成的,但是事件的处理是在任务中完成的。如果事件对应的任务的优先级足够高,中断对应的事件会立刻执行。相比前后台系统,多任务系统的实时性又被提高了。

在多任务系统中,根据程序的功能,把这个程序主体分割成一个个独立的,无限循环且不能返回的子程序,称之为任务。每个任务都是独立的,互不干扰的,且具备自身的优先级,它由操作系统调度管理。加入操作系统后,开发人员不需要关注每个功能模块之间的冲突,重心放在子程序的实现。缺点是整个系统随之带来的额外RAM开销,但对目前的单片机的来影响不大。


使用特权

评论回复
5
我只会加减乘除|  楼主 | 2022-5-1 17:25 | 只看该作者
1.3 学习RTOS的意义

学习 RTOS,一是项目需要,随着产品要实现的功能越来越多,单纯的裸机系统已经不能完美地解决问题,反而会使编程变得更加复杂,如果想降低编程的难度,就必须引入 RTOS实现多任务管理。二是技能需要,掌握操作系统,和基于RTOS的编程,实现更好的职业规划,对个人发展尤其是钱途是必不可少的。

以前一直觉得学操作系统就必须是linux,实际每个系统都有其应用场景,对于物联网行业,杀**焉用牛刀,小而美,且应用广泛的FreeRTOS 是首选。有一个操作系统的基础,即使后续基于其他系统开发软件,也可触类旁通,对新技术快速入门。目前接触的几款芯片都是基于FreeRTOS。

如何学习RTOS?最简单的就是在别人移植好的系统之上,看看 RTOS 里面的 API 使用说明,然后调用这些 API 实现自己想要的功能即可。完全不用关心底层的移植,这是最简单快速的入门方法。这种学习方式,如果是做产品,可以快速的实现功能,弊端是当程序出现问题的时候,如果对RTOS不够了解,会导致调试困难,无从下手。

各种RTOS内核实现方式都差不多,我们只需要深入学习其中一款就行。万变不离其宗,正如掌握了C51基础,后续换其他型号或者更高级的ARM单片机,在原理和方法上,都是有借鉴意义,可以比较快的熟悉并掌握新单片机的使用。


使用特权

评论回复
6
我只会加减乘除|  楼主 | 2022-5-1 17:25 | 只看该作者
二、 操作系统基础2.1 链表

链表作为 C 语言中一种基础的数据结构,在平时写程序的时候用的并不多,但在操作系统里面使用的非常多。FreeRTOS 中存在着大量的基础数据结构链表和链表项的操作(list 和 list item)。FreeRTOS 中与链表相关的操作均在 list.h 和 list.c 这两个文件中实现。

链表比数组,最大优势是占用的内存空间可以随着需求扩大或缩小,动态调整。实际FreeRTOS中各种任务的记录都是依靠链表动态管理,具体的可以参考源码的任务控制块tskTCB。任务切换状态,就是将对应的链表进行操作,链表操作涉及创建和插入、删除和查找。


使用特权

评论回复
7
我只会加减乘除|  楼主 | 2022-5-1 17:26 | 只看该作者
2.2 队列

队列是一种只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。队尾放入数据,对头挤出。先进先出,称为FIFO


使用特权

评论回复
8
我只会加减乘除|  楼主 | 2022-5-1 17:26 | 只看该作者
2.3 任务

在裸机系统中,系统的主体就是 main 函数里面顺序执行的无限循环,这个无限循环里面 CPU 按照顺序完成各种事情。在多任务系统中,根据功能的不同,把整个系统分割成一个个独立的且无法返回的函数,这个函数我们称为任务。系统中的每一任务都有多种运行状态。系统初始化完成后,创建的任务就可以在系统中竞争一定的资源,由内核进行调度。

 就绪(Ready):该任务在就绪列表中,就绪的任务已经具备执行的能力,只等待调度器进行调度,新创建的任务会初始化为就绪态。

 运行(Running):该状态表明任务正在执行,此时它占用处理器,调度器选择运行的永远是处于最高优先级的就绪态任务。

 阻塞(Blocked):任务当前正在等待某个事件,比如信号量或外部中断。

 挂起态(Suspended):处于挂起态的任务对调度器而言是不可见的。

挂起态与阻塞态的区别,当任务有较长的时间不允许运行的时候,我们可以挂起任务,这样子调度器就不会管这个任务的任何信息,直到调用恢复任务的 接口;而任务处于阻塞态的时候,系统还需要判断阻塞态的任务是否超时,是否可以解除阻塞。

各任务运行时使用消息、信号量等方式进行通信,不能是全局变量。任务通常会运行在一个死循环中,不会退出,如果不再需要,可以调用删除任务。


使用特权

评论回复
9
我只会加减乘除|  楼主 | 2022-5-1 17:27 | 只看该作者
2.4 临界区

临界区就是一段在执行的时候不能被中断的代码段。在多任务操作系统里面,对全局变量的操作不能被打断,不能执行到一半就被其他任务再次操作。一般被打断,原因就是系统调度或外部中断。对临界区的保护控制,归根到底就是对系统中断的使能控制。在使用临界区时,关闭中断响应,对部分优先级的中断进行屏蔽,因此临界区不允许运行时间过长。为了对临界区进行控制,就需要使用信号量通信,实现同步或互斥操作。


使用特权

评论回复
10
我只会加减乘除|  楼主 | 2022-5-1 17:29 | 只看该作者
三、 初识 FreeRTOS3.1 FreeRTOS源码

FreeRTOS 由美国的 Richard Barry 于 2003 年发布, 2018 年被亚马逊收购,改名为 AWS FreeRTOS,版本号升级为 V10,支持MIT开源协议,亚马逊收购 FreeRTOS 也是为了进入物联网和人工智能,新版本增加了物联网行业的网络协议等功能。

FreeRTOS 是开源免费的,可从官网 www.freertos.org 下载源码和说明手册。例如展锐的UIS8910使用的是V10。以FreeRTOSv10.4.1为例,包含 Demo 例程,Source内核的源码,License许可文件。


使用特权

评论回复
11
我只会加减乘除|  楼主 | 2022-5-1 17:30 | 只看该作者
3.1.1 Source 文件夹

FreeRTOS/ Source 文件夹下的文件:

包括FreeRTOS 的通用的头文件include和 C 文件,包括任务、队列、定时器等,适用于各种编译器和处理器,是通用的。

需要特殊处理适配的在portblle文件夹,其下内容与编译器和处理器相关, FreeRTOS 要想运行在一个单片机上面,它们就必须关联在一起,通常由汇编和 C 联合编写。通常难度比较高,不过一般芯片原厂提供移植好的接口文件。这里不介绍移植的方法,因为自己也不明白。

Portblle/MemMang 文件夹下存放的是跟内存管理相关的,总共有五个 heap 文件,有5种内存动态分配方式,一般物联网产品选用 heap4.c 。


使用特权

评论回复
12
我只会加减乘除|  楼主 | 2022-5-1 17:31 | 只看该作者
3.1.2 Demo 文件夹

里面包含了 FreeRTOS 官方为各个单片机移植好的工程代码,FreeRTOS 为了推广自己,会给针对不同半导体厂商的评估板实现基础功能范例, Demo下就是参考范例。


使用特权

评论回复
13
我只会加减乘除|  楼主 | 2022-5-1 17:32 | 只看该作者
3.1.3 FreeRTOSConfig.h配置

FreeRTOSConfig.h头文件对FreeRTOS 所需的功能的宏均做了定义,需要根据应用情况配置合适的参数,其作用类似MTK功能机平台的主mak文件,部分定义如下:

1. #define configUSE_PREEMPTION            1  
2. #define configUSE_IDLE_HOOK             0  
3. #define configUSE_TICK_HOOK             0  
4. #define configCPU_CLOCK_HZ              ( SystemCoreClock )  
5. #define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )  

例如系统时钟tick等参数在就这个文件配置,具体作用可以看注释。一般情况下使用SDK不需要改动,特殊情况下咨询原厂再调整。


使用特权

评论回复
14
我只会加减乘除|  楼主 | 2022-5-1 17:33 | 只看该作者
3.2 FreeRTOS 编码规范

接触一个新平台或者SDK,明白它的编码规范,文件作用,可以提高源码阅读效率,快速熟悉其内部实现。



使用特权

评论回复
15
我只会加减乘除|  楼主 | 2022-5-1 17:34 | 只看该作者
3.2.1 数据类型

FreeRTOS针对不同的处理器,对标准C的数据类型进行了重定义。

1. #define portCHAR        char  
2. #define portFLOAT       float  
3. #define portDOUBLE      double  
4. #define portLONG        long  
5. #define portSHORT       short  
6. #define portSTACK_TYPE  uint32_t  
7. #define portBASE_TYPE   long  
应用编码中,推荐使用的是下面这种风格。
1. typedef int int32_t;  
2. typedef short int16_t;  
3. typedef char int8_t;  
4. typedef unsigned int uint32_t;  
5. typedef unsigned short uint16_t;  
6. typedef unsigned char uint8_t;



使用特权

评论回复
16
我只会加减乘除|  楼主 | 2022-5-1 17:35 | 只看该作者
3.2.2 变量名

FreeRTOS 中,定义变量的时候往往会把变量的类型当作前缀,好处看到就知道其类型。

char 型变量的前缀是 c

short 型变量的前缀是 s

long 型变量的前缀是 l

复杂的结构体,句柄等定义的变量名的前缀是 x

变量是无符号型的再加前缀 u,是指针变量则加前缀 p


使用特权

评论回复
17
我只会加减乘除|  楼主 | 2022-5-1 17:35 | 只看该作者
3.2.3 函数名

函数名包含了函数返回值的类型、函数所在的文件名和函数的功能,如果是私有的函数则会加一个 prv(private)的前缀。

例如vTaskPrioritySet()函数的返回值为 void 型,在 task.c 这个文件中定义。

3.2.4 宏

宏内容是由大写字母表示,前缀是小写字母,表示该宏在哪个头文件定义,如:

<span style="outline: 0px; max-width: 100%; font-family: " operator="" mono",="" consolas,="" monaco,="" menlo,="" monospace;="" font-size:="" 12px;="" white-space:="" pre;="" color:="" rgb(209,="" 154,="" 102);="" line-height:="" 26px;="" box-sizing:="" border-box="" !important;"="">1.</span><span style="color: rgb(171, 178, 191); font-family: " operator="" mono",="" consolas,="" monaco,="" menlo,="" monospace;="" font-size:="" 12px;="" white-space:="" pre;="" background-color:="" rgb(40,="" 44,="" 52);"=""> </span><span style="outline: 0px; max-width: 100%; font-family: " operator="" mono",="" consolas,="" monaco,="" menlo,="" monospace;="" font-size:="" 12px;="" white-space:="" pre;="" color:="" rgb(97,="" 174,="" 238);="" line-height:="" 26px;="" box-sizing:="" border-box="" !important;"="">#<span style="outline: 0px; max-width: 100%; line-height: 26px; box-sizing: border-box !important;">define</span> taskYIELD()                 portYIELD()  </span>

表示该宏是在task.h。


使用特权

评论回复
18
我只会加减乘除|  楼主 | 2022-5-1 17:36 | 只看该作者
3.2.5 个人解读

1、编码不缺编码规范,但是实际使用中很难完全依照标准执行,即使freeRTOS源码也是如此。

2、关于函数或者宏定义中带文件名的作用,使用Source Insight 编辑代码,该前缀的意义不大。

3、规则是活的,只要所有人都按一个规则执行,它就是标准。


使用特权

评论回复
19
我只会加减乘除|  楼主 | 2022-5-1 17:37 | 只看该作者
3.3 FreeRTOS应用开发

关于freeRTOS的应用开发,主要是任务的创建和调度,任务间的通信与同步,涉及队列、信号量等操作系统通用接口。结合应用需求,涉及定时器、延时、中断控制等接口。

特别说明,有些功能的实现方式有多种形式,只针对常用方式进行说明,例如task的创建,只说明动态创建方式,因为很少使用静态方式。


使用特权

评论回复
20
我只会加减乘除|  楼主 | 2022-5-1 17:38 | 只看该作者
四、 任务4.1 创建任务

xTaskCreate()使用动态内存的方式创建一个任务。

1. ret = xTaskCreate((TaskFunction_t) master_task_main,  /* 任务入口函数 */(1)
2.                   “MASTER”,   /* 任务名字 */(2)
3.                   64*1024,   /* 任务栈大小 */(3)
4.                   NULL,    ,/* 任务入口函数参数 */(4)
5.                   TASK_PRIORITY_NORMAL,  /* 任务的优先级 */(5)
6.                   &task_master_handler);  /* 任务控制块指针 */(6)

创建任务就是软件运行时的一个while(1)的入口,一般阅读其他代码,找到这个函数,再跟踪到任务入口函数,学习基于freeRTOS系统的代码,首先就是找到main和这个接口。

(1):任务入口函数,即任务函数的名称,需要我们自己定义并且实现。

(2):任务名字,字符串形式,最大长度由 FreeRTOSConfig.h 中定义的 configMAX_TASK_NAME_LEN 宏指定,多余部分会被自动截掉,只是方便调试。

(3):任务堆栈大小,单位为字, 4 个字节,这个要注意,否则系统内存紧缺。

(4):任务入口函数形参,不用的时候配置为 0 或者NULL 即可。


(5) :任务的优先级,在 FreeRTOS 中,数值越大优先级越高,0 代表最低优先级。基于其SDK开发,可将自定义的所有业务功能task设为同一个优先级,按时间片轮询调度。

(6):任务控制块指针,使用动态内存的时候,任务创建函数 xTaskCreate()会返回一个指针指向任务控制块,也可以设为NULL,因为任务句柄后期可以不使用。


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

16

主题

159

帖子

0

粉丝