1. 程式人生 > >外部中斷EXTI

外部中斷EXTI

前置技能:

一,STM32中斷介紹

相比簡單的51微控制器,STM32的每個IO口都可以作為外部中斷輸入

STM32中斷控制器支援19個外部中斷/事件請求:
    線0-15:外部IO口輸入中斷
    線16:連線到PVD輸出
    線17:連線到RTC鬧鐘事件
    線18:連線到USB喚醒事件

每個外部中斷線可配置獨立的觸發方式,觸發/遮蔽.專用狀態位

三種中斷觸發方式:
    上升沿觸發
    下降沿觸發
    雙邊沿觸發

二,STM32外部中斷對映

STM32F103ZET6共112引腳,線0-15為外部IO口輸入中斷

外部中斷IO對映

對映關係:

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

理論上所有IO口都可以作為中斷輸入

三,中斷服務函式的分配

這裡寫圖片描述

如圖:
    中斷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();

六,硬體連線

這裡寫圖片描述

使用按鍵觸發外部中斷

    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

新增標頭檔案到工程配置
標頭檔案配置

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);
    }
 }