6505cef9df
规则:在**掩码后**的文本上匹配 malloc/calloc/realloc/strdup/strndup/ free/aligned_alloc 调用。三条判据都是踩过坑之后定的—— - 大小写敏感:pvPortMalloc / vPortFree(唯一允许的配对)不会误报; - 词边界 \b:dhara_nand_is_free(、buf_alloc( 这类同形后缀不误报; - 用掩码:注释与字符串字面量里写 malloc 不报(规范示例就踩过这种)。 覆盖名比 AGENTS 列的五枚多带上 strndup / aligned_alloc——同族同类, 漏一个就等于留后门。 出处不在规范 V1.0,在平台约束:MicroLIB 的 C 堆由 startup_stm32f407xx.s 的 Heap_Size EQU 0x200 只有 512 B,KB 级分配必返 NULL,而模拟器(宿主 CRT 堆)完全正常、**照不出来**——配置引擎 2026-09-18 那次堆分配纠偏与 lftpd 的 malloc 隐患都是**先上线才发现**的那类,靠评审等于没管。 落地前按 门禁说明/coding-standard.md §8 的纪律先全库普查:钩子范围内 C/H 共 151 个文件,命中**只有** simulator/sim/cfg_file_store_sim.c 两行, 即 cfg_store_alloc/cfg_store_free = **平台分配后端的宿主侧实现**(设计依据 写在 docs/子系统设计/配置文件设计说明.md 的"MCU 端 = pvPortMalloc、 模拟器端 = 宿主 CRT")。所以零基线、直接硬拦截;豁免用脚本里的 HEAP_ALLOC_ALLOWLIST(**文件路径 + 函数名**双条件),刻意不做目录级豁免: 下次谁要碰 C 堆就得往这张表加一行,评审看得见。 自证:临时样例文件里 malloc/calloc/free 各报一处,pvPortMalloc/vPortFree/ buf_alloc(、注释、字符串五种陷阱形态全部不误报; pre-commit run coding-standard --all-files → **Passed**(= 全库零存量)。 顺带修三处口径不一致: - 规范 V1.0 §6.1 的示例自己写着 malloc/free(通用 C 模板遗留)→ 已在示例 下加"本仓例外,别照抄"批注,事实源指向 AGENTS.md 平台约束; - docs/门禁说明/mem-budget.md 里"这条纪律只靠 AGENTS 铁律与评审"已过期 → 更正为由 CODE-RULE-20 机器拦截; - AGENTS.md 平台约束条加"已机器拦截 + 唯一豁免"一句;scripts/README.md 补登 check_test_coverage.py(上一个提交漏登记)。 另:本次一并修好上一处编辑事故——报告 §4.8 曾被我的脚本调用截成半句 (只落第一行、丢掉 malloc 那半句),已重写为"两处空白均已闭合"。 自查:pre-commit run --files(9 个改动文件)适用项全 Passed; check_doc_links.py --all = 35 处,与主干同数(本分支贡献 0); check_ids.py --strict 通过(裸编号 P0-1 改平实说法,避免记成新账); 9 个文件 BOM/CR/U+FFFD/行尾与 HEAD 逐项一致。本轮零 .c/.h 改动,未跑 @build、未占 build/bench 锁;分支已 rebase 到 v2-on-ui,覆盖率脚本全量 回归仍 16/16、line=2552/2893 不变。
525 lines
16 KiB
Markdown
525 lines
16 KiB
Markdown
## 1. 文档概述
|
||
|
||
### 1.1 目的
|
||
本文档旨在统一嵌入式C语言代码的编写风格,提高代码的可读性、可维护性、可移植性和可靠性,降低团队协作成本,减少代码缺陷。
|
||
### 1.2 适用范围
|
||
本规范适用于所有基于C语言的嵌入式软件开发项目,包括但不限于51单片机、STM32、ARM、DSP等平台。
|
||
### 1.3 修订记录
|
||
| 版本号 | 修订日期 | 修订内容 | 修订人 |
|
||
| ---- | ---------- | ------ | --- |
|
||
| V1.0 | 2026-05-11 | 创建初始版本 | 王建锋 |
|
||
| V1.0.1 | 2026-09-01 | 修复示例与规则矛盾(括号风格、示例类型、变量初始化、硬编码、注释补全);规则定案:保护宏改`XXX_H`、常量在右、头文件常量用`#define`、存储类前缀优先、允许`void`返回值 | 王建锋 |
|
||
## 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`(不得使用`__`双下划线前缀,其为C标准保留标识符)
|
||
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_`等前缀开头
|
||
7. 前缀冲突时,存储类前缀(`g_`/`s_`)优先于类型前缀(`p_`/`a_`),如静态数组`s_uart_rx_buffer`
|
||
**示例:**
|
||
```c
|
||
uint8_t g_system_status = 0; /* 全局系统状态变量 */
|
||
static uint16_t s_timer_count = 0; /* 静态定时器计数变量 */
|
||
uint8_t *p_data_buffer = NULL; /* 数据缓冲区指针 */
|
||
uint16_t a_adc_value[10] = { 0 }; /* ADC采样值数组 */
|
||
bool is_button_pressed = false; /* 按钮是否按下标志 */
|
||
```
|
||
### 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. 头文件对外常量使用`#define`(见2.3.4);源文件内私有常量可用`const`,命名遵循3.2(小写+下划线)
|
||
**示例:**
|
||
```c
|
||
/* 头文件对外常量:使用 #define */
|
||
#define UART_BAUD_RATE_9600 9600
|
||
#define UART_BUFFER_SIZE 128
|
||
|
||
/* 源文件内私有常量:可用 const,命名遵循 3.2(小写) */
|
||
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
|
||
int32_t a = 10;
|
||
int32_t b = 0;
|
||
int32_t i = 0;
|
||
uint8_t c[10] = { 0 };
|
||
|
||
if (a > 5) {
|
||
b = a + 3;
|
||
}
|
||
for (i = 0; i < 10; i++) {
|
||
c[i] = 0;
|
||
}
|
||
```
|
||
**错误示例:**
|
||
```c
|
||
int32_t a=10;
|
||
int32_t b=0;
|
||
int32_t i=0;
|
||
uint8_t c[10]={0};
|
||
if(a>5){
|
||
b=a+3;
|
||
}
|
||
for(i=0;i<10;i++){
|
||
c[i]=0;
|
||
}
|
||
```
|
||
### 4.3 换行与空行
|
||
1. 每行代码长度不超过80个字符
|
||
2. 函数之间必须空一行
|
||
3. 逻辑上相关的代码块之间可以空一行
|
||
4. 函数内变量声明区和代码执行区之间必须空一行
|
||
5. 长表达式应在运算符处换行,新行与运算符对齐
|
||
**示例:**
|
||
```c
|
||
int32_t calculate_sum(int32_t a, int32_t b, int32_t c, int32_t d) {
|
||
int32_t sum = 0;
|
||
|
||
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
|
||
#define DELAY_INNER_LOOP 123 /* 延时内层循环次数,按12MHz时钟整定 */
|
||
|
||
/*
|
||
* 函数功能:毫秒级软件延时函数
|
||
* 入口参数:ms - 需要延时的毫秒数 uint32_t 0 - 65535
|
||
* 出口参数:无
|
||
* 返回值:无(void)
|
||
* 限定条件:0 <= ms <= 65535
|
||
* 函数说明:1. 采用空指令循环方式实现延时,会阻塞CPU运行
|
||
* 2. 延时精度依赖系统时钟,默认适配12MHz时钟
|
||
* 3. 系统时钟改变时,需重新调整内层循环次数(DELAY_INNER_LOOP)
|
||
* 4. 当ms为0时,函数立即返回
|
||
*/
|
||
void delay_ms(uint32_t ms) {
|
||
uint32_t i;
|
||
uint32_t j;
|
||
|
||
for (i = 0; i < ms; i++) {
|
||
for (j = 0; j < DELAY_INNER_LOOP; 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. 注释应单独占一行(变量定义的行内注释除外,见5.4.3),与被注释代码缩进级别相同
|
||
**示例:**
|
||
```c
|
||
#define CRC16_INITIAL 0xFFFF /* CRC-16初值 */
|
||
#define CRC16_POLY_REFLECTED 0xA001 /* CRC-16/MCC反射多项式 */
|
||
#define BITS_PER_BYTE 8 /* 每字节的位数 */
|
||
|
||
/* 计算CRC校验值 */
|
||
uint16_t crc_calculate(uint8_t *data, uint16_t length) {
|
||
uint16_t crc = CRC16_INITIAL;
|
||
uint16_t i;
|
||
uint16_t j;
|
||
|
||
/* 遍历所有数据字节 */
|
||
for (i = 0; i < length; i++) {
|
||
/* 将当前字节与CRC寄存器低8位异或 */
|
||
crc ^= data[i];
|
||
|
||
/* 对每个位进行处理 */
|
||
for (j = 0; j < BITS_PER_BYTE; j++) {
|
||
/* 检查最低位是否为1 */
|
||
if (crc & 0x0001) {
|
||
/* 最低位为1,右移并与多项式异或 */
|
||
crc = (crc >> 1) ^ CRC16_POLY_REFLECTED;
|
||
} else {
|
||
/* 最低位为0,直接右移 */
|
||
crc = crc >> 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 返回计算得到的CRC值 */
|
||
return crc;
|
||
}
|
||
```
|
||
## 6. 编程实践规范
|
||
### 6.1 变量声明与初始化
|
||
1. 变量应在使用前声明,并尽可能在靠近使用的地方声明
|
||
2. 所有变量必须初始化,禁止使用未初始化的变量
|
||
3. 尽量使用局部变量,避免使用全局变量
|
||
4. 指针变量必须初始化为`NULL`
|
||
5. 使用标准数据类型(`uint8_t`、`int32_t`等),避免使用`char`、`int`等不确定长度的类型
|
||
**示例:**
|
||
```c
|
||
#define DATA_BUF_SIZE 100 /* 数据缓冲区大小(字节) */
|
||
|
||
void function(void) {
|
||
uint8_t status = 0;
|
||
uint16_t count = 0;
|
||
uint8_t *p_data = NULL;
|
||
|
||
p_data = (uint8_t *)malloc(DATA_BUF_SIZE);
|
||
if (p_data == NULL) {
|
||
return;
|
||
}
|
||
|
||
/* 使用p_data */
|
||
|
||
free(p_data);
|
||
p_data = NULL;
|
||
}
|
||
```
|
||
|
||
> ⚠ **本仓例外(2026-10-23 补,别照抄这段示例)**:示例里的 `malloc`/`free` 是**通用 C 写法**,本仓
|
||
> **禁止**——工具链是 MicroLIB,其 C 堆由 `startup_stm32f407xx.s` 的 `Heap_Size EQU 0x200` 只有 **512 B**,
|
||
> KB 级分配必然返回 NULL,而模拟器(宿主 CRT 堆)完全正常、**照不出来**(只有真机才炸)。这段示例只教
|
||
> 三件事:**声明即初始化、检查 NULL、释放后置 NULL**。实际分配一律 `pvPortMalloc`/`vPortFree`,或把
|
||
> 分配**收进平台后端**(MCU 端与宿主端各一份实现,例:`simulator/sim/cfg_file_store_sim.c` 的
|
||
> `cfg_store_alloc`)。机器拦截 = 提交门禁 `coding-standard` 的 **CODE-RULE-20**,细则见
|
||
> `docs/门禁说明/coding-standard.md`。
|
||
|
||
### 6.2 函数设计原则
|
||
1. 函数应遵循"单一职责原则",一个函数只做一件事
|
||
2. 函数长度不宜过长,一般不超过50行
|
||
3. 函数参数不宜过多,一般不超过5个
|
||
4. 有失败路径的函数必须有明确的返回值,用于表示执行状态;无失败路径的纯输出函数可返回`void`
|
||
5. 避免使用函数参数作为返回值
|
||
6. 私有函数必须声明为`static`
|
||
### 6.3 控制结构
|
||
1. `if`语句中,常量应写在比较运算符的右边
|
||
2. `switch`语句必须包含`default`分支
|
||
3. 避免使用`goto`语句,除非用于错误处理
|
||
4. 循环嵌套不宜超过3层
|
||
**示例:**
|
||
```c
|
||
if (status == 0) {
|
||
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 */
|
||
```
|