嵌入式五层级

原层级名称 文件夹名 规范说明
硬件驱动层 hardware_drivers 全语义命名,明确存放硬件驱动代码,与软件代码完全隔离
功能模块层 function_modules 明确存放纯功能组件,与业务代码完全隔离,边界清晰
应用接口层 application_interface 明确存放应用层接口定义与封装,统一全项目接口规范
业务逻辑层 business_logic 明确存放业务逻辑代码,与底层技术完全解耦
应用层 application 全语义命名,明确存放顶层应用、用户交互代码
层级 餐厅角色 核心职责 只关心什么? 不关心什么?
应用层 前台 / 服务员 1. 接待客人(接收用户按键 / 触摸)2. 给客人看菜单 / 上菜(OLED 显示数据)3. 把客人点的单递给后厨 用户怎么看、怎么操作 菜怎么做、后厨流程是什么
业务逻辑层 后厨 / 厨师长 1. 按菜单做菜(5 秒采集、判断 35℃)2. 控制出菜流程(上报云平台)3. 处理异常(记录日志) 业务规则是什么、流程怎么走 用盘子装还是用碗装、客人长什么样

1. 业务逻辑层(business):纯规则,无 UI

**这个文件夹里的代码,绝对不出现 "OLED"、"LCD"、"按键" 这些字眼!**它只默默在后台干活,通过 "函数返回值" 或 "全局变量 / 消息队列" 把结果告诉上层。

复制代码
// business_logic.c (业务逻辑层核心文件)

// 【业务规则1】:处理温湿度采集与报警
void Business_ProcessTempHumi(void) {
    // 1. 调用下层API拿数据(不关心数据怎么来的)
    float temp = API_GetTemperature(); 
    float humi = API_GetHumidity();

    // 2. 业务规则判断:超过35℃?(不关心报警是响铃还是屏幕显示)
    if (temp > g_tempThreshold) { 
        g_isAlarmActive = true; // 置报警标志位
        Business_LogError("Temperature too high!"); // 记录日志
    } else {
        g_isAlarmActive = false;
    }

    // 3. 业务规则:上报云平台(不关心上报成功后界面要不要提示)
    if (g_timeToReport) {
        API_MqttPublish(temp, humi);
    }
}

// 【业务规则2】:设置温度阈值(只关心规则,不关心是按键设置还是APP设置)
void Business_SetTempThreshold(float newThreshold) {
    if (newThreshold > 0 && newThreshold < 100) { // 业务合法性校验
        g_tempThreshold = newThreshold;
        Business_LogInfo("Threshold updated.");
    }
}

2. 应用层(app):纯 UI / 交互,无规则

这个文件夹里的代码,绝对不出现 "35℃"、"5 秒"、"MQTT" 这些业务字眼! 它只负责两件事:画界面响应用户按键

复制代码
// app_ui.c (应用层核心文件)

// 【应用层职责1】:刷新OLED显示(只关心怎么画,不关心数据哪来的、规则是什么)
void App_RefreshScreen(void) {
    // 1. 从业务逻辑层"拿状态"(但不参与判断)
    float showTemp = g_currentTemp;       // 直接拿变量
    float showHumi = g_currentHumi;
    bool showAlarm = g_isAlarmActive;
    int wifiStatus = API_GetWifiStatus(); // 也可以调用API拿

    // 2. 纯UI绘制(这里全是画线、画字的代码,没有任何业务逻辑)
    OLED_Clear();
    OLED_ShowString(0, 0, "Temp: ");
    OLED_ShowFloat(0, 1, showTemp); // 显示温度,不管它是不是超过35度
    
    if (showAlarm) { // 只是根据标志位决定"要不要画个感叹号",不做判断
        OLED_ShowString(100, 0, "!ALARM!"); 
    }
    
    // 显示WiFi图标
    if (wifiStatus == WIFI_CONNECTED) {
        OLED_DrawWifiIcon(80, 0);
    }
}

// 【应用层职责2】:处理按键(只关心用户按了什么,不关心业务怎么处理)
void App_KeyHandler(void) {
    if (Key_Scan() == KEY_PLUS) {
        // 用户按了加号,我只负责把"加阈值"的请求传给业务层
        // 绝对不在这里写 if (threshold > 35) 这种业务判断!
        float newVal = g_tempThreshold + 0.5;
        Business_SetTempThreshold(newVal); // 甩锅给业务逻辑层
    }
    
    if (Key_Scan() == KEY_OK) {
        // 用户按了确定,手动触发上报
        // 我只负责触发,不关心上报的具体协议和流程
        Business_TriggerManualReport(); 
    }
}

三、终极判断标准:做个 "换屏测试"

想知道自己分得对不对?想象一下:

如果我明天把 OLED 屏换成了 LCD 屏,或者甚至把屏幕拆了换成手机 APP 控制,我需要改哪层的代码?

  • 需要改的应用层(因为显示方式变了,交互方式变了)。
  • 一行都不用改的业务逻辑层(因为 "5 秒采集、35℃报警" 这个核心规则没变)。

这就是分层的最大意义:解耦! 以后产品经理说 "把报警阈值改成 40℃",你只需要改 business 里的一行代码;说 "把界面颜色改成红色",你只需要改 app 里的绘图代码,两者互不干扰。

层级 角色定位 核心职责 关键词
功能模块层 工具箱里的工具 提供纯技术能力(比如 "一把螺丝刀"、"一个计算器"),它不知道 "业务" 是什么,只知道 "我能拧螺丝"、"我能算数学题"。 纯技术、无业务、可复用
应用接口层 工具箱的统一把手 把工具包装一下,给上层一个简单的操作入口。它告诉上层:"你不用管螺丝刀怎么造的,按这个按钮就能拧螺丝"。 封装、统一、屏蔽细节

二、实战例子 1:MQTT 云平台上报

我们要实现 "把温湿度数据上报到阿里云" 这个需求。

1. 功能模块层(function_modules):纯 MQTT 协议工具

这里的代码完全不知道 "温湿度" 是什么,它只知道 "MQTT 协议怎么连、怎么发消息"。这是一个可以放到任何项目里用的通用模块。

复制代码
// function_modules/mqtt_protocol.c (功能模块层:纯MQTT工具)

// 【纯技术功能】:连接MQTT服务器(不关心连的是阿里云还是腾讯云)
int MQTT_Connect(char *broker_ip, int port, char *client_id) {
    // 这里全是底层的Socket连接、MQTT握手报文、Keepalive设置
    // 没有任何业务逻辑,就是纯协议实现
    return socket_status;
}

// 【纯技术功能】:发布消息(不关心发的是温湿度还是开关状态)
int MQTT_Publish(char *topic, char *payload, int qos) {
    // 这里只负责把payload字符串通过MQTT发出去
    // 它甚至不认识payload里的内容
    return mqtt_result;
}

2. 应用接口层(application_interface):封装成业务接口

这里的代码把上面的通用工具,包装成了业务逻辑层能用的 "专属接口"。它屏蔽了 "MQTT Topic 是什么"、"QoS 是 0 还是 1" 这些技术细节。

复制代码
// application_interface/cloud_api.c (应用接口层:统一封装)

// 【统一接口】:上报温湿度(给业务逻辑层用的)
void API_ReportTempHumi(float temp, float humi) {
    // 1. 把业务数据(温湿度)转换成技术格式(JSON字符串)
    char json_payload[128];
    sprintf(json_payload, "{\"temp\":%.1f, \"humi\":%.1f}", temp, humi);

    // 2. 调用功能模块层的"纯工具",但屏蔽了技术细节
    // 业务层不需要知道Topic是 "/device/sensor/data",也不需要知道QoS是1
    MQTT_Publish("/device/sensor/data", json_payload, 1);
}

// 【统一接口】:初始化云连接(业务层不用管IP和端口)
void API_CloudInit(void) {
    // 直接封装好连接阿里云的固定参数
    MQTT_Connect("aliyun.com", 1883, "dev_001");
}

三、实战例子 2:温湿度数据滤波

传感器读出来的数据通常有毛刺,我们需要滤波。

1. 功能模块层(function_modules):纯算法工具

这里的代码不知道 "温度" 是什么,它只知道 "给我一组数组,我能算出平均值"。这是一个数学工具。

复制代码
// function_modules/filter.c (功能模块层:纯算法工具)

// 【纯技术功能】:滑动均值滤波(不关心输入的是温度还是电压)
float Filter_MovingAverage(float *data_buf, int buf_size) {
    float sum = 0;
    for (int i = 0; i < buf_size; i++) {
        sum += data_buf[i];
    }
    return sum / buf_size; // 纯数学计算,没有业务
}

2. 应用接口层(application_interface):封装成业务接口

这里的代码把算法和传感器驱动捏合在一起,给上层一个 "一键拿干净数据" 的接口。

复制代码
// application_interface/sensor_api.c (应用接口层:统一封装)

// 定义一个全局缓冲区(应用接口层负责管理数据缓存)
static float temp_buf[10]; 

// 【统一接口】:获取"干净的"温度(业务层的最爱)
float API_GetCleanTemperature(void) {
    // 1. 调用底层驱动(硬件驱动层),读一个"脏数据"
    float raw_temp = DRV_SHT30_ReadRawTemp();

    // 2. 把脏数据放进缓冲区(应用接口层负责维护这个流程)
    for (int i = 0; i < 9; i++) temp_buf[i] = temp_buf[i+1];
    temp_buf[9] = raw_temp;

    // 3. 调用功能模块层的"纯算法工具"滤波
    // 业务层不需要知道缓冲区是10个还是20个,也不需要知道用的是均值滤波
    return Filter_MovingAverage(temp_buf, 10);
}

好的!我们用 **"一条完整的温湿度数据双向流转链路",把五个层级像串糖葫芦一样串起来,结合 大白话定位 **、"吃什么 / 产什么""绝对禁区"项目实例,保证你彻底通透。


整体链路预览

我们先看两条核心数据流,建立全局认知:

  1. 上行链路(硬件→用户):传感器采集数据 → 最终显示在 OLED 屏上
  2. 下行链路(用户→硬件):用户按下按键设置阈值 → 最终保存到硬件 Flash

层级 1:硬件驱动层(hardware_drivers)

🎯 核心定位

系统的 **"手脚与神经末梢"**,唯一直接触碰物理硬件的层级。它是软件与硬件之间的 "翻译官":把硬件的电信号翻译成软件能读的数字,把软件的指令翻译成硬件的动作。

  • 类比:餐厅里直接对接农场的「采购员 / 食材分拣员」,只负责把地里的菜(电信号)运回后厨,不管菜是用来做鱼香肉丝还是宫保鸡丁。

📥 吃什么(输入)

硬件寄存器地址、GPIO 引脚号、SPI/I2C 时序命令、原始电信号。

📤 产什么(输出)

硬件原始数据(如 SHT30 的 ADC 值)、硬件执行状态(如 "GPIO 置高成功")。

🚫 绝对禁区

绝对不能出现:任何业务逻辑(如 "超过 35℃")、任何 UI 交互(如 "OLED 显示文字")、任何算法(如 "滤波")。

💡 项目实例

  • DRV_SHT30_ReadRawADC():直接读 SHT30 传感器的 16 位原始 ADC 值(不做任何计算)
  • DRV_OLED_WriteSpiCmd(uint8_t cmd):通过 SPI 向 OLED 屏写命令字节(不管命令是用来清屏还是画点)
  • DRV_Flash_Write(uint32_t addr, uint8_t *data):向 Flash 指定地址写数据(不管存的是阈值还是日志)

层级 2:功能模块层(function_modules)

🎯 核心定位

系统的 **"纯技术工具箱"**,里面全是 "通用武器"。它完全不知道你的项目是 "温湿度计" 还是 "智能门锁",只知道 "我能算数学题"、"我能打包 JSON"、"我能管理文件"。

  • 类比:餐厅里的「厨具供应商」,只卖菜刀、砧板、烤箱,不管餐厅做川菜还是粤菜。

📥 吃什么(输入)

纯技术参数(如一个 float 数组、一个字符串、一个整数)。

📤 产什么(输出)

处理后的纯技术结果(如平均值、排序后的数组、MQTT 连接状态码)。

🚫 绝对禁区

绝对不能出现:任何业务名词(如 "温湿度"、"报警")、任何硬件寄存器操作、任何 UI 相关代码。

💡 项目实例

  • Filter_MovingAverage(float *buf, uint8_t len):滑动均值滤波算法(输入是数组,输出是平均值,不知道这是温度数据)
  • MQTT_Connect(char *ip, uint16_t port):纯 MQTT 协议连接(不知道连接的是阿里云还是自己家的服务器)
  • Json_PackFloat(char *key, float val):JSON 打包工具(不知道打包的是温度还是电压)

层级 3:应用接口层(application_interface)

🎯 核心定位

系统的 **"中间翻译官 + 打包员"**,承上启下的关键枢纽。它把下层 "零散的工具" 和 " raw 的数据" 打包成上层能用的 "套餐",彻底屏蔽所有技术细节,让上层专心写业务。

  • 类比:餐厅里的「配菜师」,把采购员的菜(驱动数据)洗好切好,把供应商的厨具(功能模块)准备好,递给厨师长(业务层)说:"料都备好了,您直接炒就行。"

📥 吃什么(输入)

上层的简单指令(如 "给我一个干净的温度")。

📤 产什么(输出)

封装好的业务级数据 / 接口(如滤波后的温度值、一键上报函数)。

🚫 绝对禁区

绝对不能出现:UI 交互(如 "按键扫描")、业务规则判断(如 "超过 35℃报警")。

💡 项目实例

  • API_GetCleanTemp(void):整合「驱动层读 ADC」+「模块层滤波」,直接返回给上层一个能用的温度值
  • API_ReportTempHumi(float t, float h):整合「模块层 JSON 打包」+「模块层 MQTT 发送」,上层只需传两个数,不用管 Topic 是什么
  • API_SaveThreshold(float val):整合「驱动层 Flash 操作」,上层不用管 Flash 地址是多少

层级 4:业务逻辑层(business_logic)

🎯 核心定位

系统的 **"大脑 / 厨师长",产品灵魂所在。它负责所有 业务规则 **、流程编排决策判断。它只关心 "产品需求是什么",完全不关心 "硬件是 SPI 还是 I2C"、"屏幕是 OLED 还是 LCD"。

  • 类比:餐厅里的「厨师长」,看着菜单(产品需求)指挥:"5 分钟后出菜"、"这个菜太咸了重做"、"把这个外卖送出去"。

📥 吃什么(输入)

应用接口层的封装数据(如干净的温度、WiFi 连接状态)。

📤 产什么(输出)

业务状态(如报警标志位、日志记录)、对下层的指令(如 "上报数据"、"保存阈值")。

🚫 绝对禁区

绝对不能出现:硬件操作(如 "写 SPI 寄存器")、UI 绘制(如 "画 OLED 像素")。

💡 项目实例

  • Business_Run5sCycle(void):核心业务循环 ------ 每 5 秒调用 API 拿温度 → 判断是否超过 35℃ → 置报警标志 → 调用 API 上报云平台 → 调用 API 记日志
  • Business_SetTempThreshold(float newVal):业务规则校验 ------ 判断 newVal 是否在 0-100℃之间 → 合法则调用 API 保存,不合法则调用 API 记错误日志

层级 5:应用层(application)

🎯 核心定位

系统的 **"脸面 / 服务员"**,唯一直接对接用户的层级。它只负责两件事:把数据展示给用户看接收用户的操作指令。它是用户唯一能感知到的部分。

  • 类比:餐厅里的「服务员」,给客人递菜单(OLED 显示)、记录客人点的菜(按键扫描)、把做好的菜端上桌(更新界面)。

📥 吃什么(输入)

用户操作(如按键按下、触摸屏点击)、业务层的状态(如报警标志位、当前温度值)。

📤 产什么(输出)

UI 界面(如 OLED 显示的温度数字、报警图标)、对业务层的指令(如 "把阈值加 0.5℃")。

🚫 绝对禁区

绝对不能做:业务规则判断(如 "阈值是否合法")、直接操作硬件寄存器。

💡 项目实例

  • App_RefreshOledScreen(void):UI 刷新 ------ 从业务层拿温度 / 报警标志 → 调用 OLED 绘图函数 → 画温度数字 → 如果报警则画个红色感叹号
  • App_KeyScanHandler(void):按键处理 ------ 扫描按键 → 如果是 "+" 键,则调用业务层的Business_SetTempThreshold()函数 → 如果是 "OK" 键,则调用业务层的Business_TriggerManualReport()函数

终极复盘:完整走一遍数据流

上行(传感器→OLED 显示):

  1. 硬件驱动层 :SHT30 硬件产生电信号 → DRV_SHT30_ReadRawADC() 读出 16 位原始值
  2. 功能模块层Filter_MovingAverage() 对原始值数组滤波
  3. 应用接口层API_GetCleanTemp() 整合上述两步,返回干净温度
  4. 业务逻辑层Business_Run5sCycle() 拿到温度 → 判断是否超过 35℃ → 置位g_AlarmFlag
  5. 应用层App_RefreshOledScreen() 拿到温度和g_AlarmFlag → 画在 OLED 屏上

下行(按键→保存阈值):

  1. 应用层 :用户按下 "+" 键 → App_KeyScanHandler() 捕获按键
  2. 业务逻辑层 :调用Business_SetTempThreshold(35.5) → 校验 35.5 是否合法
  3. 应用接口层 :调用API_SaveThreshold(35.5)
  4. 硬件驱动层DRV_Flash_Write() 把 35.5 写入 Flash 硬件保存

场景一:设备上电全流程初始化(冷启动)

覆盖层级 :全 5 层,从底层硬件到顶层界面。数据流方向:自下而上(硬件→应用)。

  1. 硬件驱动层:依次初始化系统时钟、GPIO 引脚复用、SHT30 传感器、OLED 显示屏、Flash 存储、WiFi 射频模块。
  2. 功能模块层:初始化滑动滤波数据缓冲区、MQTT 协议栈状态机、日志环形缓冲区。
  3. 应用接口层:调用下层初始化函数,封装成统一的 "系统启动" 接口,完成硬件与模块的联动自检。
  4. 业务逻辑层:从接口层读取 Flash 中保存的历史温度阈值,初始化业务状态机(清零报警标志、启动 5 秒定时器、设置默认上报周期)。
  5. 应用层:初始化 UI 界面框架,OLED 屏显示开机 Logo,等待 2 秒后自动切换到主监控界面。

场景二:用户按键手动触发数据上报(人机交互闭环)

覆盖层级 :应用层→业务层→接口层→模块层→驱动层→(云平台)→反向反馈。数据流方向:下行(用户→硬件)+ 上行(硬件→云→反馈)。

  1. 应用层:用户按下 "OK" 按键,UI 按键扫描函数捕获到 "手动上报" 指令。
  2. 业务逻辑层:收到指令,立即标记 "立即上报" 状态,暂停 5 秒定时逻辑,优先执行手动流程。
  3. 应用接口层:调用 "获取干净温湿度" 接口,同时调用 "云平台上报" 接口。
  4. 功能模块层:滤波模块处理数据,JSON 模块打包数据,MQTT 模块封装网络报文。
  5. 硬件驱动层:WiFi 硬件通过射频将报文发送至云平台。
  6. 业务逻辑层:收到驱动层的 "发送成功" 回调,记录一条 "手动上报成功" 的日志。
  7. 应用层:OLED 屏主界面短暂弹出 "上报成功" 的提示框,3 秒后自动消失。

场景三:温度超阈值触发硬件报警(联动控制)

覆盖层级 :驱动层→模块层→接口层→业务层→接口层→驱动层(蜂鸣器)+ 应用层。数据流方向:上行采集 + 下行控制 + 界面反馈。

  1. 硬件驱动层:SHT30 传感器硬件采集环境数据,通过 I2C 接口读出 16 位原始 ADC 值。
  2. 功能模块层:滑动均值滤波模块对最近 10 次原始值进行滤波计算,去除毛刺。
  3. 应用接口层:整合驱动与模块,直接返回一个滤波后的标准浮点数温度值。
  4. 业务逻辑层:5 秒定时周期到达,读取温度值,与阈值比较,判断超过 35℃,立即置位 "系统报警" 标志位。
  5. 应用接口层:收到业务层指令,调用 "报警控制" 接口。
  6. 硬件驱动层:蜂鸣器对应的 GPIO 引脚被置高电平,蜂鸣器开始发出 "滴滴" 声。
  7. 应用层:UI 刷新时检测到报警标志,OLED 屏背景色变为红色,中央显示 "高温报警!" 字样。

场景四:WiFi 连接状态实时监控与显示(状态同步)

覆盖层级 :驱动层→模块层→接口层→业务层→应用层。数据流方向:自下而上(硬件状态→用户界面)。

  1. 硬件驱动层:WiFi 硬件底层检测到连接状态变化(如路由器断开重连),产生一个硬件中断信号。
  2. 功能模块层:WiFi 管理模块在中断中更新内部连接状态变量(连接中 / 已连接 / 断开)。
  3. 应用接口层:封装 "获取 WiFi 连接状态" 接口,上层只需调用,无需关心 WiFi 底层是如何扫描和握手的。
  4. 业务逻辑层:每秒轮询一次 WiFi 状态。如果状态变为 "断开",则停止数据上报,改为 "离线缓存模式";如果变为 "已连接",则恢复上报并补发缓存数据。
  5. 应用层:每次 UI 刷新(每秒 1 次)时,通过接口层读取当前状态,在 OLED 屏右上角绘制对应的图标(满格 WiFi / 空格 WiFi / 加载中旋转图标)。

场景五:业务异常自动记录日志(数据持久化)

覆盖层级 :业务层→接口层→模块层→驱动层→反向提示应用层。数据流方向:自上而下(业务逻辑→硬件存储)。

  1. 业务逻辑层:在尝试读取传感器数据时,连续 3 次收到接口层返回的 "读取失败" 错误码,判定为 "传感器硬件故障"。
  2. 业务逻辑层:生成一条异常事件,包含故障代码、发生时间戳。
  3. 应用接口层:调用 "记录系统日志" 接口,传入故障信息。
  4. 功能模块层:日志工具模块将故障信息格式化为标准字符串,并计算数据校验和。
  5. 硬件驱动层:将格式化后的日志字符串写入 Flash 硬件的指定日志存储扇区。
  6. 业务逻辑层:确认写入成功后,置位 "有未读异常日志" 的状态标志。
  7. 应用层:下次 UI 刷新时,检测到该标志,在 OLED 屏右下角显示一个闪烁的小 "警告" 图标,提示用户查看。
相关推荐
天疆说2 小时前
Ubuntu 安装微软核心字体
ubuntu·microsoft·php
IT技术分享社区3 小时前
科技资讯:微软Win11再添新功能,屏幕色调可自定义,还能缓解眼疲劳
windows·科技·microsoft·微软·业界资讯
Leinwin1 天前
微软与OpenAI合作松绑:企业如何选择合规、稳定的AI服务?
microsoft·azure
sheji1051 天前
割草机器人行业市场分析报告
大数据·人工智能·microsoft
꯭爿꯭巎꯭1 天前
powertoys下载 微软powertoys中文版安装
microsoft
GEO_NEWS1 天前
2026年GEO选型全景透视:技术路径、适配场景与决策逻辑深度解析
人工智能·microsoft
默 语1 天前
从 0 到 1 实战:魔珐星云 SDK 搭建实时交互屏幕助手(附可直接运行源码)
gpt·microsoft·开源·prompt·aigc·ai写作·agi
bugcome_com1 天前
WPF + Microsoft.ToolKit.Mvvm 技术指南与实战项目
microsoft·wpf