返回博客
从零设计一个模块化嵌入式系统:接口标准化与软件分层

从零设计一个模块化嵌入式系统:接口标准化与软件分层

为什么要模块化

嵌入式系统复杂度随功能增加而指数增长。模块化设计将系统拆分为独立模块,降低耦合度,提高可维护性和可扩展性。

复用性

通用模块可在多个项目中复用,减少重复开发。如 RS485 通讯模块、ADC 采集模块可复用于不同设备。

可维护性

模块独立修改不影响其他模块,降低维护成本。如更换 MCU 型号只需修改硬件抽象层,应用层代码不变。

可扩展性

新增功能通过添加模块实现,无需重构现有代码。如新增 CAN 通讯只需添加 CAN 模块。

团队协作

不同模块可由不同人员并行开发,提高开发效率。

硬件接口标准化设计

统一连接器

使用标准连接器定义模块接口,确保模块可插拔。

推荐连接器:

  • 板间连接:2.54mm 排针排母
  • 外部接口:航空插头、端子排
  • 高速信号:板对板连接器

引脚定义规范

定义统一的引脚功能,避免混乱。

示例:通用 IO 模块接口定义

// 模块接口引脚定义
typedef struct {
    uint8_t vcc_5v;      // 5V 电源
    uint8_t vcc_3v3;     // 3.3V 电源
    uint8_t gnd;         // 地
    uint8_t dio[4];      // 数字 IO
    uint8_t aio[2];      // 模拟输入
    uint8_t uart_tx;     // UART 发送
    uint8_t uart_rx;     // UART 接收
    uint8_t i2c_sda;     // I2C 数据
    uint8_t i2c_scl;     // I2C 时钟
} Module_Interface_t;

电源规范

统一电源电压和电流规格:

  • 主电源:24V DC(工业标准)
  • 逻辑电源:5V DC(TTL 电平)
  • 核心电源:3.3V DC(CMOS 电平)
  • 电流限制:每个模块最大 500mA

信号规范

定义信号电平和时序标准:

  • 数字输入:0-0.8V 为低,2.0-5.0V 为高
  • 模拟输入:0-3.3V,12 位精度
  • 通讯速率:UART 9600-115200bps,I2C 100kHz-400kHz

软件分层架构

软件分层将系统划分为硬件抽象层、驱动层、协议层、应用层,各层职责清晰。

硬件抽象层(HAL)

HAL 层屏蔽硬件差异,提供统一接口。

// GPIO 抽象接口
typedef struct {
    void (*init)(uint8_t pin, uint8_t mode);
    void (*write)(uint8_t pin, uint8_t value);
    uint8_t (*read)(uint8_t pin);
} GPIO_Driver_t;

// STM32 实现
void STM32_GPIO_Init(uint8_t pin, uint8_t mode) {
    // STM32 GPIO 初始化
}

void STM32_GPIO_Write(uint8_t pin, uint8_t value) {
    // STM32 GPIO 写入
}

uint8_t STM32_GPIO_Read(uint8_t pin) {
    // STM32 GPIO 读取
    return 0;
}

GPIO_Driver_t gpio_driver = {
    .init = STM32_GPIO_Init,
    .write = STM32_GPIO_Write,
    .read = STM32_GPIO_Read
};

驱动层

驱动层实现具体外设驱动,如 UART、SPI、I2C。

// UART 驱动接口
typedef struct {
    int (*init)(uint32_t baudrate);
    int (*send)(uint8_t *data, uint16_t len);
    int (*recv)(uint8_t *data, uint16_t len);
} UART_Driver_t;

// UART 驱动实现
int UART_Init(uint32_t baudrate) {
    // UART 初始化
    return 0;
}

int UART_Send(uint8_t *data, uint16_t len) {
    // UART 发送
    return 0;
}

int UART_Recv(uint8_t *data, uint16_t len) {
    // UART 接收
    return 0;
}

协议层

协议层实现通讯协议,如 Modbus、CANopen。

// Modbus 协议接口
typedef struct {
    int (*parse)(uint8_t *data, uint16_t len);
    int (*build)(uint8_t *data, uint16_t *len);
} Modbus_Protocol_t;

int Modbus_Parse(uint8_t *data, uint16_t len) {
    // Modbus 帧解析
    return 0;
}

int Modbus_Build(uint8_t *data, uint16_t *len) {
    // Modbus 帧构造
    return 0;
}

应用层

应用层实现业务逻辑,调用下层接口。

void App_Task(void) {
    uint8_t data[256];
    uint16_t len;
    
    // 接收数据
    UART_Recv(data, &len);
    
    // 解析协议
    Modbus_Parse(data, len);
    
    // 处理业务逻辑
    Process_Data(data, len);
    
    // 构造响应
    Modbus_Build(data, &len);
    
    // 发送响应
    UART_Send(data, len);
}

回调函数模式实现解耦

回调函数实现模块间松耦合,驱动层通过回调通知应用层。

回调函数定义

// 定义回调函数类型
typedef void (*UART_RxCallback_t)(uint8_t *data, uint16_t len);

// UART 驱动
typedef struct {
    UART_RxCallback_t rx_callback;
} UART_Driver_t;

// 注册回调函数
void UART_Register_Callback(UART_RxCallback_t callback) {
    uart_driver.rx_callback = callback;
}

// UART 中断中调用回调
void UART_IRQHandler(void) {
    if (UART_Get_Flag(UART_FLAG_RXNE)) {
        uint8_t data = UART_Receive_Byte();
        uart_rx_buffer[uart_rx_index++] = data;
        
        if (uart_rx_index >= UART_RX_BUFFER_SIZE) {
            if (uart_driver.rx_callback != NULL) {
                uart_driver.rx_callback(uart_rx_buffer, uart_rx_index);
            }
            uart_rx_index = 0;
        }
    }
}

应用层实现回调

// 应用层回调函数
void UART_Rx_Handler(uint8_t *data, uint16_t len) {
    // 处理接收到的数据
    Modbus_Process(data, len);
}

// 注册回调
void App_Init(void) {
    UART_Init(19200);
    UART_Register_Callback(UART_Rx_Handler);
}

跨平台移植策略

条件编译

通过宏定义区分不同平台。

// 平台检测
#if defined(STM32F103)
    #include "stm32f1xx_hal.h"
    #define PLATFORM_STM32
#elif defined(ESP32)
    #include "esp32_hal.h"
    #define PLATFORM_ESP32
#elif defined(LINUX)
    #include <linux/gpio.h>
    #define PLATFORM_LINUX
#endif

// 平台相关实现
#if defined(PLATFORM_STM32)
    void GPIO_Init(uint8_t pin, uint8_t mode) {
        // STM32 实现
    }
#elif defined(PLATFORM_ESP32)
    void GPIO_Init(uint8_t pin, uint8_t mode) {
        // ESP32 实现
    }
#elif defined(PLATFORM_LINUX)
    void GPIO_Init(uint8_t pin, uint8_t mode) {
        // Linux 实现
    }
#endif

弱引用

使用弱引用实现默认实现,平台可选择性覆盖。

// 默认实现(弱引用)
__attribute__((weak)) void Platform_Delay_ms(uint32_t ms) {
    // 默认实现(空循环)
    for (volatile uint32_t i = 0; i < ms * 1000; i++);
}

// 平台特定实现(覆盖弱引用)
void Platform_Delay_ms(uint32_t ms) {
    HAL_Delay(ms);
}

硬件抽象层接口

定义统一 HAL 接口,各平台实现。

// HAL 接口定义
typedef struct {
    void (*gpio_init)(uint8_t pin, uint8_t mode);
    void (*gpio_write)(uint8_t pin, uint8_t value);
    uint8_t (*gpio_read)(uint8_t pin);
    void (*uart_init)(uint32_t baudrate);
    void (*uart_send)(uint8_t *data, uint16_t len);
    void (*delay_ms)(uint32_t ms);
} HAL_Interface_t;

// 平台实现
HAL_Interface_t stm32_hal = {
    .gpio_init = STM32_GPIO_Init,
    .gpio_write = STM32_GPIO_Write,
    .gpio_read = STM32_GPIO_Read,
    .uart_init = STM32_UART_Init,
    .uart_send = STM32_UART_Send,
    .delay_ms = HAL_Delay
};

// 使用 HAL 接口
HAL_Interface_t *hal = &stm32_hal;
hal->gpio_write(LED_PIN, 1);

配置管理

Flash 存储

使用 Flash 存储配置参数,掉电不丢失。

#include "stm32f1xx_hal_flash.h"

#define CONFIG_FLASH_ADDR 0x08080000  // Flash 最后 64KB

typedef struct {
    uint32_t magic;           // 魔数校验
    uint8_t device_addr;      // 设备地址
    uint32_t baudrate;        // 波特率
    uint8_t parity;           // 校验位
    uint16_t crc;             // CRC 校验
} Config_t;

Config_t config;

// 读取配置
int Config_Read(void) {
    Config_t *flash_config = (Config_t *)CONFIG_FLASH_ADDR;
    
    // 校验魔数
    if (flash_config->magic != 0x5A5A5A5A) {
        return -1;  // 配置无效
    }
    
    // 校验 CRC
    uint16_t crc_calc = CRC16((uint8_t *)flash_config, sizeof(Config_t) - 2);
    if (crc_calc != flash_config->crc) {
        return -1;  // CRC 错误
    }
    
    memcpy(&config, flash_config, sizeof(Config_t));
    return 0;
}

// 写入配置
int Config_Write(void) {
    config.magic = 0x5A5A5A5A;
    config.crc = CRC16((uint8_t *)&config, sizeof(Config_t) - 2);
    
    HAL_FLASH_Unlock();
    
    // 擦除 Flash
    FLASH_ErasePage(CONFIG_FLASH_ADDR);
    
    // 写入配置
    uint32_t *data = (uint32_t *)&config;
    for (int i = 0; i < sizeof(Config_t) / 4; i++) {
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 
                         CONFIG_FLASH_ADDR + i * 4, 
                         data[i]);
    }
    
    HAL_FLASH_Lock();
    return 0;
}

默认值管理

配置无效时加载默认值。

void Config_Load_Default(void) {
    config.magic = 0x5A5A5A5A;
    config.device_addr = 1;
    config.baudrate = 19200;
    config.parity = 0;
    config.crc = CRC16((uint8_t *)&config, sizeof(Config_t) - 2);
}

void Config_Init(void) {
    if (Config_Read() != 0) {
        Config_Load_Default();
        Config_Write();
    }
}

版本兼容性处理

版本号管理

定义软件版本号和配置版本号。

#define SOFTWARE_VERSION "1.0.0"
#define CONFIG_VERSION 1

typedef struct {
    uint32_t magic;
    uint16_t config_version;   // 配置版本
    uint8_t device_addr;
    uint32_t baudrate;
    uint8_t parity;
    // 保留字段用于扩展
    uint8_t reserved[8];
    uint16_t crc;
} Config_t;

向后兼容

新版本支持旧配置格式。

int Config_Read(void) {
    Config_t *flash_config = (Config_t *)CONFIG_FLASH_ADDR;
    
    if (flash_config->magic != 0x5A5A5A5A) {
        return -1;
    }
    
    // 旧版本兼容处理
    if (flash_config->config_version < CONFIG_VERSION) {
        // 迁移旧配置到新格式
        config.device_addr = flash_config->device_addr;
        config.baudrate = flash_config->baudrate;
        config.parity = flash_config->parity;
        // 设置新字段默认值
        config.config_version = CONFIG_VERSION;
        Config_Write();
    } else {
        memcpy(&config, flash_config, sizeof(Config_t));
    }
    
    return 0;
}

完整模块示例

RS485 模块

硬件接口:

  • VCC:5V
  • GND:地
  • A:RS485 A 线
  • B:RS485 B 线
  • DE/RE:收发控制

软件接口:

// rs485.h
#ifndef RS485_H
#define RS485_H

#include <stdint.h>

typedef struct {
    void (*init)(uint32_t baudrate);
    void (*send)(uint8_t *data, uint16_t len);
    int (*recv)(uint8_t *data, uint16_t len, uint32_t timeout);
    void (*set_mode_tx)(void);
    void (*set_mode_rx)(void);
} RS485_Driver_t;

extern RS485_Driver_t rs485_driver;

#endif
// rs485.c
#include "rs485.h"
#include "stm32f1xx_hal.h"

UART_HandleTypeDef huart1;

void RS485_Init(uint32_t baudrate) {
    huart1.Instance = USART1;
    huart1.Init.BaudRate = baudrate;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_TX_RX;
    HAL_UART_Init(&huart1);
}

void RS485_Send(uint8_t *data, uint16_t len) {
    RS485_Set_Mode_TX();
    HAL_UART_Transmit(&huart1, data, len, 1000);
    RS485_Set_Mode_RX();
}

int RS485_Recv(uint8_t *data, uint16_t len, uint32_t timeout) {
    return HAL_UART_Receive(&huart1, data, len, timeout);
}

void RS485_Set_Mode_TX(void) {
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
    HAL_Delay(1);
}

void RS485_Set_Mode_RX(void) {
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
    HAL_Delay(1);
}

RS485_Driver_t rs485_driver = {
    .init = RS485_Init,
    .send = RS485_Send,
    .recv = RS485_Recv,
    .set_mode_tx = RS485_Set_Mode_TX,
    .set_mode_rx = RS485_Set_Mode_RX
};

模块化设计检查清单

硬件设计

  • 统一连接器型号
  • 引脚定义文档化
  • 电源电压和电流规格明确
  • 信号电平和时序规范定义
  • PCB 布局模块化,便于更换

软件设计

  • 分层架构清晰(HAL、驱动、协议、应用)
  • 接口统一,文档完善
  • 回调函数实现解耦
  • 条件编译支持跨平台
  • 配置管理完善(Flash 存储、默认值、版本兼容)

测试验证

  • 单模块独立测试
  • 模块间接口测试
  • 跨平台移植测试
  • 版本兼容性测试

总结

模块化嵌入式系统设计关键点:

  • 硬件接口标准化(统一连接器、引脚定义、电源规范)
  • 软件分层架构(HAL、驱动、协议、应用层)
  • 回调函数实现模块间解耦
  • 条件编译和弱引用支持跨平台移植
  • Flash 存储配置,支持默认值和版本兼容
  • 完整的文档和测试验证

按此设计可构建高复用、易维护、可扩展的嵌入式系统。