在STM32中,中断服务函数(ISR)的命名和使用有特定的规则要求,下面详细说明:
一、中断服务函数命名要求
1. 官方命名规则(必须遵守)
中断服务函数的名字在启动文件(如startup_stm32fxxx.s)中已经预定义,必须使用完全相同的名字。
常见命名格式:
cs
// 外设中断
void USART1_IRQHandler(void) // USART1中断
void TIM2_IRQHandler(void) // TIM2中断
void EXTI0_IRQHandler(void) // EXTI线0中断
void EXTI15_10_IRQHandler(void) // EXTI线10-15中断
// 系统中断
void SysTick_Handler(void) // 系统滴答定时器中断
void PendSV_Handler(void) // PendSV中断
void SVC_Handler(void) // SVC调用中断
2. 命名查找方法
在启动文件中查找:
cs
; startup_stm32f103xe.s 示例
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
.word WWDG_IRQHandler ; 窗口看门狗
.word PVD_IRQHandler ; PVD通过EXTI检测
.word TAMPER_IRQHandler ; 篡改中断
; ... 更多中断向量
二、中断服务函数的使用步骤
1. 步骤1:编写中断服务函数
cs
// 在.c文件中定义中断服务函数
void TIM2_IRQHandler(void)
{
// 1. 检查中断标志位
if (TIM2->SR & TIM_SR_UIF) // 更新中断标志
{
// 2. 清除中断标志(非常重要!)
TIM2->SR &= ~TIM_SR_UIF;
// 3. 执行中断处理逻辑
GPIOA->ODR ^= GPIO_ODR_ODR5; // 翻转LED
// 4. 其他处理...
}
}
2. 步骤2:配置NVIC(嵌套向量中断控制器)
cs
// 配置TIM2中断优先级并使能
void TIM2_IRQ_Config(void)
{
// 设置中断优先级分组
NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
// 配置TIM2中断优先级
NVIC_SetPriority(TIM2_IRQn, 2); // 抢占优先级2,子优先级0
// 使能TIM2中断
NVIC_EnableIRQ(TIM2_IRQn);
}
3. 步骤3:配置外设中断
cs
void TIM2_Config(void)
{
// 1. 使能TIM2时钟
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
// 2. 配置TIM2
TIM2->PSC = 7200 - 1; // 预分频
TIM2->ARR = 10000 - 1; // 自动重载值
TIM2->EGR |= TIM_EGR_UG; // 生成更新事件
// 3. 使能更新中断
TIM2->DIER |= TIM_DIER_UIE;
// 4. 启动定时器
TIM2->CR1 |= TIM_CR1_CEN;
}
4. 步骤4:完整示例(以TIM2中断为例)
cs
#include "stm32f1xx.h"
// 中断服务函数
void TIM2_IRQHandler(void)
{
static uint32_t counter = 0;
if (TIM2->SR & TIM_SR_UIF)
{
TIM2->SR &= ~TIM_SR_UIF; // 清除标志
counter++;
if (counter >= 1000)
{
GPIOA->ODR ^= GPIO_ODR_ODR5; // 每秒翻转LED
counter = 0;
}
}
}
// 初始化函数
void TIM2_Init(void)
{
// 启用GPIOA和TIM2时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
// 配置PA5为推挽输出(LED)
GPIOA->CRL &= ~GPIO_CRL_MODE5;
GPIOA->CRL |= GPIO_CRL_MODE5_0;
GPIOA->CRL &= ~GPIO_CRL_CNF5;
// 配置TIM2
TIM2->PSC = 7200 - 1; // 72MHz/7200 = 10kHz
TIM2->ARR = 10000 - 1; // 10kHz/10000 = 1Hz中断
// 使能更新中断
TIM2->DIER |= TIM_DIER_UIE;
// 生成更新事件
TIM2->EGR |= TIM_EGR_UG;
// 启动定时器
TIM2->CR1 |= TIM_CR1_CEN;
// 配置NVIC
NVIC_SetPriority(TIM2_IRQn, 2);
NVIC_EnableIRQ(TIM2_IRQn);
}
int main(void)
{
TIM2_Init();
while(1)
{
// 主循环
}
}
三、使用HAL库的中断处理
1. HAL库中断服务函数
cs
// 使用HAL库的回调机制
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
}
}
// 仍然需要弱定义的中断服务函数
void TIM2_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim2); // HAL库处理中断
}
2. HAL库中断配置
cs
TIM_HandleTypeDef htim2;
void TIM2_Init(void)
{
htim2.Instance = TIM2;
htim2.Init.Prescaler = 7200 - 1;
htim2.Init.Period = 10000 - 1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
HAL_TIM_Base_Init(&htim2);
HAL_TIM_Base_Start_IT(&htim2); // 启动中断模式
}
四、重要注意事项
-
函数修饰符:通常不需要特殊修饰,但某些情况下可能需要:
cs__attribute__((interrupt)) // GCC编译器可选 void TIM2_IRQHandler(void) // Keil默认支持 -
必须清除中断标志:否则会不断触发中断
-
避免长时间操作:ISR应尽量简短,避免影响其他中断
-
中断优先级配置:
-
优先级数值越小,优先级越高
-
STM32支持中断嵌套
-
-
弱符号(Weak)定义:启动文件中的定义通常是弱符号,可以被用户定义覆盖
-
使用CubeMX生成代码时,会自动生成正确的中断服务函数框架
五、常见错误
-
命名错误 :
TIM2_Handler❌(正确:TIM2_IRQHandler✅) -
忘记清除标志位:导致无限中断
-
在ISR中调用阻塞函数 :如
HAL_Delay() -
未使能NVIC中断:只配置了外设,未使能NVIC
通过遵循这些规则和步骤,可以正确使用STM32的中断功能。