外观
STM32-HAL库代码编写规范(仅供参考)
一、文件结构规范
文件命名
- 驱动文件采用小写字母+下划线命名,格式为:[外设名]_driver.h头文件)和[外设名]_driver.c(源文件)。 例:aht20_driver.h、hc_sr04_driver.c。
- 若驱动包含多个相关模块,可增加子模块名,例:buzzer_timer_driver.c。
文件内容
- 头文件(.h)
- 包含宏定义(引脚、地址、命令字等)、结构体声明、枚举类型、函数声明。
- 使用 Include Guard 防止重复包含(#ifndef XXX_H + #define XXX_H + #endif)。
- 只声明对外接口,不包含实现细节。
- 源文件(.c)
- 实现头文件声明的函数,包含外设初始化、数据读写、状态处理等逻辑。
- 内部函数(仅在.c 中使用)需用 static 修饰,避免全局命名冲突
二、代码风格规范
缩进与格式
- 使用 4 个空格 缩进,不使用制表符(Tab)。
- 函数体、条件语句(if/else)、循环语句(for/while)的代码块需用 {} 包裹,即使只有一行。 例:
C
if (status != HAL_OK) {
return HAL_ERROR; // 强制换行并缩进
}
1
2
3
4
2
3
4
命名规则
- 宏定义:全大写 + 下划线,例:AHT20_ADDR、TRIG_PIN。
- 函数名:采用 驼峰命名法,前缀为外设名,动词 + 名词结构,例:AHT20_Init()、Buzzer_SetFreq()。
- 结构体 / 枚举:首字母大写 + 驼峰,例:typedef struct { ... } Ultrasonic_HandleTypeDef;。
- 变量名:小写 + 下划线,例:uint32_t echo_time;、float temp_value;。
注释规范
- 每个函数需添加 Doxygen 风格注释,说明功能、参数、返回值、注意事项。 例:
C
/**
* @brief 初始化AHT20传感器
* @param hi2c: I2C句柄指针(输入)
* @retval HAL_StatusTypeDef: HAL_OK(成功)/ HAL_ERROR(失败)
* @note 初始化前需确保I2C外设已正确配置
*/
HAL_StatusTypeDef AHT20_Init(I2C_HandleTypeDef *hi2c);
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- 复杂逻辑(如协议解析、校准算法)需添加行内注释,说明设计思路。
- 宏定义和枚举需注释其含义,例:#define AHT20_MEASURE_CMD 0xAC // 测量命令字。
三、驱动功能设计规范
初始化函数(Init)
- 函数名格式:[外设名]_Init(...),负责外设硬件初始化(引脚、时钟、通信接口)。
- 必须返回 HAL_StatusTypeDef 类型,明确初始化成功 / 失败状态。
- 输入参数需包含依赖的 HAL 句柄(如 I2C_HandleTypeDef*、TIM_HandleTypeDef*),避免硬编码外设实例。
C
HAL_StatusTypeDef Buzzer_Init(TIM_HandleTypeDef *htim, uint32_t channel);
1
2
2
数据读写函数
- 读数据函数:[外设名]_Read[数据类型](...),例:AHT20_ReadTempHum(...)。
- 写数据函数:[外设名]_Write[数据类型](...),例:Buzzer_SetFreq(...)。
- 数据类型优先使用标准 C 类型(uint8_t/float等)或自定义结构体,避免模糊的void*。
- 必须处理通信错误(如 I2C 超时、SPI 错误),返回错误码而非忽略
状态与控制函数
- 状态查询:[外设名]_CheckStatus(...),返回外设当前状态(就绪 / 忙碌 / 错误)。
- 复位 / 重启:[外设名]_Reset(...),支持外设软复位,提高容错性。
- 使能 / 禁用:[外设名]_Enable(...)/[外设名]_Disable(...),控制外设电源或功能开关。
错误处理
- 所有 HAL 库函数的返回值必须检查(如 HAL_I2C_Master_Transmit 的返回值)。
- 超时场景需定义合理的超时时间(避免无限等待),例:#define US_TIMEOUT_US 30000 // 超声波超时时间(30ms)。
- 错误状态需通过返回值明确告知上层(如 -1 表示失败,0 表示成功),避免上层猜解。
四、可移植性规范
硬件无关性
- 引脚、时钟等硬件相关配置需通过宏定义实现,集中放在头文件顶部,便于修改。 例:
C
// 头文件中定义硬件引脚
#define TRIG_PIN GPIO_PIN_0
#define TRIG_GPIO_PORT GPIOC
#define ECHO_PIN GPIO_PIN_1
#define ECHO_GPIO_PORT GPIOB
1
2
3
4
5
6
2
3
4
5
6
- 避免在代码中直接使用寄存器操作(如 GPIOC->ODR),必须通过 HAL 库函数(HAL_GPIO_WritePin)实现。
外设接口适配
- 若驱动支持多种通信方式(如 I2C/SPI),需通过条件编译区分,例:
C
#ifdef USE_I2C
HAL_I2C_Master_Transmit(&hi2c, addr, data, len, timeout);
#elif defined(USE_SPI)
HAL_SPI_Transmit(&hspi, data, len, timeout);
#endif
1
2
3
4
5
6
2
3
4
5
6
时钟与计时
- 时间相关操作需使用 HAL 库提供的函数:
- 毫秒级延时:HAL_Delay()
- 高精度计时:DWT->CYCCNT(需初始化 DWT)
- 避免使用 for 循环延时(依赖主频,移植时需重新计算)。
性能与安全性规范
资源占用
- 减少动态内存分配(malloc/free),优先使用静态数组或全局变量(MCU 内存有限)。
- 中断服务程序(ISR)中调用的驱动函数需保证无阻塞、执行时间短(如仅设置标志位,不做复杂计算)。
重入性
- 若驱动可能被多任务(RTOS)或中断调用,需通过互斥锁(osMutex)保护共享资源。
- 全局变量需谨慎使用,优先通过结构体封装为句柄(HandleTypeDef),实现多实例支持。 例:
C
typedef struct {
I2C_HandleTypeDef *hi2c; // 关联的I2C句柄
uint8_t dev_addr; // 设备地址
float calib_offset; // 校准偏移量
} AHT20_HandleTypeDef;
1
2
3
4
5
6
2
3
4
5
6
边界检查
- 对输入参数进行合法性校验,例:
C
void Seg7_SetDigit(uint8_t digit, uint8_t pos) {
if (digit > 9 || pos > 3) { // 检查数字(0-9)和位置(0-3)合法性
return;
}
// 正常逻辑
}
1
2
3
4
5
6
7
2
3
4
5
6
7
测试与文档规范
测试用例
- 驱动需包含基础测试函数(如 [外设名]_Test()),验证初始化、读写、异常处理等功能。
- 测试需覆盖正常场景和异常场景(如通信超时、无效参数)。
版本与变更记录
- 头文件顶部需注明驱动版本、作者、修改记录,例:
C
/**
* @file aht20_driver.h
* @version V1.0.0
* @date 2024-07-28
* @brief AHT20温湿度传感器驱动
* @note 变更记录:
* V1.0.0 - 初始版本,支持基本读写
*/
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9