您现在的位置是:首页 >技术交流 >STM32单片机(五)第二节:EXTI外部中断练习(对射式红外传感器计次和旋转编码器计次)网站首页技术交流
STM32单片机(五)第二节:EXTI外部中断练习(对射式红外传感器计次和旋转编码器计次)
❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分;建议先学习51单片机,其是STM32等高级单片机的基础;这样再学习STM32时才能融会贯通。
☀️ 专栏适用人群 :适用于想要从零基础开始学习入门单片机,且有一定C语言基础的的童鞋。
?专栏目标:实现从零基础入门51单片机和STM32单片机,力求在玩好单片机的同时,能够了解一些计算机的基本概念,了解电路及其元器件的基本理论等。⭐️ 专栏主要内容: 主要学习STM32单片机的功能、各个模块、单片机的外设、驱动等,最终玩好单片机和单片机的外设,全程手敲代码,实现我们所要实现的功能。
? 专栏说明 :如果文章知识点有错误的地方,欢迎大家随时在文章下面评论,我会第一时间改正。让我们一起学习,一起进步。
?专栏主页:http://t.csdn.cn/HCD8v
本学习过程参考:https://space.bilibili.com/383400717
STM3单片机安装软件、各种资料以及源码的路径:
链接:https://pan.baidu.com/s/1snD0uuTfMhchFqOMWvAiHA?pwd=asdf#list/path=%2F
提取码:asdf
链接里压缩包的解压密码:32
本大节主要学习STM32的中断系统和外部中断,包含两部分,第一小节主要学习中断系统的基础知识,第二小节是写两个程序进行练习,即对射式红外传感器计次和旋转编码器计次,最终附上所有的源代码;
本小节主要是对上一节学习的EXTI外部中断进行实战练习,编写两个程序进行练习,分别实现对射式红外传感器计次和旋转编码器计次等,并附上程序源码;
文章目录
一、本节目标
目标1:对射式红外传感器计次
实现OLED显示屏上实时显示红外传感器被挡光的次数。
过程是我们对红外传感器进行挡光,随后把手拿开;这样挡光–拿开的过程,每进行一次,OLED上显示的次数加1;如下图所示:
目标2:旋转编码器计次
转动“旋转编码器”时,OLED屏幕上的数字也跟着变化;例如当向右转动旋转编码器时,屏幕上的数字变大,向左转动旋转编码器时,屏幕上的数字变小,如下图所示,初始数字为0
当向右旋转时,数字逐渐变大:
向左旋转时,数字变小
二、设备介绍
2.1 设备介绍
2.1.1 旋转编码器介绍
- 第一个图的旋转编码器只能测位置和速度,不能测方向
- 第二个图是本课程使用的旋转编码器,第三张图是其内部拆解;既可以测位置和速度,也可以测方向;
2.1.2 旋转编码器硬件电路
三、对射式红外传感器计次源码
3.1 接线图
对射式红外传感器模块,VCC、GND分别接电源的正负极,DO数字输出端,随意选择一个GPIO口接上就行,在这里我接到了B14端口;
硬件接线图如下所示:
程序逻辑:
当我们的挡光片或者编码盘在这个对射式红外传感器模块中间经过时,这个DO就会输出电平变化的信号,然后这个电平跳变的信号触发STM32 PB14号口的中断,我们在中断函数里执行变量++的程序,并最终输出在OLED上即可。
3.2 代码
代码路径:STM32入门教程资料STM32入门教程资料程序源码程序源码STM32Project5-1 对射式红外传感器计次User
3.2.1 代码逻辑:
- 第一步,配置RCC,把涉及到的外设的时钟都打开,一定要谨记,不打开时钟,外设是没法工作的;
- 第二步,配置GPIO,选择我们的端口为输入模式
- 第三步,配置AFIO,选择我们用哦的这一路GPIO,连接到后面的EXTI
- 第四步,配置边沿触发方式,比如上升沿、下降沿或者双边沿;还有选择触发响应方式,可以选择中断响应和事件响应;
- 第五步,配置NVIC,给我们这个中断选择一个合适的优先级;
- 最后,通过NVIC,外部中断信号就能进入CPU了,这样CPU才能收到中断信号,才能跳转到中断函数里执行中断程序;
以上就是外部中断的配置流程,涉及到的外设包括RCC、GPIO、AFIO、EXTI、NVIC。
3.2.2 代码解释:
第一步开启时钟:
开启GPIOB和AFIO的时钟:
EXTI、NVIC的时钟默认是开启的,所以不需要程序开启;
第二步配置GPIO为输入模式:
第三步,配置AFIO外设:
选择GPIOB,连接PB14号口的第14个中断线路;
第四步,配置EXTI:
EXTI的所有库函数如下,
EXTI初始化:
将EXTI的第14个线路配置位中断模式,下降沿触发;
第五步:配置NVIC:
NVIC库函数:
3.2.3 完整代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"
int main(void)
{
OLED_Init();
CountSensor_Init();
OLED_ShowString(1, 1, "Count:");
while (1)
{
OLED_ShowNum(1, 7, CountSensor_Get(), 5);
}
}
CountSensor.c
:
#include "stm32f10x.h" // Device header
uint16_t CountSensor_Count;
void CountSensor_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_14;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line14;
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 = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
}
uint16_t CountSensor_Get(void)
{
return CountSensor_Count;
}
void EXTI15_10_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line14) == SET)
{
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)
{
CountSensor_Count ++;
}
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
四、旋转编码器计次源码
代码路径:STM32入门教程资料STM32入门教程资料程序源码程序源码STM32Project5-2 旋转编码器计次User
4.1 接线图
左边旋转编码器模块,上面VCC和GND接正负极,下面这两个A,B相输出引脚,分别接到STM32的PB0和PB1两个引脚;
实物接线图如下所示:
4.2 代码
#include "stm32f10x.h" // Device header
#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);
}
}
Encoder.c
:
#include "stm32f10x.h" // Device header
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)
{
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
Encoder_Count --;
}
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
void EXTI1_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line1) == SET)
{
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
Encoder_Count ++;
}
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
}