EXTI简介:
EXTI (Extern Interrupt)外部中断
EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序。
支持的触发方式:上升沿/ 下降沿/双边沿/软件触发。
支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断通道数: 16个GPIO_ _Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒。
触发响应方式:中断响应/事件响应。
原理图 1
本程序是基于原理图 1采用下降沿触发选择寄存器,软件中断寄存器。
原理图2
基于原理图 2 选择PA0-->EXTI0
AFIO主要用于引脚复用功能的选择和重定义。
在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择。
原理图 3
本程序还涉及相应的位带操作的知识点:
原理图 4
从原理图中可以看出,位带操作是在简化了代码规模的同时也提高了编写代码的便捷性。
主函数main.c代码:
#include "stm32f10x.h" // Device header
#include"led.h"
#include"exti.h"
int main(void)
{
exti_init();
led_init();
while(1);
}
位带操作sys.h代码段:
#ifndef __SYS_H_
#define __SYS_H_
//位带操作实现51类似的GPIO控制功能
//addr&0xF0000000取出最高的四位,其实就是用于区别SRAM(0x20000000)还是片上外设(0x40000000)的
//+0x2000000对于SRAM位带区则得到0x22000000,对于片上外设位带则得到0x42000000
//(addr&0xFFFFF)等效于(addr&0x000FFFFF),就是屏蔽掉高12位,地址范围是0x20000000-0x200FFFFF和0x40000000-0x400FFFFF
//<<5相当于乘以32,同样<<2相当于乘以4
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) //addr&0xF0000000:?ùéèê?????GPIOAμ??3????£?0x4001080C,???′?a???????μ?í?á°?2??à?éμ????á±?μ?£?±?3é0x4000 0000
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
//GPIO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808
#define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08
#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08
//GPIO口操作,只对单一的GPIO口,确保n值小于16;
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入
#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入
#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入
#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入
#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入
#endif
led.c代码段:
#include "stm32f10x.h" // Device header
#include"led.h"
void led_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//使能或者失能 AHB 外设时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//GPIO_Speed 用以设置选中管脚的速率。//GPIO_Speed_50MHz 最高输出速率 50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//GPIO_Mode 用以设置选中管脚的工作状态 //GPIO_Mode_Out_PP 推挽输出
GPIO_Init(GPIOC,&GPIO_InitStructure);//根据 GPIO_InitStruct 中指定的参数初始化外设 GPIOx 寄存器
GPIO_SetBits(GPIOC,GPIO_Pin_13);//设置指定的数据端口位
}
void led_on(void)
{
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);//设置或者清除指定的数据端口位//Bit_SET: 设置数据端口位//Bit_RESET: 清除数据端口位
}
void led_off(void)
{
GPIO_SetBits(GPIOC,GPIO_Pin_13);//设置指定的数据端口位
}
led.h代码段:
#ifndef __LED_H_
#define __LED_H_
void led_init(void);
void led_on(void);
void led_off(void);
#endif
exti.c代码段:
#include "stm32f10x.h" // Device header
#include"led.h"
#include"exti.h"
#include"sys.h"
void exti_init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/*使能GPIO时钟 PA 使能AFIO时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/*配置PA0上拉输入模式*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//GPIO_Mode 用以设置选中管脚的工作状态。 //上拉输入模式 低电平有效
GPIO_Init(GPIOA,&GPIO_InitStructure);//根据 GPIO_InitStruct 中指定的参数初始化外设 GPIOx 寄存器
/*连接EXT0 到 PA0 引脚*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);//选择 GPIO 管脚用作外部中断线路
/*配置EXTI0 */
EXTI_InitStructure.EXTI_Line = EXTI_Line0;//EXTI_Line 选择了待使能或者失能的外部线路。 //EXTI_Line0外部中断线 0
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//设置 EXTI 线路为中断请求
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//EXTI_Trigger 设置了被使能线路的触发边沿。//EXTI_Mode_Interrupt设置输入线路下降沿为中断请求
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//EXTI_LineCmd 用来定义选中线路的新状态
EXTI_Init(&EXTI_InitStructure);//根据 EXTI_InitStruct 中指定的参数初始化外设 EXTI 寄存器
/*配置EXTI0优先级及中断向量入口地址 */
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//NVIC_IRQChannel 该参数用以使能或者失能指定的 IRQ 通道。
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//该参数设置了成员 NVIC_IRQChannel 中的先占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//该参数设置了成员 NVIC_IRQChannel 中的从优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//该参数指定了在成员 NVIC_IRQChannel 中定义的 IRQ 通道被使能还是失能。
NVIC_Init(&NVIC_InitStructure);//根据 NVIC_InitStruct 中指定的参数初始化外设 NVIC 寄存器
}
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0) != RESET) //SET // EXTI_GetITStatus检查指定的 EXTI 线路触发请求发生与否//EXTI_Line0 外部中断线 0
{
PCout(13)^=1;//对1进行异或操作哦,进而实现按键使LED一亮一灭
EXTI_ClearITPendingBit(EXTI_Line0);//清除中断标志位
}
}
exti.h代码段:
#ifndef _EXTI_H__
#define _EXTI_H__
void exti_init(void);
#endif
代码总体布局:
现象展示:
STM32外部中断控制LED亮灭