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. 通信外设需匹配正确从机地址,否则无法完成数据交互。

相关推荐
国家一级保护废物...2 小时前
51单片机day1
单片机·嵌入式硬件·51单片机
woshihonghonga2 小时前
解决Eclipse的Copilot终端依赖问题
stm32·mcu·eclipse·copilot·ai编程
busideyang2 小时前
STM32中__weak(弱定义)函数核心总结
c语言·stm32·单片机·嵌入式硬件·嵌入式
可乐鸡翅好好吃2 小时前
RTC时钟源及其低功耗应用
单片机·嵌入式硬件·实时音视频
senijusene2 小时前
51单片机:硬件基础、开发工具与核心外设详解
单片机·嵌入式硬件·51单片机
forAllforMe2 小时前
用STM32+LAN9252的etherCAT 从站实现传感器数据采集
stm32·单片机·嵌入式硬件
G***技2 小时前
物流自动化迈入边缘智能,杰和科技AR707成为关键引擎
人工智能·嵌入式硬件·机器人·边缘计算盒
雨洛lhw2 小时前
压控晶振学习笔记
嵌入式硬件·晶振
2501_918126913 小时前
学习所有用c语言定义stm32的语句
c语言·stm32·嵌入式硬件·学习·个人开发