外部中断
- 前言
- EXTI的介绍
- EXTI是什么
- EXTI的主要特性
- 数量
- 对应中断源的命名
- EXTI的框图
- 配置流程
- 寄存器介绍
- 编程思路
- 编程
- 效果
前言
上一篇中,介绍了关于STM32的中断管理以及具体配置,本文就使用之前的配置流程来实现一下外部中断的功能。
EXTI的介绍
EXTI是什么
还是老套路,先来搞清楚EXTI是个啥,EXTI就是STM32的外部中断,既然叫做外部中断,那么其中断一定是来自内核外的,下图的描述中说它可以用以检测外部事件,其实不太完整哈,后面看了框图就清楚了,其实外部中断还可以由软件来触发的。
上图的描述中还提到了EXTI可以配置为上升沿触发、下降沿触发、上升下降沿触发等多种触发模式,这些就是上一篇中提到的中断信号,也就是整个外部中断的各个分开关。
EXTI的主要特性
数量
外部中断/事件控制器包含多达 23 个用于产生事件/中断请求的边沿检测器
主要的作用是用于通过边沿检测产生中断请求具备23个EXTI中断线
其中,0~15的中断线用于处理GPIO口对应的边沿检测,
16~22用于处理其它的片上外设,例如RTC之类的。
对应中断源的命名
上一篇中我们提到过,对应的中断会有统一的中断源名称,那么这里的外部中断源的命名是怎样的呢,在stm32f4xx.h中查阅即可看到,EXTI0、EXTI1 EXTI2、 EXTI3、 EXTI4是独立的中断源,而EXTI5_9 使用的是同一个中断源、EXTI15_10 使用的是同一个中断源;至于另外的16-22这个后面遇到了再说。
在这里需要搞清楚一个事情,那就是GPIO口和EXTI中断如何联系起来的,
举个栗子:
外部中断线:EXTI1对应的 GPIO管脚: PA1 PB1 PC1 PD1 PE1 PF1 PG1 PH1 PI1
也就是说:每一根中断线对应GPIO口的管脚 0–15的中断线对应GPIO0~15管脚。
PA0对应的是EXTI0;PC9对应的是EXTI9;PD9对应的也是EXTI9。
EXTI的框图
再了解了外部中断的一些基本信息后,按照之前的套路,肯定是要了解该如何配置外部中断了,而这中间首要任务就是看懂它的框图,搞清楚其大致的工作流程以及编程流程。
官方的框图如下所示,但是由于我们主要使用的是外部中断,所以红框内的时间控制那一部分就可以直接忽略不看了。
简化后如下图所示:
按照输入方向,来梳理一下流程。
1.输入信号经过输入线首先到达的是1号红色框内的边沿检测电路,这个电路收到上方两个寄存器的控制,一个是上升沿触发选择寄存器,另一个是下降沿触发选择寄存器,有程序员编程来选择上升沿、下降沿或者同时开启上升沿下降沿两种模式;
2.经过边沿检测电路后,紧接着来到2号橙色框的位置,这里有一个或门,这个或门的逻辑就是两个输入任意一个成立都可以往后走,也就是说,如果上一步没有配置边沿检测,直接在这里配置一个软件中断事件也是可以触发外部中断的,这就是刚刚提到的,外部中断不是只有检测边沿一种作用,它还可以用软件的方式来触发中断,这个软件中断寄存器也是程序员控制的,只要对该寄存器写入对应值它就会触发一次中断;
3.经过2框的或门后,紧接着就是一个与门,这个与门的逻辑是,输入双方同时成立,才会有对应的输出,这里的一个输入来自前面的边沿检测或者是软件触发,另一个输入是由一个叫做中断屏蔽寄存器的东西控制的,也就是说,编程者在操作时,这个寄存器一定要置一,整个电路才可以继续往下执行,其效果也就相当于前面提到的一个使能位,必须将它使能了才能执行后面的功能。
4.经过前面的操作和配置,信号来到了一个叫做挂起请求寄存器的东西,然后就直接进入了中断控制器,可以遇见的是,这个寄存器一定和是用来监测信号的状态的,可以通过它来判断是否产生了中断。
图中的/23代表的是23根外部中断线。
配置流程
通过上面的框图分析,我们可以大致的分析出外部中断的配置流程:
①确定IO口对应的中断线需要写入什么值
②配置对应的EXTI线的边沿触发
③开启EXTI线的中断使能
④配置EXTI线的中断源使能
⑤写中断服务函数
寄存器介绍
再分析完配置流程后,需要来研究一下具体的寄存器描述,注意EXTI的寄存器在中文参考手册的第10章第三节。
1.中断屏蔽寄存器 EXTI_IMR
写法:EXTI-> IMR
功能:每个位x表示对应的EXTIx
打开对应中断线中断请求(中断使能)对对应位写1即可打开对应的中断线使能。
2.事件屏蔽寄存器 (EXTI_EMR)
顾名思义,与上一个寄存器的功能刚好相反,可以用来屏蔽相关中断线的请求,一般不用,没必要屏蔽,不用的话不配置使能就可以了。
3.上升沿触发选择寄存器 (EXTI_RTSR)
写法:EXTI-> RTSR
功能:每个位x表示对应的EXTIx
打开对应中断线上升沿触发 ,对对应位置一即可使能上升沿触发。
4.下降沿触发选择寄存器 (EXTI_FTSR)
写法:EXTI-》FTSR
功能:每个位x表示对应的EXTIx
打开对应中断线下降沿触发 对对应位置一即可使能下降沿触发。
注意,边沿检测的时候是不能除夕拿毛刺信号的,也就是说,输入的波形一定要平滑,不能出现抖动。
5.软件中断事件寄存器 (EXTI_SWIER)
写法:EXTI->SWIER
功能:每个位x表示对应的EXTIx
让对应的EXTI线产生中断请求,通过软件置一的方式可以实现一次软件触发外部中断的效果。
6. 挂起寄存器 (EXTI_PR)
写法:EXTI->PR
功能:每个位x表示对应的EXTIx
对应的中断线产生了中断则置1
写入1可以对状态位做清除,中断的标志位,如果对应线上产生了中断,对应的寄存器就会被置一,判断中断时就是以这个作为标志的。
上面这部分都是在框图中可以找到的,实际上要使用外部中断还需要一个寄存器,前面提到过,一个EXTI线会对应多个管脚,那么系统要怎么知道具体是哪个管脚映射到了对应的EXTI线上呢,
这就需要使用到外部中断配置寄存器:SYSCFG->EXTICR,在手册的第八章,如下图所示,每四个为对应一个EXTI线,对这四个位写入对应的值即可选择到对应的管脚。
举个栗子:
我们需要配置PA0的外部中断,首先PA0对应的外部中短线是EXTI0,其对应的外部中断配置寄存器是SYSCFG_EXTICR1的0-3位,PA端口,应该写入的是0000。
具体写法:
SYSCFG->EXTICR[0] &=~(0xf<<0);
注意SYSCFG_EXTICR1的写法是SYSCFG->EXTICR[0];
SYSCFG_EXTICR2的写法是SYSCFG->EXTICR[1],依次类推。
再举个例子
需要配置PE4的外部中断,首先,对应的外部中断线是EXTI4,其对应的外部中断配置寄存器是SYSCFG_EXTICR2的0-3位,PE端口,所以应该写入0100;
所以其具体的写法如下:
SYSCFG->EXTICR[1] &= ~(0xf<<0); //先清零
SYSCFG->EXTICR[1] |= (0X4<<0); //GPIOE4映射到EXTI4
编程思路
好了,至此就已经大致捋清楚了外部中断的配置流程,注意,不要忘记了,使用到了中断,所以必须要有昨天的NVIC控制器相关的配置,这里再来总结一个具体的伪代码:
新建exti文件
配置思路:
EXTI的初始化函数
{
//打开时钟 PA时钟 SYSCFG时钟
//IO口配置 属于特殊的映射 不需要配置
//EXIT控制器
SYSSFG_EXTICR寄存器 IO映射
上下边沿检测
中断使能
//NVIC控制器
优先级配置
中断源使能
}
在nvic.c写中断服务函数 中断服务函数根据使用EXTI线
Void 中断服务函数(void)
{
判断触发
{
//清除标志位
//紧急事件
}
}
编程
按照上面的要求,新建对应的文件,然后添加代码,这里就不贴出全部代码了,需要的请私信,笔者实现的效果是三个按键,按下对应的变量会自增
Exti.c
#include "Exti.h"
u16 K_UP_cnt,K0_cnt,K1_cnt;
/*******************************************
*函数名 :Exti0_Init
*函数功能 :外部中断0初始化函数
*函数参数 :无
*函数返回值:无
*函数描述 :
PA0——对应EXTI0中断线,上升沿触发
PE3——对应EXTI3中断线,下降沿触发
PE4——对应EXTI4中断线,下降沿触发
*********************************************/
void Exti0_Init(void)
{
u32 pri;//存储优先级合成函数返回的优先级
RCC->AHB1ENR |= (1<<0); //打开AHB1上GPIOA端口
RCC->APB2ENR |= (1<<14); //打开SYSCFG的时钟使能,
/*---------------------------------------------------------------------------------*/
//IO配置GPIOA,通用输入,可不配置
/*-----------------------------------------------------------------------------------*/
SYSCFG->EXTICR[0] &= ~(0xf<<0); //配置GPIOA0与EXTI0映射连接——第八章系统配置控制器 (SYSCFG)
SYSCFG->EXTICR[0] &= ~(0XF<<12); //先清零
SYSCFG->EXTICR[0] |= (0X4<<12); //(GPIOE3映射到EXTI3)
SYSCFG->EXTICR[1] &= ~(0xf<<0); //先清零
SYSCFG->EXTICR[1] |= (0X4<<0); //GPIOE4映射到EXTI4
EXTI->RTSR |= (1<<0);//配置EXTI0为上升沿有效——第十章 10.3 EXTI 寄存器
EXTI->FTSR |= (1<<3);//配置EXTI3为下升沿有效
EXTI->FTSR |= (1<<4);//配置EXTI4为下升沿有效
EXTI->IMR |= (1<<0);//使能EXTI0的中断
EXTI->IMR |= (1<<3);//使能EXTI3的中断
EXTI->IMR |= (1<<4);//使能EXTI4的中断
/*------------------------------------------------------------------------------------*/
//NVIC控制器配置EXTI0
pri=NVIC_EncodePriority(7-2,0,2);
NVIC_SetPriority(EXTI0_IRQn,pri);
NVIC_EnableIRQ(EXTI0_IRQn);
//NVIC控制器配置EXTI3
pri=NVIC_EncodePriority(7-2,1,0);
NVIC_SetPriority(EXTI3_IRQn,pri);
NVIC_EnableIRQ(EXTI3_IRQn);
//NVIC控制器配置EXTI4
pri=NVIC_EncodePriority(7-2,1,1);
NVIC_SetPriority(EXTI4_IRQn,pri);
NVIC_EnableIRQ(EXTI4_IRQn);
}
/*******************************
函数名:EXTI0_IRQHandler
函数功能:EXTI0的中断服务函数函数
函数形参:无
函数返回值:void
备注:
********************************/
void EXTI0_IRQHandler(void)
{
if(EXTI->PR & (1<<0))//EXTI0产生了上升沿中断
{
EXTI->PR |= (1<<0);
K_UP_cnt++;
}
}
/*******************************
函数名:EXTI3_IRQHandler
函数功能:EXTI3的中断服务函数函数
函数形参:无
函数返回值:void
备注:
********************************/
void EXTI3_IRQHandler(void)
{
if(EXTI->PR & (1<<3))//EXTI3产生了下升沿中断
{
EXTI->PR |= (1<<3);
K0_cnt++;
}
}
/*******************************
函数名:EXTI4_IRQHandler
函数功能:EXTI4的中断服务函数函数
函数形参:无
函数返回值:void
备注:
********************************/
void EXTI4_IRQHandler(void)
{
if(EXTI->PR & (1<<4))//EXTI3产生了下升沿中断
{
EXTI->PR |= (1<<4);
K1_cnt++;
}
}
Exti.h
#ifndef _EXTI_H__
#define _EXTI_H__
#include "stm32f4xx.h"
void Exti0_Init(void);
#endif
效果
按下对应按键,右下角的WATCH得数值会对应自增。