文章目录
- 一、架构总览
-
-
- 单片机程序分层架构总览
- 详细分层说明
-
- [1. 硬件抽象层 (Hardware Abstraction Layer - HAL)](#1. 硬件抽象层 (Hardware Abstraction Layer - HAL))
- [2. 驱动层 (Driver Layer)](#2. 驱动层 (Driver Layer))
- [3. 业务逻辑层 (Business Logic Layer - BLL) / 中间件层 (Middleware Layer)](#3. 业务逻辑层 (Business Logic Layer - BLL) / 中间件层 (Middleware Layer))
- [4. 应用层 (Application Layer)](#4. 应用层 (Application Layer))
- 总结与优势
-
- 二、更详细一点的层
-
-
- [1. **硬件抽象层(Hardware Abstraction Layer, HAL)**](#1. 硬件抽象层(Hardware Abstraction Layer, HAL))
- [2. **驱动层(Driver Layer)**](#2. 驱动层(Driver Layer))
- [3. **中间层(Middleware Layer)**](#3. 中间层(Middleware Layer))
- [4. **接口层(Interface Layer)**](#4. 接口层(Interface Layer))
- [5. **板级支持包(Board Support Package, BSP)**](#5. 板级支持包(Board Support Package, BSP))
- 分层设计的优势
-
一、架构总览
一个结构良好的单片机程序通常会被划分为不同的层次,每一层都职责明确,从而提高代码的可读性、可维护性、可移植性和可测试性。
典型的单片机程序可以划分为以下四个核心层次,从最接近硬件的到最接近用户的逻辑。
单片机程序分层架构总览
下图清晰地展示了常见的四层架构及其依赖关系:
单片机程序分层架构 应用层
App Layer 业务逻辑层
BLL Layer 驱动层
Driver Layer 硬件抽象层
HAL Layer 单片机
MCU & 外设
详细分层说明
下面我们来详细解释每一层的职责、使用方法和示例。
1. 硬件抽象层 (Hardware Abstraction Layer - HAL)
职责 :这是最底层,直接与单片机寄存器和外设打交道。它的核心目标是将硬件操作封装成统一的函数接口,从而屏蔽不同型号单片机之间的寄存器操作差异。
使用:
- 对下 :直接操作单片机的特定外设寄存器(如
USART1->DR,GPIOA->ODR)。 - 对上 :为驱动层提供标准化的、与硬件无关的API(如
void HAL_UART_WriteByte(uint8_t byte))。 - 当更换单片机型号时,理论上只需要重写或替换这一层代码,上层代码几乎无需改动。
示例代码:
c
// hal_uart.h
#ifndef __HAL_UART_H
#define __HAL_UART_H
void HAL_UART_Init(uint32_t baudrate);
void HAL_UART_WriteByte(uint8_t byte);
uint8_t HAL_UART_ReadByte(void);
#endif
// hal_uart.c (针对特定MCU,如STM32)
#include "stm32f1xx.h"
#include "hal_uart.h"
void HAL_UART_Init(uint32_t baudrate) {
// ... 复杂的寄存器配置,如使能时钟、设置波特率、数据位等
USART1->BRR = ...; // 根据baudrate计算
USART1->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; // 使能USART、发送、接收
}
void HAL_UART_WriteByte(uint8_t byte) {
while (!(USART1->SR & USART_SR_TXE)); // 等待发送寄存器空
USART1->DR = byte; // 写入数据寄存器
}
2. 驱动层 (Driver Layer)
职责 :在HAL的基础上,驱动具体的硬件模块或芯片。它不再关心单片机寄存器,而是关心如何操作一个完整的设备(如温湿度传感器、LCD屏幕、电机驱动芯片)。这一层会实现该设备特定的通信协议和控制逻辑。
使用:
- 对下:调用HAL提供的函数来控制GPIO、SPI、I2C、UART等。
- 对上 :为业务逻辑层提供面向功能的API(如
DHT11_ReadTempHumidity)。
示例代码:
c
// dht11_driver.h
#ifndef __DHT11_DRIVER_H
#define __DHT11_DRIVER_H
typedef struct {
float temperature;
float humidity;
} DHT11_Data_t;
int DHT11_Init(void);
int DHT11_ReadTempHumidity(DHT11_Data_t *data);
#endif
// dht11_driver.c
#include "hal_gpio.h" // 使用HAL的GPIO函数
#include "hal_delay.h" // 使用HAL的延时函数
#include "dht11_driver.h"
// DHT11的通信时序(例如,拉低总线18ms启动信号)
int DHT11_ReadTempHumidity(DHT11_Data_t *data) {
// 1. 调用HAL_GPIO_WritePin将数据线拉低
// 2. 调用HAL_Delay_ms(18)
// 3. 调用HAL_GPIO_ReadPin读取数据线,解析40位数据
// 4. 将解析出的二进制数据转换为temperature和humidity,填入data结构体
// 5. 返回成功或失败状态
}
3. 业务逻辑层 (Business Logic Layer - BLL) / 中间件层 (Middleware Layer)
职责 :这是系统的"大脑",实现具体的产品功能和应用逻辑。它协调多个驱动,完成复杂的任务。FreeRTOS、文件系统、网络协议栈等也常被视作这一层的一部分。
使用:
- 对下:调用驱动层提供的各种设备API。
- 对上 :为应用层提供简洁的服务接口(如
CheckButtonAndControlMotor)。
示例代码:
c
// control_logic.h
#ifndef __CONTROL_LOGIC_H
#define __CONTROL_LOGIC_H
void ControlLogic_Init(void);
void ControlLogic_Run(void); // 主循环中调用
#endif
// control_logic.c
#include "dht11_driver.h"
#include "fan_driver.h"
#include "button_driver.h"
#include "control_logic.h"
void ControlLogic_Run(void) {
DHT11_Data_t env_data;
static uint32_t last_check_time = 0;
// 每5秒检查一次温湿度
if (HAL_GetTick() - last_check_time > 5000) {
if (DHT11_ReadTempHumidity(&env_data) == SUCCESS) {
// 业务逻辑:如果温度高于28度,则打开风扇
if (env_data.temperature > 28.0f) {
Fan_TurnOn();
} else {
Fan_TurnOff();
}
}
last_check_time = HAL_GetTick();
}
// 处理按键事件
if (Button_IsPressed()) {
Fan_Toggle();
}
}
4. 应用层 (Application Layer)
职责 :这是整个程序的**"总指挥",是程序的入口。它负责初始化所有下层模块、创建任务(如果使用RTOS)、启动调度器、并定义整个程序的执行框架**。这一层通常不实现复杂逻辑,只进行组织和调度。
使用:
- 对下:调用业务逻辑层的初始化函数和主循环函数。
- 对外 :它是
main.c的核心内容。
示例代码:
c
// main.c
#include "hal.h" // 初始化硬件
#include "drivers.h" // 初始化所有驱动
#include "control_logic.h" // 业务逻辑
#include "FreeRTOS.h"
#include "task.h"
// 如果没有RTOS
int main(void) {
// 1. 初始化所有底层硬件和驱动
HAL_Init();
SystemClock_Config();
DHT11_Init();
Fan_Init();
Button_Init();
// 2. 初始化业务逻辑
ControlLogic_Init();
// 3. 主循环(轮询架构)
for (;;) {
ControlLogic_Run(); // 在循环中执行业务逻辑
// 可能还有其他任务...
}
}
// 如果使用FreeRTOS
int main(void) {
// 1. 硬件和基础驱动初始化
HAL_Init();
SystemClock_Config();
// 2. 创建业务逻辑任务
xTaskCreate(ControlLogic_Task, "ControlLogic", 128, NULL, 2, NULL);
// 3. 启动RTOS调度器
vTaskStartScheduler();
// 4. 永远不会执行到这里
for (;;);
}
总结与优势
| 层次 | 核心思想 | 依赖关系 | 变化影响 |
|---|---|---|---|
| 应用层 | 组织与调度 | 依赖BLL | 产品功能改变时修改 |
| 业务逻辑层 | 功能与逻辑 | 依赖Driver | 业务规则改变时修改 |
| 驱动层 | 设备与协议 | 依赖HAL | 更换传感器/设备时修改 |
| 硬件抽象层 | 抽象与统一 | 依赖MCU硬件 | 更换单片机型号时修改 |
采用分层架构的优势:
- 高内聚,低耦合:每一层只关注自己的职责。
- 易于移植:换MCU?主要改HAL。换传感器?主要改Driver。
- 易于协作与测试:可以模拟下层接口,对上层的业务逻辑进行单元测试。
- 代码复用:为同一款MCU写的HAL和Driver,可以用在多个不同的项目中。
在实际项目中,根据系统复杂度,层次可能会合并或进一步细分(例如,在HAL和Driver之间再增加一个板级支持包BSP),但核心的"分层"思想是相通的。
二、更详细一点的层
在单片机项目开发中,为了提高代码的可维护性、复用性和模块化程度,通常会采用分层设计思想。除了直接实现业务逻辑的应用层,常见的分层还包括以下几层(不同项目可能有细微差异,核心是按功能职责划分):
1. 硬件抽象层(Hardware Abstraction Layer, HAL)
- 核心作用:屏蔽底层硬件细节,提供统一的软件接口,使上层代码无需直接操作寄存器或硬件引脚。
- 具体功能 :
- 对单片机的外设(如GPIO、UART、SPI、I2C、ADC、定时器等)进行封装,提供标准化函数(如
gpio_init()、uart_send())。 - 处理硬件相关的初始化参数(如引脚定义、波特率、时钟频率),与具体芯片型号绑定。
- 对单片机的外设(如GPIO、UART、SPI、I2C、ADC、定时器等)进行封装,提供标准化函数(如
- 举例 :
无论用STM32还是51单片机,上层调用uart_send_data(buf, len)即可发送数据,无需关心底层寄存器配置(如STM32的USART寄存器、51的SBUF寄存器)。
2. 驱动层(Driver Layer)
- 核心作用:在HAL之上,针对具体外部硬件(如传感器、执行器、模块)编写专用驱动,实现硬件的控制逻辑。
- 具体功能 :
- 基于HAL提供的接口,实现特定硬件的初始化、数据读写、状态控制等(如OLED屏驱动、温湿度传感器DHT11驱动、电机驱动L298N)。
- 处理硬件的时序要求、通信协议(如I2C协议的传感器驱动需实现起始/停止信号、数据收发逻辑)。
- 与HAL的区别 :
HAL针对单片机内部外设 (芯片自带功能),驱动层针对外部硬件模块(芯片外部的元器件)。
3. 中间层(Middleware Layer)
- 核心作用:提供通用功能或协议支持,连接驱动层与应用层,解决跨模块的共性问题。
- 常见模块 :
- 通信协议栈:如Modbus、MQTT、蓝牙协议(BLE)、WiFi协议的封装,使应用层无需处理复杂协议细节。
- 数据处理:如数据校验(CRC、校验和)、加密解密(AES)、JSON/XML解析。
- 任务调度:在多任务系统(如FreeRTOS)中,封装任务创建、信号量、队列等接口,简化应用层的任务管理。
- 通用工具:如内存管理、日志打印、延时函数等。
4. 接口层(Interface Layer)
(部分项目会单独划分,或融入中间层/HAL)
- 核心作用:定义各层之间的接口标准,明确函数的输入输出、返回值和功能职责,实现"接口与实现分离"。
- 具体形式 :
通常用头文件(.h)定义函数声明、结构体、枚举等,而.c文件实现具体逻辑。例如,sensor_interface.h定义传感器的通用接口(sensor_read()),不同传感器的驱动(DHT11、SHT30)分别实现该接口。
5. 板级支持包(Board Support Package, BSP)
(多见于复杂项目,可理解为"硬件抽象层+驱动层"的结合)
- 核心作用:针对特定电路板(而非单一芯片)提供硬件支持,包含板载资源的初始化和控制。
- 具体内容 :
例如,某块开发板上有LED、按键、OLED屏、温湿度传感器,BSP层会统一初始化这些硬件,并提供bsp_led_on()、bsp_key_scan()等接口,应用层直接调用即可,无需关心硬件在板上的具体连接(如LED接哪个GPIO引脚)。
分层设计的优势
- 模块化:每层职责清晰,修改某一层(如更换传感器驱动)不影响其他层。
- 可移植性:更换单片机型号时,只需修改HAL层,上层代码可复用。
- 协作效率:多人开发时,可按层分工(如一人写驱动,一人写应用逻辑)。
实际项目中,分层并非绝对严格,简单项目可能只分"应用层+驱动层",复杂项目(如带操作系统的工业控制)则会细分更多层级。