如果我对本翻译内容享有所有权。允许任何人复制使用本文章,不会收取任何费用。如有平台向你收取费用与本人无任何关系
第一章 . 发布内容
章节介绍和范围
FreeRTOS用一个单独的zip压缩文件发布,里面包含了所有离线的FreeRTOS文件,和大量的实例程序。
范围
本章指在让读者了解FreeRTOS的文件和目录结构:
- 提供一个FreeRTOS目录结构的顶层视角
- 说明那些文件是一个FreeRTOS项目实际需要的
- 介绍FreeRTOS的程序实例
- 提供怎么样在一个已经存在的项目中引入FreeRTOS
这里的描述只和离线的FreeRTOS相关。。本书接下来的实例用不同的目录结构。
了解FreeRTOS发布内容
定义FreeRTOS接口
FreeRTOS可以使用大约20种不同的编译器编译,可以运行在不同架构芯片上。每个支持的编译器和架构组合组成一个特定版本接口的FreeRTOS。
编译FreeRTOS
FreeRTOS可以认为是一个库,相比于裸机程序它提供了多任务的能力。
FreeRTOS是以一些C源文件形式提供。有些源文件对所有版本接口都通用。将FreeRTOS源文件编译进你的项目,以使FreeRTOS的函数可以在项目中使用。为了让用户简单就能上手,每个离线的FreeRTOS版本接口都提供了具体实例。这些程序实例都已预配置,以便可以用于源代码编译,也包含了正确的头文件。
实例程序应该可以开箱即用,尽管可能有些实例比其他的要老旧些,有时候实例相关编译工具的改变会引起错误。1.3章节有描述实例程序。
FreeRTOSConfig.h
FreeRTOS用FreeRTOSConfig.h头文件设置。
用户可以使用FreeRTOSConfig.h告诉FreeRTOS用于指定特定的程序功能。比如,FreeRTOSConfig.h中包含一个configUSE_PREEMPTION
的宏,这个设置就决定了调度器会用合作还是抢占模式。因为FreeRTOSConfig.h中包括了程序相关特定宏,它就应该包含在要被编译的具体项目中,而不是FreeRTOS的源代码目录下。
提供的每一个FreeRTOS版本接口中的每个实例程序都有一个具体的FreeRTOSConfig.h文件。因此不需要重新创建一个FreeRTOSConfig.h文件,推荐从已经有的实例或者项目中复制一个来做相应更改。
FreeRTOS离线发布包
FreeRTOS用一个单独的zip文件发布。这个zip文件包含所有FreeRTOS接口的源代码和所有FreeRTOS实例程序项目文件。也包含了FreeRTOS生态系统组件,以及FreeRTOS生态系统演示应用程序。
不要被FreeRTOS发布包中的众多文件吓到!其中只有很少的文件需要包含在你的项目中。
FreeRTOS发布包的顶层目录
图1中展示了FreeRTOS发布包的第一和第二级目录:
# FreeRTOS发布包的顶层目录结构。图1
FreeRTOS
├── FreeRTOS
│ ├── Demo # 包含特定版本接口的FreeRTOS实例项目和预配置文件
│ ├── License # 许可证
│ ├── Source # FreeRTOS源代码
├── FreeRTOS-Plus
│ ├── Demo # 包含特定版本接口的FreeRTOS+生态系统的实例项目和预配置文件
│ ├── Source # FreeRTOS+生态系统组件源文件
这个离线的zip文件只是一份FreeRTOS源代码文件; 所有的FreeRTOS实例项目,FreeRTOS+的实例项目,都需要找到FreeRTOS/Source目录下面的源文件,如果改变目录结构可能就不能顺利编译了。这里这个问题就涉及到FreeRTOS的移植和Windows和linux中的FreeRTOS模拟器搭建。
所有版本接口共用的源码文件
FreeRTOS内核源码文件只包含两个C语言的文件,它们是所有FreeRTOS版本接口共用的。它们就是task.c和list.c,它们位于FreeRTOS/Source目录中,图2所展示。为了扩展这两个文件,下面的源文件位于相同目录:
- queue.c: queue.c提供队列和信号量服务,本书后面会有介绍。大部分情况都会使用queue.c
- timers.c: timers.c提供软件定时任务,也是后面会有介绍。只有用到软件定时任务时才需要在项目中包含
event_group.c
:event_group.c
提供事件组功能,也是后面章节会介绍。只有需要事件组时才需要在项目中包含- croutine.c: croutine.c提供协同程序功能,只有在用到协同程序功能时才需要包含它。协同程序是用在非常小的微处理器上的,已经很少使用,因此不会和其他功能相同等级,本书没有协同程序的介绍。
# FreeRTOS共用内核部分源码文件。图2
FreeRTOS
├── Source
│ ├── croutine.c # 选择可用,少用
│ ├── event_groups.c # 选择可用
│ ├── list.c # 必要
│ ├── queue.c # 选择可用,常用
│ ├── stream_buffer.c # 选择可用,新版功能
│ ├── tasks.c # 必要
│ └── timers.c # 选择可用,常用
因为这里文件的名字可能引起文件名字冲突,因为许多之前的项目可能已经有同名文件。这里更改FreeRTOS中的文件是不行的,因为这样会破坏许多已经使用FreeRTOS的项目,自动化工具和IDE插件的兼容性。
特定版本接口FreeRTOS源码
特定版本接口源代码文件位于FreeRTOS/Source/portable/目录。这个接口目录是按照层次排序的,首先是编译器,然后是处理器架构。图3显示了相关层次。
如果你在一个"architecture"架构的,用"compiler"编译器的接口上使用FreeRTOS,你就可以进入FreeRTOS/Source/portable/[compiler]/[architecture]目录中,进行编译。
第二章(堆栈管理)就是这里的接口层的相关内容。在FreeRTOS的V9.0.0之前必须包含一个堆栈管理。V9.0.0之后如果没有设置configSUPPORT_DYNAMIC_ALLOCATION
,或者设置为1才需要一个堆管理。
FreeRTOS提供了5个堆管理策略。这5个策略是以C语言文件方式提供,名字从heap_1.c到heap_5.c
。这5个C文件位于FreeRTOS/Source/portable/MemMang/目录。如果你使用动态内存分配,你就需要将5和策略文件中的1个加入到你的项目,进行编译,除非你自己提供其他的堆栈管理策略。
# 特定版本接口源文件目录树。图3
FreeRTOS
|
|-Source
| |-portable
| | |-MemMang
| | |-[compiler 1]
| | | |-[architecture 1]
| | | |-[architecture 2]
| | | |-[architecture 3]
| | |-[compiler 2]
| | | |-[architecture 1]
| | | |-[architecture 2]
| | | |-[architecture 3]
头文件路径
FreeRTOS需要在编译器头文件目录中包含3个路径。它们是:
- FreeRTOS内核的头文件,FreeRTOS/Source/include/
- 特定版本接口源码文件目录,通常是FreeRTOS/Source/portable/[compiler]/[architecture]/
- FreeRTOSConfig.h所在目录
头文件
使用FreeRTOS函数的源文件需要包含FreeRTOS.h,它包含了要使用的FreeRTOS函数原型,还包含了task.h,queue.h,semaphr.h,timer.h,event_group.h
实例程序
每个FreeRTOS支持的版本接口都应该有一个最新的可以直接编译,不会生成任何错误和警告的实例程序,尽管有些程序可能稍微比其他版本接口旧一点,有时候因为编译工具改变可能会有一些错误。
Linux用户需要注意: FreeRTOS是在Windows主机上开发测试。所以偶尔会出现在Linux上编译错误。构建错误几乎总是和引用文件名时,用的字母或文件路径中使用的斜杠字符的方向有关,请用FreeRTOS联系方式"http://www.FreeRTOS.org/contact"上的联系方式反馈给我们。
这里的实例程序可以用于下面的目的:
- 提供一个包含正确文件和正确编译选项集合的实例,而且这个实例可以正常工作,有预编译。
- 可以在没有足够知识和最小化配置的前提下实现开箱即用
- 展示如何使用FreeRTOS的函数
- 作为一个实际FreeRTOS项目的基础模板
每个实例项目都保存在FreeRTOS/Demo/的子目录中。这里的子目录是指项目相关的具体处理器架构和编译器。
每个实例项目在FreeRTOS.org网站上也有网页介绍。这个网页包括了: - 如何在FreeRTOS目录结构中定位这个项目文件
- 这个项目用的那些硬件
- 怎么样设置硬件运行这个项目
- 怎么样编译这个实例
- 这个项目的具体表现是什么
所有的实例项目都创建了一个常用实例任务子集,子集的具体实现在FreeRTOS/Demo/Common/Minimal目录。这里的常用实例任务单纯是为了展示怎么样使用FreeRTOS的函数,它们没有实现任何有用的函数。
很多最近的实例项目也可以编译一个"blinky"项目,它非常简单。一般只有2个任务和一个队列。
每个实例项目都包含一个main.c的文件。其中有一个main()函数,所有的程序任务否是这里创建的。可以查看main.c中的注释了解更多实例详细信息。
图4展示了FreeRTOS/Demo的层次
# 实例目录层次。图4
FreeRTOS
| - Demo
| - [Demo X] # 实例X,特定文件
| - [Demo Y] # 实际Y,特定文件
| - [Demo Z] # 实例Z,特定文件
| - Common # 实例共用文件
创建一个FreeRTOS项目
适配一个支持的实例项目
每个FreeRTOS版本接口都有一个最新的带预配置的实例程序,这个项目应该可以顺利通过编译,没有错误和警告。推荐新的项目就通过适配这些已经存在的项目;这样确保项目包含正确的文件,头文件以及编译选项。
可以按照下面步骤从已经存在的实例项目开始一个新的程序:
- 打开这个提供的实例项目,确保正确编译和执行。
- 删除源代码中定义的实例任务。这些文件位于Demo/Common目录,它们可以从项目中删除。
- 删除所有main()函数调用的函数,除了prvSetupHardware()和vTaskStartScheduler(),就像列表1列出的一样。
- 确定项目仍然可以通过编译。
接下来的步骤就是创建一个包含FreeRTOS源代码,但没有其他任何函数的项目。
// 新项目的main()函数样板。列表1
int main(void){
/* 处理硬件配置 */
prvSetupHardware();
/* 开启调度器 */
vTaskStartScheduler();
/* 只有堆空间不足的时候才会运行到这里 */
for(;;);
return 0;
}
从头开始创建一个新的FreeRTOS项目
如上面所说,推荐从一个存在的实例项目开始创建一个新项目。如果不愿意如此,可以通过下面的步骤创建一个新项目:
- 用你选择的工具链创建一个新的项目,这个项目中还没有FreeRTOS的源码。
- 确保这个项目能顺利编译,顺利下载到你的硬件并执行。
- 确定你的新项目没有问题后,增加表1中的FreeRTOS源码到你的项目中。
- 复制你的编译器和处理器架构对应版本接口的实例中的FreeRTOSConfig.h文件到你的项目中。
- 增加下面的目录到你的工具链要搜索头文件路径
- FreeRTOS/Source/include
- FreeRTOS/Source/portable/[compiler]/[architecture]
- FreeRTOSConfig.h所在目录
- 从相应实例项目中复制编译设置表单到新项目
- 安装可能需要的中断操作。可以查看相关版本接口的网页版描述,实例项目版本接口提供的接口函数。
# 表1,项目中需要包含的FreeRTOS源码
tasks.c # FreeRTOS/Source
queue.c # FreeRTOS/Source
list.c # FreeRTOS/Source
timers.c # FreeRTOS/Source
event_group.c # FreeRTOS/Source
所有汇编和C文件 #FreeRTOS/Source/portable/[compiler]/[architecture]/
heap_n.c # FreeRTOS/Source/portable/MemMang/,这里的n可以是1到5中的一个。V9.0.0之后这是一个可选项
使用FreeRTOS的v9.0.0之前版本必须提供heap_n.c
,之后版本只有在FreeRTOSConfig.h中没有configSUPPORT_DYNAMIC_ALLOCATION
,或者它为1的时候才需要提供。可以查看第二章堆管理了解更多细节。
数据格式和代码风格指南
每个FreeRTOS版本端口都有一个唯一的portmacro.h文件,它包含两个平台相关的宏:TickType_t和BaseType_t
。下面对这两个数据类型有相关描述。
# FreeRTOS使用的平台相关宏
TickType_t: FreeRTOS配置一个周期性的tick中断。自从FreeRTOS开始运行以来总共发生的tick中断数量叫做tick计数。这里的tick数量就用来对时间进行统计。两个tick之间的时间就是tick周期,时间计数就指代多个周期。TickType_t就是用于保存tick计数值的一种类型,它对应一个特定的时钟时间。TickType_t可以是一个无符号16位值,也可以是一个无符号32位值。具体依赖于FreeRTOSConfig.h中的`configUSE_16_BIT_TICKS`的值。如果这个宏为1,TickType_t就是16位,否则就是32位的uint32_t。在8位和16位处理器上用16位的TickType_t可以有效提高性能,但会严格限制TickType_t可以保存的tick计数器最大数量。没有理由在32位处理器上使用16位的TickType_t。
BaseType_t: 总是定义为处理器中最高效的数据格式。32位处理器上就是32位格式,16位处理器上就是16位格式,8位处理器上就是8位格式。BaseType_t用作返回值,它只需要很少的值。对于布尔值只有pdTRUE和pdFALSE
有些编译器将所有未声名有无符号的变量默认为有符号的,而其他的将它们默认为无符号。因此在FreeRTOS中对char变量都明确的声名为有符号或者无符号。除非它用于保存ASCII值,或者字符指针用于保存字符串。
从不单纯的使用int格式。
变量名字
变量前缀是它们的格式: 'c’表示字符,'s’表示int16_t(short)
,‘l’表示int32_t(long)
,‘x’表示BaseType_t
或者其他非标准格式(结构体,任务句柄,队列句柄等)。
如果变量是无符号的,用字符’u’表示。如果变量是用前缀’p’表示。比如一个uint8_t
变量的前缀就是’uc’,一个指向字符的指针前缀就是’pc’。
函数名字
函数名的前缀有它们返回的类型和定义它们的文件名字共同组成,比如:
- vTaskPrioritySet()定义在task.c中,返回一个空值
- xQueueReceive()定义在queue.c中,返回一个
BaseType_t
格式的值 - pvTimerGetTimerID()定义在timer.c中,返回一个空指针
本文件内的私有函数用’prv’前缀表示。
格式
一个tab占4个空格。这是手册中的排版,有不同还请自行略过。
宏名字
大多数宏用大写字母+小写字母前缀表示定义在哪个文件中。表3提供了一个前缀表。
# 宏前缀。表3
前缀 例子 定义文件名字
port portMAX_DELAY portable.h或portmacro.h
task taskENTER_CRITICAL() task.h
pd pdTRUE projdefs.h
config configUSE_PREEMPTION FreeRTOSConfig.h
err errQUEUE_FULL projdefs.h
注意信号量函数名字相当于一个宏集合,但遵循函数命名规则,而不是宏命名规则。
表4中是FreeRTOS中大量使用的宏定义。
#常用宏定义,表4
宏 值
pdTRUE 1
pdFALSE 0
pdPASS 1
pdFAIL 0
比理论多的格式
FreeRTOS可以被许多不同的编译器编译,编译器的不同之处在于什么时候和怎么样生成警告。实际上不同编译器希望用不同的方式使用各种格式数据。也是因为如此,FreeRTOS源码中包括了比通常保证更多的格式。