STM32学习记录(三):外部中断EXTI
外部中断和外部事件的区别
粗略地解释中断和事件的区别:
- 外部中断(External interrupt),什么是中断?CPU正在执行某个任务时,突然接收到一个中断,CPU将当前正在执行的任务压入栈中,立刻去执行处理该中断(中断函数中处理该中断),中断结束后,返回到上次发生中断的地方继续执行之前的任务。
- 外部事件(External event),当发生某个事件时,CPU不会立刻去执行,而是在CPU空闲的时候,按照每个事件发生的先后次序依次处理。
大多数情况下都是使用中断,而不是事件。
外部中断线与中断处理函数对应关系
STM32F103C8T6外部中断线与中断处理函数对应关系:
中断线 | 中断通道 | 中断处理函数 |
---|---|---|
GPIO_Pin0 | EXTI0_IRQn | EXTI0_IRQHandler |
GPIO_Pin1 | EXTI1_IRQn | EXTI1_IRQHandler |
GPIO_Pin2 | EXTI2_IRQn | EXTI2_IRQHandler |
GPIO_Pin3 | EXTI3_IRQn | EXTI3_IRQHandler |
GPIO_Pin4 | EXTI4_IRQn | EXTI4_IRQHandler |
GPIO_Pin5~GPIO_Pin9 | EXTI9_5_IRQn | EXTI9_5_IRQHandler |
GPIO_Pin15~GPIO_Pin10 | EXTI15_10_IRQn | EXTI15_10_IRQHandler |
GPIO_Pin5~GPIO_Pin9共用一个中断通道,GPIO_Pin10~GPIO_Pin15共用一个中断通道。
启动文件startup_stm32f10x_md.s
中已经使用汇编伪指令DCD定义中断处理函数的名称。某些外设要使用中断,只需实现中断函数void PPP_IRQHander(void)
,其中PPP是外设的名称。
启动文件中的部分汇编代码如下,假设要实现外部中断线4的中断,只需在.c文件中实现void EXTI4_IRQHandler(void)
这个函数。 同理,要实现USART1中断,只需实现void USART1_IRQHandler(void)
中断处理函数是满足对应的中断条件时自动触发的,用户无需自行调用。
; External Interrupts
DCD WWDG_IRQHandler ; Window Watchdog
DCD PVD_IRQHandler ; PVD through EXTI Line detect
DCD TAMPER_IRQHandler ; Tamper
DCD RTC_IRQHandler ; RTC
DCD FLASH_IRQHandler ; Flash
DCD RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI Line 0
DCD EXTI1_IRQHandler ; EXTI Line 1
DCD EXTI2_IRQHandler ; EXTI Line 2
DCD EXTI3_IRQHandler ; EXTI Line 3
DCD EXTI4_IRQHandler ; EXTI Line 4
嵌套向量中断控制器(NVIC)
嵌套向量中断控制器(Nested vectored interrupt controller:NVIC),NVIC的作用就是管理所有中断,包括处理器核心异常。STM32F103C8T6中的NVIC存在于Cortex-M3处理器之中。
《Cortex-M3编程手册》1.3 About the STM32 Cortex®-M3 processor and core peripherals中写道:
Cortex-M3处理器紧密集成了可配置的嵌套中断控制器(NVIC),提供业界领先的中断性能。NVIC包括不可屏蔽中断(NMI),并提供高达256个中断优先级。
每个中断的可编程优先级为0-15。较高的级别对应较低的优先级,因此级别0是最高的中断优先级。NVIC将优先级值分组为组优先级(group priority)和次优先级(subpriority)字段。在STM32F10x固件库中的misc.h
文件中说明了NVIC的分组情况:
举个例子,可以看到当分组为NVIC_PriorityGroup_2
时,抢占优先级可以设置为0~3,次优先级可以设置为0~3。
有关中断向量表的具体内容参见《STM32F10x参考手册》。
外部中断配置流程
外部中断(EXTI)配置就是一个套路,按照对应的套路配置即可,配置流程图如下:
举个例子,配置PB11作为外部中断的程序如下:
/* 开始AFIO时钟和GPIOB时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE);
/* GPIO结构体配置 */
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* 配置中断源为PB11 */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource11);
/* 外部中断初始化结构体配置 */
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line11; //外部中断线11
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //外部中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能外部中断
EXTI_Init(&EXTI_InitStructure);
/* 设置NIVC优先级分组 */
NVIC_SetPriorityGrouping(NVIC_PriorityGroup_2);
/* NIVC初始化结构体配置 */
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; //NVIC_IRQChannel取值参考stm32f10x.h文件
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //次优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
按键控制LED
需求
使用外部中断的方式实现按键控制LED灯的亮灭
分析
GPIO控制LED灯,使用推挽输出即可。如表格所示,使用外部输入中断,GPIO要配置成浮空输入、上拉输入或下拉输入。STM32F103C8T6芯片中内置有上拉电阻,因此按键可以配置成上拉输入,让GPIO默认输出高电平。要使用外部中断必须打开AFIO的时钟,AFIO即Alternate-Function I/O,IO口的复用功能
程序
int main(void)
{
LED_Init();
EXIT1_Init();
GPIO_ResetBits(GPIOA, GPIO_Pin_0);
while (1)
{
}
}
/* 初始化连接LED的GPIO */
void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
/**
* @brief 初始化外部中断0
* @details EXTI中断线与GPIO是一一对应关系
*
* 中断线5~9公用一个中断通道,10~15公用一个中断通道
*
* 中断线 中断通道 中断处理
* GPIO_Pin0 EXTI0_IRQn EXTI0_IRQHandler
* GPIO_Pin1 EXTI1_IRQn EXTI1_IRQHandler
* GPIO_Pin2 EXTI2_IRQn EXTI2_IRQHandler
* GPIO_Pin3 EXTI3_IRQn EXTI3_IRQHandler
* GPIO_Pin4 EXTI4_IRQn EXTI4_IRQHandler
* GPIO_Pin5-GPIO_Pin9 EXTI9_5_IRQn EXTI9_5_IRQHandler
* GPIO_Pin15-GPIO_Pin10 EXTI15_10_IRQn EXTI15_10_IRQHandler
*
*/
void EXIT1_Init(void)
{
/* 开始AFIO时钟和GPIO时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE);
/* GPIO结构体初始化 */
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* 配置中断源为PB1 */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
/* 外部中断结构体初始化 */
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line1;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/* 设置NIVC优先级分组 */
NVIC_SetPriorityGrouping(NVIC_PriorityGroup_2);
/* NIVC初始化 */
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**
* 外部中断1处理函数,中断函数参照启动文件startup_stm32f10x_md.s中定义的函数名
*
*/
void EXTI1_IRQHandler(void)
{
// delay_ms(10); //消除抖动
if(EXTI_GetFlagStatus(EXTI_Line1) != RESET) //检测到触发条件时,Pending bit被置为1
{
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == RESET)
GPIO_SetBits(GPIOA, GPIO_Pin_0); //关闭LED灯
else
GPIO_ResetBits(GPIOA, GPIO_Pin_0); //打开LED灯
EXTI_ClearITPendingBit(EXTI_Line1); //清空EXTI1的中断请求Pending bit
}
}
参考资料
《STM32F10x参考手册》
《Cortex-M3编程手册》