欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32F4开发板 外部中断实验

程序员文章站 2024-02-25 18:12:57
...

STM32的中断分为内核中断外部中断,“内核中断”在《ARM CM3&CM4权威指南》中也被称作“系统异常”,如下所示:

STM32F4开发板 外部中断实验

STM32F4开发板 外部中断实验

 

STM32F4开发板 外部中断实验

今天主要讨论外部中断,即 CMSIS-Core中枚举值为正的异常(见表7.3)。


首先看关键词“中断线”,即“EXTI线”。STM32F407有23个外部中断,即23个中断线,从EXTI线0到EXTI线22。(此处书中似乎写成了22个中断线,貌似有误)

我们需要将I/O口与中断线之间建立映射关系,由于I/O口数量多于23,因此这种映射是多对一的,多个I/O口对应一个中断线。以EXTI线0为例,STM32按照GPIOA.0,GPIOB.0,GPIOC.0,GPIOD.0,GPIOE.0,GPIOF.0,GPIOG.0,GPIOH.0,GPIOI.0对应EXTI线0(即PA0,PB0,PC0,PD0,PE0,PF0,PG0,PH0,PI0均可以对应EXTI线0)。实际用到某个I/O引脚时,再通过配置决定具体哪个引脚对应EXTI线0。


接下来看关键词“步骤”,即配置外部中断的步骤。可以参考STM32的固件库函数stm32f4xx_exti.c代码和《精通STM32F4(库函数版)》。

代码中的注释部分可以看到:

                       ##### How to use this driver #####
 ==================================================
 
 [..] In order to use an I/O pin as an external interrupt source, follow steps 
      below:
   (#) Configure the I/O in input mode using GPIO_Init()
   (#) Select the input source pin for the EXTI line using SYSCFG_EXTILineConfig()
   (#) Select the mode(interrupt, event) and configure the trigger 
       selection (Rising, falling or both) using EXTI_Init()
   (#) Configure NVIC IRQ channel mapped to the EXTI line using NVIC_Init()

 [..]     
   (@) SYSCFG APB clock must be enabled to get write access to SYSCFG_EXTICRx
       registers using RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

书中的步骤也基本类似:

1.使能I/O口时钟,初始化I/O口为输入。—— GPIO_Init(),RCC_AHB1PeriphClockCmd

2.使能SYSCFG时钟,设置I/O口与中断线的映射关系。—— RCC_APB2PeriphClockCmd(),SYSCFG_EXTILineConfig()

3.初始化中断线(哪条EXTI线,中断模式是中断还是事件,触发方式是下降沿触发、上升沿触发还是任意电平触发,是否使能中断线)。—— EXTI_Init()

4.配置嵌套中断向量控制器NVIC。—— NVIC_Init()

5.编写中断服务函数。—— EXTI0_IRQHandler(),EXTI1_IRQHandler(),EXTI2_IRQHandler(),EXTI3_IRQHandler(),EXTI4_IRQHandler(),EXTI9_5_IRQHandler(),EXTI15_10_IRQHandler()。这些函数名称是在.s启动文件中定义的。


其实库函数是个壳,实际操作的还是寄存器,库函数只是相当于把对寄存器的操作“包起来”。如下所示:

STM32F4开发板 外部中断实验

EXTI_Init()操作的寄存器实际是下面这几个: 

STM32F4开发板 外部中断实验

这几个寄存器。。。再说吧

转一篇文章  https://zhuanlan.zhihu.com/p/24428018 感谢该文章作者。

stm32中断初识与实践(下)

这一部分我们将使用按键作为触发源,在产生中断时,实现控制LED灯的亮灭状态切换。

在具体应用前,我们还需先认识认识EXTI。

EXTI

全称为External interrupt/event controller,即外部中断/事件控制器。其管理了20个中断/事件线,每条线都有对应的一个边沿检测器,用于输入信号上升沿和下降沿的检测。如图6-1为stm32参考手册里的EXTI框图。

STM32F4开发板 外部中断实验

图6-1

图中有两条走向的线路,蓝色线路用于产生中断,绿色线路产生事件。我们从右往左看图。

查阅按键原理图可知,按键按下时,电平状态由低变高,会在输入线呈现出一个上升沿信号,这个信号到达边沿检测电路后,会被上升沿触发选择寄存器(EXTI_RTSR)检测并触发,输出有效信号1给编号3电路,否则输出无效信号0。如果电平由高变低,则会被下降沿触发选择寄存器(EXTI_FTSR)检测触发。

触发的信号到达编号3电路,这是一个或门电路,它的输入信号除了来源于边沿检测电路,还有来自软件中断事件寄存器(EXTI_SWIER)。无论是来自EXTI_SWIER或边沿检测电路的信号,只要有一个是有效信号1,那么便可以输出有效信号1给编号3电路。编号3电路之后,分为两条线,一条产生中断,一条产生事件。

信号沿着蓝色线路产生中断,编号3输出信号到编号4。编号4是一个与门电路,它的另一个信号来源是中断屏蔽寄存器(EXTI_IMR)。众所周知,与门电路要求输入信号都为1才能输出1,换言之,如果EXTI_IMR置为0,那么编号4电路输出的信号都为0,只有EXTI_IMR置1时,编号4输出的信号才由编号3决定。这样一来我们可以通过EXTI_IMR来控制是否产生中断。随后,编号4电路输出的信号会被保存到挂起寄存器(EXTI_PR)内。最后将EXTI_PR里的值输出到NVIC,实现中断控制。

信号沿着绿色线路产生事件,最终会输出一个脉冲信号。编号3输出信号到编号5,编号5也是一个与门电路,信号来源于编号3电路和事件屏蔽寄存器(EXTI_EMR)。和编号4的与门电路一样,我们可以通过EXTI_EMR来控制是否产生事件。当编号5输出有效信号1时会在脉冲发生器(Pulse generator)输出一个脉冲信号(无效信号不会输出脉冲)。这个脉冲信号可以给其他外设电路使用,如TIM、ADC等等,一般用来触发TIM或ADC开始转换。

EXTI的中断/事件线

EXTI有20条中断/事件线,其中有16条用于GPIO线上的外部中断/事件,占用EXTI0~EXTI15,其他4条用于特定外设的外部中断/事件。如图6-2。

 

STM32F4开发板 外部中断实验

图6-2

 

可以通过操作AFIO的四个外部中断配置寄存器(AFIO_EXTICR1~AFIO_EXTICR4)的EXTIx[3:0]位选择配置PAx、PBx、PCx...PGx等引脚。如图6-3为AFIO_EXTICR1寄存器描述。

 

STM32F4开发板 外部中断实验

图6-3

 

EXTI_InitTypeDef

EXTI_InitTypeDef是EXTI初始化结构体,其定义在stm32f10x.h文件中,如图6-4所示。

 

STM32F4开发板 外部中断实验

图6-4

 

有四个结构体成员,

  • EXTI_Line:中断/事件线,可选择EXTI0~EXTI19,如图6-2所示;
  • EXTI_Mode:EXTI模式,可设置产生中断(EXTI_Mode_Interrupt)或产生事件(EXTI_Mode_Event);
  • EXTI_Trigger:EXTI边沿触发,可选择上升沿触发(EXTI_Trigger_Rising)、下降沿触发(EXTI_Trigger_Falling)或者上升沿下降沿都触发(EXTI_Trigger_Rising_Falling);
  • EXTI_LineCmd:使能(ENABLE)/失能(DISABLE)EXTI线。

开始实验

按键按下时,产生电平变化,EXTI检测到上升沿信号,触发中断,执行中断服务函数,实现LED灯的亮灭切换。

简要分析下编程要点:

  1. 初始化产生中断的外设(GPIO);
  2. 配置NVIC;
  3. 初始化EXTI;
  4. 中断服务函数;
  5. main函数

NVIC配置

我们先对NVIC进行配置,将其封装为函数NVIC_Configuration(),供后续调用。

/**
  * @brief  NVIC配置
  * @param  无
  * @retval 无
  */
static void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;               
	
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);           // 配置优先级分组
	
    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;          // 配置按键中断源
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级为1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;        // 子优先级为1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           // 使能中断寄存器

    NVIC_Init(&NVIC_InitStructure);
}

NVIC配置部分,需要配置优先级分组、中断源、抢占优先级、子优先级以及使能中断寄存器等。关于优先级分组配置以及NVIC_InitTypeDef结构体分析,已在上篇文章里详细说明,读者可点击进入阅读。

EXTI中断配置

/**
  * @brief  EXTI按键中断配置
  * @param  无
  * @retval 无
  */
void EXTI_Key_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;
	
    /* 调用函数配置NVIC */
    NVIC_Configuration();
	
    /* 初始化GPIO */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  // 浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);
	
    /* 初始化EXTI */
    // 配置中断线的输入源
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); 

    EXTI_InitStructure.EXTI_Line = EXTI_Line0;            // 配置中断线为EXTI0
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;   // 配置为中断模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;// 上升沿触发中断	     
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;             // 使能中断
    EXTI_Init(&EXTI_InitStructure);
}

这个配置函数里用到了GPIO和EXTI两个初始化结构体,对其分别进行初始化配置,同时调用NVIC_Configuration()函数配置NVIC。

其中,

  • GPIO_EXTILineConfig()固件函数是对AFIO_EXTICR1的操作,所以我们需要开启AFIO时钟;
  • 需要把GPIO配置为输入模式(浮空输入),由外部电路决定引脚状态;
  • 通过查阅按键原理图可得,按键引脚为PA0,可得GPIO端口源和引脚源,并将其中断线配置为EXTI0;
  • 由按键原理图可得,按键按下时为高电平,所以使用上升沿触发中断。

中断服务函数

上篇文章里已经说明中断服务函数名应在启动文件里找到,并统一写在stm32f10x_it.c文件中。

/**
  * @brief  EXTI0线中断服务函数
  * @param  无
  * @retval 无
  */
void EXTI0_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line0) != RESET) // 确保产生了EXTI0线中断
    {
        LED_TOGGLE;        // LED灯状态切换 
        EXTI_ClearITPendingBit(EXTI_Line0);// 清除中断标志位
    }
}

需要先确保是否产生了中断,这一步我们直接调用stm32f10x_exti.c文件里的库函数EXTI_GetITStatus(),通过其返回值判断。EXTI_GetITStatus()函数操作的是中断屏蔽寄存器(EXTI_IMR)和挂起寄存器(EXTI_PR),通过两个寄存器的值判断是否产生中断,。由图6-1功能框图可得,如果相应线的EXTI_IMR和EXTI_PR都置1,则返回“SET”,即产生中断。具体的源码实现可查看3.5版本的stm32f10x固件库。

LED_TOGGLE是一个宏,在宏里实现LED状态切换。具体的实现在专栏(stm32):GPIO输入——按键检测文章里已经有过说明,读者可移步阅读。

中断服务实现后,需要清除该中断线的中断标志位,以免下次程序判断失误。

main函数

int main(void)
{	
    LED_GPIO_Config();  // LED端口初始化
    EXTI_Key_Config();  // EXTI按键中断配置
    while(1){}          // 等待中断产生
}

当按键按下时,即进入中断,执行中断服务函数,完成实验。

至此,stm32的中断总结完成了,文字有点多,耐心看完,肯定会有所收获。