STM32f103学习笔记(3.1)——外部中断EXTI

目录

外部中断/事件控制器(EXTI)

外部中断控制框图

 外部中断通用I/O映像

EXTI的寄存器

中断屏蔽寄存器(EXTI_IMR)

上升沿触发选择寄存器(EXTI_RTSR)

下降沿触发选择寄存器(EXTI_FTSR)

挂起寄存器(EXTI_PR)

AFIO寄存器

外部中断配置寄存器 1(AFIO_EXTICR1)

外部中断配置寄存器 2(AFIO_EXTICR2)  

外部中断配置寄存器 3(AFIO_EXTICR3)  

外部中断配置寄存器 4(AFIO_EXTICR4)  

EXTI外部中断配置

例程

寄存器代码:

库函数代码:


外部中断/事件控制器(EXTI)

对于互联型产品,外部中断 / 事件控制器由 20 个产生事件 / 中断请求的边沿检测器组成,对于其它产品,则有 19 个能产生事件 / 中断请求的边沿检测器。
每个输入线可以独立地配置输入类型 ( 脉冲 或挂起 ) 和对应的触发事件 ( 上升沿或下降沿或者双边沿都触发 )
每个输入线都可以独立地被屏 蔽。
挂起寄存器保持着状态线的中断请求。

外部中断控制框图

 外部中断通用I/O映像

 

根据框图以及STM32F103学习笔记(3.0)——中断里关于NVIC中断向量表,可以知道EXTI0-EXTI15分别挂着PA\B\C\D\E\F\G0-PA\B\C\D\E\F\G15,也就是说不同的GPIO相同的引脚号挂载在同一条EXTI线上,他们只能选择其中一个,比如EXTI0里只能在PA0-PG0里选择一个作为中断源。

在NVIC管理中,EXTI0  EXTI1  EXTI2  EXTI3  EXTI4都是独立的,而EXTI5-EXTI9产生的是同一个中断,需要通过中断标志位来判断到底是EXTI几产生的中断,同样的,EXTI10-EXTI15也是同一个中断。

EXTI的寄存器

中断屏蔽寄存器(EXTI_IMR)

显然,在相应位置1可以开放来自EXTIx上的中断请求。

上升沿触发选择寄存器(EXTI_RTSR)

下降沿触发选择寄存器(EXTI_FTSR)

挂起寄存器(EXTI_PR)

1.通过中断屏蔽寄存器,可以配置开放哪一条线产生中断

2.通过上升&下降沿触发选择寄存器可以配置外部中断的触发方式

3.通过读取挂起寄存器就可以读出哪一条线产生了触发请求

注:通过AFIO_EXTICRx配置GPIO线上的外部中断/事件,必须先使能AFIO时钟。

AFIO寄存器

外部中断配置寄存器 1(AFIO_EXTICR1)

外部中断配置寄存器 2(AFIO_EXTICR2)  

外部中断配置寄存器 3(AFIO_EXTICR3)  

 

外部中断配置寄存器 4(AFIO_EXTICR4)  

AFIO的配置主要是配置选择EXTIx的外部中断的输入源

EXTI外部中断配置

为了产生外部中断,需要先将对应的引脚配置成上下拉输入的模式,在AFIO里选择输入源,配置好EXTI寄存器,最后在NVIC里配置好响应的优先级

外部中断的配置代码可以是这样:

void EXTI_init(void)
{
	RCC->APB2ENR |= (1<<2);		//GPIOA
	RCC->APB2ENR |= (1<<0);		//AFIO
	//EXTI和NVIC两个外设时钟是一直开着的,不需要开
	//NVIC是内核外设,和CPU一起运行,RCC管的是内核以外
	
	GPIOA->CRL &= 0x00000000;	//清零
	GPIOA->CRL |= 0x88881111;	//高四位输入,低四位输出
	GPIOA->ODR |= 0xFF;			//上拉输入,和输出高电平
	
	//外部中断配置寄存器
	AFIO->EXTICR[1] = 0x0000;	//配置PA4 PA5 PA6 PA7 
	
	//配置EXTI寄存器
	EXTI->IMR |= (0xF<<4);		//开放来自4567线上的中断请求
	EXTI->FTSR |= (0xF<<4);		//允许输入线4567上的下降沿触发
	
	MY_NVIC_Init(0,0,EXTI4_IRQn,0);
	MY_NVIC_Init(0,0,EXTI9_5_IRQn,0);
}

EXTI4_IRQn和EXTI9_5_IRQn可以再stm32f10x.h头文件中找到,这是中断编号。函数MY_NVIC_Init()在STM32F103学习笔记(3.0)——中断里提到是整点原子提供在sys.c里的函数,直接利用了,在手册中并没有详细说明。

例程

PA4 PA5 PA6 PA7接按键,PA0 PA1 PA2 PA3接LED灯,用外部中断去控制灯的亮灭

寄存器的代码可以是这样:

#include "stm32f10x.h" 
#include "sys.h"
#include "MY_LED.H"
#include "delay.h"


void EXTI_init(void)
{
	RCC->APB2ENR |= (1<<2);		//GPIOA
	RCC->APB2ENR |= (1<<0);		//AFIO
	//EXTI和NVIC两个外设时钟是一直开着的,不需要开
	//NVIC是内核外设,和CPU一起运行,RCC管的是内核以外
	
	GPIOA->CRL &= 0x00000000;	//清零
	GPIOA->CRL |= 0x88881111;	//高四位输入,低四位输出
	GPIOA->ODR |= 0xFF;			//上拉输入,和输出高电平
	
	//外部中断配置寄存器
	AFIO->EXTICR[1] = 0x0000;	//配置PA4 PA5 PA6 PA7 
	
	//配置EXTI寄存器
	EXTI->IMR |= (0xF<<4);		//开放来自4567线上的中断请求
	EXTI->FTSR |= (0xF<<4);		//允许输入线4567上的下降沿触发
	
	MY_NVIC_Init(0,0,EXTI4_IRQn,0);
	MY_NVIC_Init(0,0,EXTI9_5_IRQn,0);
}

int main(void)
{				 
	delay_init(8);
	EXTI_init();
	MY_LED_Init();
	
  	while(1)
	{
		
	}	 
} 


void EXTI4_IRQHandler(void)
{
	delay_ms(10);					//消抖
	GPIOA->ODR ^= (1<<0);			//取反第0位,让LED灯状态翻转
	
	while(!(GPIOA->IDR&(1<<4)));	//松开按键的消抖
	delay_ms(10);
	EXTI->PR=1<<4;  				//清除LINE4上的中断标志位  
}

void EXTI9_5_IRQHandler(void)
{
	if(EXTI->PR&(1<<5))
	{
		delay_ms(10);					//消抖
		GPIOA->ODR ^= (1<<1);			
	
		while(!(GPIOA->IDR&(1<<5)));	//松开按键的消抖
		delay_ms(10);
		EXTI->PR=1<<5;  				 
	}else if(EXTI->PR&(1<<6))
	{
		delay_ms(10);					//消抖
		GPIOA->ODR ^= (1<<2);			
	
		while(!(GPIOA->IDR&(1<<6)));	//松开按键的消抖
		delay_ms(10);
		EXTI->PR=1<<6;  				  
	}else if(EXTI->PR&(1<<7))
	{
		delay_ms(10);					//消抖
		GPIOA->ODR ^= (1<<3);			
	
		while(!(GPIOA->IDR&(1<<7)));	//松开按键的消抖
		delay_ms(10);
		EXTI->PR=1<<7;  				  
	}
}

由于EXTI5-EXTI9是合并成一个中断号,产生的中断是同一个,所以要在中断里判断到底是哪一条线产生的中断,读PR寄存器。主要看EXIT4_IRQHandler的部分

库函数的代码可以是这样:

#include "stm32f10x.h"                  // Device header
#include "delay.h"

void EXTI9_5_IRQHandler(void)
{
	if(EXTI_GetFlagStatus(EXTI_Line5) == SET)
	{
		Delay_ms(5);	//按键消抖,按下的消抖
		while(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_5) == RESET);	//消抖,等待按键松开
		Delay_ms(5);	//按键消抖,松开的消抖
		GPIO_WriteBit(GPIOA , GPIO_Pin_1 , (BitAction)!GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_1));
		EXTI_ClearFlag(EXTI_Line5);
	}
	if(EXTI_GetFlagStatus(EXTI_Line6) == SET)
	{
		Delay_ms(5);	//按键消抖,按下的消抖
		while(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_6) == RESET);	//消抖,等待按键松开
		Delay_ms(5);	//按键消抖,松开的消抖
		GPIO_WriteBit(GPIOA , GPIO_Pin_2 , (BitAction)!GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_2));
		EXTI_ClearFlag(EXTI_Line6);
	}
	if(EXTI_GetFlagStatus(EXTI_Line7) == SET)
	{
		Delay_ms(5);	//按键消抖,按下的消抖
		while(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_7) == RESET);	//消抖,等待按键松开
		Delay_ms(5);	//按键消抖,松开的消抖
		GPIO_WriteBit(GPIOA , GPIO_Pin_3 , (BitAction)!GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_3));
		EXTI_ClearFlag(EXTI_Line7);
	}
	
}

void EXTI4_IRQHandler(void)
{
	Delay_ms(5);	//按键消抖,按下的消抖
	while(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_4) == RESET);	//消抖,等待按键松开
	Delay_ms(5);	//按键消抖,松开的消抖
	GPIO_WriteBit(GPIOA , GPIO_Pin_0 , (BitAction)!GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_0));
	EXTI_ClearFlag(EXTI_Line4);
}
void EXTI_init(void)
{
	EXTI_InitTypeDef EXTI_InitStructure;
	
	EXTI_InitStructure.EXTI_Line = EXTI_Line4 | EXTI_Line5 | EXTI_Line6 | EXTI_Line7;	//PA4/5/6/7挂在线4/5/6/7上
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;											//使能外部中断
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;									//产生中断而不是事件
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;								//下降沿触发
	EXTI_Init(&EXTI_InitStructure);
}

void NVIC_init(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;							//抢占优先级和响应优先级优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_Init(&NVIC_InitStructure);
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
	NVIC_Init(&NVIC_InitStructure);
}

void GPIOA_init(void)
{
	GPIO_InitTypeDef	GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;		//上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//没啥用,填吧
	GPIO_Init(GPIOA , &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;	//推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA , &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOA , GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3);
}


int main(void)
{
	GPIOA_init();
	EXTI_init();
	NVIC_init();
	while(1);
}

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
根据提供的引用内容,以下是关于STM32F103RET6多路外部中断同时配置的介绍: STM32F103RET6是STM32F103xC/D/E系列微控制器的一种型号,它集成了多个外部中断线。外部中断是一种用于处理外部事件的机制,可以通过配置来触发中断并执行相应的中断服务程序。 要同时配置多路外部中断,需要进行以下步骤: 1. 配置外部中断引脚:首先,需要选择要用作外部中断的引脚,并将其配置为输入模式。可以使用GPIO模块的相应寄存器来配置引脚的模式和速率。 2. 配置中断触发方式:接下来,需要选择外部中断的触发方式。可以选择边沿触发(上升沿、下降沿或双边沿)或电平触发(高电平或低电平)。可以使用外部中断线的相应寄存器来配置触发方式。 3. 使能外部中断:最后,需要使能外部中断。可以使用外部中断线的相应寄存器来使能或禁用外部中断。 以下是一个示例代码,演示了如何同时配置两个外部中断线(例如:外部中断线0和外部中断线1): ```c #include "stm32f10x.h" void EXTI0_IRQHandler(void) { // 外部中断线0的中断服务程序 // 在这里处理外部中断0的事件 } void EXTI1_IRQHandler(void) { // 外部中断线1的中断服务程序 // 在这里处理外部中断1的事件 } int main(void) { // 配置外部中断引脚 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; // 外部中断线0和外部中断线1的引脚 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 输入模式,带上拉 GPIO_Init(GPIOA, &GPIO_InitStruct); // 配置GPIOA端口 // 配置中断触发方式 EXTI_InitTypeDef EXTI_InitStruct; EXTI_InitStruct.EXTI_Line = EXTI_Line0 | EXTI_Line1; // 外部中断线0和外部中断线1 EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式 EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling; // 上升沿和下降沿触发 EXTI_InitStruct.EXTI_LineCmd = ENABLE; // 使能外部中断线 EXTI_Init(&EXTI_InitStruct); // 使能外部中断 NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn; // 外部中断线0的中断通道 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; // 子优先级 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; // 使能中断通道 NVIC_Init(&NVIC_InitStruct); NVIC_InitStruct.NVIC_IRQChannel = EXTI1_IRQn; // 外部中断线1的中断通道 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; // 子优先级 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; // 使能中断通道 NVIC_Init(&NVIC_InitStruct); while (1) { // 主循环 } } ``` 请注意,以上代码仅为示例,实际的配置可能会根据具体的应用需求有所不同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天师电通电容爆破工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值