关键函数:
- NVIC_EnableIRQ(IRQn_Type IRQn):使能中断
例5-1:单按键中断方式实现led灯的亮灭
在上一讲LP-MSPM0G3507学习--04GPIO控制中实现了通过按键控制led灯的亮灭,可以看出程序效率不高,下面采用中断的方式实现,其中的配置大部分相同,除了将管脚buttoninterrupts设置为中断:
需要注意的是触发极性(triggler polarity),有4种:
- diabled--可能是电平触发(此时应该是低电平触发,因为有上拉电阻了)
- trigger on rising edge--上升沿触发
- trigger on falling edge--下降沿触发
- trigger on both Edg--双边沿触发
从电路中可以看出,PB21应该上拉电阻,且采用边沿触发时,中断触发方式应该设为下降沿触发
#include "ti_msp_dl_config.h"int main(void)
{SYSCFG_DL_init();NVIC_EnableIRQ(GPIOB_INT_IRQn);//开启按键引脚的GPIOB端口中断while (1) {}
}
void GROUP1_IRQHandler(void){//GPIO中断服务函数DL_GPIO_togglePins(LED_PORT,LED_LED1_PIN);
}
分析:
这里涉及到两个函数NVIC_EnableIRQ(),GROUP_IRQ_IRQHandler()
首先了解,NVIC,即嵌套矢量中断控制器,用来管理外设中断。有关的函数在ti\mspm0_sdk_2_05_01_00\source\third_party\CMSIS\Core\include\core_cm0plus.h中
另外,中断号定义在i\mspm0_sdk_2_05_01_00\source\ti\devicws\msp\mspmog350x.h中
查阅数据手册的时候,发现3507中只有两个外设中断组:INT_GROUP0、INT_GROUP1.本实验要用到的只有按键和LED,所以只有GPIO这一部分的中断,根据数据手册,可以知道GPIO的中断触发后,都是通过GRP1线将中断发布到总线,总线识别到之后就进入中断服务函数中执行内容。
GPIO引脚的中断服务函数(ISR)需在代码中直接定义,其位置和声明方式遵循以下核心规则:
1. 中断服务函数的定义位置
-
直接在C文件中实现:GPIO中断属于
INT_GROUP1
组,因此中断服务函数必须命名为GROUP1_IRQHandler
,并在工程内的C文件(如main.c
或自定义的ISR文件)中实现。
示例代码:void GROUP1_IRQHandler(void) { // 中断处理逻辑 }
- 无需头文件声明:此函数是ARM Cortex-M预定义的中断向量之一,由链接脚本自动关联到中断向量表,无需在头文件中显式声明9。
2. 函数内部处理逻辑
在GROUP1_IRQHandler
内,需通过以下步骤区分具体的中断源(如不同GPIO引脚):
-
查询中断索引(IIDX):
使用DL_Interrupt_getPendingGroup(DL_INTERRUPT_GROUP_1)
获取触发中断的外设索引。 -
判断GPIO端口:
通过switch-case
匹配GPIO端口的IIDX宏(如GPIOA_INT_IIDX
或GPIOB_INT_IIDX
)。 -
检查具体引脚状态:
若同一端口有多个引脚使能中断,需调用DL_GPIO_getEnabledInterruptStatus()
并检查引脚位掩码
完整示例:
void GROUP1_IRQHandler(void) {switch (DL_Interrupt_getPendingGroup(DL_INTERRUPT_GROUP_1)) {case DL_INTERRUPT_GROUP1_IIDX_GPIOA: // GPIOA中断uint32_t status = DL_GPIO_getEnabledInterruptStatus(GPIOA);if (status & GPIO_PIN_12) { // 检查PA12引脚DL_GPIO_togglePins(GPIO_LED_PORT, GPIO_LED_PIN);DL_GPIO_clearInterruptStatus(GPIOA, GPIO_PIN_12); // 清除标志}break;// 其他GPIO端口处理...}
}
3. 配置与使能中断
-
SysConfig图形化配置:
在CCS的SysConfig工具中需启用GPIO中断,设置触发边沿(如下降沿),并指定引脚。 -
代码使能中断:
主函数中调用NVIC_EnableIRQ(GPIOx_INT_IRQN)
(如GPIOA_INT_IRQn
)激活NVIC中断。
4. 关键注意事项
-
中断标志清除:
在ISR内必须清除中断标志,否则会重复触发。使用DL_GPIO_clearInterruptStatus()
或DL_Interrupt_clearFlag()
。 -
变量声明:
跨中断共享的变量(如delay_time
)需加volatile
关键字防止优化错误。 -
调试陷阱:
若未正确定义GROUP1_IRQHandler
,程序可能跳转至默认错误处理(如死循环)。建议添加调试断点(__BKPT(0)
)辅助排查。
附:GPIO中断相关宏定义示例
宏名称 | 作用 | 示例值 |
---|---|---|
GPIO_SWITCHES_GPIOA_INT_IRQN | GPIOA中断号 | GPIOA_INT_IRQn |
DL_INTERRUPT_GROUP1_IIDX_GPIOA | GPIOA在GROUP1中的索引 | 由SDK头文件定义 |
GPIO_SWITCHES_USER_SWITCH_1_PIN | 具体引脚宏 | DL_GPIO_PIN_12 |
附:其他的中断名:
其他的一些中断名为(定义在C:\ti\mspm0_sdk_2_05_01_00\source\ti\devices\msp\m0p\startup_system_files\ticlang\startup_mspm0g350x_ticlang.c)中:
extern void GROUP0_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void GROUP1_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void TIMG8_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void UART3_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void ADC0_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void ADC1_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void CANFD0_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void DAC0_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void SPI0_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void SPI1_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void UART1_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void UART2_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void UART0_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void TIMG0_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void TIMG6_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void TIMA0_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void TIMA1_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void TIMG7_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void TIMG12_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void I2C0_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void I2C1_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void AES_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void RTC_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
extern void DMA_IRQHandler(void)__attribute__((weak, alias("Default_Handler")));
中断号定义在ti\mspm0_sdk_2_05_01_00\source\ti\devices\msp\m0p\mspm0g350x.h
typedef enum IRQn
{NonMaskableInt_IRQn = -14, /* 2 Non Maskable Interrupt */HardFault_IRQn = -13, /* 3 Hard Fault Interrupt */SVCall_IRQn = -5, /* 11 SV Call Interrupt */PendSV_IRQn = -2, /* 14 Pend SV Interrupt */SysTick_IRQn = -1, /* 15 System Tick Interrupt */SYSCTL_INT_IRQn = 0, /* 16 SYSCTL_INT Interrupt */WWDT1_INT_IRQn = 0, /* 16 WWDT1_INT Interrupt */WWDT0_INT_IRQn = 0, /* 16 WWDT0_INT Interrupt */FLASHCTL_INT_IRQn = 0, /* 16 FLASHCTL_INT Interrupt */DEBUGSS_INT_IRQn = 0, /* 16 DEBUGSS_INT Interrupt */GPIOB_INT_IRQn = 1, /* 17 GPIOB_INT Interrupt */GPIOA_INT_IRQn = 1, /* 17 GPIOA_INT Interrupt */TRNG_INT_IRQn = 1, /* 17 TRNG_INT Interrupt */COMP0_INT_IRQn = 1, /* 17 COMP0_INT Interrupt */COMP1_INT_IRQn = 1, /* 17 COMP1_INT Interrupt */COMP2_INT_IRQn = 1, /* 17 COMP2_INT Interrupt */TIMG8_INT_IRQn = 2, /* 18 TIMG8_INT Interrupt */UART3_INT_IRQn = 3, /* 19 UART3_INT Interrupt */ADC0_INT_IRQn = 4, /* 20 ADC0_INT Interrupt */ADC1_INT_IRQn = 5, /* 21 ADC1_INT Interrupt */CANFD0_INT_IRQn = 6, /* 22 CANFD0_INT Interrupt */DAC0_INT_IRQn = 7, /* 23 DAC0_INT Interrupt */SPI0_INT_IRQn = 9, /* 25 SPI0_INT Interrupt */SPI1_INT_IRQn = 10, /* 26 SPI1_INT Interrupt */UART1_INT_IRQn = 13, /* 29 UART1_INT Interrupt */UART2_INT_IRQn = 14, /* 30 UART2_INT Interrupt */UART0_INT_IRQn = 15, /* 31 UART0_INT Interrupt */TIMG0_INT_IRQn = 16, /* 32 TIMG0_INT Interrupt */TIMG6_INT_IRQn = 17, /* 33 TIMG6_INT Interrupt */TIMA0_INT_IRQn = 18, /* 34 TIMA0_INT Interrupt */TIMA1_INT_IRQn = 19, /* 35 TIMA1_INT Interrupt */TIMG7_INT_IRQn = 20, /* 36 TIMG7_INT Interrupt */TIMG12_INT_IRQn = 21, /* 37 TIMG12_INT Interrupt */I2C0_INT_IRQn = 24, /* 40 I2C0_INT Interrupt */I2C1_INT_IRQn = 25, /* 41 I2C1_INT Interrupt */AES_INT_IRQn = 28, /* 44 AES_INT Interrupt */RTC_INT_IRQn = 30, /* 46 RTC_INT Interrupt */DMA_INT_IRQn = 31, /* 47 DMA_INT Interrupt */
} IRQn_Type;
例5-2:双按键中断方式实现led灯的亮灭
![]() | ![]() |
实现按键S1按键点亮LED1,按键S2熄灭LED1
首先还是配置管脚,从电路原理图中可以看出S1按下为高电平,释放为低电平,所以S1即PA18设定为输入,内部连接下拉电阻,中断触发方式为上升沿;S2按下为低电平,释放为高电平,即PB21设定为输入,内部连接上拉电阻,中断触发方式为下降沿;led1设定为输出,初始值为高,即led初始为熄灭。
#include "ti_msp_dl_config.h"int main(void)
{SYSCFG_DL_init();NVIC_EnableIRQ(GPIOA_INT_IRQn);//开启按键引脚的GPIOA端口中断NVIC_EnableIRQ(GPIOB_INT_IRQn);//开启按键引脚的GPIOB端口中断while (1) {}
}
void GROUP1_IRQHandler(void) {uint32_t status=0; switch (DL_Interrupt_getPendingGroup(DL_INTERRUPT_GROUP_1)) {case DL_INTERRUPT_GROUP1_IIDX_GPIOA: // 处理GPIOA中断status= DL_GPIO_getEnabledInterruptStatus(Buttons_S1_PORT,Buttons_S1_PIN);if (status & Buttons_S1_PIN) { // 检查PA18引脚DL_GPIO_clearPins(LED_PORT, LED_LED1_PIN);//亮灯DL_GPIO_clearInterruptStatus(Buttons_S1_PORT, Buttons_S1_PIN); // 清除标志}break;case DL_INTERRUPT_GROUP1_IIDX_GPIOB: // 处理GPIOB中断status= DL_GPIO_getEnabledInterruptStatus(Buttons_S2_PORT,Buttons_S2_PIN);if (status & Buttons_S2_PIN) { // 检查PB21引脚DL_GPIO_setPins(LED_PORT, LED_LED1_PIN);//熄灯DL_GPIO_clearInterruptStatus(Buttons_S2_PORT, Buttons_S2_PIN); // 清除标志}break; default: // 处理其他中断// 完成处理break;}
}