617 字
2 分钟
STM32库函数调用流程详解
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. 配置 GPIOHAL_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() ← 用户实现回调函数列表
| 外设 | 回调函数 | 触发条件 |
|---|---|---|
| UART | HAL_UART_TxCpltCallback() | 发送完成 |
| UART | HAL_UART_RxCpltCallback() | 接收完成 |
| TIM | HAL_TIM_PeriodElapsedCallback() | 定时器溢出 |
| GPIO | HAL_GPIO_EXTI_Callback() | 外部中断 |
| ADC | HAL_ADC_ConvCpltCallback() | ADC转换完成 |
| DMA | HAL_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) { // 底层初始化代码}时钟配置错误
检查清单:
- HSE 晶振频率是否匹配
- PLL 倍频系数是否正确
- 总线分频是否在允许范围内
- Flash 等待周期是否匹配
总结
| 层级 | 职责 | 示例函数 |
|---|---|---|
| 用户层 | 业务逻辑 | main(), 应用函数 |
| HAL库 | 统一接口 | HAL_GPIO_Init() |
| MSP层 | 底层硬件 | HAL_UART_MspInit() |
| CMSIS | 内核访问 | NVIC_EnableIRQ() |
| 寄存器 | 直接操作 | GPIOA->ODR |
掌握 HAL 库的调用流程,重点理解:
- 初始化链:Init → SetConfig → MspInit
- 中断机制:IRQHandler → 回调函数
- MSP 分离:硬件相关代码放在 MSP 函数中
分享
如果这篇文章对你有帮助,欢迎分享给更多人!
STM32库函数调用流程详解
https://mizuki.mysqil.com/posts/stm32-library-functions/ 部分信息可能已经过时





