• STM32F4 异常与中断


    概述

      异常是导致程序流更改的事件,当一个异常发生,处理器会挂起当前正在执行的任务,并跳转执行响应的异常处理函数。在执行完异常处理函数后,处理器会恢复刚刚正常的程序执行流程。在ARM架构下,中断是异常的一部分,中断通常由外设或外部I/O引脚产生,在某些情况下,它们可以由软件触发。
      每个异常源都有一个异常号。异常号1 ~ 15表示系统异常,异常号16及以上表示中断。在Cortex-M3和Cortex-M4处理器中,NVIC的设计可以支持多达240个中断输入。然而,在实际中,在设计中实现的中断输入的数量要少得多,通常在16到100的范围内。这样设计的芯片尺寸就可以减少,也就降低了功耗。

      上图为在《stm32f4xx中文参考手册》中截取的异常向量表,异常向量表接下来就是中断向量表了,因为太长就不截取了,也可以去启动文件startup_stm32f40_41xxx.s中查看。

    嵌套向量中断控制器


      如上图所示,NVIC属于系统外设,也就是属于Cortex-M处理器的一部分。它是可编程的,它的寄存器位于内存映射的系统控制空间(SCS)
    NVIC处理异常和外部中断事件的配置、优先级设置和中断屏蔽。NVIC具有以下特点:
      1.灵活的异常和中断管理;
      2.嵌套异常/中断支持;
      3.向量异常/中断条目;
      4.中断屏蔽。
      系统重置后,所有中断都被禁用,并给出一个优先级值0,在使用任何中断之前,嵌套向量中断控制器需要进行以下设置:
        1.设置所需中断的优先级(这一步是可选的);
        2.在触发中断的外设中启用中断生成控制;
        3.在NVIC中启用中断。
    《Cortex M3与M4权威指南》章节7.3 P235

    Functions Usage
    void NVIC_EnableIRQ (IRQn_Type IRQn) Enable an external interrupt
    void NVIC_DisableIRQ (IRQn_Type IRQn) Disable an external interrupt
    void NVIC_SetPriority (IRQn_Type IRQn,uint32_t priority) Set the priority of an interrupt
    void __enable_irq(void) Clear PRIMASK to enable interrupts
    void __disable_irq(void) Set PRIMASK to disable all interrupts
    void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) Set priority grouping configuration

    异常/中断响应过程

      1.将当前程序状态寄存器CPSR的内容保存到将要执行的异常中断对应的SPSR寄存器中实现的,保存了处理器当前状态、中断屏蔽位以及各条件标志位。硬件还会自动把会被破坏的寄存器自动压栈;
      2.设置当前程序状态寄存器CPSR中相应的位。使处理器进入相应的执行模式、禁止IRQ中断等;
      3.将寄存器LR设置成需要返回地址;
      4.将程序计数寄存器PC的值,设置成该异常中断的中断向量地址,从而跳转到相应的异常中断处理程序处执行;
      5.当异常/中断函数执行完成后,恢复被中断的程序的处理器状态,即将当前的SPSR寄存器内容复制到当前程序状态寄存器CPSR中,恢复被中断的程序的处理器状态。最后返回到发生异常/中断的指令的下一条指令处执行,即将寄存器LR的内容复制到程序计数器PC中。

    抢占优先级和响应优先级

      stm32除了中断向量表上的优先级属性,还有具有抢占优先级和响应优先级属性,抢占优先级可以理解会对中断处理进行抢夺,也就是当有一个中断处理函数正在执行时,NVIC接收到了一个中断处理请求其抢占优先级高于现在当前执行的中断(数字越小优先级越高),则会中断当前正在执行的中断处理函数,去执行新产生的中断处理请求的函数,当执行完毕后再继续执行之前的中断处理函数。
      而响应优先级呢,不会进行抢夺,如当一个中断处理函数正在执行时,NVIC接收到了一个中断处理请求其抢占优先级等于现在当前执行的中断,响应优先级高于现在的中断,但是并不会进行抢夺,等待先前的中断处理完毕,才处理当前新产生的中断,只有在它们两个同时达到时,才会优先处理响应优先级高的中断。
      抢占优先级和响应优先级共同使用4个bit位来表示,通过库函数NVIC_PriorityGroupConfig()进行设置,说直白就是设置用几个bit位表示抢占优先级,用几个位表示响应优先级。当有两个抢占优先级和响应优先级都相同的中断产生时,则按中断向量表的优先级进行处理。

    外部中断/事件控制器


      上图是外部中断线或外部事件线产生的示意图,图中信号线上划有一条斜线,旁边标志23字样的注释,表示共有23条中断/事件线。图中的蓝色路线,标出了外部中断信号的传输路径,首先外部信号产生,通过边沿检测电路,与软件中断/事件寄存器相与后,如果相应位被置1则相应位的挂起请求寄存器会被清0表示中断产生,向NVIC中断控制器发出一个中断请求,当然前提是相应位的中断屏蔽寄存器未被清0,在中断处理函数中当任务处理完成,则需要手动将相应中断屏蔽寄存器置1,高速系统中断处理函数执行完成,等待下次中断进入,通过理解也可以得知只要将软件中断/事件寄存器需要的位置1便可以发生中断,即使输入线并没有相应的输入信号传输进来。
      图中红色的线是外部事件的传输路线,外部请求信号与软件中断/事件寄存器相与后,与事件屏蔽寄存器相或,这个与外部中断信号的传输路径相似,用于引入事件屏蔽寄存器的控制,最后脉冲发生器的一个跳变的信号转变为一个单脉冲,输出到芯片中的其它功能模块。从上图中我们也可以知道,从外部激励信号来看,中断和事件的产生源都可以是一样的。之所以分成两个部分,由于中断是需要CPU参与的,需要软件的中断服务函数才能完成中断后产生的结果,但是事件,是靠脉冲发生器产生一个脉冲,进而由硬件自动完成这个事件产生的结果,当然相应的联动部件需要先设置好。

      stm32f4一共有23条中断/事件线,每个I/O引脚号占用相应的中中断/事件线,如GPIOA引脚0和GPIOB引脚1,分别占用中断/事件线0和1,而GPIOA引脚0和GPIOB引脚0就共同占用中断/事件线0,因为每个端口最多有16根引脚,所以占用了16根中断/事件线,下面是剩下的7根:

    • EXTI line 16 is connected to the PVD output
    • EXTI line 17 is connected to the RTC Alarm event
    • EXTI line 18 is connected to the USB OTG FS Wakeup event
    • EXTI line 19 is connected to the Ethernet Wakeup event
    • EXTI line 20 is connected to the USB OTG HS (configured in FS) Wakeup event
    • EXTI line 21 is connected to the RTC Tamper and TimeStamp events
    • EXTI line 22 is connected to the RTC Wakeup even
    

      因为相同的I/O引脚号,都是共用同一个中断/事件线的,所以一次最多只能让一个相同引脚号的I/O起作用,正如上图所示,具体由哪个端口的I/O引脚与中断线向连接,可在系统配置控制器中进行设置。

    实验程序

      通过以上分析可知,如果需要配置GPIOA的引脚4,能够产生中断需要进行以下设置:
        1.打开GPIOA端口的时钟,初始化GPIOA的引脚4;
        2.设置系统配置控制器(SYSCFG),将GPIOA的引脚4与对应的中断/事件线4连接;
        3.设置嵌套向量中断控制器(NVIC),设置优先级,指定中断通道并使能;
        4.设置外部中断/事件控制器,设置中断/事件线,模式和触发方式,最后使能。
      GPIOA的引脚4我将其接上了一个蓝牙模块的stata引脚,当手机或者其他设备连接/断开蓝牙模块后,蓝牙模块的stata引脚电平将跳变,而GPIOA的引脚4被配置为了输入模式,上下沿触发方式,当检测到了引脚电平跳变就会产生一个中断,在中断中将LED1点亮/熄灭。
      根据本人随笔《LED和按键实验》和《Keil5环境搭建》创建和添加文件的方式进行。最先添加所需要的NVIC和EXTI的库函数,创建一个bluetooth.c,将添加在目录树的Device组中,创建头文件添加到目录树的Inc组中;
    bluetooth.c

    #include "stm32f4xx.h"
    #include "common.h"
    void led1cbt_signal_init()
    {
    	NVIC_InitTypeDef NVIC_InitStruct;
    	EXTI_InitTypeDef EXTI_InitStruct;
    	GPIO_InitTypeDef GPIO_InitStructure;
    	//配置PA4
    	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_4;//指定第9根引脚 
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN ;//配置为输入模式
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;//配置引脚的响应时间=1/50MHz.
    	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP ;//推挽的输出模式,增加输出电流和灌电流的能力
    	GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL ;//不使能内部上下拉电阻
    	GPIO_Init(GPIOA ,&GPIO_InitStructure);
    	
    	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource4);
    	NVIC_InitStruct.NVIC_IRQChannel = EXTI4_IRQn; // 指定通道
    	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级
    	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;// 响应优先级
    	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; // 打开通道
    	NVIC_Init(&NVIC_InitStruct);
    
    	EXTI_InitStruct.EXTI_Line = EXTI_Line4; 
    	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;  
    	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling; 
    	EXTI_InitStruct.EXTI_LineCmd = ENABLE;  
    	EXTI_Init(&EXTI_InitStruct);
    }
    
    void EXTI4_IRQHandler(void)
    {
    	//检查是否有中断请求
    	if(EXTI_GetITStatus(EXTI_Line4) != RESET)
    	{
    		// 上升沿触发
    		if (PAin(4)) //检测到高电平,表示有设备连接了蓝牙模块,点亮LED1
    		{
    			LED1ON; // 蓝牙连接,LED1亮
    		}
    		else //蓝牙连接被断开,LED1熄灭
    		{
    			LED1OFF; // 蓝牙断开
    		}
    		EXTI_ClearITPendingBit(EXTI_Line4);		
    	}
    }
    

    main.c

    #include "stm32f4xx.h"
    #include "hwconf.h"
    #include "common.h"
    #include "bluetooth.h"
    #include "led.h"
        
    int main()
    {
    	init_board(); //打开相关端口的时钟
    	led1_init();
    	LED1OFF;
            NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //选择使用优先级组的类别
            led1cbt_signal_init();
    }
    

    总结

      1.需要注意区别异常,中断,事件的区别;
      2.配置一个中断需要初始化相关引脚,配置SYSCFG,配置NVIC,配置EXTI一个都不能少;
      3.注意序号是相同I/O引脚不能同时使用中断线。

  • 相关阅读:
    线上查询及帮助命令:
    windows: 2.7 3.5 (主要)
    get the execution time of a sql statement.
    java-kafka安装以及使用案例
    java-黑马头条 weex前端路由
    MYSQL安装
    缓存
    Flask中current_app和g对象
    [ValueError: signal only works in main thread]
    Flask-SQLAlchemy操作
  • 原文地址:https://www.cnblogs.com/ding-ding-light/p/14418740.html
Copyright © 2020-2023  润新知