嵌入式代码编写规范1.0

作为嵌入式软件开发工程师,在敲代码的时候(而不是事后重构),需要在以下细节处时刻保持警惕:

一、写函数时

1. 参数数量控制

c 复制代码
// ❌ 高耦合信号:参数过多
void config_device(uint8_t addr, uint16_t rate, uint8_t mode, 
                    uint32_t timeout, void (*cb)(void), uint8_t retry);

// ✅ 使用配置结构体
typedef struct {
    uint16_t rate;
    uint8_t mode;
    uint32_t timeout;
    uint8_t retry;
} dev_config_t;

void config_device(uint8_t addr, const dev_config_t* cfg, void (*cb)(void));

2. 返回值只暴露必要信息

c 复制代码
// ❌ 返回内部状态码(耦合上层逻辑)
int32_t sensor_read(uint8_t* data);  // 返回-1,-2,-3等错误码

// ✅ 返回bool或简单枚举
bool sensor_read(uint8_t* data);  // 成功/失败
// 或
typedef enum { SENSOR_OK, SENSOR_BUSY, SENSOR_TIMEOUT } sensor_status_t;

二、定义变量时

3. 全局变量必须加"防火墙"

c 复制代码
// ❌ 裸全局变量
uint32_t system_tick;  // 任何文件都能改

// ✅ 访问函数
static uint32_t s_system_tick;
uint32_t sys_get_tick(void) { return s_system_tick; }
void sys_tick_update(void) { s_system_tick++; }  // 只在中断中调用

4. 文件作用域变量全部static

c 复制代码
// 每个.c文件开头
static uint8_t s_buffer[256];     // 模块私有
static void (*s_callback)(void);  // 其他文件看不到

// 只有明确需要跨文件的才在.h声明
extern const uint32_t FW_VERSION;  // 只读常量可例外

三、写头文件时

5. 前向声明优先于#include

c 复制代码
// timer.h
// ❌ 不必要的包含
#include "event.h"
#include "queue.h"

// ✅ 前向声明
typedef struct event_t event_t;  // 只告诉编译器这是个类型
void timer_register(event_t* evt);  // 不需要完整定义

6. 不透明指针强制隐藏实现

c 复制代码
// sensor.h
// ✅ 用户只看到句柄
typedef struct sensor_ctx_t sensor_ctx_t;

sensor_ctx_t* sensor_create(int pin);
bool sensor_read(sensor_ctx_t* ctx, float* out);
void sensor_destroy(sensor_ctx_t* ctx);

// sensor.c
struct sensor_ctx_t {
    int pin;
    uint8_t i2c_addr;
    uint32_t last_read_ms;
    // 所有内部细节都藏在这里
};

四、模块间通信时

7. 回调函数不要直接调用业务逻辑

c 复制代码
// ❌ 回调里做复杂处理
void button_isr(void) {
    led_toggle();        // 直接操作LED
    buzzer_beep(100);    // 直接操作蜂鸣器
    send_udp("pressed"); // 直接网络发送
}

// ✅ 回调只设置标志/发消息
void button_isr(void) {
    event_post(EVENT_BUTTON_PRESSED);  // 通知任务
}
// 任务中处理
void main_task(void) {
    if (event_wait(EVENT_BUTTON_PRESSED)) {
        led_toggle();
        buzzer_beep(100);
        send_udp("pressed");
    }
}

8. 禁止跨模块访问结构体成员

c 复制代码
// app.c
// ❌ 直接访问其他模块的私有数据
extern uart_buffer_t g_uart_buf;
memcpy(g_uart_buf.data, src, len);

// ✅ 通过接口
uart_send(src, len);

五、写条件编译时

9. 配置宏集中在单独文件

c 复制代码
// config.h - 所有配置宏的唯一来源
#define UART_BAUDRATE   115200
#define TASK_STACK_SIZE 1024

// uart.c
// ❌ 分散的宏定义
#ifndef UART_BUFFER_SIZE
#define UART_BUFFER_SIZE 64
#endif

// ✅ 统一包含
#include "config.h"

10. 平台相关代码隔离

c 复制代码
// 不好:平台代码混杂
void delay_ms(uint32_t ms) {
    #ifdef STM32
    HAL_Delay(ms);
    #elif defined(ESP32)
    vTaskDelay(ms / portTICK_PERIOD_MS);
    #endif
}

// 好:用独立文件
// hal_delay_stm32.c
// hal_delay_esp32.c
// 链接时选择

六、写循环和条件时

11. 避免长if-else链(内聚性差)

c 复制代码
// ❌ 一个函数处理多种模式
void process_mode(int mode) {
    if (mode == 1) { /* 30行 */ }
    else if (mode == 2) { /* 30行 */ }
    else if (mode == 3) { /* 30行 */ }
}

// ✅ 函数指针表(内聚到各自模块)
static const mode_handler_t s_handlers[] = {
    mode1_process,
    mode2_process,
    mode3_process,
};

12. 状态机内不要直接调用外部模块

c 复制代码
// ❌ 状态机直接调用
case STATE_ERROR:
    led_set_red();
    buzzer_alarm();
    log_error();

// ✅ 状态机只改变状态,外部查询执行
case STATE_ERROR:
    system_set_state(SYS_STATE_ERROR);  // 设置标志
    // 主循环中统一处理

七、编码时的自我检查清单

每次敲代码前/后快速过一遍:

检查项 坏味道 改进
这个函数超过50行? 拆分
这个.c文件超过800行? 拆分模块
函数有超过3个参数? 用结构体
头文件包含超过5个其他.h? 前向声明
有非const全局变量? 加static+访问函数
直接访问其他模块的结构体? 加接口函数
中断里做了超过10行操作? 只发事件
模块间有循环依赖? 致命 重新设计

八、培养的肌肉记忆

敲#includ前:真的需要完整类型定义?还是只要前向声明?

敲struct {前:这个结构体用户需要看到吗?→ 不需要就放.c里

敲函数前:这个函数应该属于哪个模块?→ 职责不清就拆分

敲static前:这个变量/函数真的需要跨文件访问?→ 默认加static

敲全局变量前:能不能用函数封装?→ 99%的情况能


这些不是死板的规则,而是你在敲键盘的瞬间就应该意识到的设计信号。当你发现自己在"绕过"这些建议时(比如为了省事直接访问其他模块的结构体),就是代码开始腐烂的时刻。

相关推荐
mftang2 小时前
Cortex-M 中断跳转: MCU内部实现原理和流程
单片机·嵌入式硬件·armv8-m
charlie1145141912 小时前
嵌入式C++教程实战之Linux下的单片机编程:从零搭建 STM32 开发工具链(5):调试进阶篇 —— 从 printf 到完整 GDB 调试环境
linux·c++·单片机·学习·嵌入式·c
老师用之于民3 小时前
【DAY36】基于 I²C 总线的通信协议分析与 ADC 转换原理研
单片机·嵌入式硬件
listhi5205 小时前
STM32 USB-HID下位机设计与实现
stm32·单片机·嵌入式硬件
Winner13005 小时前
【单片机 控制小车】
单片机·嵌入式硬件
xingzhemengyou15 小时前
STM32 UART 通信详解
stm32·单片机·嵌入式硬件
苏灵凯5 小时前
智能环境监测终端全栈设计:从单片机到微信小程序,手把手搞定!
单片机·嵌入式硬件·mcu·物联网·微信小程序·小程序·蓝牙模块
济6175 小时前
STM32串口通信实战|从基础到实战(发送 + 接收控制 LED)---STM32 HAL库专栏
stm32·嵌入式·stm32hal库编程
福尔摩斯张5 小时前
一文搞懂74HC595芯片(附详细使用方法)
linux·服务器·网络·单片机·嵌入式硬件