外部中断EXTI

前置技能:

  按键输入-GPIO输入

一,STM32中断介绍

相比简单的51单片机,STM32的每个IO口都可以作为外部中断输入

STM32中断控制器支持19个外部中断/事件请求:
    线0-15:外部IO口输入中断
    线16:连接到PVD输出
    线17:连接到RTC闹钟事件
    线18:连接到USB唤醒事件

每个外部中断线可配置独立的触发方式,触发/屏蔽.专用状态位

三种中断触发方式:
    上升沿触发
    下降沿触发
    双边沿触发

二,STM32外部中断映射

STM32F103ZET6共112引脚,线0-15为外部IO口输入中断

外部中断EXTI_第1张图片

映射关系:

GPIOx.0映射EXTI0
GPIOx.1映射EXTI1
...
GPIOx.15映射EXTI15

理论上所有IO口都可以作为中断输入

三,中断服务函数的分配

外部中断EXTI_第2张图片

如图:
    中断0,1,2,3,4单独使用一个地址(中断向量)
    中断5-9共用一个中断向量
    中断10-15共用一个中断向量

    EXTI0_IRQHandler
    EXTI1_IRQHandler
    EXTI2_IRQHandler
    EXTI3_IRQHandler
    EXTI4_IRQHandler
    EXTI9_5_IRQHandler
    EXTI15_10_IRQHandler

四,外部中断常用库函数

// 设置IO口与中断线的映射关系
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);

// 初始化中断线:触发方式等
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);

//判断中断线中断状态,是否发生
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);

//清除中断线上的中断标志位
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);

四,中断相关函数分析

stm32f10x_exit.h头文件中找到EXTI_Init函数声明:

void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);

stm32f10x_exit.h头文件中找到EXTI_InitTypeDef结构体定义

/** * @brief EXTI Init Structure definition */

typedef struct
{
  uint32_t EXTI_Line;               /*!< Specifies the EXTI lines to be enabled or disabled. This parameter can be any combination of @ref EXTI_Lines */
//指定要配置的中断线


  EXTIMode_TypeDef EXTI_Mode;       /*!< Specifies the mode for the EXTI lines. This parameter can be a value of @ref EXTIMode_TypeDef */
//模式:事件 OR中断

  EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge for the EXTI lines. This parameter can be a value of @ref EXTIMode_TypeDef */
//触发方式:上升沿/下降沿/双沿触发

  FunctionalState EXTI_LineCmd;     /*!< Specifies the new state of the selected EXTI lines. This parameter can be set either to ENABLE or DISABLE */
//使能 OR失能

}EXTI_InitTypeDef;

mode属性有效性校验IS_EXTI_MODE

/** * @brief EXTI mode enumeration */

typedef enum
{
  EXTI_Mode_Interrupt = 0x00,     //中断
  EXTI_Mode_Event = 0x04          //事件
}EXTIMode_TypeDef;

#define IS_EXTI_MODE(MODE) (((MODE) == EXTI_Mode_Interrupt) || ((MODE) == EXTI_Mode_Event))

EXTI_Trigger属性有效性校验IS_EXTI_TRIGGER

/** * @brief EXTI Trigger enumeration */

typedef enum
{
  EXTI_Trigger_Rising = 0x08,          //上升沿
  EXTI_Trigger_Falling = 0x0C,            //下降沿
  EXTI_Trigger_Rising_Falling = 0x10  //双边沿
}EXTITrigger_TypeDef;

#define IS_EXTI_TRIGGER(TRIGGER) (((TRIGGER) == EXTI_Trigger_Rising) || \
                                  ((TRIGGER) == EXTI_Trigger_Falling) || \
                                  ((TRIGGER) == EXTI_Trigger_Rising_Falling))

配置实例:

 EXTI_InitStructure.EXTI_Line=EXTI_Line2;

 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;

 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;

 EXTI_InitStructure.EXTI_LineCmd = ENABLE;

 EXTI_Init(&EXTI_InitStructure);

stm32f10x_gpio.c文件中找到GPIO_EXTILineConfig函数源码:

/** * @brief Selects the GPIO pin used as EXTI Line. * @param GPIO_PortSource: selects the GPIO port to be used as source for EXTI lines. * This parameter can be GPIO_PortSourceGPIOx where x can be (A..G). * @param GPIO_PinSource: specifies the EXTI line to be configured. * This parameter can be GPIO_PinSourcex where x can be (0..15). * @retval None */
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
{
  uint32_t tmp = 0x00;
  /* Check the parameters */
  assert_param(IS_GPIO_EXTI_PORT_SOURCE(GPIO_PortSource));
  assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource));

  tmp = ((uint32_t)0x0F) << (0x04 * (GPIO_PinSource & (uint8_t)0x03));
  AFIO->EXTICR[GPIO_PinSource >> 0x02] &= ~tmp;
  AFIO->EXTICR[GPIO_PinSource >> 0x02] |= (((uint32_t)GPIO_PortSource) << (0x04 * (GPIO_PinSource & (uint8_t)0x03)));
}

GPIO_PortSource参数有效性验证 IS_GPIO_EXTI_PORT_SOURCE:

/** @defgroup GPIO_Port_Sources * @{ */
#define GPIO_PortSourceGPIOA ((uint8_t)0x00)
#define GPIO_PortSourceGPIOB ((uint8_t)0x01)
#define GPIO_PortSourceGPIOC ((uint8_t)0x02)
#define GPIO_PortSourceGPIOD ((uint8_t)0x03)
#define GPIO_PortSourceGPIOE ((uint8_t)0x04)
#define GPIO_PortSourceGPIOF ((uint8_t)0x05)
#define GPIO_PortSourceGPIOG ((uint8_t)0x06)

#define IS_GPIO_EXTI_PORT_SOURCE(PORTSOURCE) (((PORTSOURCE) == GPIO_PortSourceGPIOA) || \
                                              ((PORTSOURCE) == GPIO_PortSourceGPIOB) || \
                                              ((PORTSOURCE) == GPIO_PortSourceGPIOC) || \
                                              ((PORTSOURCE) == GPIO_PortSourceGPIOD) || \
                                              ((PORTSOURCE) == GPIO_PortSourceGPIOE) || \
                                              ((PORTSOURCE) == GPIO_PortSourceGPIOF) || \
                                              ((PORTSOURCE) == GPIO_PortSourceGPIOG))

GPIO_PinSource参数有效性验证IS_GPIO_PIN_SOURCE:

/** @defgroup GPIO_Pin_sources * @{ */
#define GPIO_PinSource0 ((uint8_t)0x00)
#define GPIO_PinSource1 ((uint8_t)0x01)
#define GPIO_PinSource2 ((uint8_t)0x02)
#define GPIO_PinSource3 ((uint8_t)0x03)
#define GPIO_PinSource4 ((uint8_t)0x04)
#define GPIO_PinSource5 ((uint8_t)0x05)
#define GPIO_PinSource6 ((uint8_t)0x06)
#define GPIO_PinSource7 ((uint8_t)0x07)
#define GPIO_PinSource8 ((uint8_t)0x08)
#define GPIO_PinSource9 ((uint8_t)0x09)
#define GPIO_PinSource10 ((uint8_t)0x0A)
#define GPIO_PinSource11 ((uint8_t)0x0B)
#define GPIO_PinSource12 ((uint8_t)0x0C)
#define GPIO_PinSource13 ((uint8_t)0x0D)
#define GPIO_PinSource14 ((uint8_t)0x0E)
#define GPIO_PinSource15 ((uint8_t)0x0F)

#define IS_GPIO_PIN_SOURCE(PINSOURCE) (((PINSOURCE) == GPIO_PinSource0) || \
                                       ((PINSOURCE) == GPIO_PinSource1) || \
                                       ((PINSOURCE) == GPIO_PinSource2) || \
                                       ((PINSOURCE) == GPIO_PinSource3) || \
                                       ((PINSOURCE) == GPIO_PinSource4) || \
                                       ((PINSOURCE) == GPIO_PinSource5) || \
                                       ((PINSOURCE) == GPIO_PinSource6) || \
                                       ((PINSOURCE) == GPIO_PinSource7) || \
                                       ((PINSOURCE) == GPIO_PinSource8) || \
                                       ((PINSOURCE) == GPIO_PinSource9) || \
                                       ((PINSOURCE) == GPIO_PinSource10) || \
                                       ((PINSOURCE) == GPIO_PinSource11) || \
                                       ((PINSOURCE) == GPIO_PinSource12) || \
                                       ((PINSOURCE) == GPIO_PinSource13) || \
                                       ((PINSOURCE) == GPIO_PinSource14) || \
                                       ((PINSOURCE) == GPIO_PinSource15))

五,外部中断的配置步骤

1,初始化IO口为输入。

       GPIO_Init();

2,开启IO口复用时钟。

       RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

3,设置IO口与中断线的映射关系。

        void GPIO_EXTILineConfig();

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

       EXTI_Init();

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

       NVIC_Init();

6,编写中断服务函数。

      EXTIx_IRQHandler();

7,清除中断标志位

      EXTI_ClearITPendingBit();

六,硬件连接

外部中断EXTI_第3张图片

使用按键触发外部中断

    KEY0 KEY1 KEY2接低电平,所以设置IO口PE2,PE3,E4为上拉输入
    按键按下时, IO口输入低电平,检测下降沿即可判断按键按下

    相反,WK_UP连接VCC,需设置IO口PA0为下拉输入
    按键按下时,IO口输入高电平,检测上升沿即可判断按键按下

七,外部中断代码

HARDWARE/EXIT文件夹,新建exit.h文件

#ifndef __EXTI_H
#define __EXIT_H
#include "sys.h"
void EXTIX_Init(void);  // 外部中断初始化
#endif

添加头文件到工程配置
外部中断EXTI_第4张图片

HARDWARE/EXIT文件夹,新建exit.c文件实现EXTIX_Init(void)外部中断初始化函数

#include "exti.h"
#include "led.h"
#include "key.h” // 按键端口初始化
#include "delay.h"
#include "usart.h"
#include "beep.h"

void EXTIX_Init(void)
{

    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    // 1,按键端口初始化 - 初始化IO口为输入(详细参考按键部分)
    KEY_Init();

    // 使能AFIO复用时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

    // 配置中断线映射-GPIOE.2映射到中断线2-按键KEY2
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);
    //初始化外部中断-线2
    EXTI_InitStructure.EXTI_Line=EXTI_Line2;                 // 中断线
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;      // 中断OR事件
    // 触发方式-下降沿触发(因为连接GND)
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;  
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;                // 使能
    EXTI_Init(&EXTI_InitStructure);

    // 配置中断线映射-GPIOE.3 映射到中断线3-按键KEY1
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);
    // 初始化外部中断-线3
    EXTI_InitStructure.EXTI_Line=EXTI_Line3;
    EXTI_Init(&EXTI_InitStructure);

    // 配置中断线映射-GPIOE.4 映射到中断线4-按键KEY0
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource4);
    //初始化外部中断-线4
    EXTI_InitStructure.EXTI_Line=EXTI_Line4;
    EXTI_Init(&EXTI_InitStructure);

    // 配置中断线映射-GPIOA.0映射到中断线0-按键WK_UP
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
    // 初始化外部中断-线0
    EXTI_InitStructure.EXTI_Line=EXTI_Line0;
    // 触发方式-上升沿触发(因为连接VCC)
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_Init(&EXTI_InitStructure);

    // 中断优先级配置
    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;              // 通道0
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;  // 抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;         // 响应优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;               // 使能 
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

//线0中断函数
void EXTI0_IRQHandler(void)
{
    delay_ms(10);       // 延时防止按键抖动
    if(WK_UP==1)        // 等待跳过抖动后再次判断是否按下
    {
        BEEP=!BEEP;     // 蜂鸣器取反
    }
    EXTI_ClearITPendingBit(EXTI_Line0); // 手动清除中断标志-线1
}

//线2中断函数
void EXTI2_IRQHandler(void)
{
    delay_ms(10);
    if(KEY2==0)    
    {
        LED0=!LED0; // LED0取反
    }
    EXTI_ClearITPendingBit(EXTI_Line2);
}

//线3中断函数
void EXTI3_IRQHandler(void)
{
    delay_ms(10); 
    if(KEY1==0)   
    {
        LED1=!LED1; // LED1取反
    }
    EXTI_ClearITPendingBit(EXTI_Line3); 
}

//线4中断函数
void EXTI4_IRQHandler(void)
{
    delay_ms(10); 
    if(KEY0==0) 
    {
        LED0=!LED0;     // LED0取反
        LED1=!LED1;     // LED1取反
    }
    EXTI_ClearITPendingBit(EXTI_Line4);
}

USER文件夹新建main.c文件,编写主函数

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "exti.h"
#include "beep.h"

 int main(void)
 {

    delay_init();             // 延迟初始化
    //设置中断优先级分组2 2位抢占2位响应
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 
    uart_init(115200);        // 初始化串口 设置波特率为115200
    LED_Init();               // LED端口初始化
    BEEP_Init();              // 蜂鸣器端口初始化
    KEY_Init();               // 按键端口初始化
    EXTIX_Init();             // 外部中断初始化
    LED0=0;                   // LED0点亮
    while(1)
    {
        printf("打印输入\r\n");
        delay_ms(1000);
    }
 }

你可能感兴趣的:(stm32,外部中断,EXIT,中断配置,STM32,STM32学习笔记)