如何构建嵌入式软件的源代码
How to structure source code for embedded software
为了提高我的嵌入式软件技能(在过程 C 中,使用 C++11 功能),我想了解什么是 structure/package 嵌入式系统源代码的最佳方法。
我正在构建没有 RTOS 的小型系统(在具有 1-k4k RAM 内存的单个微控制器上)。为了能够在目标环境之外调试我的软件,我想在我的主机上模拟我的系统。
我发现将有关处理器及其内部设备(RAM、ROM、定时器等)的信息存储在 board support package 中是标准做法。
但在我的嵌入式项目中,我还使用其他外部设备(按钮、LED、LCD、伺服系统、IO 多路复用器等),这些设备通过 PCB 连接到微控制器。 应用特定 PCB 上的源代码抽象层的标准 way/standard 名称是什么?
我在有关实时系统工程的书籍中搜索过(David Simon 的嵌入式软件入门、Jim Cooling 的实时系统软件工程 和 LaPlante 的 实时系统:设计与分析),但无法找到嵌入式系统中代码结构的良好参考。
根据 wikipedia entry for BSP,其目的是提供特定 OS 所需的电路板支持(不仅仅是微型)。因此,对于相同的硬件,不同的 OS 可能需要不同的 BSP,因此我认为这不是您所追求的术语。
您所描述的就是我所说的硬件抽象 Layer/HAL,我相信这是一个很常见的名称。原则上,这个名称可以包括整个PCB,也可以只包括MCU。
HAL 中的重要部分是抽象。一个好的抽象将使其易于替换,例如如果您想测试构建在 HAL 之上的代码。它还将使将该代码移植到不同的硬件平台变得更加容易。
我发现很多供应商提供的库在这方面有不足:你可以调用函数而不是直接修改或读取寄存器,但函数调用往往直接对应寄存器。也就是说,他们的库可能隐藏了与芯片接口所需的编译器扩展的抽象,但仅此而已。因此,我倾向于不使用供应商提供的库作为 HAL,但可能会使用它来实现一个(或者直接访问寄存器)
HAL 的最佳结构取决于您的设计目标。您已经陈述了一个目标,即能够将您的应用程序调试到目标之外。您还声明您不打算 运行 和 OS。您可能计划支持单个 MCU 或 MCU 系列,或者来自不同供应商的多个 MCU。您可能正在规划一个单板设计,或基于一个通用平台的多个定制板。这些都会影响HAL设计中的设计选择。
我经常从事不使用 RTOS 的项目。通常,硬件从一个或多个旧设计继承某些方面,并具有一些新内容。新东西可以是小变化也可以是大变化(包括不同的 MCU 或 MCU 系列)。
在这种情况下,我对 HAL 的处理方式发展为:
为 HAL 中的每个组件定义一个 API,(通常是函数指针的结构,例如 adc_driver_api_t
、uart_driver_api_t
、i2c_driver_api_t
等)。这些 API 不使用任何 chip/compiler 扩展或包含,但通常遵守语言标准 (C89/C99)。
给定平台的 HAL 提供了这些实现,例如msp430_adc12_driver
、msp430_adc10_driver
、pic18_adc12_driver
、avr_adc_driver
等都使用各自平台上可用的不同外围设备实现 adc_driver_api_t
接口。每个实现在实现头文件中公开已实现驱动程序的 const 全局实例,例如(使用 C)extern const adc_driver_api_t msp430_adc12_driver;
。这些实现根据需要使用 chip/compiler 扩展或包含。
使用 HAL 获取 ADC 读数的组件将使用 const adc_driver_api_t *
实现和 API 需要的任何其他内容(可能是 adc 通道)进行初始化。组件和 ADC 驱动程序实现都将在程序初始化中初始化并连接在一起,例如main()
的顶部
我尝试保持 HAL API 精简。例如,某些外设可能允许您将 12 位结果放入 16 位寄存器的高 12 位或低 12 位。我不允许通过 HAL API 配置它,但可以提供一个函数来直接在实现 HAL API 的模块中设置它,所以在 msp430_adc12_driver.h 中可能有一个函数msp430_adc12_driver_set_result_alignment()
系统初始化时可以调用
我发现这种方法允许以独立于硬件的方式在 HAL 之上构建组件,因此我可以轻松地在不同平台上重用组件。它还允许编写组件、调试组件以及对组件进行脱机单元测试,并为 HAL 提供测试替身。
为了提高我的嵌入式软件技能(在过程 C 中,使用 C++11 功能),我想了解什么是 structure/package 嵌入式系统源代码的最佳方法。
我正在构建没有 RTOS 的小型系统(在具有 1-k4k RAM 内存的单个微控制器上)。为了能够在目标环境之外调试我的软件,我想在我的主机上模拟我的系统。
我发现将有关处理器及其内部设备(RAM、ROM、定时器等)的信息存储在 board support package 中是标准做法。
但在我的嵌入式项目中,我还使用其他外部设备(按钮、LED、LCD、伺服系统、IO 多路复用器等),这些设备通过 PCB 连接到微控制器。 应用特定 PCB 上的源代码抽象层的标准 way/standard 名称是什么?
我在有关实时系统工程的书籍中搜索过(David Simon 的嵌入式软件入门、Jim Cooling 的实时系统软件工程 和 LaPlante 的 实时系统:设计与分析),但无法找到嵌入式系统中代码结构的良好参考。
根据 wikipedia entry for BSP,其目的是提供特定 OS 所需的电路板支持(不仅仅是微型)。因此,对于相同的硬件,不同的 OS 可能需要不同的 BSP,因此我认为这不是您所追求的术语。
您所描述的就是我所说的硬件抽象 Layer/HAL,我相信这是一个很常见的名称。原则上,这个名称可以包括整个PCB,也可以只包括MCU。
HAL 中的重要部分是抽象。一个好的抽象将使其易于替换,例如如果您想测试构建在 HAL 之上的代码。它还将使将该代码移植到不同的硬件平台变得更加容易。
我发现很多供应商提供的库在这方面有不足:你可以调用函数而不是直接修改或读取寄存器,但函数调用往往直接对应寄存器。也就是说,他们的库可能隐藏了与芯片接口所需的编译器扩展的抽象,但仅此而已。因此,我倾向于不使用供应商提供的库作为 HAL,但可能会使用它来实现一个(或者直接访问寄存器)
HAL 的最佳结构取决于您的设计目标。您已经陈述了一个目标,即能够将您的应用程序调试到目标之外。您还声明您不打算 运行 和 OS。您可能计划支持单个 MCU 或 MCU 系列,或者来自不同供应商的多个 MCU。您可能正在规划一个单板设计,或基于一个通用平台的多个定制板。这些都会影响HAL设计中的设计选择。
我经常从事不使用 RTOS 的项目。通常,硬件从一个或多个旧设计继承某些方面,并具有一些新内容。新东西可以是小变化也可以是大变化(包括不同的 MCU 或 MCU 系列)。
在这种情况下,我对 HAL 的处理方式发展为:
为 HAL 中的每个组件定义一个 API,(通常是函数指针的结构,例如
adc_driver_api_t
、uart_driver_api_t
、i2c_driver_api_t
等)。这些 API 不使用任何 chip/compiler 扩展或包含,但通常遵守语言标准 (C89/C99)。给定平台的 HAL 提供了这些实现,例如
msp430_adc12_driver
、msp430_adc10_driver
、pic18_adc12_driver
、avr_adc_driver
等都使用各自平台上可用的不同外围设备实现adc_driver_api_t
接口。每个实现在实现头文件中公开已实现驱动程序的 const 全局实例,例如(使用 C)extern const adc_driver_api_t msp430_adc12_driver;
。这些实现根据需要使用 chip/compiler 扩展或包含。使用 HAL 获取 ADC 读数的组件将使用
const adc_driver_api_t *
实现和 API 需要的任何其他内容(可能是 adc 通道)进行初始化。组件和 ADC 驱动程序实现都将在程序初始化中初始化并连接在一起,例如main()
的顶部
我尝试保持 HAL API 精简。例如,某些外设可能允许您将 12 位结果放入 16 位寄存器的高 12 位或低 12 位。我不允许通过 HAL API 配置它,但可以提供一个函数来直接在实现 HAL API 的模块中设置它,所以在 msp430_adc12_driver.h 中可能有一个函数
msp430_adc12_driver_set_result_alignment()
系统初始化时可以调用
我发现这种方法允许以独立于硬件的方式在 HAL 之上构建组件,因此我可以轻松地在不同平台上重用组件。它还允许编写组件、调试组件以及对组件进行脱机单元测试,并为 HAL 提供测试替身。