用外部中断EXTI的方式控制LED灯
- EXTI简介
- EXTI 功能框图
-
- 中断线路流程(红色虚线)
-
- 编号 1 是输入线
- 编号 2 是一个边沿检测电路
- 编号 3 电路实际就是一个或门电路
- 编号 4 电路是一个与门电路
- 编号 5 是将 EXTI_PR 寄存器内容输出到 NVIC
- 事件线路流程(绿色虚线)
-
- 编号6 电路是一个与门
- 编号 7 是一个脉冲发生器电路
- 编号 8 是一个脉冲信号
- 配置流程和方法
-
- 配置中断优先级
- 配置中断引脚
- 配置中断
- 编写中断函数
- 编写主函数
- 注意事项
EXTI简介
EXTI(External interrupt/event controller)—外部中断/事件控制器,管理了控制器的 20个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。(以上是火哥说的)
EXTI 功能框图
上图可以看到很多在信号线上打一个斜杠并标注“20”字样,这个表示在控制器内部类似的信号线路有 20 个,这与 EXTI 总共有 20 个中断/事件线是吻合的。所以我们只要明白其中一个的原理,那其他 19 个线路原理也就知道了。
EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件。
中断线路流程(红色虚线)
编号 1 是输入线
EXTI 控制器有 19 个中断/事件输入线,这些输入线可以通过寄存器设置为任意一个 GPIO,也可以是一些外设的事件。输入线一般是存在电平变化的信号。
编号 2 是一个边沿检测电路
它会根据上升沿触发选择寄存器(EXTI_RTSR)和下降沿触发选择寄存器(EXTI_FTSR)对应位的设置来控制信号触发。边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号 1 给编号 3 电路,否则输出无效信号0。而 EXTI_RTSR 和 EXTI_FTSR 两个寄存器可以控制器需要检测哪些类型的电平跳变过程,可以是只有上升沿触发、只有下降沿触发或者上升沿和下降沿都触发。
编号 3 电路实际就是一个或门电路
它一个输入来自编号 2 电路,另外一个输入来自软件中断事件寄存器(EXTI_SWIER)。EXTI_SWIER 允许我们通过程序控制就可以启动中断/事件线,这在某些地方非常有用。我们知道或门的作用就是有 1 就为 1,所以这两个输入随便一个有有效信号 1 就可以输出 1 给编号 4 和编号 6 电路。
编号 4 电路是一个与门电路
它一个输入是编号 3 电路,另外一个输入来自中断屏蔽寄存器(EXTI_IMR)。与门电路要求输入都为 1 才输出 1,导致的结果是如果 EXTI_IMR 设置为 0 时,那不管编号 3 电路的输出信号是 1 还是 0,最终编号 4 电路输出的信号都为 0;如果 EXTI_IMR 设置为 1 时,最终编号 4 电路输出的信号才由编号 3 电路的输出信号决定,这样我们可以简单的控制 EXTI_IMR 来实现是否产生中断的目的。编号 4 电路输出的信号会被保存到挂起寄存器(EXTI_PR)内,如果确定编号 4 电路输出为 1 就会把 EXTI_PR 对应位置 1。
编号 5 是将 EXTI_PR 寄存器内容输出到 NVIC
实现系统中断事件控制。
事件线路流程(绿色虚线)
它是一个产生事件的线路,最终输出一个脉冲信号。产生事件线路是在编号 3 电路之后与中断线路有所不同,之前电路都是共用的。
编号6 电路是一个与门
它一个输入来自编号 3 电路,另外一个输入来自事件屏蔽寄存器
(EXTI_EMR)。如果 EXTI_EMR 设置为 0 时,那不管编号 3 电路的输出信号是 1 还是 0,最终编号 6 电路输出的信号都为 0;如果 EXTI_EMR 设置为 1 时,最终编号 6 电路输出的信号才由编号 3 电路的输出信号决定,这样我们可以简单的控制 EXTI_EMR 来实现是否产生事件的目的。
编号 7 是一个脉冲发生器电路
当它的输入端,即编号 6 电路的输出端,是一个有效信号 1 时就会产生一个脉冲;如果输入端是无效信号就不会输出脉冲。
编号 8 是一个脉冲信号
就是产生事件的线路最终的产物,这个脉冲信号可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC 等等,这样的脉冲信号一般用来触发 TIM 或者 ADC 开始转换。
(以上均来源于火哥,讲的非常好,不自觉的就摘录了。有需要的可以留言获得火哥的教程。)
配置流程和方法
实验名称:外部中断控制LED灯
实验现象:按下KEY0(PC5)按键实现LED状态翻转
硬件资源:STM32开发板、按键、LED灯
配置中断优先级
KEY0使用的是PC5,因此使用中断线EXTI9_5_IRQn。
static void NVIC_config(void)
{
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB2PeriphClockCmd(NVIC_CLK,ENABLE);
NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
配置中断引脚
这里设置的是KEY0(PC5),硬件设计为上拉。
RCC_APB2PeriphClockCmd(KEY0_CLK,ENABLE);
GPIO_InitStruct.GPIO_Pin = KEY0_Pin;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(KEY0_PORT,&GPIO_InitStruct);
配置中断
//1. 开启中断的时钟
RCC_APB2PeriphClockCmd(EXTI_CLK,ENABLE);
//2. 配置中断线
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource5);
//3 配置中断模式并初始化
EXTI_InitStruct.EXTI_Line = EXTI_Line5;
EXTI_InitStruct.EXTI_Mode =EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
编写中断函数
在中断服务函数中必须清除中断标志位。
void EXTI9_5_IRQHandler(void)
{
delay_ms(50);
// if(EXTI_GetITStatus(EXTI_Line5) != RESET) //²»ÄÜʹÓÃÕâ¾ä£¬ÎªÊ²Ã´ÄØ
if(KEY0==0)
{
LED=!LED;
}
EXTI_ClearITPendingBit(EXTI_Line5);
}
编写主函数
int main(void)
{
LED_Init();
delay_init();
EXTI_GPIO_Init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
while(1);
}
注意事项
- 必须配置中断优先级NVIC_Init,并设置其优先级分组NVIC_PriorityGroupConfig
- 开启AFIO时钟和中断的时钟要放在配置中断线GPIO_EXTILineConfig()之前。有次放在了后面,导致程序异常,搞了一个晚上,惨痛的教训。
- NVIC_IRQChannel如果不会配置,可以去stm32f10x.h中去复制粘贴。
程序已经过调试验证
完整程序传送门:
https://download.csdn.net/download/m0_46195580/13735851