中断系统和外部中断(标准库)

有关具体的系统异常和外部中断可在标准库文件stm32f4xx.h这个头文件中查询到。在IRQn_Type这个结构体中包含了F4系列全部的异常声明。

1、中断

(1)中断向量表

STM32F407的嵌套向量中断控制器(NVIC)管理所有中断。

中断向量控制器NVIC的结构体定义在库头文件core_cm4.h中。该结构体定义了中断控制相关的寄存器。在stm32f407中设置中断的时候我们只需要使用到ISER/ICER/IP这三个寄存器。ISER用来使能中断,ICER用来失能中断,IP用来设置中断的优先级。

82个可屏蔽中断,13个系统中断,16个优先级。

在stm32f4xx.h文件中的IRQn_Type结构体中,定义了所有的中断源。

typedef enum IRQn
{
//此处省略
  TIM1_CC_IRQn                = 27,     /*!< TIM1 Capture Compare Interrupt                                    */
  TIM2_IRQn                   = 28,     /*!< TIM2 global Interrupt                                             */
  TIM3_IRQn                   = 29,     /*!< TIM3 global Interrupt                                             */
  TIM4_IRQn                   = 30,     /*!< TIM4 global Interrupt                                             */                                          */
//此处省略
} IRQn_Type;

SYSTick定时器的定时中断,默认周期是1ms,产生周期为1ms的系统滴答信号。

(2)中断优先级

NVIC有一个专门的寄存器控制中断的优先级:NVIC_CPRx(在F407中,x = 0…981)。

中断控制器NVIC配中断的固件库函数在core_cm4.h中。这些函数在实际编程中使用较少。

void NVIC_EnableIRQ(IRQn_Type IRQn);//使能中断
void NVIC_DisableIRQ(IRQn_Type IRQn);//失能中断
uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn);//设置中断挂起位
void NVIC_SetPendingIRQ(IRQn_Type IRQn);//获取中断挂起位
void NVIC_ClearPendingIRQ(IRQn_Type IRQn);//清除中断挂起位
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority);//设置中断优先级
uint32_t NVIC_GetPriority(IRQn_Type IRQn);//获取中断优先级
NVIC_SystemReset(void);//系统复位

NVIC_CPRx寄存器宽度为8bit,原则上每个为外部中断可配置优先级0-255,但是大多数CM4芯片都会精简设计,以致实际支持的优先级数减少。在F407中,只使用了高4位。

STM32F系列的NVIC采用4位二进制数设置中断的优先级,并且分为抢占优先级和次优先级,优先级数字越小表示优先级越高。

82个可屏蔽中断可以同时发生,也可能在执行一个中断的ISR时又发生了另一个中断。

4位二级制数可以分为两段,一段用于设置抢占优先级,另一段用于设置次优先级。例如:2位用于抢占优先级,2位用于次优先级或3位用于抢占优先级,1位用于次优先级。

  • 如果两个中断的抢占优先级和次优先级相同,哪个中断先发生,就执行哪个中断的ISR。

  • 高抢占优先级的中断可以打断正在执行的低抢占优先级的ISR的执行。

  • 抢占优先级相同,次优先级高的中断不能打断正在执行的次优先级低的ISR的执行。

F407的优先级分组分为了5组。设置优先级分组可调用库函数NVIC_PriorityGroupConfig()实现。NVIC中断相关的库函数在库文件misc.c和misic.h中。

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

优先级的分组策略已经在文件中定义(misic.h)

#define NVIC_PriorityGroup_0         ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
                                                            4 bits for subpriority */
#define NVIC_PriorityGroup_1         ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
                                                            3 bits for subpriority */
#define NVIC_PriorityGroup_2         ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
                                                            2 bits for subpriority */
#define NVIC_PriorityGroup_3         ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
                                                            1 bits for subpriority */
#define NVIC_PriorityGroup_4         ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
                                                            0 bits for subpriority */

(3)中断编程

在配置每一个中断的时候一般有3个编程要点:

  1. 使能外设某个中断。具体由每个外设的相关中断使能位控制。比如串口发送完成中断,接收完成中断。

  2. 初始化NVIC_InitTypeDef结构体,配置中断优先级分组。设置抢占优先级和次优先级,使能中断请求。

    typedef struct
    {
      uint8_t NVIC_IRQChannel;                    /*!< 中断源 中断源在IRQn_Type结构体中定义,前面提到*/
    ​
      uint8_t NVIC_IRQChannelPreemptionPriority;  /*!< 抢占优先级*/
    ​
      uint8_t NVIC_IRQChannelSubPriority;         /*!< 次优先级*/
    ​
      FunctionalState NVIC_IRQChannelCmd;         /*!< 中断使能或者失能*/   
    } NVIC_InitTypeDef;
  3. 编写中断服务函数。

在启动文件stm32f40xx.s中预先编写了一个中断服务函数,但是这个函数是空的,是为了初始化中断向量表使用的。我们需要在stm32f4xx.c文件中重新编写中断服务函数。

2、外部中断

(1)外部中断(EXTI)

外部中断的来源:

MCU上的GPIO引脚作为输入引脚,引脚上的电平变化所产生的中断。

一些内部信号作为EXTI中断线的输入。

STM32F407有23个中断/事件线。每个中断/事件线都对应一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。

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

中断是将中断信号输入NVIC内,从而实现系统中断事件控制。

事件最终输出一个脉冲信号,这个脉冲信号可以给其他外设电路使用,比如定时器TIM,模拟数字转换器ADC等。

EXTI0~EXTI15这16个外部中断线以GPIO引脚作为输入线,每个GPIO引脚都可以作为某个EXTI的输入线。

 

EXTI0至EXTI4的每个中断有单独的ISR,EXTI线[9:5]中断共用一个中断号,也就是ISR。EXTI线[15:10]中断也共用ISR。

共用ISR需要在ISR中具体再判断是哪个EXTI线产生的中断。

EXTI线16~EXTI线22连接的不是某个实际的GPIO引脚,而是其他外设产生的事件信号。这7个EXTI线的中断有单独的ISR。

  • EXTI线16连接PCD输出。

  • EXTI线17连接RTC闹钟事件。

  • EXTI线18连接USB OTG FS唤醒事件。

  • EXTI线19连接以太网唤醒事件。

  • EXTI线20连接USB OTG HS唤醒事件。

  • EXTI线21连接RTC入侵和时间戳事件。

  • EXTI线22连接RTC唤醒事件。

 

(2)标准库中EXTI相关的内容

标准库函数对每个外设都建立了一个初始化结构体,结构体成员用于设置外设的工作参数,并有外设初始化函数调用。

EXTI初始化结构体定义在stm32f4xx_exti.h文件中。

typedef struct
{
  uint32_t EXTI_Line;               /*!< 中断/事件线*/
   
  EXTIMode_TypeDef EXTI_Mode;       /*!< EXTI模式,产生中断(EXTI_Mode_Interrupt)或产生事件                                                    (EXTI_Mode_Event) */
​
  EXTITrigger_TypeDef EXTI_Trigger; /*!< 触发模式,上升沿触发(EXTI_Trigger_Rising),下降沿触发                                               (EXTI_Trigger_Falling),上升沿和下降沿都触发                                                            (EXTI_Trigger_Rising_Falling) */
 
  FunctionalState EXTI_LineCmd;     /*!< EXTI使能 */ 
}EXTI_InitTypeDef;

EXTI初始化相关的函数

void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex);
//设置IO口与中断线的映射关系
example: SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource2);
​
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
//初始化中断线:触发方式等
​
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
//判断中断线中断状态,是否发生
​
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
//清除中断线上的中断标志位
​
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟
//这个函数非常重要,在使用外部中断的时候一定要先使能SYSCFG时钟

外部中断EXTI的步骤

  1. 初始化IO口为输入。

    GPIO_Init();

  2. 配置中断分组(NVIC),并使能中断。

    NVIC_Init();

  3. 使能SYSCFG时钟:

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

  4. 设置IO口与中断线的映射关系。

    void SYSCFG_EXTILineConfig();

  5. 初始化线上中断,设置触发条件等。

    EXTI_Init();

  6. 编写中断服务函数。

    EXTIx_IRQHandler();

  7. 清除中断标志位

    EXTI_ClearITPendingBit();

3、外部中断使用

(1)资源

MCU:STM32F407ZGT6

外部资源:

用户标签引脚名称引脚功能GPIO模式上拉或下拉抢占优先级次优先级
LED1PF9GPIO_Output推挽输出————
LED2PF10GPIO_Output推挽输出————
Key1PE2GPIO_Input下降沿触发外部中断上拉20
Key2PE3GPIO_Input下降沿触发外部中断上拉12
Key3PE4GPIO_Input下降沿触发外部中断上拉11
Key4PA0GPIO_Input上升沿触发外部中断下拉10

(2)引脚初始化

void gpio_exti_init()
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOF,ENABLE);
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_Pin = KeyDown_pin | KeyLeft_pin | KeyRight_pin;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(Key_right_left_down_port,&GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin = KeyUp_pin;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
    GPIO_Init(Key_up_port,&GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_Pin = Led1_pin | Led2_pin;
    GPIO_Init(led_port,&GPIO_InitStructure);    
}

(3)NVIC初始化

void nvic_gpio_init()
{
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//该函数在整个整个程序中只能出现一次
    
    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
    NVIC_Init(&NVIC_InitStructure);
}

(4)EXTI初始化

void exti_gpio_init()
{
    EXTI_InitTypeDef EXTI_InitStructure;
    //使能SYSCFG时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
    //连接EXTI中断源到GPIO
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);
    EXTI_InitStructure.EXTI_Line = EXTI_Line0;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
    
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource3); 
    EXTI_InitStructure.EXTI_Line = EXTI_Line3;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
}

(5)中断服务函数

void EXTI0_IRQHandler()
{
    if(EXTI_GetITStatus(EXTI_Line0)!= RESET)
    {
        GPIO_WriteBit(led_port,Led1_pin,Bit_SET);
        GPIO_WriteBit(led_port,Led2_pin,Bit_SET);
        
        EXTI_ClearITPendingBit(EXTI_Line0);
        Delay2();
    }
}
void EXTI3_IRQHandler()
{
    if(EXTI_GetITStatus(EXTI_Line3)!= RESET)
    {
        GPIO_WriteBit(led_port,Led1_pin,Bit_RESET);
        GPIO_WriteBit(led_port,Led2_pin,Bit_RESET);
        
        EXTI_ClearITPendingBit(EXTI_Line3);
        Delay2();
    }
}

4、库使用流程

5、注意事项

  • stm32f4xx_it.c中的中断服务函数只有写了9个特殊的中断服务函数的函数体结构。其余的在文件中没有的中断服务函数需要我们自己编写。编写是函数名必须是启动文件startup_stm32f40xx.s中已经定义好的,函数名称不对,不会有报错,但是中断会失效。

  • 此在 EXTI9_5_IRQHandler 和 EXTI15_10_IRQHandler 的中断函数里面使用多次EXTI_GetITStatus 函数来判断出线路。

  • NVIC_PriorityGroupConfifig在整个程序中只需要设置一次。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星顶照

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值