嵌入式学习笔记——STM32寄存器编程实现外部中断

前言

上一篇中,介绍了关于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得数值会对应自增。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_41954556/article/details/129615278