STM32
直播中

贾桂林

8年用户 1592经验值
私信 关注
[问答]

STM32F103外部中断大汇总,不看肯定后悔

STM32F103外部中断大汇总,不看肯定后悔

回帖(1)

刘伟

2021-11-18 11:05:17
  前言

  上一博客总结了NVIC中断优先级管理,本博总结一下外部中断的一些内容。本博会用外部中断的方法来实现之前博客:STM32F103五分钟入门系列(四)蜂鸣器实验(库函数+寄存器)中所举例子。
  一、外部中断

  (一)外部中断的数量及引脚映射关系

  1、外部中断数量

  在STM32F103中有19个外部中断:
  线0~15:对应外部IO口的输入中断
线16:连接到PVD输出
线17:连接到RTC闹钟事件
线18:连接到USB唤醒事件
  在底层中的定义:
  
  

  

  2、外部中断与GPIO的映射关系GPIO_EXTILineConfig()函数

  
  

  

  这个图详细的表述了外部中断与GPIO的映射关系:
  外部中断EXTI0——>GPIOA.0、GPIOB.0、GPIOC.0…GPIOG.0
外部中断EXTI1——>GPIOA.1、GPIOB.1、GPIOC.1…GPIOG.1
.
.
.
外部中断EXTI15——>GPIOA.15、GPIOB.15、GPIOC.15…GPIOG.15
  如EXTI0可以映射到PA0~PG0,那么EXTI0究竟映射到哪个引脚呢?当然是用到:GPIO_EXTILineConfig()函数了。
  在stm32f10x_gpio.h头文件中:

  

  


  

  

  第一个参数:

  

  

  第二个参数:
  
  

  

  通过这个函数,将外部中断线与GPIO的引脚映射起来。
  如对KEY_UP:

  

  


  

  

  中断映射:
          GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0 )   从参数中也能看到PA0…那只能是外部中断线0与PA0映射。
  (三)外部中断初始化函数 EXTI_Init()

  该函数设置某个中断的触发方式及使能,在stm32f10x_exti.h头文件中。

  

  

打开函数体:
  
  

  

  这个函数传递的参数是一个结构体,这种函数的配置应该见过N遍了。
  第一个参数:MODE

  

  

  表示模式是中断还是事件。
  第二个参数:
  
  

  

  表示触发方式是上升沿、下降沿、还是双边沿
  第三个参数:

  

  

  表示哪个引脚的中断,之前的GPIO_EXTILineConfig()已经将中断线与引脚对应起来了,所以这里只需选择是哪条中断线即可。
  第四个参数:使能和失能
  
  

  

  这类型函数设置方法之前有过基础,直接举例:
  例:设置刚刚的KEY_UP:
  ①要使用中断,所以第一个参数MODE为EXTI_Mode_Interrupt
  ②按下KEY_UP后,PA0变为高电平,所以为上升沿触发,第二个参数为:EXTI_Trigger_Rising
  ③引脚连接在PA0,所以第三个参数为:EXTI_Line0
  ④使能中断,所以第四个参数为:ENABLE
  直接附代码:
      
        EXTI_InitTypeDef  EXTI_InitStructure;
       
        EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
        EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;
        EXTI_InitStructure.EXTI_Line=EXTI_Line0;
        EXTI_InitStructure.EXTI_LineCmd=ENABLE;
        EXTI_Init(&EXTI_InitStructure);
  (三)外部中断解除函数EXTI_DeInit()

  
  

  

  与外部中断相关的寄存器在中文参考手册中,该函数将IMR、EMR、RTSR、FTSR寄存器都置0,对PR寄存器写1来清零。

  

  

  

  

  

  

  

  


  

  

  注意该函数没有参数的,即调用该函数后,所有的外部中断都被屏蔽掉,所以该函数要慎用。
  如果想清除某一个外部中断,而保留其他外部中断,则直接用寄存器的方法来设置。
  例:清除前面例子中KEY_UP的外部中断。
  ①屏蔽线0上中断,所以IMR寄存器位0置0(事件模式时可不设置)
          EXTI->IMR&=0xfffffffe;//屏蔽线0中断   ②屏蔽线0上的事件请求,所以EMR寄存器位0置0(中断模式时可不设置)
          EXTI->EMR&=0xfffffffe;//屏蔽线0事件   ③禁止线0上升沿触发中断和事件,所以RTSR寄存器位0置0(下降沿触发中断时可不设置,双边沿触发需要设置)
          EXTI->RTSR&=0xfffffffe;//禁止线0上升沿触发中断和事件   ④禁止线0下降沿触发中断和事件,所以FTSR寄存器位0置0(上升沿沿触发中断时可不设置,双边沿触发需要设置)
          EXTI->FTSR&=0xfffffffe;//禁止线0下降沿触发中断和事件   ⑤挂起线0的中断,所以PR寄存器位0软件置1来清除改位。
          EXTI->PR|=1;//挂起线0的中断和事件   (四)外部中断参数初始化函数EXTI_StructInit()

  
  

  

  该函数将所有中断设置的参数初始化为:
①中断线:无
②模式:中断
③触发方式:下降沿
④失能
  通过这种方法也能关闭全部外部中断:(慎用)
          EXTI_InitTypeDef EXTI_InitStructure;         EXTI_StructInit(& EXTI_InitStructure);         EXTI_Init(&EXTI_InitStructure);//失能全部外部中断   (五)软件中断函数EXTI_GenerateSWInterrupt()

  
  

  

  该函数是可以通过软件来触发对应中断线产生中断。该函数对SWIER寄存器直接操作。

  

  

  如果IMR和EMR寄存器允许中断和事件,即对应中断线上的中断或事件没有被屏蔽,不论设置的中断线是上升沿、下降沿还是双边沿触发,调用该函数后,都会产生一个中断。
  (六)外部中断状态函数EXTI_GetFlagStatus()

  
  

  

  如果发生中断,PR寄存器的对应位被硬件置1所以可以检测PR寄存器对应位是否为1来获得某外部中断线是否发生中断。
  注意的是:

  

  

  该函数返回的RESET为0,但是SET不是1,而是!0,所以最好不要这样写:
          if(EXTI_GetFlagStatus(EXTI_Line0)==1)//发生中断         {                  }   而应该:
          if(EXTI_GetFlagStatus(EXTI_Line0))//发生中断         {                  }   或者:
          if(EXTI_GetFlagStatus(EXTI_Line0)==SET)//发生中断         {                  }   (七)中断状态清除函数EXTI_ClearFlag()

  
  

  

  该函数也是对PR寄存器的设置,将寄存器对应软件位置1后清除该位
  
  

  

  (八)外部中断状态函数EXTI_GetITStatus()

  
  

  

  该函数获取中断的状态,返回RESET和SET,与EXTI_GetFlagStatus()函数一致,不过该函数还判断了IMR是否屏蔽了中断请求。所以EXTI_GetITStatus()比EXTI_GetFlagStatus()更严谨,以后习惯用这个函数就行。
  (九)清除中断标志位函数EXTI_ClearITPendingBit()

  
  

  

  该函数与EXTI_ClearFlag()一模一样,所以俩个函数可以随便用。
  二、外部中断的中断服务函数

  (一)外部中断服务函数数量

  STM32的外部中断服务函数只有6个:
  线0:EXTI0_IRQHandler
线1:EXTI1_IRQHandler
线2:EXTI2_IRQHandler
线3:EXTI3_IRQHandler
线4:EXTI4_IRQHandler
线5~9:EXTI9_5_IRQHandler
线10~15:EXTI15_10_IRQHandler
  中断服务函数定义在启动文件中:
  
  

  

  

  

  

  

  线0~4各用一个中断服务函数,线5 ~9共用一个中断服务函数,线10 ~15共用一个中断服务函数。
  (二)外部中断服务函数编写规则

  在中断服务函数中写的代码主要是发生中断后要执行的代码,比如之前SysTick这一博客中,用它的中断实现跑马灯,则SysTick中断服务函数中就写LED灯的亮、灭。
  在中断服务函数中,第一步要检测是否发生中断,如果发生中断,就执行中断后想要执行的代码,执行完中断服务函数后,需要清除中断标志,如果不清除中断标志,就一直显示处于中断中,一直会执行中断后的代码,导致程序跑飞。
  代码格式:
         
        void EXTIX_IRQHandler(void)
        {
        if(EXTI_GetITStatus(EXTI_Line3)!=RESET)//判断某个线上的中断是否发生
        {
          //中断逻辑…
        EXTI_ClearITPendingBit(EXTI_Line3); //清除 LINE 上的中断标志位
        }
   }
或者有时候把导致中断的举动写进中断服务函数中,如按下按键后,执行led亮、蜂鸣器响等。
      
        void EXTI0_IRQHandler(void)
        {
                delay_ms(10);//消抖
                if(WK_UP==1)                  //WK_UP按键
                {                                 
                        BEEP=!BEEP;       
                }
                EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位  
        }
当然,如上述代码,之前是检测到PA0有上升沿的,所以才会触发中断进入中断服务函数。然后再消抖后重新检查KEY_UP是否按下,确定按下后再执行蜂鸣器发声。不论消抖后检测到按下还是没按下,它都的的确确的发生了中断,即只要检测到上升沿就会发生中断,发生中断就会进入中断服务函数,PR寄存器对应位会被置1,所以中断服务函数后面也必须得对PR寄存器写1清零。
  (三)外部中断代码的编写顺序

  (1)初始化IO时钟
既然要用到线0~15的中断,所以GPIO的时钟必须先使能,不论是复用还是不复用。
  (2)初始化IO为输入
既然是外部中断,那么可能是外部的一个信号触发中断,所以IO需设置为输入,至于是上拉、下拉还是浮空输入,需要看外围电路。
  (3)开启复用时钟
  GPIO正常情况下是只做输入输出的,要实现其他功能就需要GPIO的复用。IO口复用可以查看《STM32中文参考手册》第八章GPIO和AFIO
  (4)设置IO口与中断的映射关系
即对GPIO_EXTILineConfig()函数的设置
  (5)初始化线上中断,设置触发条件等
即EXTI_Init()函数的设置
  (6)配置中断分组(NVIC),并使能中断
当然除了设置中断分组外,还需要设置外部中断的优先级,这些都在上一博客中总结了。
  (7)编写中断服务函数
  三、举例

  (一)题

  现用STM32F103五分钟入门系列(五)按键实验(库函数+寄存器)的例子:
  

   ①按下key_up后LED0、LED1交替闪烁,每0.5s闪烁一次,取消按下后,两个灯全灭。
②按下key0后,LED0常亮、蜂鸣器每隔0.5s间断发声。
③按下key2后,LED1常亮,蜂鸣器每隔0.5s间断发声。
④按下key1后,LED0、LED1同时亮,同时灭,且同时灭的时候蜂鸣器发声,同时亮的时候蜂鸣器不发声;间隔1s
  
  因为有延时,会用到:STM32F103五分钟入门系列(九)延时函数(自己重写的底层)(上限:477218ms和477218588us)
  (二)搞清楚IO和工作的高低电平

  
  

  

  

  

  
  

  


  

  

  KEY_UP:接PA0、按下后IO为高电平,下拉输入(未按下时输入未知,拉到低电平)
  KEY2:接PE2、按下后IO为低电平,上拉输入(未按下时输入未知,拉到高电平)
  KEY1:接PE3、按下后IO为低电平,上拉输入(未按下时输入未知,拉到高电平)
  KEY0:接PE4、按下后IO为低电平,上拉输入(未按下时输入未知,拉到高电平)
  LED1:接PE5、输出低电平后灯亮、通用推挽输出
  LED0:接PB5、输出低电平后灯亮、通用推挽输出
  BEEP:接PB8、输出高电平发声、通用推挽输出
  (三)代码编写

  1、led.h和led.c

  之前的几篇博客都讲解过,直接附代码:
  led.h代码:

1        //led.h
2        #ifndef LED_H
3        #define LED_H
4        void LED_Init(void);
5
6        #endif


1         //led.c
2         #include "sys.h"
3         #include "stm32f10x.h"
4         #include "led.h"
5         void LED_Init(void)
6         {
7                 GPIO_InitTypeDef GPIO_InitStruct_B;
8                 GPIO_InitTypeDef GPIO_InitStruct_E;
9                 RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE ,ENABLE);
10               
11                GPIO_InitStruct_B.GPIO_Mode=GPIO_Mode_Out_PP;
12                GPIO_InitStruct_B.GPIO_Pin=GPIO_Pin_5;
13                GPIO_InitStruct_B.GPIO_Speed=GPIO_Speed_50MHz;
14                GPIO_Init(GPIOB,&GPIO_InitStruct_B);
15               
16                GPIO_InitStruct_E.GPIO_Mode=GPIO_Mode_Out_PP;
17                GPIO_InitStruct_E.GPIO_Pin=GPIO_Pin_5;
18                GPIO_InitStruct_E.GPIO_Speed=GPIO_Speed_50MHz;
19                GPIO_Init(GPIOE,&GPIO_InitStruct_E);
20               
21                GPIO_SetBits(GPIOB, GPIO_Pin_5);//PB5置高电平
22                //GPIO_WriteBit(GPIOB, GPIO_Pin_5,1);
23                //GPIO_Write(GPIOB,0x0020);              //慎用
24                //PBout(5)=1;
25                GPIO_SetBits(GPIOE, GPIO_Pin_5);//PE5置高电平
26                //GPIO_WriteBit(GPIOE, GPIO_Pin_5,1);
27                //GPIO_Write(GPIOE,0x0020);              //慎用
28                //PEout(5)=1;
29 }
2、beep.h和beep.c

  之前的几篇博客都讲解过,直接附代码:

头文件:beep.h:


//beep.h
#ifndef BEEP_H
#define  BEEP_H


void BEEP_Init();
#endif


1
2
3
4
5
6
7
beep.c代码:


1         //beep.c
2         #include "beep.h"
3         #include "stm32f10x.h"
4         #include "sys.h"
5
6         void BEEP_Init(void)
7         {
8                 GPIO_InitTypeDef GPIO_InitStruct;
9                 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//使能GPIOB时钟
10               
11                GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
12                GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;
13                GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//PB8 IO配置
14                GPIO_Init(GPIOB,&GPIO_InitStruct);
15               
16                GPIO_ResetBits(GPIOB,GPIO_Pin_8);     //初始状态置低电平,关闭蜂鸣器
17                //GPIO_WriteBit(GPIOB,GPIO_Pin_8, 0);//初始状态置低电平,关闭蜂鸣器
18                //GPIO_Write(GPIOB,0);          //初始状态置低电平,关闭蜂鸣器
19                //PBout(8)=0;                         //初始状态置低电平,关闭蜂鸣器
20        }




3、key.h和key.c
之前的几篇博客都讲解过,直接附代码:


头文件key.h:


#ifndef KEY_H
#define KEY_H


void KEY_Init(void);


#endif


key.c文件:


//key.c
#include "stm32f10x.h"
#include "sys.h"
#include "key.h"
void KEY_Init(void)
{
        GPIO_InitTypeDef GPIO_InitStruct_A;
        GPIO_InitTypeDef GPIO_InitStruct_E;
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE , ENABLE);//使能GPIOA和GPIOE(PA0 PE2、3、4)
       
        GPIO_InitStruct_A.GPIO_Mode=GPIO_Mode_IPD;
        GPIO_InitStruct_A.GPIO_Pin=GPIO_Pin_0;
        GPIO_InitStruct_A.GPIO_Speed=GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStruct_A);//PA0 key_up  下拉输入
       
        GPIO_InitStruct_E.GPIO_Mode=GPIO_Mode_IPU;
        GPIO_InitStruct_E.GPIO_Pin=GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;
        GPIO_InitStruct_E.GPIO_Speed=GPIO_Speed_50MHz;
        GPIO_Init(GPIOE, &GPIO_InitStruct_E);//PE2、3、4 key0、key1、key2 上拉输入
}


4、delay.h和delay.c
之前博客重写过,直接附代码:


头文件:delay.h:


        //delay.h
        #ifndef _DELAY_
        #define _DELAY_
        #include  "sys.h"
        void delay_ms(u32 nms);
        void delay_Init(void);
        #endif




delay.c:


        //delay.c
        #include "delay.h"
        void delay_Init(void)
        {
                SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟源72MHZ/8=9MHZ
                 //SysTick->CTRL &=0xFFFFFFFB
               
                SysTick->VAL=0; //清空计数器
        }
       
        void delay_ms(u32 n)
        {
                u32 a;//商
                u32 b;//余数
                u32 temp;
                a=(9000*n/0xffffff);//2^24 -1=16777215=0xffffff
                b=(9000*n)%0xffffff;
                while(a)//商大于0时
                {
                        SysTick->LOAD=(u32)0xffffff; //装载最大可装载值
                        SysTick->VAL=0; //清空计数器
                        SysTick->CTRL|=1;       //使能计数器,开始计数
                        do
                        {
                                temp=SysTick->CTRL;
                        }while((temp&0x01)&&!(temp&(0x10000)));//while(计数器未使能&&(!CTRL计数器倒计数到0))
                        a--;
                        if(!a)//a为0,表示装载最大可装载值的次数用完
                        {
                                SysTick->CTRL&=0xfffffffe;//关闭计数器
                        }
                }
               
                SysTick->LOAD=b;//装载余数
                SysTick->VAL =0x00;        //清空计数器
                SysTick->CTRL|=1;       //重新使能计数器,开始计数
                do
                {
                        temp=SysTick->CTRL;
                }while((temp&0x01)&&!(temp&(0x10000)));//while(计数器未使能&&(!CTRL计数器倒计数到0))
               
                SysTick->CTRL&=0xfffffffe;//关闭计数器
                SysTick->VAL =0X00;       //清空计数器         
        }


5、添加.c和.h文件到工程

  以LED为例:
  (1)工程目录下新建文件夹LED

  

  

点击工程名——>【manage project…】

  

  


  

  

  新建text文件,并保存

  

  

并保存为led.c
  
  

  

  同理保存led.h

  

  

  添加.c文件:

  

  

  

  


  

  

  添加头文件.h

  

  

  

  


  

  

  同理将其他文件添加进来:

  

  


6、exti.h
exti.h还是老规矩:


#ifndef _EXTI_
#define _EXTI_


void exti_Init(void);
#endif




7、exti.c
因为之前的key中都使能了GPIO时钟和设置了GPIO,所以现在直接开启复用时钟。


由于中断服务函数要在exti.c文件中写,所以把lexd.h、key.h、beep.h、delay.h包含进来


代码:


        #include "stm32f10x.h"
        #include "led.h"
        #include "key.h"
        #include "beep.h"
        #include "delay.h"
        #include "exti.h"
        void exti_Init()
        {
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
        }


然后接下来就是中断线与IO的映射:
KEY_UP——>线0——>PA0
KEY2——>线2——>PE2
KEY1——>线3——>PE3
KEY0——>线4——>PE4


        GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);外部中断与PA0映射
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2);
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3);
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);


然后就是外部中断初始化函数:


#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "beep.h"
#include "delay.h"
#include "exti.h"
void exti_init()
{
        EXTI_InitTypeDef  EXTI_InitStructure;
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
       
                //外部中断与PE2、PE3、PE4映射
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2);
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3);
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);外部中断与PA0映射
       
        EXTI_InitStructure.EXTI_Line=EXTI_Line0;                //PA0
        EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
        EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;
        EXTI_InitStructure.EXTI_LineCmd=ENABLE;
        EXTI_Init(&EXTI_InitStructure);
       
        EXTI_InitStructure.EXTI_Line=EXTI_Line2;                //PE2
        EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
        EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
        EXTI_InitStructure.EXTI_LineCmd=ENABLE;
        EXTI_Init(&EXTI_InitStructure);
       
        EXTI_InitStructure.EXTI_Line=EXTI_Line3;        //PE3
        EXTI_Init(&EXTI_InitStructure);
       
        EXTI_InitStructure.EXTI_Line=EXTI_Line4;        //PE4
        EXTI_Init(&EXTI_InitStructure);
}


然后就是中断优先级分组、各中断优先级、中断使能。


中断优先级分组在main.c中写,因为用到四个中断,且不想被打断,所以可以用分组2,抢占优先级一样,响应优先级0~3.


        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
1
然后就是中断优先级设置,在exti.c中


#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "beep.h"
#include "delay.h"
#include "exti.h"
void exti_Init()
{
        EXTI_InitTypeDef   EXTI_InitStructure;
        NVIC_InitTypeDef   NVIC_InitStructure;
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
       
                //外部中断与PE2、PE3、PE4映射
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2);
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3);
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);外部中断与PA0映射
       
        EXTI_InitStructure.EXTI_Line=EXTI_Line0;                //PA0
        EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
        EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;
        EXTI_InitStructure.EXTI_LineCmd=ENABLE;
        EXTI_Init(&EXTI_InitStructure);
       
        EXTI_InitStructure.EXTI_Line=EXTI_Line2;                //PE2
        EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
        EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
        EXTI_InitStructure.EXTI_LineCmd=ENABLE;
        EXTI_Init(&EXTI_InitStructure);
       
        EXTI_InitStructure.EXTI_Line=EXTI_Line3;        //PE3
        EXTI_Init(&EXTI_InitStructure);
       
        EXTI_InitStructure.EXTI_Line=EXTI_Line4;        //PE4
        EXTI_Init(&EXTI_InitStructure);
       
        NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;                //PA0
        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
        NVIC_Init(&NVIC_InitStructure);
       
       
        NVIC_InitStructure.NVIC_IRQChannel=EXTI2_IRQn;                //PE2
        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
        NVIC_Init(&NVIC_InitStructure);
       
        NVIC_InitStructure.NVIC_IRQChannel=EXTI3_IRQn;                //PE3
        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
        NVIC_Init(&NVIC_InitStructure);
       
        NVIC_InitStructure.NVIC_IRQChannel=EXTI4_IRQn;                //PE4
        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;
        NVIC_Init(&NVIC_InitStructure);
}


接下来写中断服务函数:


PA0、KEY_UP:
注意当松开按键后,再清除中断,否则里面函数只执行一次!


void EXTI0_IRQHandler()//PA0  KEY_UP
{
        delay_ms(10);  //消抖
        while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))//检测key_up按下
        {
                PBout(5)=~PBout(5);
                delay_ms(500);                                        //初始条件下LED不亮
                PBout(5)=~PBout(5);
                PEout(5)=~PEout(5);
                delay_ms(500);
                PEout(5)=~PEout(5);
        }
                PBout(5)=1;
                PEout(5)=1;
                EXTI_ClearITPendingBit(EXTI_Line0);       
}


PE2、KEY2:


void EXTI2_IRQHandler()//PE2  KEY2
{
        delay_ms(10);  //消抖
        while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)))//检测key2按下
        {
                PBout(5)=0;
                GPIO_SetBits(GPIOB, GPIO_Pin_8);
                delay_ms(500);
                GPIO_ResetBits(GPIOB, GPIO_Pin_8);
                delay_ms(500);
        }
       
                PBout(5)=1;
                GPIO_ResetBits(GPIOB, GPIO_Pin_8);
               
                EXTI_ClearITPendingBit(EXTI_Line2);                       
}       


PE3、KEY1:


void EXTI3_IRQHandler()//PE3  KEY1
{
        delay_ms(10);  //消抖
        while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)))//检测key1按下
        {
                PEout(5)=0;
                GPIO_SetBits(GPIOB, GPIO_Pin_8);
                delay_ms(500);
                GPIO_ResetBits(GPIOB, GPIO_Pin_8);
                delay_ms(500);
        }
       
                PEout(5)=1;
                GPIO_ResetBits(GPIOB, GPIO_Pin_8);
               
                EXTI_ClearITPendingBit(EXTI_Line3);                        //必须判断,否则只能执行一次
}


PE4、KEY0:


void EXTI4_IRQHandler()//PE4  KEY0
{
        delay_ms(10);  //消抖
        while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)))//检测key0按下
        {
                PBout(5)=~PBout(5);
                PEout(5)=~PEout(5);                                        //初始条件下LED不亮
                GPIO_ResetBits(GPIOB, GPIO_Pin_8);
                delay_ms(500);
                PBout(5)=~PBout(5);
                PEout(5)=~PEout(5);
                GPIO_SetBits(GPIOB, GPIO_Pin_8);
                delay_ms(500);
        }


                PBout(5)=1;
                PEout(5)=1;
                GPIO_ResetBits(GPIOB, GPIO_Pin_8);
       
                EXTI_ClearITPendingBit(EXTI_Line4);
}


8、main.c
#include "stm32f10x.h"
#include "exti.h"


/*****************题***************
①按下key_up后LED0、LED1交替闪烁,每0.5s闪烁一次,取消按下后,两个灯全灭。


②按下key0后,LED0常亮、蜂鸣器每隔0.5s间断发声。


③按下key2后,LED1常亮,蜂鸣器每隔0.5s间断发声。


④按下key1后,LED0、LED1同时亮,同时灭,且同时灭的时候蜂鸣器发声,同时亮的时候蜂鸣器不发声;间隔1s


***********************************/


int main(void)
{       
         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
         exti_Init();
         while(1)
         {
         
         }
}



9、exti…c完整程序
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "beep.h"
#include "delay.h"
#include "exti.h"
void exti_Init()
{


        EXTI_InitTypeDef   EXTI_InitStructure;
        NVIC_InitTypeDef   NVIC_InitStructure;
       
        LED_Init();
        KEY_Init();
        BEEP_Init();
        delay_Init();
       
       
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
       
        //外部中断与PE2、PE3、PE4映射
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2);
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3);
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);外部中断与PA0映射
       
        EXTI_InitStructure.EXTI_Line=EXTI_Line0;                //PA0
        EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
        EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;
        EXTI_InitStructure.EXTI_LineCmd=ENABLE;
        EXTI_Init(&EXTI_InitStructure);
       
        EXTI_InitStructure.EXTI_Line=EXTI_Line2;                //PE2
        EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
        EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
        EXTI_InitStructure.EXTI_LineCmd=ENABLE;
        EXTI_Init(&EXTI_InitStructure);
       
        EXTI_InitStructure.EXTI_Line=EXTI_Line3;        //PE3
        EXTI_Init(&EXTI_InitStructure);
       
        EXTI_InitStructure.EXTI_Line=EXTI_Line4;        //PE4
        EXTI_Init(&EXTI_InitStructure);
       
        NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;                //PA0
        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
        NVIC_Init(&NVIC_InitStructure);
       
       
        NVIC_InitStructure.NVIC_IRQChannel=EXTI2_IRQn;                //PE2
        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
        NVIC_Init(&NVIC_InitStructure);
       
        NVIC_InitStructure.NVIC_IRQChannel=EXTI3_IRQn;                //PE3
        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
        NVIC_Init(&NVIC_InitStructure);
       
        NVIC_InitStructure.NVIC_IRQChannel=EXTI4_IRQn;                //PE4
        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;
        NVIC_Init(&NVIC_InitStructure);
       
}
void EXTI0_IRQHandler()//PA0  KEY_UP
{
        delay_ms(10);  //消抖
        while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))//检测key_up按下
        {
                PBout(5)=~PBout(5);
                delay_ms(500);                                        //初始条件下LED不亮
                PBout(5)=~PBout(5);
                PEout(5)=~PEout(5);
                delay_ms(500);
                PEout(5)=~PEout(5);
        }
                PBout(5)=1;
                PEout(5)=1;
                EXTI_ClearITPendingBit(EXTI_Line0);       
}


void EXTI2_IRQHandler()//PE2  KEY2
{
        delay_ms(10);  //消抖
        while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)))//检测key2按下
        {
                PBout(5)=0;
                GPIO_SetBits(GPIOB, GPIO_Pin_8);
                delay_ms(500);
                GPIO_ResetBits(GPIOB, GPIO_Pin_8);
                delay_ms(500);
        }
       
                PBout(5)=1;
                GPIO_ResetBits(GPIOB, GPIO_Pin_8);
               
                EXTI_ClearITPendingBit(EXTI_Line2);                       
}       


void EXTI3_IRQHandler()//PE3  KEY1
{
        delay_ms(10);  //消抖
        while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)))//检测key1按下
        {
                PEout(5)=0;
                GPIO_SetBits(GPIOB, GPIO_Pin_8);
                delay_ms(500);
                GPIO_ResetBits(GPIOB, GPIO_Pin_8);
                delay_ms(500);
        }
       
                PEout(5)=1;
                GPIO_ResetBits(GPIOB, GPIO_Pin_8);
               
                EXTI_ClearITPendingBit(EXTI_Line3);                        //必须判断,否则只能执行一次
}


void EXTI4_IRQHandler()//PE4  KEY0
{
        delay_ms(10);  //消抖
        while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)))//检测key0按下
        {
                PBout(5)=~PBout(5);
                PEout(5)=~PEout(5);                                        //初始条件下LED不亮
                GPIO_ResetBits(GPIOB, GPIO_Pin_8);
                delay_ms(500);
                PBout(5)=~PBout(5);
                PEout(5)=~PEout(5);
                GPIO_SetBits(GPIOB, GPIO_Pin_8);
                delay_ms(500);
        }
                PBout(5)=1;
                PEout(5)=1;
                GPIO_ResetBits(GPIOB, GPIO_Pin_8);
       
                EXTI_ClearITPendingBit(EXTI_Line4);
}


(四)举例的工程文件链接

  (三)中例子的工程链接
  (五)加一个重要说明(重要)

  外部中断不一定是外部信号触发的中断,中断线对应IO口的模式也不一定非要是输入模式。虽然外部中断映射到了GPIO,但是不影响GPIO的输入输出,即只要设置好中断线的上升沿触发、下降沿触发,无论对应IO是输入模式还是输出模式,都可以触发中断
  例如:
  LED0——>PB5——>通用推挽输出——>中断线5
  BEEP——>PB8
  我们对LED0实现闪烁,然后设置外部中断线5与PB5映射,且下降沿触发中断,在中断服务函数中让蜂鸣器取反。
  就前面的例子修改代码:
  exti.c:

//exti.c
#include "stm32f10x.h"
#include "sys.h"
#include "beep.h"
#include "exti.h"
void exti_Init()
{


        EXTI_InitTypeDef   EXTI_InitStructure;
        NVIC_InitTypeDef   NVIC_InitStructure;
       
        BEEP_Init();
       
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
       
       
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource5);//外部中断线5与PB5映射


        EXTI_InitStructure.EXTI_Line=EXTI_Line5;
        EXTI_InitStructure.EXTI_LineCmd=ENABLE;
        EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;        //下降沿触发中断  LED亮时为低电平
        EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
        EXTI_Init(&EXTI_InitStructure);
       
        NVIC_InitStructure.NVIC_IRQChannel=EXTI9_5_IRQn;                //PB5
        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;
        NVIC_Init(&NVIC_InitStructure);
       
}


void EXTI9_5_IRQHandler()
{


        PBout(8)=~PBout(8);
        EXTI_ClearITPendingBit(EXTI_Line5);
}
测试后发现,LED0每亮一次,蜂鸣器就取反一次(发声或停止发声)。而此时LED0是推挽输出,由软件置0和1的,这样同样可以发生外部中断!
举报

更多回帖

发帖
×
20
完善资料,
赚取积分