## 1. 文档概述

### 1.1 目的
本文档旨在统一嵌入式C语言代码的编写风格，提高代码的可读性、可维护性、可移植性和可靠性，降低团队协作成本，减少代码缺陷。
### 1.2 适用范围
本规范适用于所有基于C语言的嵌入式软件开发项目，包括但不限于51单片机、STM32、ARM、DSP等平台。
### 1.3 修订记录
| 版本号  | 修订日期       | 修订内容   | 修订人 |
| ---- | ---------- | ------ | --- |
| V1.0 | 2026-05-11 | 创建初始版本 | 王建锋 |
## 2. 文件结构规范

### 2.1 头文件(.h)结构
```c
#ifndef __MODULE_NAME_H
#define __MODULE_NAME_H

/*
 * 模块名称：模块英文名称
 * 模块功能：简要描述模块的主要功能
 * 适用平台：列出支持的硬件平台
 * 作者：作者姓名
 * 创建日期：YYYY-MM-DD
 * 修改记录：
 *     YYYY-MM-DD  修改人  修改内容说明
 */

#ifdef __cplusplus
extern "C" {
#endif

/* 头文件包含区 - 仅包含本模块必需的头文件 */
#include "stdint.h"

/* 宏定义区 */
#define MODULE_NAME_CONSTANT    100

/* 类型定义区 */
typedef enum {
    MODULE_NAME_STATUS_OK = 0,
    MODULE_NAME_STATUS_ERROR
} module_name_status_t;

/* 函数声明区 */
void module_name_init(void);

#ifdef __cplusplus
}
#endif

#endif /* __MODULE_NAME_H */
```
### 2.2 源文件(.c)结构
```c
/*
 * 模块名称：模块英文名称
 * 模块功能：简要描述模块的主要功能
 * 适用平台：列出支持的硬件平台
 * 作者：作者姓名
 * 创建日期：YYYY-MM-DD
 * 修改记录：
 *     YYYY-MM-DD  修改人  修改内容说明
 */

/* 头文件包含区 - 先包含系统头文件，再包含自定义头文件 */
#include "module_name.h"

/* 私有宏定义区 */
#define MODULE_NAME_PRIVATE_CONSTANT    200

/* 私有类型定义区 */
typedef struct {
    uint8_t data;
} module_name_private_t;

/* 全局变量定义区 - 尽量避免使用全局变量 */
uint8_t g_module_name_global_var = 0;

/* 静态变量定义区 */
static module_name_private_t s_module_name_private_var;

/* 私有函数声明区 */
static void module_name_private_function(void);

/* 函数定义区 - 先写公共函数，再写私有函数 */
void module_name_init(void) {
    /* 函数实现 */
}

static void module_name_private_function(void) {
    /* 函数实现 */
}
```
### 2.3 头文件包含规则
1. 头文件必须包含头文件保护宏，格式为`__MODULE_NAME_H`
2. 头文件中只包含本模块接口必需的其他头文件
3. 源文件中先包含系统头文件，再包含自定义头文件
4. 禁止在头文件中定义变量和函数体
5. 禁止使用相对路径包含头文件
## 3. 命名规范
### 3.1 通用命名原则
1. 所有名称必须使用英文，禁止使用拼音和中文
2. 名称必须准确反映其实际含义，做到"见名知意"
3. 名称长度适中，避免过长或过短
4. 禁止使用单个字母作为变量名（循环变量i、j、k除外）
5. 禁止使用关键字和保留字作为名称
### 3.2 变量命名
1. 采用**小写字母+下划线**命名法
2. 全局变量以`g_`前缀开头
3. 静态变量以`s_`前缀开头
4. 指针变量以`p_`前缀开头
5. 数组变量以`a_`前缀开头
6. 布尔变量以`is_`、`has_`、`can_`等前缀开头
**示例：**
```c
uint8_t g_system_status;        /* 全局系统状态变量 */
static uint16_t s_timer_count;  /* 静态定时器计数变量 */
uint8_t *p_data_buffer;         /* 数据缓冲区指针 */
uint16_t a_adc_value[10];       /* ADC采样值数组 */
bool is_button_pressed;         /* 按钮是否按下标志 */
```
### 3.3 函数命名
1. 采用**小写字母+下划线**命名法
2. 公共函数以**模块名**作为前缀
3. 私有函数以**模块名+private**作为前缀
4. 函数名应为"动词+名词"结构，明确表示函数功能
**示例：**
```c
/* 公共函数 */
void uart_init(uint32_t baud_rate);
uint8_t uart_send_byte(uint8_t data);

/* 私有函数 */
static void uart_private_handle_interrupt(void);
```
### 3.4 宏和常量命名
1. 采用**大写字母+下划线**命名法
2. 以**模块名**作为前缀
3. 常量优先使用`const`定义，而非`#define`
**示例：**
```c
#define UART_BAUD_RATE_9600    9600
#define UART_BUFFER_SIZE       128

const uint8_t UART_DEFAULT_DATA_BITS = 8;
```
### 3.5 类型定义命名
1. 采用**小写字母+下划线**命名法
2. 以`_t`作为后缀
3. 枚举类型成员以**模块名**作为前缀
**示例：**
```c
typedef enum {
    UART_STATUS_OK = 0,
    UART_STATUS_ERROR,
    UART_STATUS_TIMEOUT
} uart_status_t;

typedef struct {
    uint8_t data_bits;
    uint8_t stop_bits;
    uint32_t baud_rate;
} uart_config_t;
```
## 4. 格式与排版规范

### 4.1 缩进
1. 使用**4个空格**进行缩进，禁止使用Tab键
2. 所有包含关系的内容必须缩进
3. 同一级别的代码保持相同的缩进级别
### 4.2 空格使用
1. 所有赋值语句、比较语句、算术运算符前后必须加空格
2. 函数参数列表中，逗号后面必须加空格
3. 关键字后面必须加空格
4. 括号内部两侧不加空格
5. 一元运算符前后不加空格
**正确示例：**
```c
int a = 10;
if (a > 5) {
    b = a + 3;
}
for (i = 0; i < 10; i++) {
    c[i] = 0;
}
```
**错误示例：**
```c
int a=10;
if(a>5){
    b=a+3;
}
for(i=0;i<10;i++){
    c[i]=0;
}
```
### 4.3 换行与空行
1. 每行代码长度不超过80个字符
2. 函数之间必须空一行
3. 逻辑上相关的代码块之间可以空一行
4. 函数内变量声明区和代码执行区之间必须空一行
5. 长表达式应在运算符处换行，新行与运算符对齐
**示例：**
```c
int calculate_sum(int a, int b, int c, int d) {
    int sum;
    
    sum = a + b 
        + c 
        + d;
    
    return sum;
}
```
### 4.4 大括号使用
1. **所有包含关系必须加大括号**，即使只有一条语句或为空
2. 左大括号`{`与前面的语句在同一行，前面加一个空格
3. 右大括号`}`单独占一行，与对应的左大括号缩进级别相同
4. 空函数体的大括号也必须单独占一行
**正确示例：**
```c
if (condition) {
    do_something();
}

while (1) {
}
```
**错误示例：**
```c
if (condition)
    do_something();

while (1) ;
```
## 5. 注释规范

### 5.1 通用注释原则
1. 注释必须清晰、准确、简洁，与代码保持一致
2. 解释性注释使用`/* */`，调试性注释使用`//`
3. 注释应解释"为什么这么做"，而不是"做了什么"
4. 代码修改时，必须同步修改相关注释
5. 禁止注释掉的代码提交到版本库
### 5.2 文件头注释
每个文件开头必须包含文件头注释，格式见2.1和2.2节。
### 5.3 函数注释
所有函数（包括私有函数）必须包含完整的函数注释，格式如下：
```c
/*
 * 函数功能：详细描述函数的功能
 * 入口参数：param1 - 参数1说明  类型  取值范围
 *           param2 - 参数2说明  类型  取值范围
 * 出口参数：param3 - 参数3说明  类型  取值范围
 * 返回值：返回值说明  类型  取值范围
 * 限定条件：函数使用的前提条件和限制
 * 函数说明：1. 函数的详细说明
 *           2. 注意事项
 *           3. 其他需要说明的内容
 */
```
**示例：**
```c
/*
 * 函数功能：毫秒级软件延时函数
 * 入口参数：ms - 需要延时的毫秒数  unsigned int  0 - 65535
 * 限定条件：0 <= ms <= 65535
 * 函数说明：1. 采用空指令循环方式实现延时，会阻塞CPU运行
 *           2. 延时精度依赖系统时钟，默认适配12MHz时钟（12T模式）
 *           3. 系统时钟改变时，需重新调整内层循环次数
 *           4. 当ms为0时，函数立即返回
 */
void delay_ms(unsigned int ms) {
    unsigned int i;
    unsigned int j;
    
    for (i = 0; i < ms; i++) {
        for (j = 0; j < 123; j++) {
        }
    }
}
```
### 5.4 变量注释
1. 全局变量和静态变量必须添加注释
2. 重要的局部变量应添加注释
3. 注释可以写在变量定义的同一行或上一行
**示例：**
```c
/* 系统运行时间，单位：毫秒 */
uint32_t g_system_time = 0;

static uint8_t s_uart_rx_buffer[UART_BUFFER_SIZE];  /* UART接收缓冲区 */
```
### 5.5 代码行注释
1. **关键逻辑代码每一行都要添加注释**
2. 复杂的算法和逻辑必须添加详细注释
3. 注释应单独占一行，与被注释代码缩进级别相同
**示例：**
```c
/* 计算CRC校验值 */
uint16_t crc_calculate(uint8_t *data, uint16_t length)
{
    uint16_t crc = 0xFFFF;
    uint16_t i;
    uint16_t j;
    
    /* 遍历所有数据字节 */
    for (i = 0; i < length; i++) {
        /* 将当前字节与CRC寄存器低8位异或 */
        crc ^= data[i];
        
        /* 对每个位进行处理 */
        for (j = 0; j < 8; j++) {
            /* 检查最低位是否为1 */
            if (crc & 0x0001) {
                /* 最低位为1，右移并与多项式异或 */
                crc = (crc >> 1) ^ 0xA001;
            } else {
                /* 最低位为0，直接右移 */
                crc = crc >> 1;
            }
        }
    }
    
    /* 返回计算得到的CRC值 */
    return crc;
}
```
## 6. 编程实践规范
### 6.1 变量声明与初始化
1. 变量应在使用前声明，并尽可能在靠近使用的地方声明
2. 所有变量必须初始化，禁止使用未初始化的变量
3. 尽量使用局部变量，避免使用全局变量
4. 指针变量必须初始化为`NULL`
5. 使用标准数据类型（`uint8_t`、`int32_t`等），避免使用`char`、`int`等不确定长度的类型
**示例：**
```c
void function(void) {
    uint8_t status = 0;
    uint16_t count = 0;
    uint8_t *p_data = NULL;
    
    p_data = (uint8_t *)malloc(100);
    if (p_data == NULL) {
        return;
    }
    
    /* 使用p_data */
    
    free(p_data);
    p_data = NULL;
}
```
### 6.2 函数设计原则
1. 函数应遵循"单一职责原则"，一个函数只做一件事
2. 函数长度不宜过长，一般不超过50行
3. 函数参数不宜过多，一般不超过5个
4. 函数必须有明确的返回值，用于表示执行状态
5. 避免使用函数参数作为返回值
6. 私有函数必须声明为`static`
### 6.3 控制结构
1. `if`语句中，常量应写在比较运算符的左边
2. `switch`语句必须包含`default`分支
3. 避免使用`goto`语句，除非用于错误处理
4. 循环嵌套不宜超过3层
**示例：**
```c
if (0 == status) {
    do_something();
}

switch (command) {
    case COMMAND_START:
        start_process();
        break;
        
    case COMMAND_STOP:
        stop_process();
        break;
        
    default:
        handle_unknown_command();
        break;
}
```
### 6.4 错误处理
1. 所有可能失败的函数都必须检查返回值
2. 对输入参数进行合法性检查
3. 对指针进行非空检查
4. 数组访问时检查下标是否越界
5. 使用断言`assert`检查开发阶段的错误
**示例：**
```c
uint8_t uart_send_data(uint8_t *data, uint16_t length) {
    /* 检查输入参数合法性 */
    if (data == NULL) {
        return UART_STATUS_ERROR;
    }
    
    if (length == 0 || length > UART_BUFFER_SIZE) {
        return UART_STATUS_ERROR;
    }
    
    /* 发送数据 */
    
    return UART_STATUS_OK;
}
```
## 7. 可移植性与安全规范
1. 避免使用编译器特有的扩展功能
2. 避免使用硬编码的数值，使用宏定义代替
3. 注意字节序问题，多字节数据传输时进行字节序转换
4. 注意数据类型的长度和符号问题
5. 避免使用不安全的函数，如`strcpy`、`sprintf`等，使用`strncpy`、`snprintf`代替
6. 禁止使用可变参数函数
7. 禁止使用递归函数
## 8. 版本控制规范
1. 每次提交必须填写清晰、准确的提交信息
2. 提交信息格式：`[模块名] 修改内容说明`
3. 每次提交只包含一个逻辑修改
4. 提交前必须进行代码编译和测试
5. 禁止提交编译错误的代码
6. 禁止提交调试信息和注释掉的代码
## 9. 附录
### 9.1 完整示例代码
```c
#ifndef __LED_H
#define __LED_H

/*
 * 模块名称：LED控制模块
 * 模块功能：提供LED初始化、点亮、熄灭和翻转功能
 * 适用平台：STM32F103系列单片机
 * 作者：张三
 * 创建日期：2026-05-11
 * 修改记录：
 *     2026-05-11  张三  创建初始版本
 */

#ifdef __cplusplus
extern "C" {
#endif

#include "stdint.h"

/* LED编号定义 */
#define LED_NUM_1    0
#define LED_NUM_2    1
#define LED_NUM_MAX  2

/* LED状态定义 */
#define LED_OFF    0
#define LED_ON     1

/*
 * 函数功能：LED初始化函数
 * 入口参数：led_num - LED编号  uint8_t  0 - LED_NUM_MAX-1
 * 返回值：0 - 成功，其他 - 失败
 * 限定条件：无
 * 函数说明：初始化LED对应的GPIO引脚为推挽输出模式
 */
uint8_t led_init(uint8_t led_num);

/*
 * 函数功能：点亮LED
 * 入口参数：led_num - LED编号  uint8_t  0 - LED_NUM_MAX-1
 * 返回值：0 - 成功，其他 - 失败
 * 限定条件：led_init()函数已成功调用
 * 函数说明：将LED对应的GPIO引脚置为低电平
 */
uint8_t led_on(uint8_t led_num);

/*
 * 函数功能：熄灭LED
 * 入口参数：led_num - LED编号  uint8_t  0 - LED_NUM_MAX-1
 * 返回值：0 - 成功，其他 - 失败
 * 限定条件：led_init()函数已成功调用
 * 函数说明：将LED对应的GPIO引脚置为高电平
 */
uint8_t led_off(uint8_t led_num);

/*
 * 函数功能：翻转LED状态
 * 入口参数：led_num - LED编号  uint8_t  0 - LED_NUM_MAX-1
 * 返回值：0 - 成功，其他 - 失败
 * 限定条件：led_init()函数已成功调用
 * 函数说明：将LED对应的GPIO引脚电平取反
 */
uint8_t led_toggle(uint8_t led_num);

#ifdef __cplusplus
}
#endif

#endif /* __LED_H */
```