深入了解STM32 EXTI中断功能

EXTI中断系统

EXTI中断获取的信号源是外部驱动的很快的突发信号,在主程序运行过程中,出现中断请求,编译器会保护主程序断点现场,跳出优先处理中断,中断程序运行完成后,跳回主程序继续运行断点后的主程序。

外部中断

中断响应:中断触发,主程序优先处理中断程序,中断程序结束后回到主程序保护点继续执行主程序。

EXTI触发方式

上升沿:电平从低电平变到高电平的瞬间触发中断

下降沿:电平从高电平变到低电平的瞬间触发中断

双边沿:上升沿和下降沿都可以触发中断

软件触发:程序触发

注意:Pin不能同时触发中断,例:PA1 , PB1 , PC1

外部中断基本结构

EXTI9_5为共用通道,表示EXTI5\6\7\8\9到NVIC使用同一个通道,外部中断的5~9会触发同一个中断函数,10~15也会触发同一个中断函数,可在函数中再根据标志位来区分到底是哪条中断。

AFIO

AFIO:中断引脚选择器,因为GPIO_Pin只有16个中断通道,所以同一个Pin,只有一个能接到通道内,例如PinA0  PinB0 PinC0 只有一个可以接到通道0上。

 NVIC

1、中断通道就是中断源,STM32具体到某个型号不一定有68个中断这么全,基本上所有外设均可触发中断。一个外设可以有多个中断源,每个中断通道都有16个可编程的优先等级。

2、中断使用NVIC(嵌套中断向量控制器)统一管理(可以把它比作是医院里面的叫号系统,CPU是医生)。 管理中断、分配优先级都由它来控制。NVIC是一个内核外设,服务与CPU。图中n的意思是一个外设可能会占用多个中断通道。

中断排队规则:

中断是按照中断号来进行排队,并不是按照先来后到的顺序,按照优先级排队,值越小优先级越高。

响应优先级:
CPU目前处理的中断完成后,如果排队待处理的中断中有响应优先级的中断,即使排在末尾,也可以插队优先让CPU处理。

抢占优先级:
CPU目前正在处理中断的情况下,处于抢占优先级的中断源出现,可以让CPU暂停当前正在处理的中断,先处理抢占优先级的中断,处理完抢占优先级的中断后再处理刚才暂停的中断。

EXTI中断控制逻辑图

工程实施

GPIO到NVIC将所有外设配置

第一步:配置RCC,将涉及的的外设时钟都打开

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);    //开启APB2时钟外设GPIOB
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);     //开启APB2时钟外设AFIO

第二步:配置GPIO,将涉及端口设置为输入模式

GPIO_InitTypeDef GPIO_InitStructure;                  //创建结构体变量
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;            //选择引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;     //输出速率50MHz(速率)
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;         //上拉输入(模式)
GPIO_Init(GPIOB,&GPIO_InitStructure);                 //初始化

第三步:配置AFIO,选择用到的GPIO,连接到后面的EXTI

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);     //配置输入端GPIOB端口,PB14号引脚;输出端固定连接的是EXTI的第14个中断线路

第四步:配置EXTI,选择边沿触发方式,比如上升沿,下降沿,或者双边沿。可以选择响应方式,中断响应 或者 事件响应

EXTI_InitTypeDef EXTI_Initstructure;                      //创建结构变量
EXTI_Initstructure.EXTI_Line = EXTI_Line14;               //配置的中断线,EXTI第14线路
EXTI_Initstructure.EXTI_LineCmd = ENABLE;                 //指定选择的中断线的新状态(开启中断)
EXTI_Initstructure.EXTI_Mode = EXTI_Mode_Interrupt;       //中断模式(Event:事件模式)
EXTI_Initstructure.EXTI_Trigger = EXTI_Trigger_Falling;   //下降沿触发
EXTI_Init(&EXTI_Initstructure);                           //初始化

第五步:配置NVIC,设置中断选择优先级

//选择中断分组,中断分组2,抢占优先级和响应优先级的取值范围都是0~3
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);              

NVIC_InitTypeDef NVIC_InitStructure;                         //创建结构体变量
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;         //指定中断通道来开启或关闭,EXTI_Line14(GPIO_Pin_14)对应EXTI15_10_IRQn中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;              //中断通道为ENABLE(使能)
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;    //抢占优先级设置为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;           //响应优先级设置为1
NVIC_Init(&NVIC_InitStructure);                              //初始化

配置完以上步骤就可以写中断函数了

中断函数

在启动文件中,已经定义好了中断函数的名字,每个中断通道都定义了一个中断函数。

我们设置的外部中断源是从 pin14 这个引脚进来的,所以函数名选择为:EXTI15_10_IRQHandler,pin10 到 pin15,六个通道的中断发生时,都会执行这个函数。

所以,当执行这个函数时,应该先判断是不是pin14发生的中断,如果是,则执行相应的命令。

注意:中断函数不需要在主函数中调用,中断触发时会自动执行中断函数

void EXTI15_10_IRQHandler(void){
//获取中断标志位, 判断EXTI14的中断标志位是不是为1(SET)(若发生中断,则标志位置1)
	if (EXTI_GetITStatus(EXTI_Line14) == SET){             
		  CountSensor_Count ++;                          
		  //中断程序结束后,清除中断标志位,否则陷入中断循环
		  EXTI_ClearITPendingBit(EXTI_Line14);    
	}
}

常用函数

void EXTI_DeInit(void);                                    //重置
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);         //初始化
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);   //初始化默认值
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);         //软件触发
//能不能触发中断的标志位都能读取(建议用于主程序)
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);         //获取中断标志位
void EXTI_ClearFlag(uint32_t EXTI_Line);                   //清除中断标志位
//这两个函数只能读写与中断有关的标志位(建议用于中断程序)
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);             //获取中断标志位
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);           //清除中断标志位


旋转编码器计次样例

接线图

旋转编码器正反判断

A相B相都触发中断,只有在B相下降沿和A相低电平时,才判断为正转,在A相下降沿和B相低电平时,才判断为反转,就可以保证正转反转都是转到位了,才执行数字加减的操作。

样例工程实施

代码

#include "stm32f10x.h"                  

int16_t Encoder_Count;

void Encoder_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
	
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	EXTI_Init(&EXTI_InitStructure);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);

	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_Init(&NVIC_InitStructure);
}
//返回计数值
int16_t Encoder_Get(void)
{
	int16_t Temp;
	Temp = Encoder_Count;
	Encoder_Count = 0;
	return Temp;
}

void EXTI0_IRQHandler(void){
	//获取中断标志位
	if (EXTI_GetITStatus(EXTI_Line0) == SET){         //判断EXTI1的中断标志位是不是为1(SET)
		  if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0){
				Encoder_Count --;                                 //计中断数
			}
		  EXTI_ClearITPendingBit(EXTI_Line1);               //清除中断标志位
	}
}

void EXTI1_IRQHandler(void){
	//获取中断标志位
	if (EXTI_GetITStatus(EXTI_Line1) == SET){        //判断EXTI0的中断标志位是不是为1(SET)
		if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0) == 0){
				Encoder_Count ++;                                 //计中断数
			}		                             
		  EXTI_ClearITPendingBit(EXTI_Line0);            //清除中断标志位
	}
}

main代码

#include "stm32f10x.h"                  
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"

int16_t Num;

int main(void)
{
	OLED_Init();
	Encoder_Init();
	
	OLED_ShowString(1, 1, "Num:");
	
	while (1)
	{
		Num += Encoder_Get();
		OLED_ShowSignedNum(1, 5, Num, 5);
	}
}

作者:尘世中迷茫的小菜呀

物联沃分享整理
物联沃-IOTWORD物联网 » 深入了解STM32 EXTI中断功能

发表回复