STM32–中断使用(超详细!)
在嵌入式开发中,中断是一种非常重要的机制,能够在不影响主程序运行的情况下响应外部事件。STM32 作为一款功能强大的 ARM Cortex-M 微控制器,提供了丰富的中断功能。本文将详细讲解 STM32 的中断使用,涵盖中断的基本原理、配置方法、以及多个丰富的示例代码,帮助读者掌握如何在实际项目中灵活使用中断。
1. 中断的基本原理
中断(Interrupt)是由硬件或软件引发的异步事件,当中断发生时,处理器暂停当前的任务,跳转到中断服务程序(ISR,Interrupt Service Routine)去处理中断事件,处理完后继续执行原来的任务。
中断的基本过程:
- 外部或内部事件触发中断。
- 当前任务暂停,保存当前上下文。
- 跳转到中断向量表找到对应的中断处理程序(ISR)。
- 执行 ISR。
- ISR 执行完毕,恢复之前的上下文,继续执行原任务。
2. STM32 中断架构
STM32 使用了 ARM Cortex-M 内核的 NVIC(Nested Vectored Interrupt Controller)管理中断。NVIC 提供了对中断优先级、嵌套等的管理功能。
3. STM32 中断配置步骤
3.1 启用外设时钟
要启用与中断相关的外设,比如 GPIO、定时器等,首先需要为这些外设启用时钟:
// 启用 GPIOA 的时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
3.2 配置 GPIO 引脚为中断模式
假设我们使用外部按键触发中断,首先需要配置对应的 GPIO 引脚为中断输入模式:
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 配置 PA0 为输入模式并启用中断
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; // 上升沿触发中断
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上拉或下拉
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
3.3 配置中断优先级并启用中断
通过 NVIC 设置中断优先级,并启用对应的中断:
// 设置 EXTI0(PA0)中断的优先级为 2
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
// 启用 EXTI0 中断
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
3.4 编写中断服务程序(ISR)
当中断被触发时,程序会跳转到对应的中断服务程序。对于 EXTI0 中断的处理程序,可以在 stm32f1xx_it.c
文件中实现:
void EXTI0_IRQHandler(void)
{
// 检查中断标志位
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET)
{
// 清除中断标志位
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
// 在此处编写中断处理逻辑
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 切换 LED 状态
}
}
3.5 使能中断
确保在主函数中,启用了外部中断并进入主循环:
int main(void)
{
// 初始化 HAL 库
HAL_Init();
// 配置系统时钟
SystemClock_Config();
// 初始化 GPIO
MX_GPIO_Init();
while (1)
{
// 主循环可以执行其他任务
}
}
至此,我们完成了一个基本的 GPIO 外部中断的配置和使用。每当按下连接到 PA0 的按钮,程序将触发中断,并在 ISR 中切换 LED 的状态。
4. 中断优先级
STM32 的 NVIC 支持中断优先级的配置,可以通过 HAL_NVIC_SetPriority()
函数设置中断优先级。中断优先级的范围取决于具体的 STM32 芯片。例如,STM32F103 支持 4 位的优先级配置,优先级范围为 0-15,数值越小优先级越高。
中断优先级的配置可以帮助我们处理多个中断源时更灵活地管理系统的响应。
// 配置 EXTI0 和 EXTI1 中断的优先级
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
HAL_NVIC_SetPriority(EXTI1_IRQn, 1, 0); // EXTI1 优先级高于 EXTI0
在实际应用中,如果有多个中断源,我们可以根据中断的重要性配置不同的优先级。
5. 示例:定时器中断
除了外部中断,STM32 还支持定时器中断。我们可以通过定时器来周期性地触发中断。下面我们以 TIM3 为例,展示如何配置定时器中断。
5.1 定时器初始化
首先,我们需要初始化 TIM3 定时器:
void MX_TIM3_Init(void)
{
TIM_HandleTypeDef htim3;
__HAL_RCC_TIM3_CLK_ENABLE(); // 启用 TIM3 时钟
htim3.Instance = TIM3;
htim3.Init.Prescaler = 7999; // 预分频值
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 9999; // 自动重装载值
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim3);
// 启用定时器中断
HAL_NVIC_SetPriority(TIM3_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(TIM3_IRQn);
}
5.2 启动定时器并进入中断
在主函数中启动定时器:
int main(void)
{
// 初始化 HAL 库和系统时钟
HAL_Init();
SystemClock_Config();
// 初始化 TIM3
MX_TIM3_Init();
// 启动定时器中断
HAL_TIM_Base_Start_IT(&htim3);
while (1)
{
// 主循环
}
}
5.3 定时器中断处理程序
最后,编写 TIM3 的中断服务程序:
void TIM3_IRQHandler(void)
{
// 检查定时器中断标志位
if (__HAL_TIM_GET_FLAG(&htim3, TIM_FLAG_UPDATE) != RESET)
{
if (__HAL_TIM_GET_IT_SOURCE(&htim3, TIM_IT_UPDATE) != RESET)
{
__HAL_TIM_CLEAR_IT(&htim3, TIM_IT_UPDATE);
// 中断触发时执行的操作
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 切换 LED 状态
}
}
}
每当 TIM3 溢出时,将触发中断,并在中断服务程序中切换 LED 的状态。
6. 总结
本文通过多个代码示例详细讲解了 STM32 中断的使用,包括外部中断和定时器中断。中断是嵌入式开发中提高实时响应性的重要机制,熟练掌握中断的配置和使用可以让我们更加灵活地处理各种异步事件。
通过本文的学习,读者可以尝试在自己的项目中配置中断,处理外部按键、传感器信号或定时器事件。如果有更多的中断需求,还可以根据不同的外设和应用场景进行相应的中断优化配置。
作者:一只蜗牛儿