STM32外部中断EXTI学习

开启EXTI外部中断

EXTI外部中断

中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行。
在这里插入图片描述
EXTI(Extern Interrupt)外部中断
EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序。

支持的触发方式:上升沿/下降沿/双边沿/软件触发;
支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断;
通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒;
触发响应方式:中断响应/事件响应;

EXTI基本结构
在这里插入图片描述
如何使用EXTI
思路就是根据上图从左到右顺序依次配置,
1、首先开启和配置GPIO,要触发中断是需要指定引脚的,上面也提到;
2、其次开启和配置AFIO,AFIO中断引脚是一个数据选择器,它会选择前面3个GPIO外设的16个引脚中一个来连接到后面的EXTI,但注意PA0、PB0、PC0是不可以接到同一个AFIO通道上;
3、然后配置EXTI,主要配置连接NVIC的通道、通道开启、中断响应还是事件响应以及触发中断方式;
4、最后配置NVIC,配置优先级、优先级分组、开启NVIC通道。

开始

第一步RCC开启时钟,开启外设时钟

开启GPIO时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

开启AFIO时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

EXTI和NVIC这两个外设时钟一直都在打开,不用管
时钟配置完成

第二步配置GPIO

关于引脚模式参考手册外设的GPIO配置,这里使用上拉输入,PB14
在这里插入图片描述
参考配置

GPIO_InitTypeDef GPIO_InitStructure;              //GPIO结构体类型和它的结构体变量
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;       //GPIO上拉输入模式
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_14;          //选择14口
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;   //GPIO速度50MHz
GPIO_Init(GPIOB,&GPIO_InitStructure);             //GPIO初始化,且为GPIOB

第三步配置AFIO

下面函数用来配置AFIO的数据选择器,来选择我们想要的中断引脚
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);

第四步配置EXTI

在stm32f10x_exti.h文件,下面是函数功能简要说明;

void EXTI_DeInit(void);//清楚EXTI的配置,恢复成上电默认状态
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);//根据结构体里的参数配置EXTI外设
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);//把参数传递给结构体变量赋一个默认值
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);//软件触发外部中断
//在主程序里查看和清除标志位
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);//获取指定标志位是否被置1
void EXTI_ClearFlag(uint32_t EXTI_Line);//标志位清除
//在中断函数里查看和清除标志位
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);

参考配置

EXTI_InitTypeDef EXTI_InitStructure;                            //外部中断结构体类型和该结构体变量
EXTI_InitStructure.EXTI_Line=EXTI_Line14;                       //连接14口通道
EXTI_InitStructure.EXTI_LineCmd=ENABLE;                         //使能通道
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;               //触发模式中断响应
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;	//中断触发方式上升沿
EXTI_Init(&EXTI_InitStructure);                                 //外部中断初始化

第五步配置NVIC

在misc.h,主要使用下面前两个函数,先指定中断分组,然后初始化NVIC
在这里插入图片描述

NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级;
抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队。

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);//中断分组,参数是中断分组方式
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);//根据结构体里面指定参数初始化NVIC
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);//设置中断向量表
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);//系统低功耗配置
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource);

参考配置

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//优先级分组,2组

NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn ;    //中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;          //通道开启
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级置1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;     //响应优先级置1
NVIC_Init(&NVIC_InitStructure);

注意中断通道5~9 为EXTI5_9_IRQn,10~15为EXTI15_10_IRQn
外部中断的信号从GPIO到AFIO,再到EXTI,再到NVIC,最终通向CPU,这样才能让CPU跳转到中断程序执行。

中断函数
参考启动文件
在这里插入图片描述

在中断函数里,一般先进行一个中断标志位的判断,来确定是我们想要的中断源触发的这个函数。

void EXTI15_10_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line14) == SET)//判断是不是Pin14触发的中断
	{
	    
		EXTI_ClearITPendingBit(EXTI_Line14);//清除14脚的标志位
	}
}

注:中断函数不用声明,它不需要调用,它是自动执行的
ps: 中断函数里,最好不要执行耗时过长的代码,中断函数要简短快速,别刚进中断就执行一个delay多少毫秒这样的代码,因为中断是处理突发的事情,如果你为了处理一个突发的事情待在中断里不出来了,那主程序就会受到严重的阻塞。还有最好不要在中断函数和主函数调用相同的函数,或者操作同一个硬件,尤其是硬件相关的函数,比如OLED显示函数,如果你既在主函数里调用OLED,又在中断里调用OLED,OLED就会显示错误。

结束

验证代码的正确性
这里使用OLED显示屏来显示运行结果
首先把上面的参考配置都放进一个初始化函数里
如:

void CountSensor_Init(void)
{
    //参考配置...
}


void EXTI15_10_IRQHandler(void)      //中断程序
{
  if (EXTI_GetITStatus(EXTI_Line14) == SET)
  {
	/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 1)
	{
		CountSensor_Count ++;
	}
	EXTI_ClearITPendingBit(EXTI_Line14);
  }
}	

uint16_t CountSensor_Get(void)  //获取CountSensor_Count函数
{
    return CountSensor_Count;
}

main.c

int main(void)
{
   OLED_Init();
   CountSensor_Init();

   OLED_ShowString(1, 1, "Count:");	
   while (1)
   {
	 OLED_ShowNum(1, 7, CountSensor_Get(), 5);
   }
}

现象

上升沿触发,引脚高电平判断,挡光的时候Count+1,移开时Count无变化;
下降沿触发,引脚低电平判断,挡光的时候Count无变化,移开时Count+1;
双边沿触发方式,把中断函数里内容改成下面即可。

if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==0)
{
  ACount ++;
}
  if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==1)
  {		
	BCount++;
  }
Count=ACount+BCount;

内容及代码:bilibili up主江协科技
链接地址:https://www.bilibili.com/video/BV1th411z7sn?p=12&vd_source=d1ebc01bc0767886dfaea9651fc58640

  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值