嵌入式分层架构实战:从“面条代码”到高内聚低耦合
·
通过分层架构和依赖倒置,实现:
高内聚低耦合:每层只关心自己的职责 ;
可测试性:服务层可脱离硬件进行单元测试(mock DAL) ;
可移植性:更换 MCU 或外设只需重写硬件驱动层 ;
清晰的数据流向:从传感器/按键输入,经服务层处理,再输出到 LCD/加热器/串口 ;
以上方案完整覆盖功能梳理、层次划分、接口定义、数据流图、文件组织及模块实现。
嵌入式分层架构实战:从“面条代码”到高内聚低耦合
很多嵌入式初学者把全部代码塞进
main.c,用switch-case和if-else堆砌出几千行的“意大利面条”。换一个传感器要改几十处,换MCU等于重写项目。
本文通过对比的方式,展示传统“不分层”代码的痛点,再引出分层架构 + 依赖倒置的优雅解法。以智能温控器为例,手把手实现高内聚低耦合、可测试、可移植的嵌入式软件。
目录
- 传统写法 vs 分层架构:对比示例
- 分层架构核心原则
- 智能温控器实例:功能与模块划分
- UML建模:组件图、依赖关系图、数据流图
- 项目文件结构
- 详细模块实现(驱动抽象层 → 硬件驱动层 → 服务层 → 应用层)
- 6.1 驱动抽象层(DAL)—— 定义硬件无关接口
- 6.2 硬件驱动层 —— 实现具体外设操作
- 6.3 服务层 —— 业务逻辑与算法
- 6.4 应用层 —— 任务调度与初始化
- 可测试性实战:如何Mock DAL进行单元测试
- 可移植性实战:更换MCU/传感器只需改驱动层
- 总结
1. 传统写法 vs 分层架构:对比示例
1.1 不推荐:传统“不分层”写法(伪代码)
// main.c - 所有东西搅在一起
void main() {
// 初始化各种硬件寄存器
GPIO_Init(); // 温度传感器引脚
ADC_Init(); // 如果用模拟传感器
I2C_Init(); // 如果用数字传感器
LCD_Init();
UART_Init();
Timer_Init();
while(1) {
// 直接操作寄存器读取温度
uint8_t raw[2];
OneWire_Reset();
OneWire_Write(0xCC);
OneWire_Write(0x44);
// ... 几十行时序代码
// 计算温度
float temp = (raw[0] | (raw[1]<<8)) * 0.0625;
// 控制逻辑
if (temp < setpoint - 1.0) {
GPIO_WritePin(HEATER_PIN, 1); // 直接操作GPIO
} else if (temp > setpoint + 1.0) {
GPIO_WritePin(HEATER_PIN, 0);
}
// LCD显示
LCD_Clear();
sprintf(buf, "T:%.1f", temp);
LCD_Print(buf);
// 串口上报
UART_Send(buf);
// 按键扫描
if (GPIO_ReadPin(KEY_PIN)) {
setpoint += 0.5;
}
delay(200);
}
}
痛点:
- 换一个温度传感器(DS18B20 → LM35)需要在主循环里修改读取代码,甚至改写ADC逻辑。
- 想增加滤波算法?直接塞进主循环,代码膨胀。
- 想加一个Wi-Fi上报?继续往主循环里加,耦合度爆炸。
- 无法单独测试控制算法,必须连接真实硬件。
- 换MCU(比如从STM32换到GD32),所有寄存器操作都要重写。
1.2 推荐:分层架构 + 依赖倒置
核心思想:上层只依赖抽象接口,不依赖具体硬件。硬件通过“适配”方式注入。
// ========== 驱动抽象层(接口) ==========
// temp_sensor_if.h
typedef struct {
int (*read_celsius)(float *temp);
} TempSensorOps;
extern const TempSensorOps *g_temp_sensor;
// ========== 服务层 ==========
float get_filtered_temperature(void) {
float raw;
g_temp_sensor->read_celsius(&raw); // 通过接口调用,不依赖具体硬件
return apply_filter(raw);
}
// ========== 应用层 ==========
void main() {
// 初始化:注册具体硬件驱动(适配器)
g_temp_sensor = &ds18b20_ops; // 或 &lm35_ops
while(1) {
float temp = get_filtered_temperature();
control_heater(temp);
display_temp(temp);
send_uart(temp);
delay(200);
}
}
优势:
- 高内聚低耦合:每层只关心自己职责,服务层不知道硬件细节。
- 可测试:测试控制算法时,用
mock_temp_sensor返回固定温度即可。 - 可移植:换MCU只需重写驱动层,服务层和应用层零修改。
- 数据流向清晰:输入(传感器/按键)→ 服务层(滤波/控制)→ 输出(LCD/加热器/串口)。
2. 分层架构核心原则
2.1 分层模型(自顶向下)
| 层级 | 职责 | 依赖方向 |
|---|---|---|
| 应用层 | 任务调度、系统状态机、初始化顺序 | 依赖服务层接口 |
| 服务层 | 数据处理(滤波、算法)、业务逻辑(温度控制、参数管理) | 依赖驱动抽象层接口 |
| 驱动抽象层 | 定义硬件操作的标准接口(读温度、写GPIO、发UART) | 不依赖任何层(纯接口) |
| 硬件驱动层 | 实现具体外设的寄存器操作、时序 | 实现驱动抽象层接口 |
| 硬件层 | 物理设备(MCU、传感器、执行器) | - |
2.2 依赖倒置原则(DIP)
高层模块不应依赖低层模块,二者都应依赖抽象。
抽象不应依赖细节,细节应依赖抽象。
- 服务层(高层)不直接调用
DS18B20_Read(),而是调用g_temp_sensor->read_celsius()。 - 硬件驱动层(低层)实现
TempSensorOps接口,并注册到全局指针。 - 这样,服务层和驱动层都依赖于
TempSensorOps这个抽象,实现了“倒置”。
2.3 数据流向图(清晰可见)
3. 智能温控器实例:功能与模块划分
3.1 功能需求
- 周期性采集温度(DS18B20 或 LM35)
- 根据设定温度控制加热器(继电器)
- 三个按键:增加设定温度、减少设定温度、手动/自动模式切换
- LCD 1602 显示:当前温度、设定温度、加热状态、模式
- 串口每2秒上报一次温度、设定值、加热状态
- 参数掉电保存(EEPROM)
3.2 模块识别
| 层级 | 模块 | 说明 |
|---|---|---|
| 应用层 | main_task | 主循环调度,周期性调用各服务 |
| 服务层 | temp_svc | 温度采集、滑动滤波 |
| control_svc | 开关控制算法(带回差) | |
| param_svc | 参数管理(目标温度、模式、EEPROM读写) | |
| display_svc | LCD刷新(根据当前状态更新显示) | |
| comm_svc | 串口上报 | |
| key_svc | 按键扫描、去抖、触发参数修改 | |
| 驱动抽象层 | temp_sensor_if | read_celsius() |
| heater_if | set_state(), get_state() | |
| key_if | scan() 获取按键码 | |
| lcd_if | init(), clear(), print() | |
| uart_if | init(), send(), receive() | |
| storage_if | read(), write() (EEPROM) | |
| 硬件驱动层 | ds18b20_drv / lm35_drv | 实现 temp_sensor_if |
| gpio_heater_drv | 实现 heater_if | |
| key_matrix_drv | 实现 key_if | |
| lcd1602_drv | 实现 lcd_if | |
| stm32_uart_drv | 实现 uart_if | |
| eeprom_drv | 实现 storage_if |
4. UML建模:组件图、依赖关系图、数据流图
4.1 分层组件图(依赖关系)
4.2 数据流时序图(一次温度采集与控制周期)
5. 项目文件结构
smart_thermostat/
├── docs/ # 文档
├── drivers/ # 硬件驱动层
│ ├── inc/
│ │ ├── ds18b20.h
│ │ ├── lm35.h
│ │ ├── gpio_heater.h
│ │ ├── key_matrix.h
│ │ ├── lcd1602.h
│ │ ├── stm32_uart.h
│ │ └── eeprom_24cxx.h
│ └── src/
│ ├── ds18b20.c
│ ├── lm35.c
│ ├── gpio_heater.c
│ ├── key_matrix.c
│ ├── lcd1602.c
│ ├── stm32_uart.c
│ └── eeprom_24cxx.c
├── dal/ # 驱动抽象层(接口定义)
│ ├── inc/
│ │ ├── temp_sensor_if.h
│ │ ├── heater_if.h
│ │ ├── key_if.h
│ │ ├── lcd_if.h
│ │ ├── uart_if.h
│ │ └── storage_if.h
│ └── src/
│ └── dal_common.c # 全局接口指针定义
├── service/ # 服务层
│ ├── inc/
│ │ ├── temp_svc.h
│ │ ├── control_svc.h
│ │ ├── param_svc.h
│ │ ├── display_svc.h
│ │ ├── comm_svc.h
│ │ └── key_svc.h
│ └── src/
│ ├── temp_svc.c
│ ├── control_svc.c
│ ├── param_svc.c
│ ├── display_svc.c
│ ├── comm_svc.c
│ └── key_svc.c
├── app/ # 应用层
│ ├── inc/
│ │ └── main_task.h
│ └── src/
│ └── main.c
├── platform/ # MCU启动文件、链接脚本
│ └── stm32f1xx/
├── test/ # 单元测试(mock DAL)
│ ├── mock/
│ │ └── mock_temp_sensor.c
│ └── test_control_svc.c
└── build/
6. 详细模块实现(驱动抽象层 → 硬件驱动层 → 服务层 → 应用层)
6.1 驱动抽象层(DAL)—— 定义硬件无关接口
dal/inc/temp_sensor_if.h
#ifndef TEMP_SENSOR_IF_H
#define TEMP_SENSOR_IF_H
#include <stdint.h>
typedef struct {
int (*init)(void);
int (*read_celsius)(float *temp);
} TempSensorOps;
extern const TempSensorOps *g_temp_sensor; // 全局指针,由驱动层赋值
#endif
dal/inc/heater_if.h
#ifndef HEATER_IF_H
#define HEATER_IF_H
typedef enum { HEATER_OFF = 0, HEATER_ON = 1 } HeaterState;
typedef struct {
void (*init)(void);
void (*set_state)(HeaterState state);
HeaterState (*get_state)(void);
} HeaterOps;
extern const HeaterOps *g_heater;
#endif
dal/inc/key_if.h
#ifndef KEY_IF_H
#define KEY_IF_H
#include <stdint.h>
typedef struct {
int (*init)(void);
int (*scan)(uint8_t *key_code, uint8_t *pressed); // 返回按键值及是否按下
} KeyOps;
extern const KeyOps *g_key;
#endif
dal/inc/lcd_if.h
#ifndef LCD_IF_H
#define LCD_IF_H
#include <stdint.h>
typedef struct {
int (*init)(void);
void (*clear)(void);
void (*print)(uint8_t row, uint8_t col, const char *str);
} LcdOps;
extern const LcdOps *g_lcd;
#endif
dal/inc/uart_if.h
#ifndef UART_IF_H
#define UART_IF_H
#include <stdint.h>
typedef struct {
int (*init)(uint32_t baud);
int (*send)(const uint8_t *data, uint16_t len);
int (*receive)(uint8_t *buf, uint16_t max_len, uint32_t timeout_ms);
} UartOps;
extern const UartOps *g_uart;
#endif
dal/inc/storage_if.h
#ifndef STORAGE_IF_H
#define STORAGE_IF_H
#include <stdint.h>
typedef struct {
int (*init)(void);
int (*write)(uint16_t addr, const uint8_t *data, uint16_t len);
int (*read)(uint16_t addr, uint8_t *data, uint16_t len);
} StorageOps;
extern const StorageOps *g_storage;
#endif
6.2 硬件驱动层 —— 实现具体外设操作
drivers/src/ds18b20.c(实现温度传感器接口)
#include "temp_sensor_if.h"
#include "stm32f1xx_hal.h"
static int ds18b20_init(void) {
// 初始化单总线GPIO
// ...
return 0;
}
static int ds18b20_read_celsius(float *temp) {
uint8_t rom[8];
uint8_t data[2];
// 单总线通信时序(省略具体实现)
// 假设读取成功,data[0]为低字节,data[1]为高字节
int16_t raw = (data[1] << 8) | data[0];
*temp = raw * 0.0625f;
return 0;
}
static const TempSensorOps ds18b20_ops = {
.init = ds18b20_init,
.read_celsius = ds18b20_read_celsius
};
const TempSensorOps *g_temp_sensor = &ds18b20_ops; // 注册
drivers/src/gpio_heater.c(实现加热器接口)
#include "heater_if.h"
#include "stm32f1xx_hal.h"
#define HEATER_PIN GPIO_PIN_13
#define HEATER_PORT GPIOB
static void heater_init(void) {
GPIO_InitTypeDef gpio = { .Pin = HEATER_PIN, .Mode = GPIO_MODE_OUTPUT_PP };
HAL_GPIO_Init(HEATER_PORT, &gpio);
HAL_GPIO_WritePin(HEATER_PORT, HEATER_PIN, GPIO_PIN_RESET);
}
static void heater_set_state(HeaterState state) {
HAL_GPIO_WritePin(HEATER_PORT, HEATER_PIN, state == HEATER_ON ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
static HeaterState heater_get_state(void) {
return HAL_GPIO_ReadPin(HEATER_PORT, HEATER_PIN) == GPIO_PIN_SET ? HEATER_ON : HEATER_OFF;
}
static const HeaterOps heater_ops = {
.init = heater_init,
.set_state = heater_set_state,
.get_state = heater_get_state
};
const HeaterOps *g_heater = &heater_ops;
drivers/src/lcd1602.c(实现LCD接口)
#include "lcd_if.h"
#include "stm32f1xx_hal.h"
static void lcd_init(void) { /* 初始化1602时序 */ }
static void lcd_clear(void) { /* 发送清屏命令 */ }
static void lcd_print(uint8_t row, uint8_t col, const char *str) {
// 设置光标位置,逐字符输出
}
static const LcdOps lcd_ops = { .init = lcd_init, .clear = lcd_clear, .print = lcd_print };
const LcdOps *g_lcd = &lcd_ops;
其他驱动(按键、UART、EEPROM)类似实现,此处省略。
6.3 服务层 —— 业务逻辑与算法(可脱离硬件测试)
service/inc/temp_svc.h 与 src/temp_svc.c
// temp_svc.h
#ifndef TEMP_SVC_H
#define TEMP_SVC_H
void temp_svc_init(void);
void temp_svc_update(void); // 每200ms调用
float temp_svc_get_current(void); // 获取滤波后温度
#endif
// temp_svc.c
#include "temp_svc.h"
#include "temp_sensor_if.h"
#define FILTER_WIN 5
static float buf[FILTER_WIN];
static uint8_t idx = 0, count = 0;
static float filtered_temp = 0.0f;
void temp_svc_init(void) {
g_temp_sensor->init();
}
void temp_svc_update(void) {
float raw;
if (g_temp_sensor->read_celsius(&raw) == 0) {
buf[idx] = raw;
idx = (idx + 1) % FILTER_WIN;
if (count < FILTER_WIN) count++;
float sum = 0;
for (int i = 0; i < count; i++) sum += buf[i];
filtered_temp = sum / count;
}
}
float temp_svc_get_current(void) {
return filtered_temp;
}
service/inc/param_svc.h 与 src/param_svc.c
// param_svc.h
#ifndef PARAM_SVC_H
#define PARAM_SVC_H
#include <stdint.h>
typedef struct {
float target_temp;
uint8_t mode; // 0: manual, 1: auto
uint8_t eeprom_valid;
} SystemParams;
extern SystemParams g_params;
void param_svc_init(void);
void param_svc_save(void);
void param_svc_set_target(float temp);
float param_svc_get_target(void);
void param_svc_set_mode(uint8_t mode);
uint8_t param_svc_get_mode(void);
#endif
// param_svc.c
#include "param_svc.h"
#include "storage_if.h"
#define PARAM_ADDR 0x00
SystemParams g_params;
void param_svc_init(void) {
g_storage->init();
if (g_storage->read(PARAM_ADDR, (uint8_t*)&g_params, sizeof(g_params)) != 0 ||
g_params.eeprom_valid != 0xAA) {
// 默认参数
g_params.target_temp = 25.0f;
g_params.mode = 1; // auto
g_params.eeprom_valid = 0xAA;
param_svc_save();
}
}
void param_svc_save(void) {
g_storage->write(PARAM_ADDR, (const uint8_t*)&g_params, sizeof(g_params));
}
void param_svc_set_target(float temp) {
g_params.target_temp = temp;
param_svc_save();
}
float param_svc_get_target(void) { return g_params.target_temp; }
void param_svc_set_mode(uint8_t mode) {
g_params.mode = mode;
param_svc_save();
}
uint8_t param_svc_get_mode(void) { return g_params.mode; }
service/inc/control_svc.h 与 src/control_svc.c
// control_svc.h
#ifndef CONTROL_SVC_H
#define CONTROL_SVC_H
void control_svc_init(void);
void control_svc_update(void); // 根据当前温度控制加热器
#endif
// control_svc.c
#include "control_svc.h"
#include "temp_svc.h"
#include "param_svc.h"
#include "heater_if.h"
#define HYSTERESIS 1.0f
static HeaterState last_state = HEATER_OFF;
void control_svc_init(void) {
g_heater->init();
}
void control_svc_update(void) {
if (param_svc_get_mode() == 0) return; // 手动模式,不自动控制
float current = temp_svc_get_current();
float target = param_svc_get_target();
if (last_state == HEATER_OFF && current <= target - HYSTERESIS) {
last_state = HEATER_ON;
g_heater->set_state(HEATER_ON);
} else if (last_state == HEATER_ON && current >= target + HYSTERESIS) {
last_state = HEATER_OFF;
g_heater->set_state(HEATER_OFF);
}
}
service/inc/display_svc.h 与 src/display_svc.c
// display_svc.h
void display_svc_init(void);
void display_svc_refresh(void);
// display_svc.c
#include "display_svc.h"
#include "lcd_if.h"
#include "temp_svc.h"
#include "param_svc.h"
#include "heater_if.h"
#include <stdio.h>
void display_svc_init(void) {
g_lcd->init();
}
void display_svc_refresh(void) {
char line1[17], line2[17];
float temp = temp_svc_get_current();
float target = param_svc_get_target();
HeaterState heat = g_heater->get_state();
uint8_t mode = param_svc_get_mode();
snprintf(line1, sizeof(line1), "T:%.1f Set:%.1f", temp, target);
snprintf(line2, sizeof(line2), "Heater:%s Mode:%s",
heat == HEATER_ON ? "ON" : "OFF",
mode ? "Auto" : "Man");
g_lcd->clear();
g_lcd->print(0, 0, line1);
g_lcd->print(1, 0, line2);
}
service/src/comm_svc.c(串口上报)
#include "comm_svc.h"
#include "uart_if.h"
#include "temp_svc.h"
#include "param_svc.h"
#include "heater_if.h"
#include <stdio.h>
void comm_svc_init(void) {
g_uart->init(115200);
}
void comm_svc_report(void) {
char buf[64];
float temp = temp_svc_get_current();
float target = param_svc_get_target();
HeaterState heat = g_heater->get_state();
int len = snprintf(buf, sizeof(buf), "TEMP:%.2f,TARGET:%.2f,HEATER:%d\r\n",
temp, target, heat == HEATER_ON ? 1 : 0);
g_uart->send((const uint8_t*)buf, len);
}
service/src/key_svc.c(按键处理)
#include "key_svc.h"
#include "key_if.h"
#include "param_svc.h"
#include "display_svc.h"
#define KEY_UP 1
#define KEY_DOWN 2
#define KEY_MODE 3
void key_svc_init(void) {
g_key->init();
}
void key_svc_task(void) {
uint8_t key, pressed;
if (g_key->scan(&key, &pressed) && pressed) {
switch (key) {
case KEY_UP:
param_svc_set_target(param_svc_get_target() + 0.5f);
display_svc_refresh();
break;
case KEY_DOWN:
param_svc_set_target(param_svc_get_target() - 0.5f);
display_svc_refresh();
break;
case KEY_MODE:
param_svc_set_mode(!param_svc_get_mode());
display_svc_refresh();
break;
}
}
}
6.4 应用层 —— 任务调度与初始化
app/src/main.c
#include "main_task.h"
#include "temp_svc.h"
#include "control_svc.h"
#include "param_svc.h"
#include "display_svc.h"
#include "comm_svc.h"
#include "key_svc.h"
#include "stm32f1xx_hal.h"
int main(void) {
HAL_Init();
// 初始化各层(驱动抽象层接口已在驱动层注册,此处只需调用服务层初始化)
param_svc_init();
temp_svc_init();
control_svc_init();
display_svc_init();
comm_svc_init();
key_svc_init();
uint32_t last_temp_time = 0;
uint32_t last_report_time = 0;
uint32_t last_display_time = 0;
const uint32_t TEMP_INTERVAL_MS = 200;
const uint32_t REPORT_INTERVAL_MS = 2000;
const uint32_t DISPLAY_INTERVAL_MS = 500;
while (1) {
uint32_t now = HAL_GetTick();
if (now - last_temp_time >= TEMP_INTERVAL_MS) {
last_temp_time = now;
temp_svc_update(); // 读取温度并滤波
control_svc_update(); // 控制加热器
}
if (now - last_display_time >= DISPLAY_INTERVAL_MS) {
last_display_time = now;
display_svc_refresh(); // 刷新LCD
}
if (now - last_report_time >= REPORT_INTERVAL_MS) {
last_report_time = now;
comm_svc_report(); // 串口上报
}
key_svc_task(); // 按键扫描(可20ms一次,这里简单放主循环)
HAL_Delay(10);
}
}
7. 可测试性实战:如何Mock DAL进行单元测试
由于服务层只依赖 g_temp_sensor 等抽象接口,我们可以编写模拟驱动,在PC上测试控制逻辑。
7.1 创建 Mock 驱动(用于测试)
// test/mock/mock_temp_sensor.c
#include "temp_sensor_if.h"
static float mock_temperature = 25.0f;
static int mock_read_celsius(float *temp) {
*temp = mock_temperature;
return 0;
}
static const TempSensorOps mock_ops = {
.read_celsius = mock_read_celsius
};
const TempSensorOps *g_temp_sensor = &mock_ops;
// 测试辅助函数
void mock_temp_sensor_set(float temp) { mock_temperature = temp; }
7.2 编写控制服务的单元测试(使用Unity框架示例)
// test/test_control_svc.c
#include "unity.h"
#include "control_svc.h"
#include "mock_temp_sensor.h"
#include "mock_heater.h"
#include "param_svc.h"
void setUp(void) {
// 初始化参数服务,设置目标温度为25.0,模式为自动
param_svc_init(); // 需提供mock storage
g_params.target_temp = 25.0f;
g_params.mode = 1;
}
void test_control_should_turn_on_heater_when_temp_below_threshold(void) {
mock_temp_sensor_set(23.5f); // 低于25-1=24
control_svc_update();
TEST_ASSERT_EQUAL(HEATER_ON, g_heater->get_state());
}
void test_control_should_turn_off_heater_when_temp_above_threshold(void) {
// 先让加热器开启
g_heater->set_state(HEATER_ON);
mock_temp_sensor_set(26.5f); // 高于25+1=26
control_svc_update();
TEST_ASSERT_EQUAL(HEATER_OFF, g_heater->get_state());
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_control_should_turn_on_heater_when_temp_below_threshold);
RUN_TEST(test_control_should_turn_off_heater_when_temp_above_threshold);
return UNITY_END();
}
效果:无需任何真实硬件,在PC上即可完成控制算法的单元测试,极大提升代码质量。
8. 可移植性实战:更换MCU/传感器只需改驱动层
8.1 更换MCU(从STM32换到GD32)
只需要重写硬件驱动层的文件,例如:
drivers/src/gpio_heater.c中修改GPIO寄存器操作为GD32的库函数。drivers/src/stm32_uart.c换为gd32_uart.c,实现相同的uart_if接口。- 服务层和应用层 一行代码都不需要改。
8.2 更换温度传感器(从DS18B20换到LM35)
- 编写
drivers/src/lm35.c,实现TempSensorOps接口:
static int lm35_read_celsius(float *temp) {
uint16_t adc_val = HAL_ADC_GetValue(); // 读取ADC
*temp = adc_val * 3.3f / 4096.0f * 100.0f; // 假设LM35输出10mV/℃
return 0;
}
static const TempSensorOps lm35_ops = { .read_celsius = lm35_read_celsius };
const TempSensorOps *g_temp_sensor = &lm35_ops; // 只需改变这个指针
- 在
main.c之前,确保g_temp_sensor指向新的驱动(通过链接或条件编译)。 - 服务层
temp_svc.c中调用g_temp_sensor->read_celsius()保持不变。
移植完成,上层逻辑完全不受影响。
9. 总结
通过分层架构和依赖倒置,我们实现了:
| 目标 | 实现方式 |
|---|---|
| 高内聚低耦合 | 每层只负责自己的职责:驱动层管寄存器,服务层管算法,应用层管调度。依赖方向单向。 |
| 可测试性 | 服务层依赖抽象接口,可替换为Mock驱动,在PC上完成单元测试。 |
| 可移植性 | 更换MCU或外设只需要重写硬件驱动层,服务层和应用层零修改。 |
| 清晰数据流向 | 输入(传感器/按键)→ 驱动抽象层 → 服务层处理 → 驱动抽象层 → 输出(LCD/加热器/串口)。 |
对比传统写法(如第一节所示),分层架构让代码:
- 更易于阅读理解(每个文件有明确职责)
- 更易于团队协作(接口稳定,并行开发)
- 更易于维护和扩展(新增功能只需添加新模块,不影响旧模块)
这就是嵌入式工程师从“码农”走向“架构师”的必经之路。
更多推荐


所有评论(0)