4. HAL 概念与设计原则

硬件抽象层(HAL)是嵌入式系统中位于操作系统内核与硬件平台之间的关键中间层。它的核心目标是将上层软件(包括操作系统、驱动框架、应用程序)与底层硬件细节解耦,使得同一套软件栈能够在不同硬件平台上平滑迁移。本章将系统阐述 HAL 的定义、分层架构思想、HAL 与 BSP 的关系以及接口隔离原则。

4.1 硬件抽象层定义

硬件抽象层(Hardware Abstraction Layer, HAL)是一组软件接口、数据结构和协议的集合,它向上层提供统一的硬件操作视图,屏蔽底层寄存器的差异、外设型号的差异以及 SoC 厂商的实现细节。HAL 不是简单的“封装”,而是一种接口契约:上层只依赖 HAL 定义的 API,不直接访问硬件寄存器或中断向量。

HAL 的核心特征:
  • 接口标准化:同一类外设(如 GPIO、I2C、SPI、UART)提供一致的 API 签名。
  • 平台无关性:上层代码不包含任何 #ifdef 或平台宏判断。
  • 二进制兼容:在满足接口契约的前提下,HAL 实现可独立替换。
  • 性能可预测:HAL 调用开销通常为几个函数调用层级,不引入动态分配或阻塞等待。

在 ARM 嵌入式 Linux 环境中,HAL 通常以内核模块或用户态共享库的形式存在。例如,一个典型的 GPIO HAL 接口可能包含以下函数原型:

/* gpio_hal.h — 硬件抽象层 GPIO 接口 */
int  gpio_hal_request(unsigned int pin, const char *label);
int  gpio_hal_direction_output(unsigned int pin, int value);
int  gpio_hal_direction_input(unsigned int pin);
int  gpio_hal_set_value(unsigned int pin, int value);
int  gpio_hal_get_value(unsigned int pin);
void gpio_hal_free(unsigned int pin);

不同的 SoC(如 i.MX8M、STM32MP1、RK3588)各自实现这些函数,但上层驱动只调用上述接口,无需关心底层寄存器地址或 GPIO 控制器类型。

4.2 分层架构思想

HAL 在嵌入式软件栈中处于中间层位置,其上下分层关系可以用一张架构图清晰表达。下图展示了典型的 ARM 嵌入式 Linux 软件分层模型:

ARM 嵌入式 Linux 软件分层架构 应用层(Application) 系统服务层(System Services / C Library) Linux 内核(Kernel Core / VFS / Scheduler) 内核驱动框架(Platform / SPI / I2C Subsystem) 硬件抽象层(HAL) 硬件平台(SoC / 外设 / 总线) 软件层 硬件层

从图中可以看出,HAL 位于内核驱动框架之下、硬件平台之上。这种分层带来的关键收益包括:

分层收益 说明
关注点分离 驱动开发者只需关注 HAL 接口实现,无需理解上层业务逻辑。
可移植性 更换 SoC 时只需替换 HAL 实现层,上层代码零修改。
可测试性 HAL 接口可被 Mock 或 Stub 替换,支持单元测试和硬件在环测试。
并行开发 硬件尚未就绪时,上层团队可基于 HAL 接口规范进行开发和调试。

4.3 HAL 与 BSP 的关系

BSP(Board Support Package,板级支持包)和 HAL 是嵌入式开发中经常被混淆的两个概念。它们既有重叠又有明确分工。下表从多个维度对比两者的差异:

对比维度 HAL(硬件抽象层) BSP(板级支持包)
关注范围 外设接口的标准化抽象 整个板级硬件初始化与配置
接口形态 函数 API、数据结构、回调 设备树、内核配置、启动脚本、固件
可移植性 跨平台、跨 SoC 复用 通常绑定特定板卡或 SoC 系列
典型内容 GPIO/I2C/SPI/UART 抽象接口 DTS 文件、DDR 时序、时钟树、PMIC 配置
生命周期 产品软件栈的长期资产 随板卡升级而更新
依赖关系 HAL 实现可能依赖 BSP 的时钟/GPIO 配置 BSP 通常不依赖 HAL,但提供 HAL 所需的底层信息
实践建议:在项目初期,先定义 HAL 接口规范,再开发 BSP 和 HAL 实现。BSP 负责“让板子跑起来”,HAL 负责“让外设用起来”。两者配合时,HAL 实现应通过 BSP 提供的设备树节点获取硬件资源(如寄存器基址、中断号),而不是硬编码。

下图展示了 HAL 与 BSP 在软件栈中的协作关系:

HAL 与 BSP 协作关系 上层软件(应用 / 系统服务 / 内核驱动框架) 硬件抽象层(HAL) gpio_hal_read() / i2c_hal_write() / spi_hal_transfer() BSP 板级支持包 设备树 / 时钟 / 复位 / 电源管理 SoC 外设驱动实现 寄存器操作 / 中断处理 / DMA 配置 硬件平台(SoC + 外设芯片 + 板级电路) 资源查询

4.4 接口隔离原则

接口隔离原则(Interface Segregation Principle, ISP)是 HAL 设计的核心指导原则之一。其原始定义是:“客户端不应被迫依赖它们不使用的接口”。在 HAL 上下文中,这意味着:

  • 按外设类型拆分接口:不要设计一个“万能外设 HAL”,而是为 GPIO、I2C、SPI、UART、PWM 等分别定义独立的接口头文件。
  • 按功能粒度拆分接口:例如,一个 SPI HAL 应拆分为“SPI 控制器接口”和“SPI 设备接口”,而不是混在一起。
  • 避免胖接口:如果一个 HAL 接口中有超过 30% 的函数在特定平台上返回 -ENOTSUP,说明接口隔离失败。
常见反模式:某些 BSP 厂商提供一个 hal.h 头文件,里面包含所有外设的枚举、结构体和函数声明。上层驱动被迫包含这个巨大的头文件,即使它只需要其中的 GPIO 功能。这不仅增加编译时间,还导致接口耦合——修改 I2C 部分可能意外影响 GPIO 使用者。

遵循接口隔离原则的 HAL 设计示例如下:

/* hal_gpio.h — 仅包含 GPIO 相关接口 */
#ifndef __HAL_GPIO_H__
#define __HAL_GPIO_H__

struct hal_gpio_cfg {
    unsigned int pin;
    int direction;   /* 0: input, 1: output */
    int pull;        /* 0: none, 1: pull-up, 2: pull-down */
};

int hal_gpio_init(struct hal_gpio_cfg *cfg);
int hal_gpio_set(unsigned int pin, int value);
int hal_gpio_get(unsigned int pin);
void hal_gpio_deinit(unsigned int pin);

#endif /* __HAL_GPIO_H__ */
/* hal_i2c.h — 仅包含 I2C 相关接口 */
#ifndef __HAL_I2C_H__
#define __HAL_I2C_H__

struct hal_i2c_msg {
    unsigned short addr;
    unsigned int flags;   /* 0: write, 1: read */
    unsigned char *buf;
    unsigned int len;
};

int hal_i2c_transfer(int bus_id, struct hal_i2c_msg *msgs, int num);
int hal_i2c_init(int bus_id, unsigned int freq_khz);
void hal_i2c_deinit(int bus_id);

#endif /* __HAL_I2C_H__ */

两个头文件完全独立,上层驱动按需包含。如果某个平台不支持 I2C,只需不实现 hal_i2c.c,不会影响 GPIO 驱动的编译和使用。

4.5 设计原则总结

综合以上讨论,HAL 设计应遵循以下五项核心原则:

原则 描述 实践要点
接口隔离 按外设类型和功能粒度拆分接口 每个头文件只包含一类外设的 API
单向依赖 上层依赖 HAL 接口,HAL 实现依赖硬件 禁止 HAL 接口反向依赖上层数据结构
最小完备 接口数量足够覆盖常见场景,但不冗余 每个接口函数都有明确的使用场景
错误透明 所有 HAL 函数返回标准错误码(负 errno) 不使用自定义错误枚举,统一用 -EIO、-ETIMEDOUT 等
零全局状态 HAL 实现不依赖全局变量或隐式状态 所有上下文通过参数传递,支持多实例
本章要点回顾:
  • HAL 是位于内核驱动框架与硬件平台之间的接口契约层,核心价值是解耦与可移植。
  • 分层架构让关注点分离,支持并行开发和硬件在环测试。
  • HAL 与 BSP 是互补关系:BSP 负责板级初始化,HAL 负责外设接口抽象。
  • 接口隔离原则要求按外设类型拆分头文件,避免胖接口和编译耦合。
  • 五项核心原则(接口隔离、单向依赖、最小完备、错误透明、零全局状态)是 HAL 设计的质量标尺。