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