STM32精英版(正点原子STM32F103ZET6开发板)学习篇9——外部中断

(70) 2024-07-19 23:01:01

外部中断概述

  STM32的每个IO都可以作为外部中断输入。
  STM32的中断控制器支持19个外部中断/事件请求:
    线0~15:对应外部IO口的输入中断。
    线16:连接到PVD输出。
    线17:连接到RTC闹钟事件。
    线18:连接到USB唤醒事件。

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

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

外部中断通用I/O映像

STM32精英版(正点原子STM32F103ZET6开发板)学习篇9——外部中断 (https://mushiming.com/)  第1张
  通过该图可知,每一个中断线都对应了7组中的对应编号的IO口(这边以ZET6:112个IO=16个 * 7组)。
    有如下关系:
      GPIOx.0映射到EXTI0
      GPIOx.1映射到EXTI1
      …
      GPIOx.15映射到EXTI15
注意:每个时间只能有一个I/O可映射到对应的中断线。

外部中断服务函数

  是不是16个中断线就可以分配16个中断服务函数呢?
  IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数(下表少写了EXTI0)
STM32精英版(正点原子STM32F103ZET6开发板)学习篇9——外部中断 (https://mushiming.com/)  第2张
  从表中可以看出,外部中断线5~9分配一个中断向量,共用一个服务函数外部中断线10 ~15分配一个中断向量,共用一个中断服务函数。

中断服务函数列表

  EXTI0_IRQHandler
  EXTI1_IRQHandler
  EXTI2_IRQHandler
  EXTI3_IRQHandler
  EXTI4_IRQHandler
  EXTI9_5_IRQHandler
   EXTI15_10_IRQHandler

外部中断设置步骤

  ①映射
  ②中断使能、触发模式等使能方式。
  ③编写中断服务函数

一般配置步骤:
  ①初始化IO口为输入。
    GPIO_Init();
  ② 开启IO口复用时钟。    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
  ③设置IO口与中断线的映射关系。
    void GPIO_EXTILineConfig();
  ④初始化线上中断,设置触发条件等。
    EXTI_Init();
  ⑤配置中断分组(NVIC),并使能中断。
    NVIC_Init();
  ⑥编写中断服务函数。
    EXTIx_IRQHandler();
  ⑦清除中断标志位
    EXTI_ClearITPendingBit();

外部中断常用库函数

①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);
//清除中断线上的中断标志位

硬件电路

LED

STM32精英版(正点原子STM32F103ZET6开发板)学习篇9——外部中断 (https://mushiming.com/)  第3张

按键

STM32精英版(正点原子STM32F103ZET6开发板)学习篇9——外部中断 (https://mushiming.com/)  第4张

具体实现

新建模版,在HARDWARE文件夹添加exit文件。然后就是填入工程,添加头文件路径等等操作。

第一步:初始化IO口为输入

#include "stm32f10x.h" void EXTIX_Init(void){ 
    GPIO_InitTypeDef GPIO_InitStruce; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_GPIOE , ENABLE); GPIO_InitStruce.GPIO_Mode=GPIO_Mode_IPU;//上拉输入 GPIO_InitStruce.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_3; GPIO_InitStruce.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOE, &GPIO_InitStruce); GPIO_InitStruce.GPIO_Mode=GPIO_Mode_IPD;//下拉输入 GPIO_InitStruce.GPIO_Pin=GPIO_Pin_0; GPIO_InitStruce.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStruce); } 

第二步:开启IO口复用时钟

增加:RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO , ENABLE);

第三步:设置IO口与中断线的映射关系

该函数存在于:stm32f10x_gpio.h中
增加:GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);

第四步:初始化线上中断,设置触发条件等。

该函数存在于:stm32f10x_exti.h中
增加:

EXTI_InitTypeDef EXTI_InitStruce; GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4); EXTI_InitStruce.EXTI_Line=EXTI_Line4; //确定中断线 EXTI_InitStruce.EXTI_LineCmd=ENABLE;//使能 EXTI_InitStruce.EXTI_Mode=EXTI_Mode_Interrupt;//模式:中断 EXTI_InitStruce.EXTI_Trigger=EXTI_Trigger_Falling;//触发方式(根据按键电路图,选择下降沿触发) EXTI_Init(&EXTI_InitStruce); 

第五步:配置中断分组(NVIC),并使能中断。

该函数存在于:misc.h中
通道的参数是在stm32f10x,h中找到IRQn结尾的。
增加:

EXTI_Init(&EXTI_InitStruce); NVIC_InitTypeDef NVIC_InitStruce; NVIC_InitStruce.NVIC_IRQChannel=EXTI4_IRQn ;//设置通道 NVIC_InitStruce.NVIC_IRQChannelCmd = ENABLE;//使能 NVIC_InitStruce.NVIC_IRQChannelPreemptionPriority=2;//设置优先级 NVIC_InitStruce.NVIC_IRQChannelSubPriority=2;//设置子优先级 NVIC_Init(&NVIC_InitStruce); 

这边要注意,在这同时要在main.c中先写下该函数NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置NVIC中断分组,2位抢占优先级,2位响应优先级,设置优先级后,这边填写的抢占优先级才有效。

第六步: 编写中断服务函数&清除中断标志位

该函数在启动文件CORE文件夹下的startup_stm32f10x_hd.s中:EXTI4_IRQHandler
故可在exti,c文件中编写一下程序:

void EXTI4_IRQHandler(void){ 
    delay_ms(10);//防抖延时 if(KEY0 == 0){ 
    LED0 = !LED0; LED1 = !LED1; } EXTI_ClearITPendingBit(EXTI_Line4);//手动清除标志位,exti.h中 } 

程序代码总结:

exti.c文件:

#include "stm32f10x.h" #include "delay.h" #include "exti.h" void EXTIX_Init(void){ 
    GPIO_InitTypeDef GPIO_InitStruce; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_GPIOE , ENABLE);//IO口时钟 GPIO_InitStruce.GPIO_Mode=GPIO_Mode_IPU;//上拉输入 GPIO_InitStruce.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_3;//KEY0|KEY1 GPIO_InitStruce.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOE, &GPIO_InitStruce); GPIO_InitStruce.GPIO_Mode=GPIO_Mode_IPD;//下拉输入 GPIO_InitStruce.GPIO_Pin=GPIO_Pin_0;;//KEY UP GPIO_InitStruce.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStruce); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO , ENABLE);//IO口复用时钟 EXTI_InitTypeDef EXTI_InitStruce; GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4); EXTI_InitStruce.EXTI_Line=EXTI_Line4; //确定中断线 EXTI_InitStruce.EXTI_LineCmd=ENABLE;//使能 EXTI_InitStruce.EXTI_Mode=EXTI_Mode_Interrupt;//模式:中断 EXTI_InitStruce.EXTI_Trigger=EXTI_Trigger_Falling;//触发方式(根据按键电路图,选择下降沿触发) EXTI_Init(&EXTI_InitStruce); NVIC_InitTypeDef NVIC_InitStruce; NVIC_InitStruce.NVIC_IRQChannel=EXTI4_IRQn;//设置通道 NVIC_InitStruce.NVIC_IRQChannelCmd = ENABLE;//使能 NVIC_InitStruce.NVIC_IRQChannelPreemptionPriority=2;//设置优先级 NVIC_InitStruce.NVIC_IRQChannelSubPriority=2;//设置子优先级 NVIC_Init(&NVIC_InitStruce); GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP; //模式选择推挽输出 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5; //引脚选择5 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //速度旋转50MHz GPIO_Init(GPIOB, &GPIO_InitStructure); //第一个参数GPIOB //第二个参数是一个结构体变量,要用&去取,且该结构体中有三个变量 //要根据assert_param中去确定要填写什么样的格式数值。 GPIO_SetBits(GPIOB, GPIO_Pin_5); GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP; //模式选择推挽输出 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5; //引脚选择5 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //速度旋转50MHz GPIO_Init(GPIOE, &GPIO_InitStructure); //第一个参数GPIOE  //第二个参数是一个结构体变量,要用&去取,且该结构体中有三个变量 //要根据assert_param中去确定要填写什么样的格式数值。 GPIO_SetBits(GPIOE, GPIO_Pin_5); } void EXTI4_IRQHandler(void){ 
    delay_ms(10);//防抖延时 if(KEY0 == 0){ 
    LED0 = !LED0; LED1 = !LED1; } EXTI_ClearITPendingBit(EXTI_Line4);//手动清除标志位,exti.h中 } 

exti.h

#ifndef __EXIT_H #define __EXIT_H #include "sys.h" void EXTIX_Init(void); //外部中断初始化 #define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) #define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) #define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) #define LED0 PBout(5) // DS0 #define LED1 PEout(5) // DS1 #endif 

main.c

#include "stm32f10x.h" //stm32必要的头文件 #include "delay.h" #include "exti.h" int main(void){ 
    delay_init(); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置NVIC中断分组,2位抢占优先级,2位响应优先级 EXTIX_Init(); LED0 = 0; GPIO_SetBits(GPIOB, GPIO_Pin_5); GPIO_SetBits(GPIOE, GPIO_Pin_5); while(1){ 
    delay_ms(1000); } } 

实验成果

按下按键KEY0实现两个LED的点亮与熄灭。

THE END

发表回复