嵌入式系统开发中函数错误码的返回实现方案该如何选择?
结论
方案1是当前场景下的最优选择,也是工业界通用HAL层的主流设计规范
各方案对比
方案1的优势
- 符合嵌入式开发的普遍心智习惯:ST HAL、NXP MCUXpresso、瑞萨FSP等主流厂商的外设读写接口,全部采用「返回值传错误码,输出数据放指针入参」的设计,开发者不需要额外适配就能上手,基本不会用错。
- 错误处理逻辑严谨:调用时必须先判断返回值是否为
I2C_ERR_OK,确认成功后再读取data指向的内容,从编码逻辑上避免了错误场景下误用无效数据的问题,示例调用代码如下:
uint8_t recv_data; i2c_err_t err = I2C_read(&hi2c1, TARGET_DEV_ADDR, TARGET_REG_ADDR, &recv_data); if (err != I2C_ERR_OK) { // 按需处理NACK、总线超时、总线忙等错误 return err; } // 正常使用recv_data
- 扩展性极强:后续如果要扩展读多字节的
I2C_read_bytes接口,仅需要新增一个长度入参即可,接口风格和单字节读取完全统一,不会出现设计分裂的问题。
方案2的缺陷
- 错误容易被忽略:很多开发者会直接使用函数返回的uint8_t值,忘记检查
err指针指向的错误码,错误场景下拿到的无效数据会引发后续逻辑异常,排查成本极高。 - 扩展性差:如果后续要实现读多字节的接口,无法复用「返回值传数据」的设计,会导致整个I2C模块的接口风格不统一。
- 额外增加边界处理逻辑:你需要在函数内部额外判断
err指针是否为NULL,避免用户传空指针触发崩溃,增加了不必要的代码分支。
原始int32_t返回值方案的隐患
虽然该方案逻辑上可以跑,但存在两个致命问题:
- 容易出现截断错误:如果开发者没注意返回值类型,直接把返回值赋值给uint8_t变量,负值错误码会被截断为0xFF,和正常读取到的0xFF字节完全无法区分,排查难度极大。
- 扩展性不足:后续如果要实现读16位寄存器的接口,该设计直接失效,需要整体重构。
其他实现思路
你提到的结构体封装方案确实属于过度设计,单字节和多字节场景都不需要用到,反而会增加调用复杂度。如果需要兼顾易用性和调试需求,可以额外加一个可选的I2C_get_last_err()辅助接口,给需要细粒度查错的场景使用,核心接口还是保留方案1的设计即可。
内容的提问来源于stack exchange,提问作者moonraccoon
相关产品推荐
相关产品推荐

