历史上的今天
返回首页

历史上的今天

今天是:2024年09月28日(星期六)

2019年09月28日 | 再造STM32---第十五部分:EXTI—外部中断/事件控制器

2019-09-28 来源:eefocus

        本章参考资料:《STM32F4xx 中文参考手册》系统配置控制器以及中断和事件章节。上一章节我们已经详细介绍了 NVIC,对 STM32F4xx 中断管理系统有个全局的了解,我们这章的内容是 NVIC 的实例应用,也是 STM32F4xx 控制器非常重要的一个资源。


        学习本章时,配合《STM32F4xx 中文参考手册》系统配置控制器以及中断和事件章节一起阅读,效果会更佳,特别是涉及到寄存器说明的部分。

        特别说明,本书内容是以 STM32F42xxx 系列控制器资源讲解。



15.1 EXTI 简介:

        外部中断/事件控制器(EXTI)管理了控制器的 23 个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。 EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。


15.2 EXTI 功能框图:

        EXTI 的功能框图包含了 EXTI 最核心内容,掌握了功能框图,对 EXTI 就有一个整体的把握,在编程时就思路就非常清晰。 EXTI 功能框图见图 17-1。

        在图 17-1 可以看到很多在信号线上打一个斜杠并标注“23”字样,这个表示在控制器内部类似的信号线路有 23 个,这与 EXTI 总共有 23 个中断/事件线是吻合的。所以我们只要明白其中一个的原理,那其他 22 个线路原理也就知道了。




       EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件上就有所不同。

       首先我们来看图 17-1 中红色虚线指示的电路流程。它是一个产生中断的线路,最终信号流入到 NVIC 控制器内。

       编号 1 是输入线, EXTI 控制器有 23 个中断/事件输入线,这些输入线可以通过寄存器设置为任意一个 GPIO,也可以是一些外设的事件,这部分内容我们将在后面专门讲解。输入线一般是存在电平变化的信号。

       编号 2 是一个边沿检测电路,它会根据上升沿触发选择寄存器(EXTI_RTSR)和下降沿触发选择寄存器(EXTI_FTSR)对应位的设置来控制信号触发。边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号 1 给编号 3 电路,否则输出无效信号0。而 EXTI_RTSR 和 EXTI_FTSR 两个寄存器可以控制器需要检测哪些类型的电平跳变过程,可以是只有上升沿触发、只有下降沿触发或者上升沿和下降沿都触发。

       编号 3 电路实际就是一个或门电路,它一个输入来自编号 2 电路,另外一输入来自软件中断事件寄存器(EXTI_SWIER)。 EXTI_SWIER 允许我们通过程序控制就可以启动中断/事件线,这在某些地方非常有用。我们知道或门的作用就是有”就为 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 等等。


        产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。另外, EXTI 是在 APB2 总线上的,在编程时候需要注意到这点。


15.3 中断/事件线:

        EXTI 有 23 个中断/事件线,每个 GPIO 都可以被设置为输入线,占用 EXTI0 至EXTI15,还有另外七根用于特定的外设事件,见表 17-1。

        七根特定外设中断/事件线由外设触发,具体用法参考《STM32F4xx 中文参考手册》中对外设的具体说明。

表 15-1 EXTI 中断/事件线

image.png?imageView2/2/w/550

      EXTI0 至 EXTI15 用于 GPIO,通过编程控制可以实现任意一个 GPIO 作为 EXTI 的输入源。由表 17-1 可知, EXTI0 可以通过 SYSCFG 外部中断配置寄存器1(SYSCFG_EXTICR1)的 EXTI0[3:0]位选择配置为 PA0、 PB0、 PC0、 PD0、 PE0、 PF0、PG0、 PH0 或者 PI0,见图 17-2。其他 EXTI 线(EXTI 中断/事件线)使用配置都是类似的。

15.4 EXTI 初始化结构体详解:

       标准库函数对每个外设都建立了一个初始化结构体,比如 EXTI_InitTypeDef,结构体成员用于设置外设工作参数,并由外设初始化配置函数,比如 EXTI_Init()调用,这些设定参数将会设置外设相应的寄存器,达到配置外设工作环境的目的。

       初始化结构体和初始化库函数配合使用是标准库精髓所在,理解了初始化结构体每个成员意义基本上就可以对该外设运用自如了。初始化结构体定义在 stm32f4xx_exti.h 文件中,初始化库函数定义在 stm32f4xx_exti.c 文件中,编程时我们可以结合这两个文件内注释使用。

代码清单 17-1 EXTI 初始化结构体


typedef struct {

uint32_t EXTI_Line; // 中断/事件线

EXTIMode_TypeDef EXTI_Mode; // EXTI 模式

EXTITrigger_TypeDef EXTI_Trigger; // 触发事件

FunctionalState EXTI_LineCmd; // EXTI 控制

} EXTI_InitTypeDef;

     1) EXTI_Line: EXTI 中断/事件线选择,可选 EXTI0 至 EXTI22,可参考表 17-1 选择。

     2) EXTI_Mode: EXTI 模式选择,可选为产生中断(EXTI_Mode_Interrupt)或者产生事件(EXTI_Mode_Event)。

     3) EXTI_Trigger: EXTI 边沿触发事件,可选上升沿触发(EXTI_Trigger_Rising)、下降 沿 触 发 ( EXTI_Trigger_Falling) 或 者 上 升 沿 和 下 降 沿 都 触 发( EXTI_Trigger_Rising_Falling)。

     4) EXTI_LineCmd:控制是否使能 EXTI 线,可选使能 EXTI 线(ENABLE)或禁用(DISABLE)。


15.5 外部中断控制实验:

     中断在嵌入式应用中占有非常重要的地位,几乎每个控制器都有中断功能。中断对保证紧急事件得到第一时间处理是非常重要的


     我们设计使用外接的按键来作为触发源,使得控制器产生中断,并在中断服务函数中实现控制 RGB 彩灯的任务。


15.5.1 硬件设计:

     轻触按键在按下时会使得引脚接通,通过电路设计可以使得按下时产生电平变化,见图 15-1。

15.5.2 软件设计:


       这里只讲解核心的部分代码,有些变量的设置,头文件的包含等并没有涉及到,完整的代码请参考本章配套的工程。 我们创建了两个文件: bsp_exti.c 和 bsp_exti.h 文件用来存放 EXTI 驱动程序及相关宏定义,中断服务函数放在 stm32f4xx_it.h 文件中。

1. 编程要点:

       1) 初始化 RGB 彩灯的 GPIO;

       2) 开启按键 GPIO 时钟和 SYSCFG 时钟;

       3) 配置 NVIC;

       4) 配置按键 GPIO 为输入模式;

       5) 将按键 GPIO 连接到 EXTI 源输入;


       6) 配置按键 EXTI 中断/事件线;

       7) 编写 EXTI 中断服务函数。

2. 软件分析:

按键和 EXTI 宏定义

代码清单 15-2 按键和 EXTI 宏定义


//引脚定义

/*******************************************************/

#define KEY1_INT_GPIO_PORT GPIOA

#define KEY1_INT_GPIO_CLK RCC_AHB1Periph_GPIOA

#define KEY1_INT_GPIO_PIN GPIO_Pin_0

#define KEY1_INT_EXTI_PORTSOURCE EXTI_PortSourceGPIOA

#define KEY1_INT_EXTI_PINSOURCE EXTI_PinSource0

#define KEY1_INT_EXTI_LINE EXTI_Line0

#define KEY1_INT_EXTI_IRQ EXTI0_IRQn

#define KEY1_IRQHandler EXTI0_IRQHandler

#define KEY2_INT_GPIO_PORT GPIOC

#define KEY2_INT_GPIO_CLK RCC_AHB1Periph_GPIOC

#define KEY2_INT_GPIO_PIN GPIO_Pin_13

#define KEY2_INT_EXTI_PORTSOURCE EXTI_PortSourceGPIOC

#define KEY2_INT_EXTI_PINSOURCE EXTI_PinSource13

#define KEY2_INT_EXTI_LINE EXTI_Line13

#define KEY2_INT_EXTI_IRQ EXTI15_10_IRQn

#define KEY2_IRQHandler EXTI15_10_IRQHandler

       使用宏定义方法指定与电路设计相关配置,这对于程序移植或升级非常有用的。嵌套向量中断控制器 NVIC 配置

代码清单 15-3 NVIC 配置

 


static void NVIC_Configuration(void)

{

NVIC_InitTypeDef NVIC_InitStructure;

/* 配置 NVIC 为优先级组 1 */

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

/* 配置中断源:按键 1 */

NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;

/* 配置抢占优先级: 1 */

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

/* 配置子优先级: 1 */

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

/* 使能中断通道 */

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

/* 配置中断源:按键 2,其他使用上面相关配置 */

NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;

NVIC_Init(&NVIC_InitStructure);

}

        有关 NVIC 配置问题可参考上一章节内容,这里不做过多解释。

EXTI 中断配置

代码清单 15-4 EXTI 中断配置


void EXTI_Key_Config(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

EXTI_InitTypeDef EXTI_InitStructure;

/*开启按键 GPIO 口的时钟*/

RCC_AHB1PeriphClockCmd(KEY1_INT_GPIO_CLK|KEY2_INT_GPIO_CLK ,ENABLE);

/* 使能 SYSCFG 时钟 ,使用 GPIO 外部中断时必须使能 SYSCFG 时钟*/

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

/* 配置 NVIC */

NVIC_Configuration();

/* 选择按键 1 的引脚 */

GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN;

/* 设置引脚为输入模式 */

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;

/* 设置引脚不上拉也不下拉 */

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

/* 使用上面的结构体初始化按键 */

GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);

/* 连接 EXTI 中断源 到 key1 引脚 */

SYSCFG_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE,

KEY1_INT_EXTI_PINSOURCE);

/* 选择 EXTI 中断源 */

EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;

/* 中断模式 */

EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;

/* 下降沿触发 */

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;

/* 使能中断/事件线 */

EXTI_InitStructure.EXTI_LineCmd = ENABLE;

EXTI_Init(&EXTI_InitStructure);

/* 选择按键 2 的引脚 */

GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN;

/* 其他配置与上面相同 */

GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure);

/* 连接 EXTI 中断源 到 key2 引脚 */

SYSCFG_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE,

KEY2_INT_EXTI_PINSOURCE);

/* 选择 EXTI 中断源 */

EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE;

EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;

/* 上升沿触发 */

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;

EXTI_InitStructure.EXTI_LineCmd = ENABLE;

EXTI_Init(&EXTI_InitStructure);

}

       首先,使用 GPIO_InitTypeDef 和 EXTI_InitTypeDef 结构体定义两个用于 GPIO 和EXTI 初始化配置的变量,关于这两个结构体前面都已经做了详细的讲解。

       使用 GPIO 之前必须开启 GPIO 端口的时钟;用到 EXTI 必须开启 SYSCFG 时钟。

       调用 NVIC_Configuration 函数完成对按键 1、按键 2 优先级配置并使能中断通道。

       作为中断/时间输入线把 GPIO 配置为输入模式,这里不使用上拉或下拉,有外部电路完全决定引脚的状态。

       SYSCFG_EXTILineConfig 函数用来指定中断/事件线的输入源,它实际是设定SYSCFG 外部中断配置寄存器的值,该函数接收两个参数,第一个参数指定 GPIO 端口源,第二个参数为选择对应 GPIO 引脚源编号。

       我们的目的是产生中断,执行中断服务函数, EXTI 选择中断模式,按键 1 使用下降沿触发方式,并使能 EXTI 线。

       按键 2 基本上采用与按键 1 相关参数配置,只是改为上升沿触发方式。

EXTI 中断服务函数

代码清单 15-5 EXTI 中断服务函数


void KEY1_IRQHandler(void)

{

//确保是否产生了 EXTI Line 中断

if (EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) {

// LED1 取反

LED1_TOGGLE;

//清除中断标志位

EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);

}

}

void KEY2_IRQHandler(void)

{

//确保是否产生了 EXTI Line 中断

if (EXTI_GetITStatus(KEY2_INT_EXTI_LINE) != RESET) {

// LED2 取反

LED2_TOGGLE;

//清除中断标志位

EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE);

}

}

       当中断发生时,对应的中断服务函数就会被执行,我们可以在中断服务函数实现一些控制。

       一般为确保中断确实发生,我们会在中断服务函数调用中断标志位状态读取函数读取外设中断标志位并判断标志位状态。

       EXTI_GetITStatus 函数用来获取 EXTI 的中断标志位状态,如果 EXTI 线有中断发生函数返回“SET”否则返回“RESET”。实际上, EXTI_GetITStatus 函数是通过读取EXTI_PR 寄存器值来判断 EXTI 线状态的。

       按键 1 的中断服务函数我们让 LED1 翻转其状态,按键 2 的中断服务函数我们让 LED2翻转其状态。执行任务后需要调用 EXTI_ClearITPendingBit 函数清除 EXTI 线的中断标志位。

主函数

代码清单 15-6 主函数


int main(void)

{

/* LED 端口初始化 */

LED_GPIO_Config();

/* 初始化 EXTI 中断,按下按键会触发中断,

* 触发中断会进入 stm32f4xx_it.c 文件中的函数

* KEY1_IRQHandler 和 KEY2_IRQHandler,处理中断,反转 LED 灯。

*/

EXTI_Key_Config();

/* 等待中断,由于使用中断方式, CPU 不用轮询按键 */

while (1) {

}

}

       主函数非常简单,只有两个任务函数。 LED_GPIO_Config 函数定义在 bsp_led.c 文件内,完成 RGB 彩灯的 GPIO 初始化配置。 EXTI_Key_Config 函数完成两个按键的 GPIO 和 EXTI配置。


15.5.3 下载验证:

       保证开发板相关硬件连接正确,把编译好的程序下载到开发板。此时 RGB 彩色灯是暗的,如果我们按下开发板上的按键 1, RGB 彩灯变亮,再按下按键 1, RGB 彩灯又变暗;如果我们按下开发板上的按键 2 并弹开, RGB 彩灯变亮,再按下开发板上的 KEY2 并弹开,RGB 彩灯又变暗。

推荐阅读

史海拾趣

EF Johnson Technologies Inc公司的发展小趣事

面对日益激烈的国内市场竞争,EF Johnson公司制定了国际化战略,将目光投向了海外市场。公司通过与国外代理商的合作,成功将产品打入国际市场,并获得了良好的口碑。同时,公司还积极参与国际展会和技术交流活动,不断提升品牌影响力。在国际化战略的推动下,EF Johnson的海外市场份额逐步扩大,成为一家具有全球影响力的电子通信企业。

CONTRINEX公司的发展小趣事

在数控加工领域,Contrinex的数字测量智能传感器带来了革命性的变革。这些先进的传感器能够精确实时地测量拉杆位置,通过智能决策,确保加工精度,防止潜在损坏。其卓越的性能和稳定性,使得Contrinex在这一领域取得了显著的成绩,并赢得了众多客户的赞誉。

Gowanda Electronics公司的发展小趣事

自1972年在瑞士成立以来,Contrinex便踏上了成为全球传感器行业领军企业的征程。起初,公司专注于开发工业用的位置判定传感器,凭借其卓越的技术和精准的性能,很快便在市场上崭露头角。随着技术的不断进步和市场的不断扩大,Contrinex逐渐将业务拓展至全球60多个国家,旗下拥有500多名优秀员工,成为一家真正的国际化企业。

Automatic Connector公司的发展小趣事

随着全球电子市场的不断扩大,Automatic Connector公司积极寻求市场拓展的机会。公司首先在国内市场取得了良好的业绩,然后逐步将目光投向了国际市场。通过与国外代理商的合作,Automatic Connector的产品成功进入了欧洲、北美等发达地区的市场。在国际市场的竞争中,公司不断提升产品性能和服务水平,逐渐树立了良好的品牌形象。

东晨(DC)公司的发展小趣事

东晨(DC)公司深知人才是企业发展的核心动力。因此,公司高度重视人才培养和引进工作。公司建立了完善的培训体系,为员工提供全面的职业发展和技能培训。同时,公司还积极引进优秀人才,通过股权激励等措施留住人才。这些措施为公司的持续发展提供了有力的人才保障。

Delta Electronics Manufacturing Corp公司的发展小趣事

随着公司实力的不断增强,Delta开始积极拓展全球市场。公司通过设立海外分支机构、参加国际展会等方式,不断拓宽销售渠道,提升品牌知名度。同时,Delta还与国际知名企业展开合作,共同开发新产品,扩大市场份额。如今,Delta的产品已远销全球多个国家和地区,成为国际电源市场的领军企业之一。

问答坊 | AI 解惑

贝尔PCB设计规范

希望对大家有用!…

查看全部问答>

一个非常适合单片机的滤波算法(内附匠人分析)

匠人按:今天在论坛里看到一位网友发了个滤波算法。开始以为是一种新算法,后来仔细分析,发现原来是一阶滤波(低通滤波)的变形。原文及匠人的分析附录如下: -------------------------------------------------以下为原文------------------- ...…

查看全部问答>

24C02

请问:芯片24C02 可以用哪种芯片代替?…

查看全部问答>

if语句

if((t10min==1)&(IC4511=0))        /*定时时间到*/              {               baojing=1;        /*报警标志* ...…

查看全部问答>

如何使用wince自带的web server进行开发

最近想用wince自带的web server做一点东西,碰到了麻烦。 一是把做好的网页放到windows/www/wwwpub下,但是重启CE后,放进去的网页就又消失了。我看Moxa的串口服务器内也有一个web server,似乎是ce 自带的那种,但是它将网页放到了根目录下一个we ...…

查看全部问答>

陕西杨工c8051f单片机博客

手机 13002928013 http://c8051fmcu.blog.sohu.com …

查看全部问答>

电压的概念?请高人解释下

  自认为电流的概念弄清楚了,电流就跟水流一样,单位时间内定向通过的电子越多电流就越大, 但是电压的概念就没这么清楚了,有书上说是将电子从A点推到B点所做的功, 那这么说来在电源的正极加个电阻后亮电阻悬空一端对地的电压,是不是 ...…

查看全部问答>

分享前一段时间买的热风枪

总价184+25运费,一共209元   数字显示当前温度和设定温度,热风枪和烙铁直接拿起就能用了,直接看当前工作的温度,不想以前需要用手或者用焊锡丝去试了,真的很方便 …

查看全部问答>

关于使用STM32F103定时器比较匹配问题

本人想使用STM32F103输出占空比50%的方波用于控制步进电机,共需四路.看STM32资料上说有4路定时器,每路定时器又有四路独立的比较匹配通道.如果我用一个定时器的四路通道输出4路频率不同的占空比50%的方波行不行呢, ...…

查看全部问答>