API REFERENCE / CONVENTIONS

API 约定

本文介绍 SDK 应用编程接口(API)中常见的约定和假设,包括接口构成、分层依赖、命名与参数、错误处理、配置结构体、私有接口以及 API 稳定性。阅读各模块 API 参考前,建议先了解这些约定,以便正确理解接口的分层位置、调用语义和兼容性范围。

OVERVIEW

接口构成

SDK 提供以下几类编程接口:

  • 各模块公共头文件中声明的 C 函数、结构体、枚举、类型定义和宏,按 LL / HAL / middleware 分层组织,对外头文件集中在 code/include/。

  • 构建系统接口:imde 命令、CMake 函数、Kconfig 选项,详见「SDK文档 / SDK编译」。

  • 主机工具及其命令行参数,集中在 tools/ 目录。

SDK 由多个模块组成,其中既有为芯片专门编写的代码,也有第三方组件(位于 code/third_party/)。部分第三方组件由 SDK 提供包装接口,部分直接呈现底层库的原始 API,使用前请确认所用接口属于哪一类。

LAYERING

分层与依赖方向

SDK 逻辑分层自下而上为 ll → hal → middleware → app,依赖方向只能向下,禁止反向依赖和跨层绕过:

层级 面向 核心约束
ll 寄存器、外设实例、中断、DMA 不含业务语义;不依赖 middleware;默认不动态分配内存。
hal 设备能力(UART、I2C、SPI、Flash、Timer) 不承载业务流程;可依赖 ll 和通用 core/utils;对外不暴露寄存器细节。
middleware 能力与流程编排(升级、日志、存储) 不直接访问寄存器;所有硬件访问必须经过 hal。

应用和上层业务不应绕过 hal 直接调用 ll;确需底层操作时,应先评估是否应在 hal 中补充能力接口。

NAMING

命名与参数

公共函数按 命名,例如 ll_gpio_init、hal_uart_write、mid_upgrade_start。常用动词模板:

层级 典型接口
LLinit / deinit / enable / disable / set_xxx / get_xxx / irq_handler
HALopen / close / read / write / ioctl / register_callback
Middlewareinit / start / stop / execute / get_state / set_xxx

类型统一使用小写蛇形加后缀:_t(类型)、_config_t(配置)、_handle_t(句柄)、_event_t(事件)、_state_t(状态)。宏和枚举值使用全大写下划线并带层级或模块前缀:LL 层用 LL_,HAL 层用 HAL_,Middleware 和 SDK 公共基础宏用 IM_。带物理或时间单位的参数必须在名称中体现单位,如 timeout_ms、clock_hz、voltage_mv。

参数顺序统一为:句柄或实例 → 输入数据 → 输入长度 → 输出参数 → 超时或选项。输出参数使用 _out 后缀:

error_code_t hal_flash_read(hal_flash_handle_t *handle,
                            uint32 address,
                            uint8 *buffer_out,
                            size_t buffer_size,
                            size_t *read_len_out);

ERRORS

错误处理

可能失败的操作型公共 API 优先返回 error_code_t,错误码统一定义在 error_code.h,命名使用 ERR_ 前缀,参数错误、超时、忙、未初始化、IO 错误等均有标准定义。不会发生错误的查询、判断、转换接口,或返回值本身具有明确业务语义的接口,可以返回 bool、数值、枚举或指针:

bool hal_gpio_is_pin_valid(const gpio_regs_t *gpiox, uint32 pin_mask);
uint32 ll_gpio_read(uint32 base);
error_code_t hal_gpio_read_pin(const gpio_regs_t *gpiox,
                               uint32 pin_mask,
                               hal_gpio_pin_state_t *pin_state_out);

公共 API 必须校验关键输入参数,校验顺序统一为空指针、长度、状态。断言只用于发现开发期错误,可恢复错误必须通过返回值处理,不允许用断言代替对外接口的参数检查。

CONFIG

配置结构体

重要:为确保应用程序与未来 SDK 版本的兼容性,请正确初始化配置结构体的所有字段。

初始化和配置函数(通常以 _init、_config、_open 命名)需要一个指向配置结构体的指针作为参数。初始化函数不会保存该指针,因此在栈上分配结构体是安全的。推荐使用 C99 指定初始化器,将关心的字段显式赋值,其余字段初始化为零:

const hal_gpio_config_t gpio_config = {
    .gpiox = gpioa,
    .pin_mask = GPIO_PIN_MASK_ALL,
    .default_state = HAL_GPIO_PIN_RESET,
    /* 其余字段(若有)初始化为零 */
};

error_code_t ret = hal_gpio_init(&gpio_config);

错误示例:只给部分字段赋值而直接使用未初始化的局部结构体,未赋值字段的内容是不确定的。C++ 同样支持指定初始化器,但初始化器必须遵循字段声明顺序;若需兼容更早的 C++ 标准,可以先整体清零再逐个赋值:

hal_uart_config_t uart_config = {};   /* 所有字段初始化为零 */
uart_config.baudrate = 115200;
uart_config.timeout_ms = 1000;

当模块提供默认配置初始化函数或宏时,优先使用它,再按需覆盖个别字段。

SEMANTICS

调用语义与生命周期

  • 阻塞接口必须显式提供 timeout_ms 或注明「永久阻塞」语义;异步接口命名需体现异步性质,如 _start、_async。

  • 回调统一通过 user_ctx 传入用户上下文;中断回调、线程回调的运行上下文必须在接口注释中标明。

  • 资源生命周期动作统一使用 init/deinit、open/close、start/stop、create/destroy,同一类资源只选一组主语义。

  • LL 和 HAL 默认禁止动态内存分配;middleware 若使用动态内存,必须明确申请与释放的所有权。

PRIVATE

私有接口与内部实现

SDK 中部分头文件仅限内部使用,不支持在应用程序中调用。此类头文件命名为 *_internal.h 或位于 internal/ 目录;寄存器映射结构体(struct)属于 LL 或 SoC 寄存器头文件,HAL 公共头文件不得直接展开其字段定义。

私有接口可能在次要或补丁版本之间以不兼容方式被删除或更改。应用程序不应依赖:

  • *_internal.h 与 internal/ 下的任何声明。

  • HAL 公共接口中的寄存器位、寄存器偏移、IRQ 号、DMA 通道等底层细节。

  • 通过 extern 暴露的全局变量或寄存器实例;跨文件访问状态必须使用 get_xxx()、set_xxx() 接口。

EXAMPLES

示例代码

code/examples/ 提供演示各模块用法的示例工程。示例中的通用辅助组件是为减少重复而抽取的,不属于 SDK API 范畴,不同版本之间可能存在显著变化。不建议在自定义项目中直接引用这些组件;基于示例开始新项目时,应将所需公共组件复制出来并视为项目的一部分。示例组件针对演示场景编写,可能不包括生产应用所需的完整错误处理,使用前请阅读代码确认适用性。

STABILITY

API 稳定性

对外暴露的公共头文件、类型、函数名一经发布,即遵守源代码级别兼容性:应用程序无需修改即可在新版本 SDK 上重新编译。扩展优先新增接口或新增结构体字段,避免修改旧接口语义。

SDK 不保证版本间的二进制兼容性。以下更改可以保持源代码兼容,但基于旧版本预编译的库可能失效:

  • 更改枚举成员的数值。

  • 新增结构体成员或调整成员顺序。

  • 用具有相同签名的 static inline 函数替换 extern 函数,或反之。

以下内容不在兼容性保证范围内:私有接口与内部实现、示例代码及其公共组件、明确标记为 beta/preview/experimental 的功能、为修复安全问题或替换不安全默认行为所做的更改,以及 Kconfig 选项在菜单中的位置和示例工程的位置与名称。