第16章 外部中断实验

2025/01/03 STM32
第16章 外部中断实验

一、外部中断介绍

  1. EXTI简介: EXTI(External interrupt/event controller)外部中断/事件控制器
  2. STM32的EXTI大概有20条
  3. EXTI结构框图

pESbDaT.png 20表示在控制器的内部有20条信号线路

  1. EXTI分两大部分的功能
    • 产生中断:12345
    • 产生事件:123678

二、中断线路介绍

pESbDaT.png

  1. 输入线: EXTI控制器有20个中断/事件输入线,可通过寄存器来设.置任意一个IO,或一些外设事件,输入线一般是电平变化的信号
  2. 边沿检测电路: EXTI可以对触发方式进行选择(上升沿,下降沿),边沿检测电路以输入线信号作为信号的输入端,如果检测到有边沿信号的跳变,就会输出有效信号1,如果没有检测到有效信号,则输出0,输出有效信号之后,传递给下一级。上升沿,下降沿用来控制检测的类型,即电平的跳变过程,如选择的上升沿(电平升高),边沿检测电路检测到上升沿之后,就会产生一个有效信号1,如果是下降沿过来,上升沿并不会检测到,产生0,传给下一级。如果选择的是下降沿(电平下降),边沿检测电路检测到下降沿之后,就会产生一个有效信号1。也可以同时检测上升沿和下降沿。
  3. 3(或门电路): 当输入级只要有一个为有效信号,则输出有效。即输入端有一个为真,那输出就为真。或门电路连接边沿检测电路和软件中断事件寄存器。软件中断寄存器允许使用软件来启动中断或事件线。通常使用的多的是边沿检测电路
  4. 4(与门电路): 当输入端全部为真的时候输出才会有效,输入只要有一个为假或无效,则输出无效。4是3的输出和中断屏蔽寄存器提供,只有当两者都为有效信号1时,4输出有效,否则无效。这样就可以简单的通过中断屏蔽寄存器来控制中断是否输出。请求挂起寄存器用来存储3号电路的输出信号,再由请求挂起寄存器的信号传递给4号电路
  5. NVIC: 连接在芯片内核,由芯片来响应中断

三、事件线路介绍

pESbDaT.png

  1. 123与前面的一样,不在过多介绍
  2. 6(与门电路): 两个输入端都为有效信号,才输出有效。一个连接到3号输出,另一个连接到事件屏蔽寄存器。两者均为有效电平1的时候,标号6电路才输出有效,供给下一级。
  3. 脉冲发生器: 输入端连接6号的输出,6号有效,脉冲发生器输出一个脉冲信号,8是产生的脉冲信号,脉冲信号的产生是事件线路的终端。信号可以供其他外设电路使用,如:定时器、ADC等。

总结:中断线路流向NVIC控制器当中,从而运行中断服务函数,实现中断相应的功能,是软件级别的。事件线路最终产生的脉冲信号,会流向其他的外设电路,是硬件级别的。外设接口时钟是PCLK2,使用的时候要使能APB2。

四、外部中断/事件线映射

  1. STM32的EXTI有20条中断事件线

pESqxXR.png

pESLZjA.png

每个IO口都可以对应作为中断,几个IO口为一组映射到一个中断线上,如GPIOx.0映射到EXTI0,GPIOx.1映射到EXTI1,…,GPIOx.15映射到EXTI15。当PA0使用时,则PB0-PG0不可以使用。同一时刻,只能有一个IO映射到中断线上。对于其他中断线是一样的。

五、EXTI配置步骤

EXTI相关库函数在stm32f10x_exti.c和stm32f10x_exti.h文件中。

  1. 使能IO口时钟,配置IO口模式为输入
  2. 开启 AFIO 时钟,设置 IO 口与中断线的映射关系
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
    // 两个参数:第一个:端口;第二个:引脚
    void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
    // 将GPIO引脚映射到EXIT引脚
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
    
  3. 配置中断分组(NVIC),使能中断
  4. 初始化EXTI,选择触发方式
    void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
    typedef struct
    {
      uint32_t EXTI_Line;               //中断/事件线
      EXTIMode_TypeDef EXTI_Mode;       //EXTI模式
      EXTITrigger_TypeDef EXTI_Trigger; //EXTI触发方式
      FunctionalState EXTI_LineCmd;     //中断线使能或失能 
    }EXTI_InitTypeDef;
    
  5. 编写EXTI中断服务函数
    EXTI0_IRQHandler                                                          
    EXTI1_IRQHandler                                                           
    EXTI2_IRQHandler                                                          
    EXTI3_IRQHandler                                                         
    EXTI4_IRQHandler 
    EXTI9_5_IRQHandler      // 5-9条共用一个中断服务函数
    EXTI15_10_IRQHandler    // 10-15条共用一个中断服务函数
    

六、硬件设计

pAxEIDU.png

开发板上一共有4个按键:

  • KEY_UP:PA0,按键按下检测到有效电平是3.3V,高电平有效
  • KEY0:PE4,按键按下检测到低电平有效
  • KEY1:PE3,按键按下检测到低电平有效
  • KEY2:PE2,按键按下检测到低电平有效

七、软件设计

  1. 要实现外部中断方式控制LED,程序框架如下:
    • 初始化对应端口的EXTI
    • 编写EXTI中断函数
    • 编写主函数
  2. 中断是通过外部中断线(EXTI Line)来触发的,这些外部中断线通常与GPIO引脚相连。

EXTI.c

#include "EXTI.h"
#include "SysTick.h"
#include "key.h"
#include "LED.h"
#include "beep.h"

void My_EXTI_Init(void)
{
	// 因为要用按键来切换中断线,开始配置按键初始化,在KEY_Init中已经配置,此处可不进行配置
	
	// 定义NVIC结构体
	NVIC_InitTypeDef NVIC_InitStrycture;
	// 定义EXTI结构体
	EXTI_InitTypeDef EXTI_InitStructure;
	
	// 开启AFIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	// 外部中断线配置
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);
	
	NVIC_InitStrycture.NVIC_IRQChannel = EXTI0_IRQn;	// 通道配置
	// 抢占式优先级和响应式优先级设置的数值取决于对NVIC分组
	// 如分两组,即各占两位,可选0-3
	NVIC_InitStrycture.NVIC_IRQChannelPreemptionPriority = 2;	// 抢占式优先级
	NVIC_InitStrycture.NVIC_IRQChannelSubPriority = 3;	// 响应式优先级
	NVIC_InitStrycture.NVIC_IRQChannelCmd = ENABLE;	// 通道使能
	// 配置中断分组
	NVIC_Init(&NVIC_InitStrycture);	// NVIC初始化
	
	NVIC_InitStrycture.NVIC_IRQChannel = EXTI2_IRQn;
	NVIC_InitStrycture.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStrycture.NVIC_IRQChannelSubPriority = 2;
	NVIC_InitStrycture.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStrycture);
	
	NVIC_InitStrycture.NVIC_IRQChannel = EXTI3_IRQn;
	NVIC_InitStrycture.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStrycture.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStrycture.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStrycture);
	
	NVIC_InitStrycture.NVIC_IRQChannel = EXTI4_IRQn;
	NVIC_InitStrycture.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStrycture.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStrycture.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStrycture);
	
	EXTI_InitStructure.EXTI_Line = EXTI_Line0;	// 中断线
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	// 模式
	// KEY_UP高电平有效,按下按键有上升沿,所以选择上升沿触发
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;	// 触发方式
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);
	
	// 配置EXTI2,3,4
	EXTI_InitStructure.EXTI_Line = EXTI_Line2 | EXTI_Line3 | EXTI_Line4;	// 中断线
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	// 模式
	// KEY2,3,4低电平有效,按下按键有上升沿,所以选择上升沿触发
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;	// 触发方式
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);
}

void EXTI0_IRQHandler(void)
{
	// 检查是否真的触发中断,防止误触发,检查中断标志
	if(EXTI_GetITStatus(EXTI_Line0) == SET)	// 参数:中断线,返回值:SET(1)表示中断线产生了中断,RESET(0)
	{
		delay_ms(10);	// 消除按键抖动
		if(KEY_UP == 1)
		{
			LED2 = 0;
		}
	}
	
	// 清除中断标志,让下一次能够响应中断
	EXTI_ClearITPendingBit(EXTI_Line0);
}

void EXTI2_IRQHandler(void)
{
	// 检查是否真的触发中断,防止误触发,检查中断标志
	if(EXTI_GetITStatus(EXTI_Line2) == SET)	// 参数:中断线,返回值:SET(1)表示中断线产生了中断,RESET(0)
	{
		delay_ms(10);	// 消除按键抖动
		if(KEY2 == 0)
		{
			BEEP = 1;
		}
	}
	
	// 清除中断标志,让下一次能够响应中断
	EXTI_ClearITPendingBit(EXTI_Line2);
}

void EXTI3_IRQHandler(void)
{
	// 检查是否真的触发中断,防止误触发,检查中断标志
	if(EXTI_GetITStatus(EXTI_Line3) == SET)	// 参数:中断线,返回值:SET(1)表示中断线产生了中断,RESET(0)
	{
		delay_ms(10);	// 消除按键抖动
		if(KEY1 == 1)
		{
			LED2 = 1;
		}
	}
	
	// 清除中断标志,让下一次能够响应中断
	EXTI_ClearITPendingBit(EXTI_Line3);
}

void EXTI4_IRQHandler(void)
{
	// 检查是否真的触发中断,防止误触发,检查中断标志
	if(EXTI_GetITStatus(EXTI_Line4) == SET)	// 参数:中断线,返回值:SET(1)表示中断线产生了中断,RESET(0)
	{
		delay_ms(10);	// 消除按键抖动
		if(KEY0 == 1)
		{
			BEEP = 0;
		}
	}
	
	// 清除中断标志,让下一次能够响应中断
	EXTI_ClearITPendingBit(EXTI_Line4);
}

EXTI.h

#ifndef __EXTI_H
#define __EXTI_H

#include "system.h"
void My_EXTI_Init(void);
void EXTI0_IRQHandler(void);
void EXTI2_IRQHandler(void);
void EXTI3_IRQHandler(void);
void EXTI4_IRQHandler(void);

#endif

main.c

#include "LED.h"       // 包含 LED 模块的头文件
#include "system.h"
#include "SysTick.h"
#include "beep.h"
#include "key.h"
#include "EXTI.h"

// 主函数
int main()
{
	u8 i = 0;	//让LED1指示灯闪烁,确保程序在执行
	
	SysTick_Init(72);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    LED_Init(); // 初始化 LED1 和 LED2
	BEEP_Init();
	KEY_Init();
	My_EXTI_Init();

    // 主循环
    while(1)
    {
		i ++;
		if(i % 20 == 0)	// 每经过200ms,让LED1翻转一次
			LED1 = !LED1;
		delay_ms(10);
    }
	
}

文档信息

搜索

    Table of Contents