首页 > 技术文章 > STM32之外部中断

greatpumpkin 2020-07-04 15:14 原文

本文介绍如何使用STM32标准外设库设置并使用中断,单片机中断系统的目的是为了让MCU对内部或外部的突发事件及时地作出响应,并执行相应的程序,最简单直观的为外部中断,可以用作检测按键的按下和弹起,本例程设置GPIOA的Pin0作为外部中断。

本文适合对单片机及C语言有一定基础的开发人员阅读,MCU使用STM32F103VE系列。

 

中断分为两部分,初始化和处理。

1.    初始化

初始化分为三步,包括设置优先级分组、结构体初始化和初始化库函数调用。

1.1. 优先级分组

共5组,一般选择NVIC_PriorityGroup_2,即2位抢占优先级、2位子优先级。

调用库函数

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

1.2. 结构体初始化

typedef struct {

  uint8_t NVIC_IRQChannel;                   

  uint8_t NVIC_IRQChannelPreemptionPriority; 

  uint8_t NVIC_IRQChannelSubPriority;        

  FunctionalState NVIC_IRQChannelCmd;        

} NVIC_InitTypeDef;
  • 中断源:不同的中断设置不同的中断源,具体的成员配置可参考 stm32f10x.h 头文件里面的 IRQn_Type 结构体定义,这个结构体包含了所有的中断源。  
  /* 配置中断源:外部中断0 */
  NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
  • 抢占优先级,根据中断的优先级程度设置不同的优先级,优先级高的中断设置高的优先级,数值越小优先级越高,优先级分组为NVIC_PriorityGroup_2时,取值范围为0-3,优先级从高到低分别为0、1、2、3。
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  • 子优先级,根据中断的优先级程度设置不同的优先级,优先级高的中断设置高的优先级,数值越小优先级越高,优先级分组为NVIC_PriorityGroup_2时,取值范围为0-3,优先级从高到低分别为0、1、2、3。
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  • 使能

  直接设置为ENABLE即可。

  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

1.3. 库函数

调用库函数:

  NVIC_Init(&NVIC_InitStructure);

 

2.    处理

2.1. 中断服务函数

可以把中断服务函数统一写在 stm32f10x_it.c 这个库文件中,也可以写在其他文件中,中断服务函数的函数名必须跟启动文件(startup_stm32f10x_hd.s)里面预先设置的一样,比如外部中断0的中断服务函数名称为EXTI0_IRQHandler ()。

  void EXTI0_IRQHandler(void)

2.2. 中断处理

中断服务函数进入之后首先判定中断标志位是否置位,如果未置位则直接退出,如果已置位则执行相应的处理,退出中断之前需要清除中断标志位。有些操作如读串口数据会自动清除中断读数据寄存器非空标志位,因此无需手动清除。

 

外部中断分为两部分,初始化和处理。

 1.  外部中断初始化项目

  分三部分:GPIO、通用中断、EXTI(External interrupt/event controller)

1.1. GPIO:时钟、端口、引脚、输入模式

  • 时钟:需要同时使能GPIO时钟
  • 引脚:需要选择指定的引脚
  • 输入模式:一般选择浮空输入

1.2. 通用中断:优先级分组、中断源、优先级、使能

  • 优先级分组:设定合适的优先级分组
  • 中断源:选择指定的EXTI中断源
  • 优先级:设定合适的优先级
  • 使能:调用库函数即可

1.3. EXTI:时钟、信号源、线选、EXTI模式、触发类型、使能

    结构体:

typedef struct {
  uint32_t EXTI_Line;
  EXTIMode_TypeDef EXTI_Mode;
  EXTITrigger_TypeDef EXTI_Trigger;
  FunctionalState EXTI_LineCmd;
}EXTI_InitTypeDef;
  • 时钟:需要使能AFIO时钟
  /*开启AFIO时钟*/
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);   
  • 信号源:需要配置指定引脚的信号源

  直接调用库函数GPIO_EXTILineConfig()即可

  /* 配置信号源 */
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
  • 线选:初始化需要配置的线
  /* EXTI中断线选择Line0 */
  EXTI_InitStructure.EXTI_Line = EXTI_Line0;
  • EXTI模式:一般选择中断模式
  /* EXTI为中断模式 */
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  • 触发类型:可选上升沿或者下降沿
  /* 上升沿中断 */
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
  • 使能:
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  • 初始化库函数
  EXTI_Init(&EXTI_InitStructure);

 

2.    处理

2.1. 中断服务函数

外部中断0的中断服务函数名称为EXTI0_IRQHandler ()。

  void EXTI0_IRQHandler(void);

2.2. 中断处理

中断服务函数中调用EXTI_GetITStatus()函数判定中断标志位状态以确定中断是否发生,调用EXTI_ClearITPendingBit()函数清除中断标志位。

if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
  EXTI_ClearITPendingBit(EXTI_Line0);    
}  

 

完整代码(仅自己编写的部分)

 1 void GPIO_Config(void)
 2 {        
 3     /*定义一个GPIO_InitTypeDef类型的结构体*/
 4     GPIO_InitTypeDef GPIO_InitStructure;
 5 
 6     /*开启指定端口的GPIO外设时钟*/
 7     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
 8     
 9     /*选择要控制的GPIO引脚*/
10     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;    
11 
12     /*设置引脚模式为浮空输入*/
13     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   
14 
15     /*调用库函数,初始化GPIO*/
16     GPIO_Init(GPIOA, &GPIO_InitStructure);    
17 }
18 
19 void NVIC_Config(void)
20 {
21     NVIC_InitTypeDef NVIC_InitStructure;
22     
23     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
24 
25     /* 配置中断源:外部中断0 */
26     NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
27     /* 配置抢占优先级 */
28     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
29     /* 配置子优先级 */
30     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
31     /* 使能中断通道 */
32     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
33     
34     NVIC_Init(&NVIC_InitStructure);
35 }
36 
37 void EXTI0_Config(void)
38 {
39     EXTI_InitTypeDef EXTI_InitStructure;
40 
41     /*开启AFIO时钟*/
42     RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
43     
44     /*配置信号源*/
45     GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); 
46 
47     /* EXTI中断线选择Line0 */
48     EXTI_InitStructure.EXTI_Line = EXTI_Line0;
49     /* EXTI为中断模式 */
50     EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
51     /* 上升沿中断 */
52     EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
53     /* 使能中断 */    
54     EXTI_InitStructure.EXTI_LineCmd = ENABLE;
55     
56     EXTI_Init(&EXTI_InitStructure);
57 }
58 
59 void EXTI0_init(void)
60 {
61     GPIO_Config();
62     NVIC_Config();
63     EXTI0_Config();
64 }
65     
66 //中断服务函数,待完善
67 void EXTI0_IRQHandler(void)
68 {
69     if(EXTI_GetITStatus(EXTI_Line0) != RESET) 
70     {
71         EXTI_ClearITPendingBit(EXTI_Line0);     
72     }      
73 }
74 
75 
76 int main(void)
77 {    
78     // 外部中断0中断初始化
79     EXTI0_init();    
80 
81     while (1)
82     {
83     }
84 }

 

 

仿真结果

程序编译成功后,点击开始仿真,打开菜单栏Peripherals→General Purpose I/O→GPIOA,如果中断触发类型为上升沿,那么触发条件为指定端口先低电平,再设置为高电平;反之如果触发类型为下降沿,那么触发条件为指定端口先高电平,再设置为低电平。

可在中断服务函数内发生中断后设置断点,程序开始运行,当满足中断触发条件后,程序在断点处暂停,表明中断成功。

 

源码下载:(不包括工程文件和库文件)

https://files.cnblogs.com/files/greatpumpkin/EXTI_int.zip

推荐阅读