STM32常用HAL常见库函数快速运用和讲解

本手册按STM32核心外设功能分类,以通俗雅致的文风拆解函数原理,搭配极简可复用实战案例,无需钻研晦涩术语,新手亦可快速上手实操,所有代码均适配CubeMX自动初始化框架,粘贴即可运行。

1. GPIO通用输入输出------芯片的外设交互枢纽

GPIO是STM32与外部器件沟通的核心接口,可外接LED、按键、继电器等模块,兼具电平输出控制外设 与电平读取感知状态两大核心功能,以下为四类高频常用函数。

🔧 HAL_GPIO_Init(GPIOx, &GPIO_InitStruct)

原理解析

此函数为引脚设定运行规约:明确引脚工作模式为输出型(调控LED通断)或输入型(检测按键触发) ,配置上下拉防干扰机制,使用前务必开启对应GPIO时钟,否则芯片无法识别该引脚。

实战案例

c 复制代码
// 案例:配置PC13为推挽输出(外接LED),PA0为上拉输入(外接按键)
void GPIO_Init_Example(void)
{
  // 开启外设时钟:为GPIOA、GPIOC供电,必备前置操作
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  // 配置PC13推挽输出,适配LED控制
  GPIO_InitStruct.Pin = GPIO_PIN_13;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  // 配置PA0上拉输入,未按键为高电平,按键触发为低电平
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

🔧 HAL_GPIO_ReadPin(GPIOx, GPIO_PIN_x)

原理解析

读取引脚实时电平状态,仅返回两种结果:高电平GPIO_PIN_SET 、低电平GPIO_PIN_RESET,常用于按键触发检测、传感器状态感知等场景。

实战案例

c 复制代码
// 案例:检测PA0外接按键是否触发
uint8_t Key_Check(void)
{
  // 按键按下时PA0为低电平
  if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
  {
    HAL_Delay(10); // 按键消抖,规避机械抖动误判
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
    {
      return 1; // 确认按键有效触发
    }
  }
  return 0; // 按键未触发
}

🔧 HAL_GPIO_WritePin(GPIOx, GPIO_PIN_x, PinState)

原理解析

强制引脚输出固定电平,可直接设定高、低电平,精准控制LED亮灭、继电器通断等外设动作。

实战案例

c 复制代码
// 案例:调控PC13外接LED的亮灭状态
void LED_Control(uint8_t status)
{
  if(status == 1)
  {
    // 输出低电平,LED点亮
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
  }
  else
  {
    // 输出高电平,LED熄灭
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
  }
}

🔧 HAL_GPIO_TogglePin(GPIOx, GPIO_PIN_x)

原理解析

翻转引脚当前电平状态,高电平转低、低电平转高,无需预判现有状态,是实现LED闪烁、方波输出的最简方案。

实战案例

c 复制代码
// 案例:实现LED每隔500毫秒周期性闪烁
void LED_Flash(void)
{
  while(1)
  {
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 翻转引脚电平
    HAL_Delay(500); // 延时500毫秒
  }
}

2. UART串口通信------芯片的数据传声纽带

串口是STM32与上位机、外设模块数据交互的核心通道,实现芯片与外界的收发通信,分为三种工作模式:阻塞模式(执行完毕再响应后续任务) 、中断模式(并行处理任务,触发后响应) 、DMA模式(硬件自动传输,无需CPU干预)。

🔧 HAL_UART_Transmit(&huartx, pData, Size, Timeout)

原理解析

阻塞式串口发送函数,CPU需等待数据全部发送完毕或超时后,方可执行后续指令,适用于短帧数据、调试信息打印等场景。

实战案例

c 复制代码
// 案例:串口1向上位机发送字符串数据
void UART1_Send(void)
{
  uint8_t str[] = "你好,STM32!\r\n";
  // 发送数据,设定超时时间100毫秒
  HAL_UART_Transmit(&huart1, str, sizeof(str), 100);
}

🔧 HAL_UART_Receive(&huartx, pData, Size, Timeout)

原理解析

阻塞式串口接收函数,CPU持续等待,直至接收指定长度数据或触发超时,适用于固定长度数据接收场景。

实战案例

c 复制代码
// 案例:串口1阻塞接收上位机发送的单字节数据
void UART1_Receive(void)
{
  uint8_t rx_data;
  // 等待接收1字节数据,最长等待1000毫秒
  if(HAL_UART_Receive(&huart1, &rx_data, 1, 1000) == HAL_OK)
  {
    // 接收成功,将数据回传至上位机
    HAL_UART_Transmit(&huart1, &rx_data, 1, 100);
  }
}

🔧 HAL_UART_Transmit_IT(&huartx, pData, Size)

原理解析

中断式串口发送函数,启动发送后CPU可并行处理其他任务,数据发送完毕后触发中断,主动通知CPU执行后续逻辑。

实战案例

c 复制代码
uint8_t send_buf[] = "中断模式发送数据\r\n";
// 启动中断式串口发送
void UART1_IT_Send(void)
{
  HAL_UART_Transmit_IT(&huart1, send_buf, sizeof(send_buf));
}
// 发送完成回调函数,数据发送完毕后自动执行
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
  if(huart == &huart1)
  {
    // 此处编写发送完成后的后续业务逻辑
  }
}

🔧 HAL_UART_Receive_IT(&huartx, pData, Size)

原理解析

中断式串口接收函数,开启后CPU无需阻塞等待,接收到指定长度数据时触发中断,适用于实时性要求高的不定长数据接收。

实战案例

c 复制代码
uint8_t recv_buf[1];
// 开启中断接收,等待单字节数据
void UART1_IT_Recv(void)
{
  HAL_UART_Receive_IT(&huart1, recv_buf, 1);
}
// 接收完成回调函数,数据接收完毕后自动执行
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if(huart == &huart1)
  {
    // 将接收数据回传至上位机
    HAL_UART_Transmit(&huart1, recv_buf, 1, 100);
    // 重新开启接收,持续等待下一组数据
    HAL_UART_Receive_IT(&huart1, recv_buf, 1);
  }
}

🔧 HAL_UART_Transmit_DMA(&huartx, pData, Size)

原理解析

DMA模式串口发送函数,由硬件独立完成数据搬运传输,CPU完全无需参与,大幅降低负载,适用于大批量数据高速发送场景。

实战案例

c 复制代码
uint8_t dma_buf[] = "DMA模式全自动发送数据\r\n";
// DMA模式发送数据,启动后无需CPU干预
void UART1_DMA_Send(void)
{
  HAL_UART_Transmit_DMA(&huart1, dma_buf, sizeof(dma_buf));
}

3. TIM定时器------芯片的精准时序管家

定时器是STM32实现精准时序控制的核心,兼具定时触发任务 与PWM波形输出两大功能,计时精度可达毫秒、微秒级,常用于延时、调速、调光等场景。

🔧 HAL_TIM_Base_Start(&htimx)

原理解析

启动定时器基础计数模式,仅开启静默计时、不触发中断,适用于非阻塞延时、时序计数等场景。

实战案例

c 复制代码
// 启动TIM2基础计数功能
void TIM2_Start(void)
{
  HAL_TIM_Base_Start(&htim2);
}
// 非阻塞延时,计时期间CPU可并行处理其他任务
void TIM_Delay(uint16_t ms)
{
  uint16_t start = __HAL_TIM_GET_COUNTER(&htim2);
  while(__HAL_TIM_GET_COUNTER(&htim2) - start < ms);
}

🔧 HAL_TIM_Base_Start_IT(&htimx)

原理解析

启动定时器并开启溢出中断,计时达到设定值时触发中断,通知CPU执行预设定时任务,实现精准周期触发。

实战案例

c 复制代码
// 启动TIM2定时中断,每100毫秒触发一次
void TIM2_IT_Start(void)
{
  HAL_TIM_Base_Start_IT(&htim2);
}
// 定时器溢出回调函数,定时时间到自动执行
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if(htim == &htim2)
  {
    // 每100毫秒翻转LED电平,实现闪烁
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
  }
}

🔧 HAL_TIM_PWM_Start(&htimx, TIM_CHANNEL_x)

原理解析

启动指定通道PWM波形输出,生成高频交替的高低电平信号,常用于LED调光、电机调速、舵机角度控制。

实战案例

c 复制代码
// 启动TIM3通道1的PWM波形输出
void TIM3_PWM_Start(void)
{
  HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
}

🔧 HAL_TIM_PWM_Stop(&htimx, TIM_CHANNEL_x)

原理解析

停止指定通道PWM波形输出,终止电平交替信号,避免外设误动作。

实战案例

c 复制代码
// 停止TIM3通道1的PWM输出
void TIM3_PWM_Stop(void)
{
  HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1);
}

🔧 __HAL_TIM_SET_COMPARE(&htimx, TIM_CHANNEL_x, CompareValue)

原理解析

动态调整PWM占空比,即调控高电平持续时长,数值越大,LED亮度越高、电机转速越快,无需重启定时器即可实时调节。

实战案例

c 复制代码
// 调节TIM3通道1 PWM占空比,取值范围0-1000
void TIM3_PWM_Set(uint16_t duty)
{
  __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, duty);
}
// 实现LED渐变亮灭效果
void LED_Gradient(void)
{
  uint16_t duty;
  while(1)
  {
    // 亮度逐步提升
    for(duty=0; duty<1000; duty++)
    {
      TIM3_PWM_Set(duty);
      HAL_Delay(2);
    }
    // 亮度逐步降低
    for(duty=1000; duty>0; duty--)
    {
      TIM3_PWM_Set(duty);
      HAL_Delay(2);
    }
  }
}

4. ADC模数转换------芯片的模拟信号译官

STM32仅能识别数字信号,ADC模块可将外部模拟电压(如0-3.3V)转换为芯片可解析的数字量(12位ADC对应0-4095),常用于电压检测、传感器数据采集。

🔧 HAL_ADC_Start(&hadcx)

原理解析

启动ADC阻塞式转换,开启电压采集、量化编码的全流程,为数据读取做准备。

实战案例

c 复制代码
// 启动ADC1开始模数转换
void ADC1_Start(void)
{
  HAL_ADC_Start(&hadc1);
}

🔧 HAL_ADC_PollForConversion(&hadcx, Timeout)

原理解析

查询ADC转换状态,等待转换完成或超时,确保读取的数字量有效准确。

实战案例

c 复制代码
// 等待ADC1转换完成,最长等待100毫秒
if(HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK)
{
  // 转换完成,可读取有效数据
}

🔧 HAL_ADC_GetValue(&hadcx)

原理解析

读取ADC转换完成后的数字量结果,结合参考电压可换算出实际模拟电压值。

实战案例

c 复制代码
// 读取ADC1转换结果并返回数字量
uint16_t ADC1_Get(void)
{
  uint16_t adc_val;
  HAL_ADC_Start(&hadc1);
  // 等待转换完成
  if(HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK)
  {
    adc_val = HAL_ADC_GetValue(&hadc1);
  }
  HAL_ADC_Stop(&hadc1);
  return adc_val; // 实际电压计算公式:adc_val * 3.3 / 4095
}

🔧 HAL_ADC_Start_DMA(&hadcx, pData, Length)

原理解析

DMA模式连续采集,硬件自动将转换结果存入指定数组,无需CPU干预,适用于多通道、高频次数据采集。

实战案例

c 复制代码
uint16_t adc_buf[10]; // 数组存储10次采集结果
// 启动ADC1 DMA连续采集,存储10组数据
void ADC1_DMA_Start(void)
{
  HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buf, 10);
}

5. I2C串行通信------芯片的双线静默信使

I2C为双线制低速通信协议,布线简洁、适配性强,常用于连接OLED屏、EEPROM、温湿度传感器等外设,芯片作为主机,与指定地址从机完成数据交互。

🔧 HAL_I2C_Master_Transmit(&hi2cx, DevAddress, pData, Size, Timeout)

原理解析

I2C主机阻塞式发送裸数据,定向传输至指定地址从机,无需寄存器寻址。

实战案例

c 复制代码
#define I2C_ADDR 0x78 // 外设从机地址
// I2C1主机发送单字节数据
void I2C_Send(void)
{
  uint8_t data = 0x01;
  HAL_I2C_Master_Transmit(&hi2c1, I2C_ADDR, &data, 1, 100);
}

🔧 HAL_I2C_Master_Receive(&hi2cx, DevAddress, pData, Size, Timeout)

原理解析

I2C主机阻塞式接收裸数据,从指定地址从机获取数据信息。

实战案例

c 复制代码
// I2C1主机接收单字节数据
void I2C_Recv(void)
{
  uint8_t rx_data;
  HAL_I2C_Master_Receive(&hi2c1, I2C_ADDR, &rx_data, 1, 100);
}

🔧 HAL_I2C_Mem_Write(&hi2cx, DevAddress, MemAddress, MemAddSize, pData, Size, Timeout)

原理解析

I2C主机向从机指定寄存器地址写入数据,是操控OLED、传感器等带寄存器外设的核心函数。

实战案例

c 复制代码
// 向OLED屏写入控制指令
void OLED_Write_Cmd(uint8_t cmd)
{
  HAL_I2C_Mem_Write(&hi2c1, I2C_ADDR, 0x00, I2C_MEMADD_SIZE_8BIT, &cmd, 1, 100);
}

🔧 HAL_I2C_Mem_Read(&hi2cx, DevAddress, MemAddress, MemAddSize, pData, Size, Timeout)

原理解析

I2C主机从从机指定寄存器地址读取数据,为传感器数据采集的常用函数。

实战案例

c 复制代码
// 读取传感器指定寄存器数据
uint8_t Sensor_Read(void)
{
  uint8_t data;
  HAL_I2C_Mem_Read(&hi2c1, I2C_ADDR, 0x01, I2C_MEMADD_SIZE_8BIT, &data, 1, 100);
  return data;
}

6. SPI串行外设接口------芯片的高速数据驿道

SPI为四线制高速全双工通信协议,传输速率远高于I2C,适用于Flash、显示屏、无线模块等高速外设,支持数据同步收发,效率出众。

🔧 HAL_SPI_Transmit(&hspix, pData, Size, Timeout)

原理解析

SPI阻塞式单向发送数据,适用于简单指令下发、数据传输场景。

实战案例

c 复制代码
// SPI1单向发送单字节数据
void SPI_Send(void)
{
  uint8_t data = 0x55;
  HAL_SPI_Transmit(&hspi1, &data, 1, 100);
}

🔧 HAL_SPI_Receive(&hspix, pData, Size, Timeout)

原理解析

SPI阻塞式单向接收数据,定向获取外设传输的信息。

实战案例

c 复制代码
// SPI1单向接收单字节数据
void SPI_Recv(void)
{
  uint8_t rx_data;
  HAL_SPI_Receive(&hspi1, &rx_data, 1, 100);
}

🔧 HAL_SPI_TransmitReceive(&hspix, pTxData, pRxData, Size, Timeout)

原理解析

SPI全双工同步收发函数,发送数据的同时同步接收外设反馈,最大化传输效率,是SPI高速通信的核心方案。

实战案例

c 复制代码
// SPI1同步收发单字节数据,返回接收结果
uint8_t SPI_SendRecv(uint8_t tx_data)
{
  uint8_t rx_data;
  HAL_SPI_TransmitReceive(&hspi1, &tx_data, &rx_data, 1, 100);
  return rx_data;
}

7. 延时与系统函数------芯片的时序标尺

🔧 HAL_Delay(ms)

原理解析

基于SysTick定时器的毫秒级阻塞延时,CPU暂停执行其他任务,直至延时结束。

实战案例

c 复制代码
// 延时1秒(1000毫秒)
HAL_Delay(1000);

🔧 HAL_GetTick(void)

原理解析

获取系统开机至今的毫秒计数值,非阻塞特性适配多任务并行,可实现无卡顿延时、超时判断。

实战案例

c 复制代码
uint32_t last_time = 0;
// 每1秒执行一次任务,不阻塞CPU其他操作
void Task_Run(void)
{
  if(HAL_GetTick() - last_time >= 1000)
  {
    last_time = HAL_GetTick();
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
  }
}

8. 中断与回调函数------芯片的应急响应机制

中断是STM32的优先级响应机制,当按键触发、数据接收等紧急事件发生时,CPU暂停当前任务,优先处理中断逻辑,执行完毕后回归原有流程,高效保障实时性。

🔧 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_x)

原理解析

GPIO外部中断底层处理函数,负责清除中断标志位,需在中断服务函数中调用。

实战案例

c 复制代码
// GPIO外部中断服务函数
void EXTI0_IRQHandler(void)
{
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}

🔧 HAL_GPIO_EXTI_Callback(GPIO_PIN_x)

原理解析

GPIO外部中断业务回调函数,用户可重写此函数,中断触发后自动执行预设业务逻辑。

实战案例

c 复制代码
// 按键中断触发后,翻转LED电平
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin == GPIO_PIN_0)
  {
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
  }
}

🔧 HAL_UART_RxCpltCallback(&huartx)

原理解析

串口接收完成回调函数,中断/DMA接收数据完毕后自动触发,用于处理接收数据。

实战案例

c 复制代码
// 串口数据接收完成回调逻辑
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if(huart == &huart1)
  {
    // 此处编写串口数据处理逻辑
  }
}

🔧 HAL_TIM_PeriodElapsedCallback(&htimx)

原理解析

定时器溢出中断回调函数,定时周期结束后自动触发,执行定时业务任务。

实战案例

c 复制代码
// 定时器定时结束回调逻辑
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if(htim == &htim2)
  {
    // 此处编写定时任务逻辑
  }
}

新手实操须知:1. 外设启用前务必开启对应时钟;2. 中断、DMA功能需在CubeMX中开启全局开关;3. 中断回调函数严禁编写延时、循环等耗时逻辑,避免系统卡顿;4. 通信外设需匹配正确从机地址,否则无法完成数据交互。

相关推荐
Zhao1368245539111 小时前
DP4330A 与DP4363 测试对比
嵌入式硬件
辛迪聊物业数字化11 小时前
智慧社区小程序落地避坑指南:3类物业方案选型+成本测算,线上服务零翻车
大数据·嵌入式硬件·百度·系统架构
电子科技圈11 小时前
Arm电机控制开发工具哪家强?
arm开发·单片机·嵌入式硬件·设计模式·软件构建
JasmineX-111 小时前
下位机直接控制57步进电机匀速转动
stm32·单片机·嵌入式硬件
聆思科技AI芯片12 小时前
【AI硬件开发上手教程】怎么使用AI编程实现小聆AI语音控制舵机
人工智能·嵌入式硬件·开源·ai编程·智能硬件
abc light12 小时前
ARM 裸机开发(6)——时钟树与定时器
arm开发·单片机·fpga开发
吠品1 天前
智谱开源 ZCode 之后,真正的考试才开始
单片机·嵌入式硬件·1024程序员节
Chery11401 天前
高速ADC与精密DAC怎么选?DP9230D与DP8532D应用解析
嵌入式硬件·物联网·射频工程
小李不想当小白1 天前
SPI通信外设与W25Q64 (硬件SPI)(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·嵌入式
LCG米1 天前
STM32驱动W5500以太网模块实测:TCP吞吐瓶颈定位、TX指针回绕陷阱与断线重连保活落地
stm32·单片机·嵌入式硬件·tcp/ip