搜索
您的当前位置:首页正文

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

来源:步旅网


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

外部中断控制框图

 外部中断通用I/O映像

根据框图以及里关于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()在里提到是整点原子提供在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);
}

因篇幅问题不能全部显示,请点此查看更多更全内容

Top