[牛人杂谈] 如何让单片机代码写的不乱糟糟易于修改管理

[复制链接]
 楼主| 稳稳の幸福 发表于 2025-3-30 17:42 | 显示全部楼层 |阅读模式
要让单片机代码结构清晰、易于维护和修改,需要遵循良好的软件工程实践,并结合嵌入式开发的特点进行优化。以下是一些关键建议,分为不同维度:
1. 模块化设计
分层架构:将代码分为硬件抽象层(HAL)、驱动层、中间件层、应用逻辑层,每层只依赖下层。

功能模块化:每个模块负责单一功能(如按键扫描、串口通信、传感器驱动),使用 .c/.h 文件对封装。

接口隔离:模块之间通过明确的接口(函数和结构体)通信,避免直接操作全局变量。

示例:

  1. // sensor.h
  2. typedef struct {
  3.     float temperature;
  4.     float humidity;
  5. } SensorData;

  6. void Sensor_Init(void);
  7. SensorData Sensor_Read(void);
2. 编码规范
命名规则:

变量/函数名:模块名_功能描述(如 ADC_ReadValue())。

宏定义:全大写,带模块前缀(如 GPIO_LED_PIN)。

函数设计:

单一职责:一个函数只做一件事(如 UART_SendString() 而非 UART_Process())。

控制函数长度(建议不超过屏幕高度)。

注释规范:

文件头注释:说明模块功能、作者、版本。

关键代码段:解释算法或硬件操作逻辑。

Doxygen 风格注释:

  1. /**
  2. * [url=home.php?mod=space&uid=247401]@brief[/url] 初始化PWM模块
  3. * @param freq PWM频率(单位:Hz)
  4. * [url=home.php?mod=space&uid=266161]@return[/url] 成功返回0,失败返回错误码
  5. */
  6. int PWM_Init(uint32_t freq);
3. 减少全局变量
封装数据:使用结构体封装相关变量。

访问控制:

静态全局变量(static)限制在模块内。

通过函数接口访问数据:

  1. // motor.c
  2. static int motor_speed;
  3. void Motor_SetSpeed(int speed) { motor_speed = speed; }
  4. int Motor_GetSpeed(void) { return motor_speed; }
4. 状态机与设计模式
状态机:复杂流程用状态机实现(如通信协议解析)。

  1. typedef enum { STATE_IDLE, STATE_RX, STATE_PROCESS } UART_State;
  2. void UART_Handler(void) {
  3.     static UART_State state = STATE_IDLE;
  4.     switch(state) {
  5.         case STATE_IDLE: ... break;
  6.         case STATE_RX: ... break;
  7.     }
  8. }
设计模式:

观察者模式:用于事件通知(如按键事件触发多模块响应)。

工厂模式:外设初始化统一接口。

5. 代码复用与配置
硬件无关代码:将平台相关代码(如GPIO操作)与业务逻辑分离。

配置文件:使用 config.h 集中管理硬件参数:

  1. // config.h
  2. #define LED_PIN      GPIO_PIN_5
  3. #define LED_PORT     GPIOA
  4. #define PWM_FREQ     1000  // Hz
6. 工具链与工程管理
版本控制:使用 Git 管理代码,合理分分支(如 develop, feature/adc)。

Makefile/IDE:自动化编译流程,分离编译选项(如 CFLAGS = -Wall -O2)。

静态检查:启用编译器警告(-Wall -Wextra),使用工具如 PC-lint。

单元测试:对关键模块进行测试(如使用 Unity 框架):

  1. void test_ADC_Conversion(void) {
  2.     TEST_ASSERT_EQUAL(2048, ADC_Read(ADC_CH1));
  3. }
7. 文档与注释
模块文档:在 .h 文件中说明接口用法。

数据流图:绘制模块间数据流和状态迁移图。

版本日志:记录关键修改和BUG修复。


8. 资源管理优化
内存规划:

使用内存池管理动态内存(避免碎片)。

合理使用 const 修饰只读数据(节省RAM)。

中断优化:

中断服务程序(ISR)尽量简短,通过标志位通知主循环。

避免在中断中调用阻塞函数。


 楼主| 稳稳の幸福 发表于 2025-3-30 17:43 | 显示全部楼层
持续改进

定期重构:删除无用代码,合并重复逻辑。

代码审查:团队内部分享最佳实践。

性能分析:使用Profiler工具定位瓶颈(如执行时间、栈使用)。
huangcunxiake 发表于 2025-3-30 18:06 | 显示全部楼层
这个总结的挺好,不要都写在一个文件里,每一个功能,放在一个头文件里和对应的.c实现函数。
复古留声机 发表于 2025-3-30 19:13 | 显示全部楼层
在实际项目中,如何保证编码规范被团队成员严格遵守呢
旧时光放映机 发表于 2025-3-30 22:10 | 显示全部楼层
模块化设计听起来很关键,那如果一个功能模块依赖多个硬件设备,怎么更好地进行分层和模块化呢
zhouyong77 发表于 2025-3-31 07:42 来自手机 | 显示全部楼层
看看C语言编程规范,养成良好的代码编程习惯和风格。
chenqianqian 发表于 2025-3-31 07:45 来自手机 | 显示全部楼层
按照编程规范来写代码基本上就很好了
迷雾隐者 发表于 2025-3-31 13:34 | 显示全部楼层
中断服务程序需要尽量简短
魔法森林精灵 发表于 2025-3-31 16:34 | 显示全部楼层
在设计状态机时,有没有一些常见的陷阱需要避免呢
星空魔法师 发表于 2025-3-31 22:43 | 显示全部楼层
减少全局变量这个建议很好
您需要登录后才可以回帖 登录 | 注册

本版积分规则

203

主题

3409

帖子

8

粉丝
快速回复 在线客服 返回列表 返回顶部