快速发展的技术需要软件支持(固件驱动程序和示例代码)来简化设计过程。本文介绍了如何使用 no-OS(无操作系统)驱动程序和平台驱动程序来构建具有 Analog Devices 模数转换器和数模转换器的应用固件,这些转换器在以下方面提供了高水平的性能速度、功率、尺寸和分辨率。
ADI 提供基于无操作系统驱动程序的嵌入式固件示例,以支持AD转换器。无操作系统驱动程序负责设备配置、从转换器捕获数据、执行校准等,而基于无操作系统驱动程序的固件示例则有助于将数据传输到主机 PC 以进行显示、存储和进一步处理。
无操作系统和平台驱动程序简介
顾名思义,无操作系统驱动程序设计用于通用(或非特定)操作系统。该名称还意味着这些驱动程序可以在没有任何操作系统支持的 BareMetal 系统上使用。无操作系统驱动程序旨在为给定精度转换器的数字接口访问提供高级 API。使用这些 API 的无操作系统驱动程序与设备连接以访问、配置、读取和写入数据,而无需了解寄存器地址(内存映射)及其内容。
无操作系统驱动程序利用平台驱动程序层,允许在多个硬件/软件平台上重复使用相同的无操作系统驱动程序,从而使您的固件具有高度可移植性。平台驱动程序层的使用使无操作系统驱动程序无法了解平台特定接口(例如 SPI、I 2 C、GPIO 等)的低级详细信息,这使得无操作系统驱动程序可以跨多个平台重用,而无需任何操作。改变他们。
图 1. AD转换器固件架构。
使用无操作系统驱动程序
图 2 显示了无操作系统驱动程序的典型代码结构。
图 2. 无操作系统驱动程序代码结构。
图 3. 设备配置枚举、结构和 API。
AD转换器的无操作系统驱动程序代码通常包含在两个用 C 编程语言编写的源文件中:adxxxx.c和adxxxx.h,其中 xxxx 代表设备名称(例如 AD7606、AD7124 等)。设备头文件 ( adxxxx.h ) 包含设备特定结构、枚举、寄存器地址和位掩码的公共编程接口,通过将此文件包含到所需的源文件中,可以供公共访问。设备源文件 ( adxxxx.c ) 包含用于初始化和删除设备、读/写设备寄存器、从设备读取数据、获取/设置设备特定参数等接口的实现。
典型的无操作系统驱动程序是围绕一组通用功能构建的:
设备特定寄存器地址、位掩码宏、设备配置枚举以及用于读/写设备特定参数(例如,过采样、增益、参考等)的结构的声明。
通过无操作系统驱动程序的设备初始化/删除函数以及设备特定的 init 和驱动程序结构和描述符来初始化/取消初始化物理设备。
使用设备寄存器读/写函数访问设备内存映射或寄存器详细信息;例如,adxxxx_read_register()或adxxxx_write_register()。
无操作系统驱动程序代码使用
使用设备特定地址、位掩码以及参数配置枚举和结构
如前所述,adxxxx.h头文件包含所有设备特定枚举和结构的声明,这些枚举和结构将传递给设备特定函数或 API 以配置或访问设备参数。图 3 对此进行了说明。
图 3 中所示的adxxxx_config结构允许用户选择多路复用器通道并为其设置过采样率。该结构的两个成员(afe_mux_channel和oversampling)都是存在于同一头文件中的枚举,其中包含用户可以选择的两个字段的所有可能值的数字常量。
adxxxx.c文件中定义的adxxxx_set_adc_config ()函数通过配置结构获取用户传递的配置/参数,并进一步调用 adxxxx_spi_reg_write ()函数通过数字接口将数据写入ADXXXX_REG_CONFIG设备寄存器(在前面的情况下, SPI)。
使用无操作系统驱动程序设备结构和初始化函数初始化设备
图 4. 设备初始化和驱动程序结构的声明。
除了设备配置枚举和结构之外,无操作系统驱动程序还提供两个附加结构:
- 设备初始化结构
- 设备驱动程序结构。
设备初始化结构允许用户在用户应用程序代码中定义设备特定的参数和配置。init 结构包含其他特定于设备的参数结构和枚举的成员。图 5 显示了设备初始化结构的定义方式。
图 5. 用户应用程序中的设备初始化结构定义。
设备驱动结构体通过设备初始化函数adxxxx_init()加载设备初始化参数。设备驱动程序结构是在运行时(动态)内存中从堆空间分配的。设备驱动程序结构和设备初始化结构中声明的参数几乎彼此相同。设备驱动程序结构是设备 init 结构的运行时版本。
典型的设备初始化函数和初始化流程描述如下:
步骤 1:在应用程序中创建设备 init 结构的定义(或实例)(例如 struct adxxxx_init_params),以初始化用户特定的设备参数和平台相关的驱动程序参数。这些参数是在编译时定义的。
注意:init 结构体中定义的参数因设备而异。
步骤2:在应用程序代码中创建设备驱动程序结构体的指针实例(变量)。
用户应用程序需要创建设备驱动程序结构的单个指针实例。该实例被传递给所有无操作系统驱动程序 API/函数以访问设备特定参数。应用程序代码中定义的此指针实例指向堆中动态分配的内存,这是通过设备初始化函数(例如在无操作系统驱动程序中定义的adxxxx_init() )完成的。
步骤 3:通过调用设备 init 函数来初始化设备和其他特定于平台的外设。
无操作系统驱动程序中定义的 adxxxx_init() 函数使用通过 adxxx_init_param 结构传递的用户特定参数来初始化设备。设备驱动程序结构的指针实例和设备 init 结构的实例作为两个参数传递给此 init 函数。用户应用程序代码可以多次调用adxxxx_init()函数,前提是 init 调用通过对设备删除函数的调用来平衡。
通过设备寄存器读/写函数访问存储器映射(寄存器内容)如图 6 所示。
图 6. 访问寄存器内容。
用户可以通过无操作系统驱动程序设备特定的adxxx_read/write()函数访问设备寄存器内容(例如产品 ID、暂存器值、OSR 等)。
大多数时候,用户不直接使用寄存器访问函数。设备特定函数通过这些寄存器访问函数进行调用,例如adxxxx_ spi_reg_read/write()。建议尽可能使用设备配置和状态 API 来访问设备内存映射,而不是使用直接寄存器访问函数,因为这可以确保设备驱动程序结构与设备中的配置保持同步。
平台驱动程序
平台驱动程序是包装特定于平台的 API 的硬件抽象层 (HAL) 之一。它们由无操作系统设备驱动程序或用户应用程序代码调用,以提供独立于底层硬件和软件平台的独立性。平台驱动程序封装了低级平台特定的硬件功能,例如 SPI/I 2 C 初始化和读/写、GPIO 初始化和读/写、UART 初始化和接收/发送、用户特定的延迟、中断等。
SPI平台驱动模块的典型文件结构如图7所示。
图 7. SPI 平台驱动程序代码结构。
使用平台驱动程序
平台驱动程序代码通常合并在用 C/C++ 编程语言编写的三个源文件中。
1) spi.h:这是一个与平台无关的文件,包含 SPI 功能所需的设备结构和枚举。此头文件中定义的 C 编程接口没有平台依赖性。
init 和 device 结构中声明的所有参数对于任何平台上的 SPI 接口都是通用的。
设备初始化结构中使用的void *extra参数允许用户传递附加(额外)参数,这些参数可能特定于所使用的平台。
SPI 驱动程序结构和 SPI init 结构中声明的参数几乎彼此相同。SPI 驱动程序结构是 SPI init 结构的运行时版本。
2) spi.cpp/.c:该文件包含spi.h文件中声明的函数的实现,这些函数用于初始化 SPI 外设并针对特定平台从其读取/写入数据。从更广泛的意义上讲,术语“平台”是指硬件微控制器(目标设备)和软件(例如 RTOS 或 Mbed-OS)的组合。该文件与平台相关,在移植到其他平台时需要进行修改。
图 9 详细介绍了 Mbed 平台的 SPI 接口,并展示了如何使用这些接口和设备初始化/驱动程序结构来初始化 SPI 和读/写数据。
图 8. SPI 初始化和驱动程序结构。
图 9. SPI API 或函数。注意:为 spi_init() 和 spi_write_and_read() 添加的代码是缩写代码,为了清楚起见,省略了详细信息。
图 10. SPI 额外初始化和驱动程序结构。
3) spi_extra.h:该文件包含特定于给定平台的附加设备结构或枚举。这允许用户应用程序代码提供通用spi.h文件中未涵盖的配置。例如,SPI 引脚可能因平台而异,因此可以将其添加为这些平台特定的额外结构的一部分。
移植平台驱动程序
通常可以通过创建特定于平台的.cpp/.c和_extra.h文件,将平台驱动程序从一个平台(微控制器)移植到另一平台。平台驱动程序位于微控制器单元供应商提供的设备特定硬件抽象层 (HAL) 之上的一层。因此,将平台驱动程序从一个平台移植到另一个平台需要对与调用其供应商提供的 HAL 中存在的函数或 API 相关的平台驱动程序代码进行一些最小的更改。
图 12 中的图表区分了基于 Mbed 的 SPI 平台驱动程序和ADuCM410 SPI 平台驱动程序。
ADI 的无操作系统存储库和平台驱动程序的 GitHub 源代码链接https://github.com/analogdevicesinc/no-OS