mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3
617 字
2 分钟
STM32库函数调用流程详解
2026-06-18

STM32库函数调用流程详解#

STM32 开发有两种主流库:标准外设库(StdPeriph)HAL 库。理解库函数的调用架构是掌握 STM32 开发的关键。

库架构总览#

分层结构#

┌─────────────────────────────────┐
│ 用户应用程序 │
├─────────────────────────────────┤
│ HAL库 / 标准外设库 │
├─────────────────────────────────┤
│ CMSIS(ARM内核接口层) │
├─────────────────────────────────┤
│ 寄存器操作 │
├─────────────────────────────────┤
│ 硬件外设 │
└─────────────────────────────────┘

CMSIS 层#

CMSIS(Cortex Microcontroller Software Interface Standard)是 ARM 定义的内核访问接口:

// core_cm3.h 中定义的内核访问函数
__enable_irq() // 开启中断
__disable_irq() // 关闭中断
NVIC_EnableIRQ() // 使能指定中断
NVIC_SetPriority() // 设置中断优先级
SysTick_Config() // 配置系统滴答定时器

HAL 库调用流程#

初始化流程#

以 GPIO 为例,HAL 库的典型调用链:

// 1. 开启时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 2. 配置 GPIO
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 3. 读写操作
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
GPIO_PinState state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);

HAL 库函数命名规则#

HAL_外设名_操作类型()
操作类型说明示例
Init初始化HAL_GPIO_Init()
DeInit反初始化HAL_UART_DeInit()
Read读取HAL_ADC_GetValue()
Write写入HAL_DAC_SetValue()
Transmit发送HAL_UART_Transmit()
Receive接收HAL_UART_Receive()
Start启动HAL_TIM_Base_Start()
Stop停止HAL_TIM_Base_Stop()
MSPInit底层初始化HAL_UART_MspInit()

GPIO 完整调用流程#

配置结构体#

typedef struct {
uint32_t Pin; // 引脚号
uint32_t Mode; // 模式
uint32_t Pull; // 上下拉
uint32_t Speed; // 速度
uint32_t Alternate; // 复用功能
} GPIO_InitTypeDef;

初始化流程详解#

void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) {
// 1. 配置模式寄存器 (MODER)
// 2. 配置输出类型寄存器 (OTYPER)
// 3. 配置速度寄存器 (OSPEEDR)
// 4. 配置上下拉寄存器 (PUPDR)
// 5. 配置复用功能寄存器 (AFR)
}

实战:LED 闪烁#

#include "stm32f1xx_hal.h"
void LED_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 1. 开启 GPIOA 时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 2. 配置 PA5 为推挽输出
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 3. 初始状态关闭 LED
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
}
int main(void) {
HAL_Init();
SystemClock_Config();
LED_Init();
while (1) {
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
HAL_Delay(500);
}
}

USART 完整调用流程#

配置结构体#

typedef struct {
uint32_t BaudRate; // 波特率
uint32_t WordLength; // 数据位
uint32_t StopBits; // 停止位
uint32_t Parity; // 校验位
uint32_t Mode; // 收发模式
uint32_t HwFlowCtl; // 硬件流控
uint32_t OverSampling; // 过采样
} UART_InitTypeDef;

初始化调用链#

HAL_UART_Init()
└── UART_SetConfig() // 配置波特率、数据位等
└── UART_SetBaudRate() // 计算并设置波特率寄存器
└── HAL_UART_MspInit() // 底层硬件初始化(用户重写)
├── __HAL_RCC_USART1_CLK_ENABLE() // 开启时钟
├── GPIO 配置 // TX/RX 引脚
└── NVIC 配置 // 中断优先级

实战:串口发送#

UART_HandleTypeDef huart1;
void USART1_Init(void) {
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
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;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart1);
}
// 底层初始化(重写 MSP 函数)
void HAL_UART_MspInit(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
// PA9 - TX
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// PA10 - RX
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
}
// 发送字符串
void USART1_SendString(char *str) {
HAL_UART_Transmit(&huart1, (uint8_t *)str, strlen(str), 100);
}
int main(void) {
HAL_Init();
SystemClock_Config();
USART1_Init();
while (1) {
USART1_SendString("Hello STM32!\r\n");
HAL_Delay(1000);
}
}

定时器完整调用流程#

配置结构体层次#

TIM_HandleTypeDef htim; // 句柄
└── TIM_Base_InitTypeDef Init; // 基础配置
├── Prescaler // 预分频器
├── CounterMode // 计数模式
├── Period // 周期/重装值
└── ClockDivision // 时钟分频

初始化调用链#

HAL_TIM_Base_Init()
└── TIM_Base_SetConfig() // 配置 PSC、ARR、计数模式
└── HAL_TIM_Base_MspInit() // 底层初始化(用户重写)
├── 时钟使能
├── GPIO 配置(如需要)
└── NVIC 配置(如需要中断)

实战:定时器中断#

TIM_HandleTypeDef htim2;
void TIM2_Init(void) {
// 72MHz 主频,定时 1ms
// 72MHz / (71+1) / (999+1) = 1kHz
htim2.Instance = TIM2;
htim2.Init.Prescaler = 71;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 999;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_Base_Init(&htim2);
// 启动定时器中断
HAL_TIM_Base_Start_IT(&htim2);
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM2) {
__HAL_RCC_TIM2_CLK_ENABLE();
HAL_NVIC_SetPriority(TIM2_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
}
}
// 中断服务函数
void TIM2_IRQHandler(void) {
HAL_TIM_IRQHandler(&htim2);
}
// 定时器回调函数
volatile uint32_t tick = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM2) {
tick++;
if (tick >= 1000) { // 1秒
tick = 0;
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
}
}
}

中断处理流程#

HAL 库中断处理机制#

硬件中断触发
中断向量表 → IRQHandler()
HAL_XXX_IRQHandler()
检查中断标志位
清除标志位 + 调用回调函数
HAL_XXX_Callback() ← 用户实现

回调函数列表#

外设回调函数触发条件
UARTHAL_UART_TxCpltCallback()发送完成
UARTHAL_UART_RxCpltCallback()接收完成
TIMHAL_TIM_PeriodElapsedCallback()定时器溢出
GPIOHAL_GPIO_EXTI_Callback()外部中断
ADCHAL_ADC_ConvCpltCallback()ADC转换完成
DMAHAL_DMA_XferCpltCallback()DMA传输完成

标准库 vs HAL 库对比#

GPIO 操作对比#

// 标准库
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_SetBits(GPIOA, GPIO_Pin_5);
// HAL 库
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);

函数映射表#

操作标准库HAL库
开启时钟RCC_APB2PeriphClockCmd()__HAL_RCC_XXX_CLK_ENABLE()
GPIO初始化GPIO_Init()HAL_GPIO_Init()
设置引脚GPIO_SetBits()HAL_GPIO_WritePin()
清除引脚GPIO_ResetBits()HAL_GPIO_WritePin()
读取引脚GPIO_ReadInputDataBit()HAL_GPIO_ReadPin()
串口发送USART_SendData()HAL_UART_Transmit()
定时器启动TIM_Cmd()HAL_TIM_Base_Start()

系统初始化流程#

HAL_Init() 做了什么#

HAL_StatusTypeDef HAL_Init(void) {
// 1. 配置 Flash 预取缓冲区
// 2. 设置中断优先级分组(默认 Group 4)
// 3. 配置 SysTick 定时器(1ms 中断)
// 4. 调用 HAL_MspInit()(用户可重写)
return HAL_OK;
}

SystemClock_Config() 流程#

void SystemClock_Config(void) {
// 1. 配置 Flash 等待周期
__HAL_FLASH_SET_LATENCY(FLASH_LATENCY_2);
// 2. 配置 HSE(外部高速晶振)
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
// 3. 配置 PLL(锁相环)
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; // 8MHz × 9 = 72MHz
// 4. 配置总线时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB = 72MHz
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; // APB1 = 36MHz
HAL_RCC_OscConfig(&RCC_OscInitStruct);
HAL_RCC_ClockConfig(&RCC_ClkInitStruct);
}

常见问题#

HAL_Delay() 卡死#

原因:中断优先级问题,SysTick 中断被更高优先级中断阻塞

解决

// 确保 SysTick 优先级为最低
HAL_NVIC_SetPriority(SysTick_IRQn, 15, 0);

MSP 函数不被调用#

原因:MSP 函数需要在 stm32f1xx_hal_msp.c 中定义,或在主文件中重新实现

解决

// 确保函数名正确,且在 HAL 库调用前定义
void HAL_UART_MspInit(UART_HandleTypeDef *huart) {
// 底层初始化代码
}

时钟配置错误#

检查清单

  1. HSE 晶振频率是否匹配
  2. PLL 倍频系数是否正确
  3. 总线分频是否在允许范围内
  4. Flash 等待周期是否匹配

总结#

层级职责示例函数
用户层业务逻辑main(), 应用函数
HAL库统一接口HAL_GPIO_Init()
MSP层底层硬件HAL_UART_MspInit()
CMSIS内核访问NVIC_EnableIRQ()
寄存器直接操作GPIOA->ODR

掌握 HAL 库的调用流程,重点理解:

  1. 初始化链:Init → SetConfig → MspInit
  2. 中断机制:IRQHandler → 回调函数
  3. MSP 分离:硬件相关代码放在 MSP 函数中
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

STM32库函数调用流程详解
https://mizuki.mysqil.com/posts/stm32-library-functions/
作者
まつざか ゆき
发布于
2026-06-18
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时