图均源自网络,如有侵权请联系本人
上篇内容讲述了EXTI的理论框架部分,我们大致了解了外部中断的电路框架,知道要配置哪一部分的电路来实现中断功能,那么这篇文章就是通过电路搭建以及编写代码来控制单片机执行中断功能。
我们本篇文章主要利用外部中断来实现两个功能,第一个是对射式红外传器计次,第二个是旋转编码器计次。
我们首先来介绍一下对射式红外传感器。
一般市面上的红外传感器模块是四个引脚,VCC(电源),GND(接地),DO(开关信号输出),AO(模拟信号输出),AO引脚一般不会用到。
还有下图中的三个引脚的模块,那这里的OUT端就相当于DO端了。
对射式红外传感器由发射端,接收端,光耦三个部分组成。发射端可以发出人眼看不见的红外光,接收端可以接受发射端发射的红外光,当有物体经过光耦遮挡了接收端接受红外光,DO引脚就会输出信号1,当无遮挡物时,DO引脚就会输出0;由此,我们可以通过判断DO引脚的信号来判断是否有物体遮挡,以实现计次的功能。
我们把传感器模块VCC,GND端利用面包板,杜邦线接好后,将DO端与单片机的任一GPIO口连接好,这里用GPIOB_Pin_14。再使用OLED显示计次数据,这里的OLED两个信号口也是任意接到单片机的GPIO口,用于显示数据。
那么就到了代码部分。首先就是要开启GPIO口与AFIO与EXTI,NVIC的时钟了,不过这里需要我们手动开启时钟的外设只有GPIO与AFIO,EXTI单片机已经自动开启了,NVIC是内核外设,也不需要我们手动开启。
void CountSensor_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/*初始化GPIO口*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;/*上拉输入,默认为高电平*/
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_14;
GPIO_InitStructure.GPIO_SPpeed=GPIO_SPpeed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
/*初始化AFIO*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);/*选择GPIOB的14号引脚为触发源*/
/*初始化EXTI*/
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line14;/*上拉输入,默认为高电平*/
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrput;/*触发中断*/
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;/*下降沿触发*/
EXTI_Init(&EXTI_InitStructure);
/*初始化NVIC*/
NVIC_priorityGroupConfig(NVIC_PriorityGroup_2);/*分组2*/
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure. NVIC_IRQChannel=EXTI15_10_IRQn;/*上拉输入,默认为高电平*/
NVIC_InitStructure.IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;/*设置抢占优先级,分组2范围为0-3*/
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;/*设置响应优先级,分组2范围为0-3*/
NVIC_Init(&NVIC_InitStructure);
}
进行完一些简单的初始化配置后,就可以写中断函数了,这里中断函数的名字是固定的,线路14的中断函数名字STM公司已经规定好了,我们在启动文件里面可以看到相关中断线路的中断函数名字,像线路14的中断函数就叫EXTI15_10_IRQHandler,这里我们希望在中断函数里面实现Count_Sensor_Count自增的功能,每当遮挡一次,就加1,用Count_Sensor_Count++实现,这里还需要注意的是,因为我们使用的线路14是与10-15线路共同使用一个通道的,所以这里还需要判断一下是不是线路14的中断源有信号了,这里可以通过访问挂起寄存器,看看对应线路的标志位是否被置1了,如果被置1了,说明就是线路14的中断源有信号跳变了。再函数最后,千万不要忘记了给挂起手动寄存器清零,不然挂起寄存器会一直被置1,然后程序会不断执行中断函数,这样程序就会被卡死在中断函数里面了。
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line14)==SET)
{
Count_Sensor_Count++;
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
在主程序里面,我们只需要调用OLED与CountSensor的初始化函数,再利用OLED显示一下变量就可以了。
接下来就是旋转编码器计次了,我们先来介绍一下旋转编码器。
我们所用的模块是机械触电式的旋转编码器,有5个引脚,分别接VCC与GND,当按下编码器时,这两端会被连通,这时候就相当于一个普通按键。不过我们这里使用到的是旋转功能,按键功能暂时不用。A、B引脚为信号输出角,这两个引脚会同时输出相位相差90度的波形,以A引脚为观察方,当顺时针旋转时,A信号为上升沿时,B信号为低电平,A信号为下降沿时,B信号为高电平。当逆时针旋转时,A信号为上升沿时,B信号为高电平,A信号为下降沿时,B信号为低电平。通过判断AB引脚的电平信号,就可以知道是顺时针旋转还是逆时针旋转了。C引脚内部已经被接地了,这里我们不用管。
(此图为顺时针旋转时,AB信号的输出波形,黄色线为A信号,蓝色线为B信号)
这里我们把AB引脚分别接入到GPIOB_Pin_0与GPIOB_Pin_1,只需要改一下初始化即可。
我们用上升沿来分析的话,顺时针旋转时,就是A信号为上升沿时,B信号应该为0(此时我们设置变量++)。逆时针旋转时,B信号为上升沿时,A信号为0(此时我们设置变量--)。
我们把A信号接到中断线路1,B信号接到中断线路2,当中断1(信号A)有中断信号时,判断完标志位后,再判断B信号引脚是否为0,当中断2(信号B)有中断信号时,判断完标志位后,再判断A信号引脚是否为0即可完成方向判断,实现变量的自增自减。
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0)==SET)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) //PB0的上升沿触发中断,此时检测另一相PB1的电平,目的是判断旋转方向
{
Encoder_Count++; //此方向定义为正转,计数变量自加
}
EXTI_ClearITPendingBit(EXTI_Line0); //清除外部中断0号线的中断标志位
}
}
void EXTI1_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line1)==SET)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) //PB1的上升沿触发中断,此时检测另一相PB0的电平,目的是判断旋转方向
{
Encoder_Count --; //此方向定义为反转,计数变量自减
}
EXTI_ClearITPendingBit(EXTI_Line1); //清除外部中断1号线的中断标志位
}
}
以上为外部中断的全部学习内容,关于中断,后续还有许多地方会学习到,继续挖掘知识吧~