Skip to content

嵌入式系统概述与 HAL 库

介绍嵌入式系统的基本概念、MCU 的运作方式,以及 STM32 HAL 库在开发体系中的位置与设计思想。


1. 什么是嵌入式系统

快速导航:本文提到的概念均有专题讲解——GPIOUARTSPII2CCAN时钟树回调机制

1.1 类比:单片机 vs PC

一台 PC 上运行着操作系统(Windows / Linux),可以同时打开浏览器、写代码、听音乐——它是一个通用计算平台

嵌入式系统则不同:它只做一件事,但要做到极致——实时、可靠、低功耗。你的微波炉、无人机飞控、汽车 ECU 都是嵌入式系统。

┌──────────────────────────────────────────────────────┐
│                    PC (通用计算)                       │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐              │
│  │ 浏览器   │  │ VS Code │  │ 播放器   │  ← 多任务    │
│  └─────────┘  └─────────┘  └─────────┘              │
│  ┌──────────────────────────────────────┐            │
│  │       Windows / Linux 操作系统        │            │
│  └──────────────────────────────────────┘            │
│  ┌──────────────────────────────────────┐            │
│  │    Intel/AMD CPU + 内存 + 硬盘        │            │
│  └──────────────────────────────────────┘            │
└──────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────┐
│                嵌入式系统 (专用)                       │
│  ┌──────────────────────────────────────┐            │
│  │        FreeRTOS (轻量实时操作系统)     │  ← 可选    │
│  └──────────────────────────────────────┘            │
│  ┌──────────────────────────────────────┐            │
│  │  STM32F407 (ARM Cortex-M4, 168MHz)   │            │
│  │  192KB RAM, 512KB Flash              │            │
│  └──────────────────────────────────────┘            │
│  ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐               │
│  │ GPIO │ │ UART │ │ CAN  │ │ SPI  │  ← 外设直连     │
│  └──────┘ └──────┘ └──────┘ └──────┘               │
└──────────────────────────────────────────────────────┘

1.2 本质

嵌入式系统的核心是一块微控制器(MCU, Microcontroller Unit)。MCU 将 CPU、内存(RAM/Flash)、外设(GPIOUARTSPI 等)集成在同一块芯片上,形成一个自给自足的计算单元。

GSRL 项目所用的 STM32F407 基于 ARM Cortex-M4 内核,主频 168MHz,属于中高性能 MCU。GSRL_H7 所用的 STM32H7 系列则基于 Cortex-M7,主频可达 480MHz。


2. MCU 的运作方式

2.1 三大核心组成

┌─────────────────────────────────────────┐
│                 STM32 MCU               │
│  ┌───────────┐  ┌─────────────────────┐ │
│  │ Cortex-M4 │  │     Flash (512KB)   │ │
│  │   内核     │  │     程序存储         │ │
│  └─────┬─────┘  └─────────────────────┘ │
│        │                                │
│  ┌─────┴─────────────────────────────┐  │
│  │         总线矩阵 (Bus Matrix)       │  │
│  └─────┬─────────────────────────────┘  │
│        │                                │
│  ┌─────┴─────┐ ┌──────────┐ ┌───────┐  │
│  │ RAM 192KB │ │  GPIO×N  │ │ UART  │  │
│  │  数据存储  │ │ 引脚控制  │ │ 串口  │  │
│  └───────────┘ └──────────┘ └───────┘  │
│  ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐  │
│  │ SPI  │ │ I2C  │ │ CAN  │ │ TIM  │  │
│  │ ...  │ │ ...  │ │ ...  │ │ ...  │  │
│  └──────┘ └──────┘ └──────┘ └──────┘  │
└─────────────────────────────────────────┘
  • 内核(Core):执行指令,处理运算。Cortex-M4 支持 DSP 指令和单精度浮点(FPU),这对机器人控制中的 PID 计算、姿态解算至关重要。
  • 内存:Flash 存程序,RAM 存运行时数据。嵌入式系统内存极度受限(192KB RAM),需要精打细算。
  • 外设(Peripheral):MCU 与外部世界交互的桥梁。GPIO 控制引脚电平,UART/SPI/I2C/CAN 负责通信,TIM 产生 PWM 驱动电机。

2.2 启动流程

上电 → 复位向量 → SystemInit() → main()
                    │
                    ├─ 初始化时钟树(配置 HSE/PLL,产生 168MHz 系统时钟)
                    ├─ HAL_Init()
                    ├─ SystemClock_Config()
                    ├─ MX_XXX_Init()  ← 逐个初始化外设
                    ├─ osKernelInitialize()
                    ├─ MX_FREERTOS_Init()
                    └─ osKernelStart()  ← 调度器接管,不再返回

对应工程中的 CubeMX_BSP/Src/main.c:

int main(void)
{
    HAL_Init();                      // 初始化 HAL 库
    SystemClock_Config();            // 配置系统时钟 168MHz
    MX_GPIO_Init();                  // 初始化 GPIO
    MX_DMA_Init();
    MX_CAN1_Init();                  // 初始化 CAN1
    MX_SPI1_Init();                  // 初始化 SPI1
    // ... 其他外设初始化
    osKernelInitialize();            // FreeRTOS 内核初始化
    MX_FREERTOS_Init();              // 创建任务
    osKernelStart();                 // 启动调度器
    while (1) {}                     // 永远不会执行到这里
}

2.3 代码如何变成运行的程序

源文件 (.c/.cpp)  →  交叉编译  →  .elf  →  Flash 烧录
     │                    │
     │           arm-none-eabi-gcc
     │           生成 ARM 机器码
     │
     └── 注意:不是用 PC 上的 gcc,而是交叉编译器
         因为目标 CPU 是 ARM 架构,不是 x86

3. HAL 库是什么

3.1 问题的提出

直接操作 STM32 的寄存器可以控制外设,例如让 PA5 引脚输出高电平:

// 寄存器方式——直接操作地址
*(volatile uint32_t *)0x40020014 |= (1 << 5);  // GPIOA BSRR 寄存器

这种方式的问题: - 不可读0x40020014 是什么?不查手册完全不知道。 - 不可移植:STM32F4 和 STM32H7 的寄存器地址不同,同样的功能需要写两份代码。 - 容易出错:位操作稍有不慎就会影响其他引脚。

3.2 HAL 的解决方案

HAL(Hardware Abstraction Layer,硬件抽象层)是 ST 官方提供的一套函数库,为所有 STM32 外设提供统一的 API:

// HAL 方式——语义清晰
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);  // PA5 输出高电平

// 同样的 API 在 STM32F4、STM32H7 上都可用(重新编译即可)

3.3 HAL 库的层次化设计

┌───────────────────────────────────────────┐
│           用户应用程序 (Task/)             │  ← 你的业务逻辑
├───────────────────────────────────────────┤
│         GSRL 设备层 (Device/)             │  ← 电机、IMU、遥控器对象
├───────────────────────────────────────────┤
│         GSRL 驱动层 (Driver/)             │  ← CAN/UART/SPI 封装
├───────────────────────────────────────────┤
│     STM32CubeMX 生成代码 (CubeMX_BSP/)    │  ← HAL 初始化代码
├───────────────────────────────────────────┤
│          HAL 库 (stm32f4xx_hal_*.c)       │  ← ST 官方 HAL API
├───────────────────────────────────────────┤
│        CMSIS (Cortex 微控制器软件接口标准)   │  ← ARM 统一标准
├───────────────────────────────────────────┤
│              硬件寄存器                     │  ← 芯片物理层
└───────────────────────────────────────────┘

3.4 HAL 的核心设计模式

HAL 库围绕两个核心概念组织 API:

概念 含义 示例
句柄(Handle) 代表一个外设实例的结构体,包含配置和状态 CAN_HandleTypeDef hcan1
回调(Callback) 外设事件发生时 HAL 自动调用的函数 HAL_CAN_RxFifo0MsgPendingCallback()

初始化外设的典型模式:

// 1. 声明句柄(通常在 CubeMX 生成的代码中)
CAN_HandleTypeDef hcan1;

// 2. CubeMX 生成初始化函数,配置句柄的参数
void MX_CAN1_Init(void)
{
    hcan1.Instance = CAN1;
    hcan1.Init.Prescaler = 7;
    hcan1.Init.Mode = CAN_MODE_NORMAL;
    // ... 更多配置
    HAL_CAN_Init(&hcan1);  // 调用 HAL API 完成初始化
}

// 3. 用户代码中使用句柄操作外设
HAL_CAN_AddTxMessage(&hcan1, &txHeader, txData, &mailbox);

4. CubeMX 的角色

STM32CubeMX 是一个图形化配置工具,通过它你可以:

  • 选引脚:指定 PA9 做 USART1_TX,PA10 做 USART1_RX
  • 时钟:设定 HSE → PLL → 168MHz 的时钟路径
  • 配外设:设定 CAN 的波特率、UART 的波特率、SPI 的模式
  • 配 FreeRTOS:创建任务、队列、信号量

CubeMX 最终生成一整套初始化代码,放在 CubeMX_BSP/ 目录下。这些代码不应手动修改,因为重新生成会覆盖你的改动。所有用户自定义代码应写在 /* USER CODE BEGIN *//* USER CODE END */ 之间。


5. FreeRTOS 概述

GSRL 使用 FreeRTOS 作为实时操作系统。相对于裸机编程(Bare Metal),FreeRTOS 提供了:

  • 任务调度:多个任务按优先级轮流执行,看起来像"同时运行"
  • 任务间通信:队列、信号量、互斥锁
  • 软件定时器:不占用硬件定时器资源
裸机(Bare Metal):
┌──────────────────────────────────┐
│  while(1) {                      │
│      读取传感器();                │
│      控制电机();                  │
│      发送数据();                  │  ← 顺序执行,一个慢了全卡住
│  }                               │
└──────────────────────────────────┘

FreeRTOS:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 传感器任务 │ │ 控制任务  │ │ 通信任务  │  ← 调度器切换
│ (10ms)   │ │ (1ms)    │ │ (100ms)  │
└──────────┘ └──────────┘ └──────────┘
       ↓            ↓            ↓
┌──────────────────────────────────────┐
│         FreeRTOS 调度器               │
│         按优先级+时间片切换            │
└──────────────────────────────────────┘

GSRL 中的任务定义在 CubeMX_BSP/Src/freertos.c 中:

osThreadId_t testHandle;
const osThreadAttr_t test_attributes = {
    .name = "test",
    .stack_size = 128 * 4,           // 栈大小 512 字节
    .priority = (osPriority_t) osPriorityNormal,  // 优先级
};

// 在 MX_FREERTOS_Init() 中创建:
testHandle = osThreadNew(test_task, NULL, &test_attributes);

6. 总结

要点 说明
MCU CPU + 内存 + 外设 集成在单芯片上
STM32F407 Cortex-M4 内核,168MHz,192KB RAM
HAL 库 ST 提供的硬件抽象层,屏蔽寄存器细节
句柄 代表外设实例的结构体,HAL API 的核心参数
CubeMX 图形化配置工具,生成初始化代码
FreeRTOS 轻量实时操作系统,负责任务调度
启动流程 上电 → 时钟初始化 → 外设初始化 → FreeRTOS 接管

与 GSRL 的关系

GSRL 正是基于 HAL 库分层构建的:Driver 层封装 HAL API,Device 层使用 Driver 层的接口控制具体设备,Algorithm 层提供数学和控制算法。理解 HAL 库的句柄与回调机制,是理解整个 GSRL 代码运转方式的基础。

下一篇:时钟树与总线架构

作者: Qing | 修改日期: 2026-07-18