第1章 初识FreeRTOS

1.1 FreeRTOS版权

FreeRTOS由美国的Richard Barry于2003年发布,Richard Barry是FreeRTOS的拥有者和维护者,在过去的十多年中FreeRTOS历经了9个版本,与众多半导体厂商合作密切,有数百万开发者,是目前市场占有率最高的RTOS。

FreeRTOS于2018年被亚马逊收购,改名为AWS FreeRTOS,版本号升级为V10,且开源协议也由原来的GPLv2+修改为MIT,与GPLv2+相比,MIT更加开放,你完全可以理解为完全免费。V9以前的版本还是维持原样,V10版本相比于V9就是加入了一些物联网相关的组件,内核基本不变。亚马逊收购FreeRTOS也是为了进军物联网和人工智能领域。本书还是以V9版本来讲解。

1.2 FreeRTOS收费问题

1.2.1 FreeRTOS

FreeRTOS是一款“开源免费”的实时操作系统,遵循的是GPLv2+的许可协议。这里说到的开源,指的是可以免费获取FreeRTOS的源代码,且当你的产品使用了FreeRTOS而没有修改FreeRTOS内核源码时,你的产品的全部代码都可以闭源,不用开源,但是当你修改了FreeRTOS内核源码时,就必须将修改的这部分开源,反馈给社区,其他应用部分不用开源。免费的意思是无论你是个人还是公司,都可以免费地使用,不需要花费一分钱。

1.2.2 OpenRTOS

FreeRTOS和OpenRTOS拥有的代码是一样的,但是可从官方获取的服务却是不一样的。FreeRTOS号称免费,OpenRTOS号称收费,它们的具体区别如表1-1所示。

表1-1 FreeRTOS开源授权与OpenRTOS商业授权的区别

1.2.3 SaveRTOS

SaveRTOS也基于FreeRTOS,但是SaveRTOS为某些特定的领域做了安全相关的设计,有关SaveRTOS获得的安全验证具体如表1-2所示。SaveRTOS需要收费。

表1-2 SaveRTOS获得的安全方面的验证

1.3 FreeRTOS资料获取

FreeRTOS的源码和相应的官方书籍均可从官网www.freertos.org获得,如图1-1所示为官网首页。

1.3.1 获取源码

单击图1-1中的Download Source按钮,可以下载FreeRTOS最新版本的源码。如果想下载以往版本,可从托管网址https://sourceforge.net/projects/freertos/files/FreeRTOS/下载。截至本章编写时,FreeRTOS已经更新到V10.0.1,具体如图1-2所示。

图1-1 FreeRTOS官网首页

图1-2 FreeRTOS版本更新目录

1.3.2 获取书籍

单击图1-1中的PDF Books按钮可以下载FreeRTOS官方的两本电子书(英文版),分别为FreeRTOS V10.0.0 Reference Manual.pdf和Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf,一本是API参考手册,另外一本是手把手入门教程。

1.3.3 快速入门

单击图1-1中的Quick Start按钮,可获取网页版的快速入门教程。

1.4 FreeRTOS的编程风格

学习一个RTOS,弄清楚它的编程风格很重要,这可以大大提高我们阅读代码的效率。下面我们就从FreeRTOS中的数据类型、变量名、函数名、宏以及格式这几个方面做简单介绍。

1.4.1 数据类型

在FreeRTOS中,使用的数据类型虽然都是标准C里面的数据类型,但是针对不同的处理器,对标准C的数据类型又进行了重定义,给它们设置了一个新的名字,比如为char重新定义了一个名字portCHAR,这里的port表示接口,在将FreeRTOS移植到处理器上时,需要用这些接口文件把它们连接在一起。但是用户在写程序时并非一定要遵循FreeRTOS的风格,仍可以直接用C语言的标准类型。在FreeRTOS中,int型从不使用,只使用short型和long型。在Cortex-M内核的MCU中,short为16位,long为32位。

FreeRTOS中详细的数据类型重定义在portmacro.h头文件中实现,具体参见表1-3和代码清单1-1。

表1-3 FreeRTOS中的数据类型重定义

代码清单1-1 FreeRTOS中的数据类型重定义

 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
 8
 9 typedef portSTACK_TYPE StackType_t;
10 typedef long BaseType_t;
11 typedef unsigned long UBaseType_t;
12
13 #if( config USE_16_BIT_TICKS == 1 )
14 typedef uint16_t TickType_t;
15 #define portMAX_DELAY ( TickType_t ) 0xffff
16 #else
17 typedef uint32_t TickType_t;
18 #define portMAX_DELAY ( TickType_t ) 0xffffffffUL

在编程时,如果用户没有明确指定char的符号类型,那么编译器会默认指定char型的变量为无符号或者有符号。正是基于这个原因,在FreeRTOS中,我们都需要明确指定变量char是有符号的还是无符号的。在KEIL中,默认char是无符号的,但是也可以配置为有符号的,具体配套过程如图1-3所示。

图1-3 char型变量的符号配置(KEIL)

1.4.2 变量名

在FreeRTOS中,定义变量时往往会把变量的类型当作前缀加在变量上,这样做的好处是让用户一看到这个变量就知道该变量的类型。比如char型变量的前缀是c,short型变量的前缀是s,long型变量的前缀是l,portBASE_TYPE类型变量的前缀是x。还有其他的数据类型,比如数据结构、任务句柄、队列句柄等定义的变量名的前缀也是x。

如果一个变量是无符号型的,那么会有一个前缀u,如果是一个指针变量,则会有一个前缀p。因此,当我们定义一个无符号的char型变量时会加一个uc前缀,当定义一个char型的指针变量时会加一个pc前缀。

1.4.3 函数名

函数名包含了函数返回值的类型、函数所在的文件名和函数的功能,如果是私有的函数,则会加一个prv(private)的前缀。特别地,在函数名中加入了函数所在的文件名,这将帮助用户提高寻找函数定义的效率并了解函数作用,具体举例如下:

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

2)xQueueReceive()函数的返回值为portBASE_TYPE型,在queue.c文件中定义。

3)vSemaphoreCreateBinary()函数的返回值为void型,在semphr.h文件中定义。

1.4.4 宏

宏均由大写字母表示,并配有小写字母的前缀,前缀用于表示该宏在哪个头文件定义,部分举例具体如表1-4所示。

表1-4 FreeRTOS宏定义举例

这里要注意的是信号量的函数都是一个宏定义,但是其函数的命名方法是遵循函数的命名方法而不是宏定义的方法。

在贯穿FreeRTOS的整个代码中,还有几个通用的宏定义也要注意一下,都是表示0和1的宏,具体如表1-5所示。

表1-5 FreeRTOS通用宏定义

1.4.5 格式

1个Tab键等于4个空格键。我们在编程时最好使用空格键而不是使用Tab键,当2个编译器的Tab键大小设置得不一样时,移植代码时格式就会变乱,而使用空格键不会出现这种问题。