1. 课程导论与开发环境搭建:为什么需要分层封装?

各位同学好,我是你们这门课的老周。做嵌入式这行十几年了,踩过的坑比写过的代码还多。今天咱们开始第一讲,先聊聊一个核心问题:为什么非要搞分层封装?

你想想看,是不是经常遇到这种情况——项目做到一半,老板说换个MCU型号,或者要移植到另一个平台。结果呢?整个工程里GPIO、UART、I2C的代码全混在一起,改一个地方牵一发动全身。我当年第一个产品就吃过这个亏,一个简单的LED闪烁程序,因为直接操作寄存器,换了个板子硬是调了两天。

分层封装,说白了就是把硬件相关的代码和业务逻辑代码拆开。底层驱动只负责“怎么操作寄存器”,上层应用只关心“我要做什么功能”。这样以后换芯片、换平台,你只需要改底层那几层,上层代码几乎不用动。

核心思想: 驱动分层 = 硬件抽象层(HAL) + 中间层 + 应用层。每一层只依赖下一层,不跨层调用。

1.1 分层封装到底长什么样?

我画了一张图,你看一眼就明白了。这是咱们整个课程的知识骨架,也是你以后写任何STM32项目的标准套路。

STM32裸机驱动分层封装架构 应用层(Application) main()、任务逻辑、业务算法、用户交互 中间层(Middleware) 协议栈、数据解析、状态机、通用工具函数 硬件抽象层(HAL) GPIO、UART、I2C、SPI、TIM 等外设驱动封装 寄存器层(Register) 直接操作STM32寄存器、位运算、内存映射 每一层只调用下一层的接口,严禁跨层调用。这就是“分层”的核心纪律。

这张图就是咱们整个课程的总纲。从上到下依次是:应用层 → 中间层 → 硬件抽象层 → 寄存器层。每一层只调用下一层的接口,不能越级。比如应用层不能直接写寄存器,必须通过HAL层。

我的习惯: 每个外设驱动都单独建一个.c/.h文件,比如gpio.c、uart.c。这样找代码快,移植也方便。别把所有驱动塞到一个文件里,那是给自己挖坑。

1.2 开发环境搭建——别在起跑线上摔跤

环境配置这块,我见过太多新手卡在这里了。其实没那么复杂,咱们一步步来。

1.2.1 你需要准备什么?

工具 版本建议 用途
STM32CubeMX 6.x 以上 图形化配置时钟、外设、引脚,生成初始化代码
Keil MDK-ARM 5.36 以上 编译、下载、调试(也可以用IAR或GCC)
STM32F1/F4 固件包 1.8.x / 2.0.x HAL库和LL库,CubeMX依赖它生成代码
USB转串口工具 CH340 / FT232 串口打印调试信息,必备
避坑: 我曾经因为Keil版本太低,打不开CubeMX生成的工程,折腾了一下午。建议Keil至少用5.36,CubeMX用最新版。另外,安装路径不要有中文和空格。

1.2.2 搭建步骤(我习惯的顺序)

  1. 先装Keil MDK,装完再装对应的器件包(比如STM32F1的DFP)。
  2. 再装STM32CubeMX,它会自动检测Keil的安装路径。
  3. 下载固件包。CubeMX里可以直接在线下载,但网速慢的话,建议去官网手动下载后导入。
  4. 准备一个串口工具,后面调试全靠它打印信息。

1.3 第一个裸机工程模板——从CubeMX到Keil

咱们直接上手,创建一个最简单的工程:点灯 + 串口打印。别小看这个,它是一切复杂项目的基础。

1.3.1 在CubeMX里配置

  • 选择芯片型号(比如STM32F103C8T6)
  • 配置RCC:HSE外部晶振,8MHz
  • 配置SYS:Debug选Serial Wire(否则烧录一次后第二次可能连不上)
  • 配置GPIO:PB0推挽输出,接LED
  • 配置USART1:PA9(TX)、PA10(RX),波特率115200
  • 时钟树:HCLK设为72MHz(F103最高主频)
  • Project Manager:项目名、路径、IDE选MDK-ARM V5
经验之谈: 时钟配置是新手最容易出错的地方。我建议你每次配完时钟树,都点一下“Resolve Clock”按钮,看看有没有红色报错。红色就说明某个外设时钟源不对。

1.3.2 生成代码,在Keil里打开

CubeMX生成后,用Keil打开工程。你会看到它自动生成了main.c、gpio.c、usart.c等文件。这就是HAL库的初始化代码。

咱们在main.c里写点实际功能:

/* main.c - 第一个裸机工程模板 */
#include "main.h"
#include "gpio.h"
#include "usart.h"

/* 重定向printf到串口 */
int fputc(int ch, FILE *f) {
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 100);
    return ch;
}

int main(void) {
    HAL_Init();                     // 初始化HAL库
    SystemClock_Config();           // 配置系统时钟
    MX_GPIO_Init();                 // 初始化GPIO
    MX_USART1_UART_Init();          // 初始化串口

    printf("Hello from STM32!\r\n");

    while (1) {
        HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);  // 翻转LED
        HAL_Delay(500);                         // 延时500ms
        printf("LED toggled\r\n");
    }
}

这段代码很简单:LED每500ms翻转一次,同时串口打印一条信息。但它的意义在于——验证了整个工具链是否通顺。

1.3.3 编译、下载、验证

  • 点击Build(或者按F7),0 Error 0 Warning才算通过。
  • 用ST-Link连接板子,点击Download(F8)。
  • 打开串口助手,波特率115200,看能不能收到打印信息。
  • 看板子上的LED是不是在闪烁。
常见问题: 如果下载一次后第二次连不上,八成是Debug模式没配成Serial Wire。回去CubeMX里改一下,重新生成代码就好了。这个问题我当年至少遇到过5次。

1.4 这个工程模板,以后每个章节都会用

咱们这个工程模板,就是后面29章的地基。以后每讲一个新外设,比如I2C、SPI、定时器,都是在这个模板上添加新的驱动文件。你不需要每次都从头配CubeMX,直接复制这个模板工程,改改配置就行。

嗯,第一讲就到这儿。环境搭好了,模板跑通了,后面咱们就可以专心搞驱动封装了。记住:分层不是花架子,是让你少加班的真功夫。