10. 初识瑞萨FSP固件库

本章配套视频介绍:

../../_images/video.png

《12-初识瑞萨FSP固件库》

https://www.bilibili.com/video/BV1Lp4y1n7oN/

10.1. 瑞萨FSP库简介

FSP 全称为 “Flexible Software Package”,中文译为“灵活配置软件包”。

FSP 旨在以较低的内存占用量提供快速高效的驱动程序和协议栈。 FSP 集成了中间件协议栈、独立于 RTOS 的硬件抽象层(HAL)驱动程序, 以及最基础的板级支持包(BSP)驱动程序。FSP 还支持 FreeRTOS™ 实时操作系统(RTOS)。

如图所示为 FSP 的软件架构。

图

FSP 由瑞萨公司维护和提供支持, 开发人员可以从集成开发环境(e2s)或 FSP 的 GitHub 仓库(https://github.com/renesas/fsp) 查看或下载 FSP 的源代码,从而全面地了解 FSP。

提示

FSP 是使用 C99(C语言标准)编写的开源软件,提供完整的源代码,但仅限用于瑞萨的硬件。

10.2. 瑞萨FSP库架构分析

下面简单分析一下瑞萨FSP库架构。 本小节内容当中的一些概念可能会让初学者觉得较难理解,对此,建议是大概浏览然后跳过即可, 这不影响后面的章节阅读,现在只需要大概、初步地了解一遍,不求甚解, 等到学习到了后期再回过头来看,或对FSP库架构会有新的体会。

10.2.1. FSP库层次结构

如图所示,展示了 FSP 库的层次划分,我们可以很直观地看到进行层次划分之后的软件结构。

图
RA MCU

位于最底层的是 RA 系列微控制器硬件。不同系列的 RA 微控制器之间具有非常一致、高度兼容的外设硬件, 这为软件开发人员提供了极大的便利。

BSP

往上一层是板级支持包(BSP)。BSP 处于 FSP 软件的底层,是 FSP 的功能基础。 BSP 负责 MCU 复位后初始化系统使程序执行进入 main 函数,并为上层软件提供其他服务。

BSP 函数名称以 R_BSP_ 开头,BSP 宏以 BSP_ 开头,数据类型定义以 _bsp 开头, 以便于与 FSP 的其他部分区分开来。

HAL

在 BSP 之上是硬件抽象层(Hardware Abstraction Layer (HAL)), 它以较小的内存占用量为外设提供高效的设备驱动程序,实现易于使用的接口, 使开发人员不必直接处理单片机的寄存器组,并使处于 HAL 层以上的软件更容易在整个 RA 产品家族中移植。 它是模块(Modules)的集合, 每个模块都是 RA 系列微控制器中可用的外设的驱动程序(比如 SPI、I2C、ADC), 其名称以 r_ 开头。所有这些模块本质上均与 RTOS 无关。 HAL 层除了“模块”以外还有:“接口”“实例” 等关键概念。

HAL 层的函数的名称以 R_ 开头,格式一般为 R_<MODULE>_<Function>。 默认情况下,所有驱动函数都是非阻塞的,并返回执行状态。驱动函数本身不分配任何内存,调用时需要将内存传递给函数。

操作系统和中间件

FSP 库首选支持 FreeRTOS,可以通过软件快速配置。 FreeRTOS 是非常流行的实时操作系统,支持多任务调度、任务通知、队列、互斥、信号量和软件计时器等功能, 其系统开销非常小,占用的内存也很小,性能可靠,经常被用于内存资源十分有限且需要实时响应处理的操作环境。

中间件介于 HAL 硬件抽象层和用户应用层之间,为应用层提供服务。 FSP 的中间件支持包括:TCP/IP协议栈、USB协议栈、WiFi和蓝牙BLE协议栈、电容式触摸、FAT文件系统、图形库、加密等等。 FSP 中间件函数的名称命名格式一般为:RM_<MODULE>_<Function>

应用层

该层为 FSP 层次划分的最顶层,包含了用户的应用代码。 用户通过 FSP 底层提供的直观、简单和统一的 API 接口调用下面各层,从而访问 FSP 的所有功能, 这样用户就能以非常简单和直接的方式编写易于理解、维护简单、移植方便的代码。

10.2.2. FSP库工程结构

我们再来看由软件自动生成的 FSP 库工程的结构究竟是什么样子的。

FSP库工程结构
Project
├─ ra
│  ├─ arm               包含 ARM CMSIS 代码
│  └─ fsp               包含 FSP 库本体
│     ├─ inc
│     │  ├─ api         FSP 接口 (FSP Interfaces)(接口包含 API 定义)
│     │  └─ instances   FSP 实例 (FSP Instances)(接口的实例)
│     └─ src
│        ├─ bsp         BSP 层 (Board Support Package)
│        │  ├─ cmsis    包含寄存器定义文件和启动文件
│        │  └─ mcu      包含 BSP 代码
│        └─ r_<module>  FSP 模块 (FSP Modules)(接口由模块实现,模块通过接口提供通用功能)
├─ ra_cfg               包含 FSP 库的配置(包括 BSP 和 HAL 层的配置)
├─ ra_gen               包含用户的 FSP 配置数据(包括时钟、引脚、各个外设、中断向量等配置数据)
├─ Debug/Release        包含编译后生成的中间文件和最终可执行文件等
└─ src
   └─hal_entry.c        包含了用户裸机应用程序的入口函数 hal_entry。
                        当没有使用 RTOS 的时候,hal_entry 函数由C语言 main 函数调用,
                        所以其作用基本等同于 main

上面的 FSP 库工程的结构其实非常的简单。只要我们把 “Project” 下的内容分为三部分:

  • 第一部分为 FSP 库及其配置,包括 ra、ra_cfg、ra_gen 这3个文件夹,它们由软件生成。

  • 第二部分为用户代码,包括 src 文件夹。

  • 第三部分为编译输出文件,包括 Debug 或 Release 文件夹。

那么,我们便不需要深入地理解 FSP 库的架构也可以很好的上手 FSP 库进行开发, 因为我们在配置 FSP 库的时候,是通过软件的图形界面(FSP 配置器)来配置的。

警告

注意:任何由软件生成的文件用户都不应该去编辑! 因为在每次进行 FSP 配置之后单击 “生成项目内容(Generate Project Content)” 图标按钮时, 软件都会自动重新创建这些文件,因此任何的更改都将被覆盖。

对于想要深入理解 FSP 库的读者,可以尝试阅读其源码。 同时,要想清晰地理解 FSP 库的架构,需要掌握以下几个相关的重要概念。

  1. 模块(Modules):模块可以是外设驱动程序、纯软件或介于这两者之间,并且是 FSP 的构建模块。 模块通常是独立的单元,但它们可能依赖于其他模块。可以通过组合多个模块来构建应用程序,为用户提供所需功能。

  2. 模块实例(Module Instance):模块的单个、独立实例化(模块配置)。 例如,USB 端口可能需要使用 r_dmac 模块的两个实例与其他端口之间来回传输数据; 又例如,当应用程序需要使用两个 GPT 定时器时,每一个这个定时器都是 r_gpt 模块的实例。

  3. 接口(Interfaces):接口包含 API 定义,具有相似功能的模块可以共用这些 API 定义。 模块通过这些定义提供通用功能。通过这些 API 定义,使用相同接口的模块可以互换使用。 可以将接口视为两个模块之间的合同,两个模块均同意使用合同中达成一致的信息进行协作。 接口只是定义,并不会增加代码的大小。

  4. 实例(Instances):接口规定所提供的功能,而接口的实例则真正实现了这些功能。 每个实例都与特定的接口关联,并使用接口中的枚举、数据结构和 API 原型。这样,应用程序便可以在需要时交换实例。

  5. 堆叠(Stacks):FSP 架构所采用的设计方式是,模块可以堆叠起来协同工作,从而形成了一个 FSP 堆。 将一个模块所能提供的功能与另一个模块所需要的功能相匹配,这就是堆叠过程。堆由顶层模块及其所有依赖项组成。

  6. 应用程序(Application):归用户所有并由用户维护的代码。

  7. 回调函数(Callback Functions):当有事件发生时(例如,USB 接收到一些数据时),中断服务程序(ISR)将调用这些函数。 中断回调函数是应用程序的组成部分,如果是在中断使用,应尽量简短,因为它们将在中断服务程序内运行,会阻碍其他中断执行。

关于接口与实例的概念:

接口规定所提供的功能,而实例则真正实现了这些功能。每个实例都与特定的接口关联,并使用接口中的枚 举、数据结构和 API 原型。这样,使用接口的应用程序便可以根据需要交换实例,从而在需要更改代码或所用 外设时节省大量的时间。在 RA 产品家族 MCU 上,一些外设(例如 IIC)将具有一对一的映射关系(只映射到 IIC 接口),而其他外设(例如 SCI)将具有一对多的映射关系(实现三个接口:IIC、UART、SPI)。 如下图所示展示了这种映射关系。

图