外设的中断控制
如ADC、SPI、I2C、TIM等使用STM32 HAL库时的中断函数调用方式和UART非常类似,都有底层直接使能中断和上层库函数管理两种方式。下面详细说明几种典型外设:
一、ADC外设
(1)直接使能中断(底层控制):
只需调用一次,一直有效。
// 开启ADC转换完成中断
__HAL_ADC_ENABLE_IT(&hadc1, ADC_IT_EOC);
// ADC IRQ中断函数(stm32f1xx_it.c)
void ADC1_IRQHandler(void)
{
if(__HAL_ADC_GET_FLAG(&hadc1, ADC_FLAG_EOC))
{
uint16_t adc_value = HAL_ADC_GetValue(&hadc1); // 手动读取ADC数据
__HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_EOC); // 清除标志位
}
}
(2)使用HAL库中断(库函数控制):
一般调用一次即启动一次ADC转换,中断每次触发后自动关闭,需再次调用。
// 启动一次ADC中断转换
HAL_ADC_Start_IT(&hadc1);
// 中断回调函数(用户实现)
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
uint16_t adc_value = HAL_ADC_GetValue(hadc);
// 处理数据...
HAL_ADC_Start_IT(hadc); // 再次启动中断以实现连续采样
}
// ADC中断服务函数(自动调用回调)
void ADC1_IRQHandler(void)
{
HAL_ADC_IRQHandler(&hadc1);
}
二、SPI外设
(1)直接使能中断(底层控制):
通常只调用一次保持长期有效。
// 启用SPI接收缓冲区非空中断
__HAL_SPI_ENABLE_IT(&hspi1, SPI_IT_RXNE);
// SPI中断函数
void SPI1_IRQHandler(void)
{
if(__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_RXNE))
{
uint8_t data = *(__IO uint8_t *)&hspi1.Instance->DR; // 手动读取数据寄存器
// 数据处理
}
}
(2)使用HAL库中断(库函数控制):
需要在每次传输完成后再次调用。
uint8_t rx_data[10];
HAL_SPI_Receive_IT(&hspi1, rx_data, 10); // 开启一次接收
// 回调函数
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
// 接收完成处理
HAL_SPI_Receive_IT(hspi, rx_data, 10); // 再次调用以连续接收
}
// SPI中断函数
void SPI1_IRQHandler(void)
{
HAL_SPI_IRQHandler(&hspi1);
}
三、I2C外设
(1)直接使能中断(底层控制):
使能一次即可长期有效,但需手动处理通信时序(不推荐新手使用)。
// 启用I2C接收中断
__HAL_I2C_ENABLE_IT(&hi2c1, I2C_IT_RXNE);
// I2C中断函数
void I2C1_EV_IRQHandler(void)
{
if(__HAL_I2C_GET_FLAG(&hi2c1, I2C_FLAG_RXNE))
{
uint8_t data = hi2c1.Instance->DR; // 手动读取数据寄存器
// 数据处理
}
}
(2)使用HAL库中断(库函数控制):
每次传输完成后需要再次调用。
uint8_t rx_data[10];
HAL_I2C_Slave_Receive_IT(&hi2c1, rx_data, 10); // 开启一次接收
// 回调函数
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
// 数据处理...
HAL_I2C_Slave_Receive_IT(hi2c, rx_data, 10); // 再次调用
}
// I2C中断函数
void I2C1_EV_IRQHandler(void)
{
HAL_I2C_EV_IRQHandler(&hi2c1);
}
四、TIM定时器外设
(1)直接使能中断(底层控制):
调用一次后自动循环触发中断(只要计数器开启)。
// 使能更新中断(溢出中断)
__HAL_TIM_ENABLE_IT(&htim1, TIM_IT_UPDATE);
HAL_TIM_Base_Start(&htim1); // 启动定时器计数
// 定时器中断函数
void TIM1_UP_IRQHandler(void)
{
if(__HAL_TIM_GET_FLAG(&htim1, TIM_FLAG_UPDATE))
{
__HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_UPDATE);
// 定时器溢出处理
}
}
(2)使用HAL库中断(库函数控制):
调用一次后自动循环触发(推荐使用,使用简单)。
// 启动TIM定时器中断模式
HAL_TIM_Base_Start_IT(&htim1);
// 回调函数(用户实现)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM1)
{
// 定时处理
}
}
// TIM中断函数
void TIM1_UP_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim1);
}
不同外设两种方式的共同规律总结:
方式 | 中断生效时长 | 数据处理方式 | 再次调用需求 |
---|---|---|---|
底层控制 (__HAL_xxx_ENABLE_IT ) | 持续有效(一次调用长期有效) | 用户手动读取寄存器 | 一般无需 |
库函数控制 (HAL_xxx_IT ) | 一次有效(传输结束自动关闭) | 库自动管理缓冲区,使用回调函数 | 必须重复调用 |
推荐方式:
- 一般应用场景下,推荐使用
HAL_xxx_IT
(易用、稳定)。 - 对于底层、实时要求严格、或者对数据有精细控制需求的场景,才考虑使用
__HAL_xxx_ENABLE_IT
手动管理。