持续关注阿杰在线更新保姆式笔记~~坚持日更

战舰/精英STM32F1开发板

《STM32F1开发指南-库函数版本》-10章 外部中断实验

 STM32F1xx官方资料:

《STM32中文参考手册V10》-9  中断和事件

一、外部中断概述

STM32 的每个 IO 都可以作为外部中断的中断输入口。

STM32F103 的中断控制器支持 19 个外部中断/事件请求):

线 0~15:对应外部 IO 口的输入中断。

线 16:连接到 PVD 输出。

线 17:连接到 RTC 闹钟事件。 

线 18:连接到 USB 唤醒事件。

每个外部中断线可以独立的配置触发方式(上升沿,下降沿或者双边沿触发),触发/屏蔽,专用的状态位。 

从上面可以看出,STM32供IO使用的中断线只有16个,但是STM32F10x系列的IO口多达上百个,STM32F103ZET6(112),STM32F103RCT6(51),那么中断线怎么跟io口对应呢?

通过映射关系。

GPIOx.0映射到EXTI0

GPIOx.1映射到EXTI1

GPIOx.15映射到EXTI15

对于每个中断线,我们可以设置相应的触发方式(上升沿触发,下降沿触发,边沿触发)以及使能。

是不是16个中断线就可以分配16个中断服务函数呢? 

IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数

从表中可以看出,外部中断线5~9分配一个中断向量,共用一个服务函数外部中断线10~15分配一个中断向量,共用一个中断服务函数。
中断服务函数列表:
EXTI0_IRQHandler          

EXTI1_IRQHandler

EXTI2_IRQHandler          

EXTI3_IRQHandler          

EXTI4_IRQHandler          

EXTI9_5_IRQHandler        

EXTI15_10_IRQHandler      

二、外部中断常用库函数

①void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);

   //设置IO口与中断线的映射关系

   exp:  GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);

②void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);

 //初始化中断线:触发方式等

③ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);

//判断中断线中断状态,是否发生

④void EXTI_ClearITPendingBit(uint32_t EXTI_Line);

//清除中断线上的中断标志位

EXTI_Init函数

void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);

三、外部中断的一般配置步骤

① 初始化 IO 口为输入。
       GPIO_Init();

②   开启IO口复用时钟。

       RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

③ 设置 IO 口与中断线的映射关系。
        void GPIO_EXTILineConfig();

④ 初始化线上中断,设置触发条件等。
       EXTI_Init();

⑤ 配置中断分组( NVIC ),并使能中断。
       NVIC_Init();

⑥   编写中断服务函数。

      EXTIx_IRQHandler();

⑦ 清除中断标志位
      EXTI_ClearITPendingBit();

四、手把手用外部中断写按键实验 

1、STM32配置的步骤以及相关的步骤,通过触发按键外部中断实现摁键点灯操作,即摁下摁键,触发中断,LED灯亮,松开LED灯灭。 

*exti.c
 
#include "exti.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "beep.h"
 
//外部中断0服务程序
void EXTIX_Init(void)
{
 
       EXTI_InitTypeDef EXTI_InitStructure;
       NVIC_InitTypeDef NVIC_InitStructure;
 
    KEY_Init();     //    按键端口初始化
 
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);    //使能复用功能时钟
 
 
 
   //GPIOE.3      中断线以及中断初始化配置 下降沿触发 //KEY1
      GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);
      EXTI_InitStructure.EXTI_Line=EXTI_Line3;
      EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;    
      EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
      EXTI_Init(&EXTI_InitStructure);          
    //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
 
   //GPIOE.4      中断线以及中断初始化配置  下降沿触发    //KEY0
      GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource4);
      EXTI_InitStructure.EXTI_Line=EXTI_Line4;
      EXTI_Init(&EXTI_InitStructure);          
    //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
 
 
   //GPIOA.0      中断线以及中断初始化配置 上升沿触发 PA0  WK_UP
       GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0); 
 
      EXTI_InitStructure.EXTI_Line=EXTI_Line0;
      EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
      EXTI_Init(&EXTI_InitStructure);        
    //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
 
 
      NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//使能按键WK_UP所在的外部中断通道
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;//抢占优先级2, 
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;        //子优先级3
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                //使能外部中断通道
      NVIC_Init(&NVIC_InitStructure); 
 
      NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;    //使能按键KEY1所在的外部中断通道
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;    //抢占优先级2 
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;            //子优先级1 
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                    //使能外部中断通道
      NVIC_Init(&NVIC_InitStructure);       
     //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
 
      NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;    //使能按键KEY0所在的外部中断通道
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;    //抢占优先级2 
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;            //子优先级0 
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                    //使能外部中断通道
      NVIC_Init(&NVIC_InitStructure);        
    //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
 
}
 
//外部中断0服务程序 
void EXTI0_IRQHandler(void)
{
    delay_ms(10);//消抖
    if(WK_UP==1)          //WK_UP按键
    {                 
        BEEP=!BEEP;    
    }
    EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位  
}
 
 
//外部中断3服务程序
void EXTI3_IRQHandler(void)
{
    delay_ms(10);//消抖
    if(KEY1==0)     //按键KEY1
    {                 
        LED1=!LED1;
    }         
    EXTI_ClearITPendingBit(EXTI_Line3);  //清除LINE3上的中断标志位  
}
 
void EXTI4_IRQHandler(void)
{
    delay_ms(10);//消抖
    if(KEY0==0)     //按键KEY0
    {
        LED0=!LED0;
        LED1=!LED1; 
    }         
    EXTI_ClearITPendingBit(EXTI_Line4);  //清除LINE4上的中断标志位  
}

*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();             //延时函数初始化      
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
     //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
    uart_init(115200);     //串口初始化为115200
    LED_Init();                  //初始化与LED连接的硬件接口 
    BEEP_Init();             //初始化蜂鸣器IO
    EXTIX_Init();             //初始化外部中断输入 
    LED0=0;                    //先点亮红灯
    while(1)
    {        
        printf("OK\r\n");    
        delay_ms(1000);      
    }     
}

2、按键按下的触发方式编程思路
按键按下可以有 支持连续按下,不支持连续按。

2.1 支持连续按下

在这里插入图片描述

2.2 不支持连续

在这里插入图片描述

2.3 连续,不连续二合一
思路:

定义static KEY_UP=1
判断是否是模式1:连续按下模式?是则标志KEY_UP置1,表示松开
按键被按下?上次松开了则干活,上次没有松开则啥都不干
按键被松开?标志KEY_UP置1,表示松开

在这里插入图片描述

遇到的问题

外部中断3,4,没有反应的问题。
对比了许久才发现是使用初始化EXTI结构体时,或运算出现bug的问题。extist.EXTI_Line = EXTI_Line3|EXTI_Line4;使用这句语句初始化结构体将失败。注意,注意,注意。