# hal_mcu **Repository Path**: TanzeLin/hal_mcu ## Basic Information - **Project Name**: hal_mcu - **Description**: Design and Implementation of a Hardware Abstraction Layer (HAL) Framework for MCU - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-17 - **Last Updated**: 2026-07-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # hal_mcu 一个**跨 MCU 平台的 HAL(Hardware Abstraction Layer)抽象层**。本层以统一的 C 语言接口封装底层芯片差异,使上层驱动代码、通信协议栈及业务逻辑在异构 MCU 平台之间无需修改源码即可迁移。 - **语言**:C(`.cc` 源文件以 `extern "C"` 包裹,兼容 C++ 工程) - **许可证**:[MIT](LICENSE) - **仓库**: - Gitee: - GitHub: > **使用帮助文档**:[docs/USAGE_zh.md](docs/USAGE_zh.md) — 详细介绍各模块的变量、宏定义、枚举、结构体、回调函数类型以及全部 API 函数接口的功能、参数与返回值说明。English: [docs/USAGE_en.md](docs/USAGE_en.md) --- ## 设计原理 本层采用**基于弱符号的链接时覆盖(weak-symbol overlay)** 机制解耦接口定义与硬件实现: ``` ┌──────────────────────────────────┐ │ 上层应用 / 驱动层 / 协议栈 │ #include "hal.h" ├──────────────────────────────────┤ │ hal_mcu 公共 API(本仓库) │ __WEAK 默认实现返回 _HAL_FAIL ├──────────────────────────────────┤ │ 芯片适配层(由用户按目标平台实现)│ 强符号覆盖同名的 __WEAK 函数 ├──────────────────────────────────┤ │ 厂商 HAL / 寄存器层 │ │ (STM32 HAL, HC32, GD32 等) │ └──────────────────────────────────┘ ``` 本仓库的职责是**定义标准 API 接口与数据结构**,并为每个接口函数提供 `__WEAK` 修饰的默认实现(统一返回 `_HAL_FAIL` 或等价错误码)。用户在芯片适配层中以强符号重新定义同名函数,链接器将自动选取强符号版本覆盖弱符号默认实现。整个过程中无需修改本仓库的任何文件。 该设计具备以下优势: - **接口稳定性**:上层代码仅依赖 `hal_*` 命名空间下的标准 API,与特定芯片解耦 - **零侵入移植**:更换目标 MCU 时仅需替换适配层实现,上层代码无感知 - **增量适配**:可仅实现当前项目所需的外设子集;未实现的外设函数返回明确错误码而非产生链接错误,便于逐步完善 --- ## 目录结构 ``` hal_mcu/ ├── hal.h # 顶层聚合头文件 —— 用户仅需 #include 此文件 ├── hal_types.h # 通用类型定义、编译器属性宏、返回码约定 ├── gpio/ │ ├── hal_gpio.h # GPIO 子系统 API 声明 │ └── hal_gpio.cc # __WEAK 默认实现 ├── uart/ │ ├── hal_uart.h # UART 子系统 API 声明 │ └── hal_uart.cc ├── i2c/ │ ├── hal_i2c.h # I2C 子系统 API 声明(含主/从模式及 SMBus) │ └── hal_i2c.cc ├── spi/ │ ├── hal_spi.h # SPI 子系统 API 声明(主/从、单/双/四线、3 线半双工、软/硬 CS、CRC) │ └── hal_spi.cc ├── qspi/ │ ├── hal_qspi.h # QSPI 子系统 API 声明(面向 SPI Flash:5 阶段命令、XIP 内存映射、自动轮询) │ └── hal_qspi.cc ├── timer/ │ ├── hal_timer.h # Timer 子系统 API 声明(时基、PWM、输出比较、输入捕获、互补输出+死区、刹车、编码器、单脉冲、主从级联) │ └── hal_timer.cc ├── docs/ │ ├── USAGE_en.md # Detailed usage & API reference (English) │ └── USAGE_zh.md # 详细使用与 API 参考(中文) └── LICENSE ``` --- ## 已提供的外设子系统 | 外设 | 头文件 | 能力覆盖 | | ---- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------ | | GPIO | [`gpio/hal_gpio.h`](gpio/hal_gpio.h) | 输入 / 输出 / 模拟 / 中断 / 复用功能;内部上下拉;推挽 / 开漏输出;输出速率;软件消抖;边沿与电平中断 | | UART | [`uart/hal_uart.h`](uart/hal_uart.h) | 波特率 / 数据位 / 停止位 / 校验;硬件流控(RTS/CTS);调制解调器信号线;RS-485 DE 控制;轮询 / 中断 / DMA 传输;9 位地址模式 | | I2C | [`i2c/hal_i2c.h`](i2c/hal_i2c.h) | 主 / 从模式;任意地址宽度(uint8_t 位数:7/10 为标准,其他值为非标 IC);SCL 频率以 Hz 给出(连续值,非固定档位);SMBus(PEC、快速命令、块传输);总线死锁恢复 | | SPI | [`spi/hal_spi.h`](spi/hal_spi.h) | 主 / 从模式;SPI 模式 0–3(CPOL/CPHA);任意数据帧宽度(uint8_t 位数,由硬件校验);LSB/MSB;SCK 频率以 Hz 给出(连续值,非固定档位);单线 / 双线 / 四线半双工;3 线双向;软 / 硬 / 多 CS 片选;CRC;轮询 / 中断 / DMA;FIFO 水位;TX/RX 异步传输;CS 链式传输 | | QSPI | [`qspi/hal_qspi.h`](qspi/hal_qspi.h) | 间接 / 内存映射 XIP / 自动轮询三种模式;1 / 2 / 4 / 8 线;SDR / DDR;8 / 16 / 24 / 32 位地址;5 阶段命令(指令 / 地址 / Alt / Dummy / Data);预取 / 缓存 / Wrap;轮询匹配(AND/OR/CLR);DMA 传输 | | Timer | [`timer/hal_timer.h`](timer/hal_timer.h) | 时基(向上 / 向下 / 中心对齐 1-3);预分频 / 周期 / 重复计数;内部 / 外部 ETR / 级联 / 编码器时钟;最多 6 通道捕获比较 + 3 互补输出(CH1N-CH3N);输出比较(11 种模式);PWM(频率 / 占空比千分率 / 微秒纳秒脉宽);输入捕获(边沿 / 预分频 / 滤波 / 直接 / 间接 / TRC);死区 + 双刹车输入 + BDTR 高级控制;正交编码器接口(x2/x4 + 索引);单脉冲模式;主从级联 + TRGO / 触发选择 | 每个外设子系统均按统一的 API 分层提供三类接口: 1. **配置接口** `hal_xxx_init` / `set_*` / `get_*` —— 初始化与运行时参数逐项配置 2. **传输接口** `hal_xxx_send` / `recv` / `transfer` —— 提供阻塞版本与 `_async` 后缀的异步版本(中断 / DMA 驱动) 3. **状态接口** `hal_xxx_status` / `get_error` / `clear_error` —— 运行时状态查询与错误诊断 --- ## 快速开始 ### 1. 集成至工程 将 `hal_mcu/` 以 Git 子模块或源码复制的方式纳入工程,并在编译选项中添加以下头文件搜索路径: ``` -I path/to/hal_mcu -I path/to/hal_mcu/gpio -I path/to/hal_mcu/uart -I path/to/hal_mcu/i2c -I path/to/hal_mcu/spi -I path/to/hal_mcu/qspi -I path/to/hal_mcu/timer ``` 编译 `gpio/hal_gpio.cc`、`uart/hal_uart.cc`、`i2c/hal_i2c.cc`、`spi/hal_spi.cc`、`qspi/hal_qspi.cc` 与 `timer/hal_timer.cc`。上述源文件中所有函数均已用 `__WEAK` 修饰,将在链接阶段被适配层中的强符号版本覆盖。 CMake 集成示例: ```cmake add_library(hal_mcu STATIC gpio/hal_gpio.cc uart/hal_uart.cc i2c/hal_i2c.cc spi/hal_spi.cc qspi/hal_qspi.cc timer/hal_timer.cc ) target_include_directories(hal_mcu PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/gpio ${CMAKE_CURRENT_SOURCE_DIR}/uart ${CMAKE_CURRENT_SOURCE_DIR}/i2c ${CMAKE_CURRENT_SOURCE_DIR}/spi ${CMAKE_CURRENT_SOURCE_DIR}/qspi ${CMAKE_CURRENT_SOURCE_DIR}/timer ) ``` > **编译器兼容性说明**:`__WEAK` 由 `__attribute__((weak))` 展开,该属性受 GCC、Clang 及 armcc (Arm Compiler 5/6) 等主流工具链原生支持。若使用 IAR Embedded Workbench 或其他非 GCC 系编译器,请在 [`hal_types.h`](hal_types.h) 中按目标工具链语法重新定义 `__WEAK` 宏。 ### 2. 编写芯片适配层 以在 STM32F4 平台上实现 GPIO 输出为例,在适配层源文件 `port_stm32_gpio.c` 中定义强符号函数: ```c #include "hal.h" #include "stm32f4xx_hal.h" int8_t hal_gpio_write(GPIO_PIN pin, GPIO_VALUE value) { GPIO_TypeDef *port = /* pin >> 8 映射至 GPIOA / GPIOB / … */; uint16_t mask = 1u << (pin & 0xFF); HAL_GPIO_WritePin(port, mask, value ? GPIO_PIN_SET : GPIO_PIN_RESET); return _HAL_OK; } ``` 链接器将自动选取上述强符号覆盖 hal_mcu 中对应的 `__WEAK` 版本。未实现的 API 保持返回 `_HAL_FAIL`,可依据项目需要逐步补齐。 > **设计约束**:`hal_gpio_read` 的函数签名返回 `int8_t`(错误码),其输出引脚电平值通过额外参数指针返回,具体形式参见 [`gpio/hal_gpio.h`](gpio/hal_gpio.h) 中的声明。代码示例中已注意到此设计并将在后续版本中统一。 ### 3. 上层代码调用示例 ```c #include "hal.h" int main(void) { /* ---- GPIO:PA5 推挽输出,驱动 LED ---- */ GPIO_CFG led_cfg = { .mode = GPIO_MODE_OUTPUT, .pull = GPIO_PULL_UP, .pp_od = GPIO_PUSHPULL, .speed = GPIO_SPEED_HIGH, .af = GPIO_AF_INVALID, }; GPIO_PIN led = GPIO_PIN_(GPIO_PORT_A, 5); hal_gpio_init(led, &led_cfg); hal_gpio_write(led, GPIO_VAL_HIGH); /* ---- UART1:115200-8-N-1,阻塞发送 ---- */ UART_CFG uart_cfg = { .txd_pin = GPIO_PIN_(GPIO_PORT_A, 9), .rxd_pin = GPIO_PIN_(GPIO_PORT_A, 10), .baudrate = 115200, .data_bits = UART_DATA_BITS_8, .stop_bits = UART_STOP_BITS_1, .parity = UART_PARITY_NONE, .flow_ctrl = UART_FLOW_CTRL_NONE, .tx_mode = UART_MODE_POLLING, .rx_mode = UART_MODE_INTERRUPT, }; hal_uart_init(1, &uart_cfg); hal_uart_send_buffer(1, (uint8_t *)"hello\r\n", 7); /* ---- I2C1:主机模式,7 位地址 0x50,读单字节寄存器 0x00 ---- */ I2C_CFG i2c_cfg = { .role = I2C_ROLE_MASTER, .addr_mode = 7, /* 标准 7 位寻址 */ .speed_hz = 400000, /* 400 kHz Fast 模式 */ .pins = { .sda_pin = GPIO_PIN_(GPIO_PORT_B, 7), .scl_pin = GPIO_PIN_(GPIO_PORT_B, 6), }, }; hal_i2c_init(1, &i2c_cfg); uint8_t val; hal_i2c_master_read_reg(1, 0x50, 7, 0x00, &val, 1, 100); /* ---- SPI1:主机模式,SPI 模式 0,8 位帧,4 MHz,软 CS,全双工读写 ---- */ SPI_CFG spi_cfg = { .role = SPI_ROLE_MASTER, .mode = SPI_MODE_0, .bit_order = SPI_BIT_ORDER_MSB, .data_size = 8, /* 8 位帧 */ .lines = SPI_LINES_SINGLE, .ss_mode = SPI_SS_SOFT, .speed_hz = 5000000, /* 5 MHz */ .tx_mode = SPI_XFER_MODE_POLLING, .rx_mode = SPI_XFER_MODE_POLLING, .pins = { .sck_pin = GPIO_PIN_(GPIO_PORT_A, 5), .mosi_pin = GPIO_PIN_(GPIO_PORT_A, 7), .miso_pin = GPIO_PIN_(GPIO_PORT_A, 6), .cs_pin = GPIO_PIN_(GPIO_PORT_A, 4), }, }; hal_spi_init(1, &spi_cfg); uint8_t spi_tx[3] = {0x9F, 0x00, 0x00}; /* 假设的读 ID 命令 */ uint8_t spi_rx[3] = {0}; hal_spi_select(1); SPI_MSG spi_msg = { .tx_buf = spi_tx, .tx_len = 3, .rx_buf = spi_rx, .rx_len = 3, .lines = SPI_LINES_SINGLE, .dummy_byte = 0xFF, .keep_cs_active = false, }; hal_spi_master_transfer(1, &spi_msg, 100); hal_spi_deselect(1); /* ---- QSPI1:间接模式,4 线,读 SPI Flash JEDEC ID(指令 0x9F) ---- */ QSPI_CFG qspi_cfg = { .mode = QSPI_MODE_INDIRECT, .lines = QSPI_LINES_4, .clock_mode = QSPI_CLOCK_MODE_0, .ddr = QSPI_DDR_SDR, .clock_hz = 50000000, .xfer_mode = QSPI_XFER_MODE_POLLING, .pins = { .sck_pin = GPIO_PIN_(GPIO_PORT_B, 2), .cs_pin = GPIO_PIN_(GPIO_PORT_B, 6), .io0_pin = GPIO_PIN_(GPIO_PORT_B, 1), .io1_pin = GPIO_PIN_(GPIO_PORT_B, 0), .io2_pin = GPIO_PIN_(GPIO_PORT_A, 7), .io3_pin = GPIO_PIN_(GPIO_PORT_A, 6), }, }; hal_qspi_init(1, &qspi_cfg); uint8_t jedec_id[3] = {0}; QSPI_CMD id_cmd = { .instruction = 0x9F, .inst_lines = QSPI_LINES_1, .addr_width = QSPI_ADDR_WIDTH_INVALID, .alt_len = 0, .dummy_cycles = 0, .rx_buf = jedec_id, .data_len = sizeof(jedec_id), .data_lines = QSPI_LINES_1, .keep_cs_active = false, }; hal_qspi_send_cmd(1, &id_cmd, 100); } ``` --- ## 返回码约定 所有 `hal_*` 系列函数(部分状态查询函数除外)均返回 `int8_t` 类型的状态码,定义于 [`hal_types.h`](hal_types.h): | 符号常量 | 数值 | 语义 | | ----------------------------- | ---- | ------------------------------------ | | `_HAL_OK` | 0 | 操作成功完成 | | `_HAL_FAIL` | -1 | 通用失败(`__WEAK` 默认实现返回值) | | `_HAL_ERR_NOT_SUPPORTED` | -2 | 当前硬件或本实现不支持该操作 | | `_HAL_ERR_NOT_FOUND` | -3 | 目标资源不存在 | | `_HAL_ERR_NOT_ALLOWED` | -4 | 当前状态不允许执行该操作 | | `_HAL_ERR_NOT_FINISHED` | -5 | 操作未完成(异步传输仍在进行中) | | `_HAL_ERR_NOT_MEM` | -6 | 内存不足 | | `_HAL_ERR_INVALID_ARG` | -7 | 参数非法 | | `_HAL_ERR_INVALID_STATE` | -8 | 状态非法 | | `_HAL_ERR_INVALID_SIZE` | -9 | 数据大小非法 | | `_HAL_ERR_OVERFLOW` | -10 | 缓冲区或计数器溢出 | | `_HAL_ERR_TIMEOUT` | -11 | 操作超时 | | `_HAL_ERR_BUSY` | -12 | 外设正忙,无法响应本次请求 | | `_HAL_ERR_OP` | -13 | 外设操作执行失败 | | `_HAL_ERR_REPEAT_OP` | -14 | 重复操作(同一操作已在执行中) | 外设级的细粒度错误(如 UART 帧错误、校验错误、I2C ACK/NACK 失败等)以位掩码形式通过各外设头文件中定义的 `XXX_ERR_*` 常量组合返回,并通过 `hal_xxx_get_error()` 接口获取。 --- ## 类型定义与编译器抽象 [`hal_types.h`](hal_types.h) 集中提供跨平台嵌入式开发中常用的基础定义: | 类别 | 内容 | | ------------------ | ---------------------------------------------------------------------------------- | | 定长整型别名 | `u8` / `s8` / `u16` / `s16` / `u32` / `s32` / `u64` / `s64`,以及大写 / 带 `_T` 后缀的等价别名 | | 传统宽度别名 | `byte` / `word` / `dword` / `ascii` | | 编译器属性宏 | `__WEAK` / `__INLINE` / `__ALIGN(x)` / `__PACKED` / `__UNUSED` / `__UNUSED_ARG(x)` | | 通用计算宏 | `_MAX` / `_MIN` / `_ABS` / `_FABS` | 所有宏均以 `#ifndef` 守护,确保在目标平台已有等价定义时不会引发重复定义冲突。 --- ## 路线图 已完成(API 定义 + `__WEAK` 默认骨架): - [x] GPIO - [x] UART - [x] I2C - [x] SPI - [x] QSPI - [x] Timer 规划中: - [ ] PWM(从 Timer 拆出,用于带独立 PWM 外设的 MCU,可选) - [ ] ADC / DAC - [ ] Flash / EEPROM - [ ] RTC - [ ] CAN - [ ] USB - [ ] SDIO / SDMMC - [ ] Ethernet MAC --- ## 贡献指南 欢迎通过 Issue 或 Pull Request 参与贡献。新增外设子系统时请遵循以下约定: 1. 每个外设占用一个独立子目录(如 `spi/`、`timer/`) 2. 头文件按以下顺序组织:`XXX_ID` 类型与范围宏 → 枚举定义(模式、速率、事件等)→ 位掩码错误码 → 回调函数指针 typedef → `XXX_CFG` 配置结构体,最后是 API 函数声明 3. 提供 `hal_xxx_init` / `hal_xxx_close` / `hal_xxx_get_config` 标准三件套 4. 每个可独立配置的属性均提供 `set_*` / `get_*` 读写对 5. 阻塞版本与异步版本函数命名一致,异步版本追加 `_async` 后缀 6. `.cc` 源文件中所有函数以 `__WEAK` 修饰,返回 `_HAL_FAIL` 7. 每个源文件首部包含 SPDX-License-Identifier 声明与 Copyright 头部注释 --- ## 许可证 本项目依据 MIT 许可证分发,详见 [LICENSE](LICENSE)。 Copyright (c) 2026 tanze.L <2534793694@qq.com>