逻辑视图:模块划分原则、接口定义规范、层与分区设计

好,咱们接着聊逻辑视图。这东西说白了,就是系统的骨架图。你想想看,一个嵌入式系统动辄几十万行代码,如果没有清晰的逻辑结构,那维护起来简直就是噩梦。我个人习惯,拿到需求后第一件事不是写代码,而是画逻辑视图。嗯,这一步省了,后面全是坑。

模块划分原则:高内聚、低耦合的落地

模块划分,教科书上天天讲「高内聚、低耦合」。但怎么落地?我总结了几条实战经验。

  • 单一职责原则:一个模块只做一件事。比如驱动层就管硬件操作,别把业务逻辑塞进去。我在项目中遇到过有人把PID控制算法直接写在GPIO中断里,结果后来想复用驱动,发现根本拆不开。
  • 接口最小化:暴露给外部的接口越少越好。能隐藏的细节全部隐藏。你想想看,接口多了,耦合就重了,改一个地方可能牵动全身。
  • 依赖方向稳定:高层模块不要依赖低层模块的具体实现。说白了,业务逻辑应该依赖抽象接口,而不是具体的硬件寄存器操作。

核心原则:模块之间的通信,尽量通过定义好的接口进行,不要直接访问对方的内部数据结构。这是血的教训换来的。

接口定义规范:别让接口成为瓶颈

接口定义,我见过太多团队在这上面翻车。有的接口参数多达十几个,有的返回值含义模糊。嗯,这里要注意几点。

  1. 命名要自解释:函数名、参数名要让人一看就懂。比如 adc_read_channel(uint8_t ch) 就比 get_data(int a) 好得多。
  2. 参数数量控制:我个人习惯,参数超过4个就要考虑封装成结构体了。否则调用者很容易搞错顺序。
  3. 返回值统一:建议统一返回错误码,而不是混合返回数据和状态。我曾经在一个项目里看到有的函数返回-1表示错误,有的返回NULL,有的返回false,调试起来简直想砸键盘。
// 好的接口示例
typedef struct {
    uint8_t channel;
    uint16_t sample_rate;
    uint8_t resolution;
} adc_config_t;

int32_t adc_init(adc_config_t *config);
int32_t adc_read(uint16_t *value);
int32_t adc_deinit(void);

个人技巧:接口文档一定要和代码同步更新。我见过太多接口文档还写着V1.0的接口,代码已经改到V3.0了。建议用Doxygen这类工具从代码注释自动生成文档。

层与分区设计:把系统切成蛋糕

分层设计,说白了就是把系统按职责切分成若干层。常见的三层架构:硬件抽象层(HAL)、中间件层、应用层。但实际项目中,我建议再加一个「服务层」。

层次 职责 典型内容
应用层 业务逻辑、用户交互 状态机、任务调度、UI
服务层 系统服务、资源管理 日志、内存管理、任务间通信
中间件层 协议栈、算法库 TCP/IP、FATFS、加密算法
硬件抽象层 屏蔽硬件差异 GPIO、UART、SPI驱动接口
硬件层 物理硬件 MCU、传感器、执行器

分区设计呢,则是把同一层内的功能再切分成更小的区块。比如硬件抽象层,可以按外设类型分区:GPIO分区、定时器分区、通信接口分区。每个分区独立编译、独立测试。

避坑指南:我曾经在一个项目里,把所有驱动都写在一个文件里,结果后来换MCU,那个文件改了两周还没改完。从那以后,我坚持每个外设一个文件,每个文件不超过500行。

层间通信:规矩要定好

层与层之间怎么通信?我建议遵循「单向依赖」原则。上层可以调用下层,下层绝对不能反向调用上层。如果下层需要通知上层事件,用回调函数或者消息队列。

// 下层向上层通知事件的正确姿势
typedef void (*event_callback_t)(uint32_t event_id, void *data);

void hal_register_callback(event_callback_t cb);
void hal_event_trigger(uint32_t event_id, void *data);

你想想看,如果下层直接调用上层函数,那分层就失去了意义。改上层代码时,下层也得跟着改,这不又回到一锅粥的状态了吗?

实际案例:一个温控系统的逻辑视图

拿一个温控系统举例。应用层负责温度设定和显示逻辑;服务层负责温度数据的滤波和报警判断;中间件层跑着PID算法库;硬件抽象层封装了温度传感器和加热器的驱动。每个层之间通过定义好的接口通信,互不干扰。

我记得有一次,客户要求把温度传感器从DS18B20换成NTC热敏电阻。我只需要改硬件抽象层的驱动代码,上层逻辑完全不用动。这就是分层设计的好处——改一层,不影响其他层。

总结一下:逻辑视图不是画完就完事的。它需要你在编码过程中不断迭代优化。模块划分、接口定义、分层分区,这三件事做好了,你的系统架构就稳了一半。另一半,嗯,留到后面的章节再聊。