目录
前言
本期接着上一期的内容继续学习stm32的外部中断系统(上一期链接:stm32入门-----EXTI外部中断(上 ------理论篇)-CSDN博客)。本期的主要是进行实践操作stm32的外部中断,分为两个部分:红外线传感器进行计数和转动旋转编码器进行计数。(视频:[5-2] 对射式红外传感器计次&旋转编码器计次_哔哩哔哩_bilibili)
一、硬件介绍
1.对射红外线传感器
工作电压:3.3V-5V
输出形式:数字开关量D0输出(0 和 1),模拟输出A0引脚无效
使用方式: 接好VCC和GND,模块电源指示灯会亮,模块槽中无遮挡时,接收管导通,模块DO输出低电平,开关指示灯亮;遮挡时,DO输出高电平,开关指示灯灭。模块DO可与继电器相连,组成限位开关等功能,也可以与有源蜂鸣器模块相连,组成报警器。DO输出接口可以与单片机I0口直接相连,一般接外部中断,检测传感器是否有遮档,如用电机码盘则可检测电机的转速。
硬件电路:
2.旋转编码器
- 旋转编码器:用来测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向
- 类型:机械触点式/霍尔传感器式/光栅式
硬件电路:
这里可以看到旋转编码器有两个输出口分别是A和B口,当没有进行旋转的时候此时两个输出口都会被拉升为高电平,当逆时针转动的时候A口的电路与GND导通,此时A口拉倒低电平,输出为低电平,但B处不变,顺时针就刚好反过来。(想了解详细可查看:江协科技STM32------旋转编码器计次(软件消抖)_旋转编码器消抖-CSDN博客)
二、EXTI外部中断C编程
我们可以用C语言的库函数来取调用这些外部中断,实际上只需要按照上一期理论的知识去进行相关的初始化设置就行了。大体上分为5步。
EXIT外部中断结构图:
1.开启RCC时钟
设置时钟包括GPIO口的时钟和AFIO的时钟,GPIO的时钟我们前面学习GPIO口的时候就已经学习过了,如果没有去指定开启GPIO的时钟,那么这个GPIO口是无法使用的。同样的AFIO的时钟如果不开的话也是无法使用的。
对于EXIT外部中断,这两个时钟都要开启,GPIO是作为输入口,二AFIO是作为中断引脚的选择口。这里我们就使用APB2外设的时钟
示例如下:
cpp
// 1.开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
2.配置GPIOK口初始化
这个的我们之前就已经学习过了,这里就不多讲了,不过要注意的是配置GPIOK口的工作方式最好选择上拉输入 或者下拉输入 或者浮空输入,以保证输入的电平是保持稳定的,当且仅当外部硬件输入变化才产生变化。
示例如下:
cpp
//2.配置GPIO口
GPIO_InitTypeDef GPIO_initstruct;
GPIO_initstruct.GPIO_Mode=GPIO_Mode_IPU;//选择上拉输入
GPIO_initstruct.GPIO_Pin=GPIO_Pin_14; //选用14号引脚
GPIO_initstruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_initstruct); //GPIOB口初始化
3.配置AFIO
这里要用到**void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)**这个函数,这个函数是用来去配置AFIO,不过这个函数的名称连AFIO都没出现反而出现了EXIT,我觉得也挺奇怪的,但是这个函数的内部是实际对AFIO进行操作的。
AFIO配置GPIO线路关系图如下:
相关定义如下:
示例如下:
cpp
//3.配置AFIO
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14); //AFIO打到GPIOB处的14号引脚
4.配置EXIT
配置EXIT包括配置工作方式(中断模式还是事件模式)、触发方式、以及配置外部中断的中断线路,上面我们配置好了AFIO与GPIO之间的线路,这里我们只需要去配置AFIO与EXIT之间的线路就行了。如下图所示:
下图是EXIT在C语言中的结构体定义:
代码的写法跟配置GPIO口的基本上是一模一样的,示例如下:
cpp
//4.配置EXTI
EXTI_InitTypeDef EXTI_initstruct;//定义EXIT结构体
EXTI_initstruct.EXTI_Line=EXTI_Line14;//选择中断线,这里选择外部中断14号线
EXTI_initstruct.EXTI_LineCmd=ENABLE;//设置上面选择好的中断线是否使用,进行使能操作,这里是设置ENABLE,进行使能
EXTI_initstruct.EXTI_Mode=EXTI_Mode_Interrupt;//配置模式,中断模式还是事件模式,这里选择中断模式
EXTI_initstruct.EXTI_Trigger=EXTI_Trigger_Falling;//选择触发方式,上升沿触发还是下降沿触发等等,这里选择下降沿触发
EXTI_Init(&EXTI_initstruct);//初始化
5.配置NVIC
配置NVIC包括配置优先级(抢占优先级和响应优先级)、EXIT通道(也就是NVIC与上面配置好的EXIT之间的通道)、分组情况。
NVIC是分为5组的,不同的分组指定的抢占优先级和响应优先级的个数是不同的,分组配置的注释如下:
代码示例如下:
cpp
//5.配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置分组,可放到主函数去分
NVIC_InitTypeDef NVIC_initstruct;//定义结构体
NVIC_initstruct.NVIC_IRQChannel= EXTI15_10_IRQn ; //选择通道,EXTI通道EXTI15_10_IRQn
NVIC_initstruct.NVIC_IRQChannelCmd=ENABLE;//指定上面通道是否开启,同理
//由于当前只有一个中断,优先级可以根据当前分组随便设置,如果多个的话才涉及这个
NVIC_initstruct.NVIC_IRQChannelPreemptionPriority=1;//设置抢占优先级
NVIC_initstruct.NVIC_IRQChannelSubPriority=1;//设置响应优先级
NVIC_Init(&NVIC_initstruct);//初始化
三、EXIT外部中断项目实操
1.对射红外传感器计数
先看现象:
对射红外传感器计数
硬件连线图:
工程文件:
可以看到我们要编写的是下图两个箭头指向的文件,然后将结果在OLED显示屏上显示出来。
C编程代码如下
Countsensor.c代码:
cpp
#include "stm32f10x.h" // Device header
#include "Delay.h"
uint16_t count;
//初始化
void Countsensor_init(){
// 1.开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//2.配置GPIO口
GPIO_InitTypeDef GPIO_initstruct;
GPIO_initstruct.GPIO_Mode=GPIO_Mode_IPU;
GPIO_initstruct.GPIO_Pin=GPIO_Pin_14;
GPIO_initstruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_initstruct);
//3.配置AFIO
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14); //AFIO打到GPIOB处的14号引脚
//4.配置EXTI
EXTI_InitTypeDef EXTI_initstruct;//定义EXIT结构体
EXTI_initstruct.EXTI_Line=EXTI_Line14;//选择中断线,这里选择外部中断14号线
EXTI_initstruct.EXTI_LineCmd=ENABLE;//设置上面选择好的中断线是否使用,进行使能操作
EXTI_initstruct.EXTI_Mode=EXTI_Mode_Interrupt;//配置模式,中断模式还是事件模式,这里选择中断模式
EXTI_initstruct.EXTI_Trigger=EXTI_Trigger_Falling;//选择触发方式,上升沿触发还是下降沿触发等等,这里选择下降沿触发
EXTI_Init(&EXTI_initstruct);//初始化
//5.配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置分组,可放到主函数去分
NVIC_InitTypeDef NVIC_initstruct;//定义结构体
NVIC_initstruct.NVIC_IRQChannel= EXTI15_10_IRQn ; //选择通道,EXTI通道EXTI15_10_IRQn
NVIC_initstruct.NVIC_IRQChannelCmd=ENABLE;//指定上面通道是否开启,同理
//由于当前只有一个中断,优先级可以根据当前分组随便设置,如果多个的话才涉及这个
NVIC_initstruct.NVIC_IRQChannelPreemptionPriority=1;//设置抢占优先级
NVIC_initstruct.NVIC_IRQChannelSubPriority=1;//设置响应优先级
NVIC_Init(&NVIC_initstruct);//初始化
}
//返回count的值
uint16_t count_get()
{
return count;
}
//中断函数,其函数的名字是固定的,参数为空
void EXTI15_10_IRQHandler(){
//中断标志位判断,这个中断是EXTI 10~15的,我们要的是EXTI14的,所以要去判断一下是不是当前需要的中断
if(EXTI_GetITStatus(EXTI_Line14)==SET){ //判断,返回值为set就表示确定当前没有错误是执行这个
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==0)//消抖
count++;
//程序进入到中断的时候会产生中断标志位,强制进入中断
//执行完成了之后要去清除中断的标志位,不然就会一直执行中断函数
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
Countsensor.h代码 :
cpp
#ifndef __COUNT_SENSOR_H
#define __COUNT_SENSOR_H
void Countsensor_init();
uint16_t count_get();
#endif // !__COUNT_
main.c代码:
cpp
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Countsensor.h"
int main(void)
{
Countsensor_init();
OLED_Init();
OLED_ShowString(1,1,"count:");
while(1){
OLED_ShowNum(1,7,count_get(),4);
Delay_ms(500);
}
}
2.选择编码器计数
还是一样,我们先看现象:
选择编码器读取
电路连接图:
工程文件:
C语言代码如下
Encode.c代码
cpp
#include "stm32f10x.h"
int16_t count;
int16_t turn;//转动方向
void Encode_init(){
// 1.开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//2.配置GPIO口
GPIO_InitTypeDef GPIO_initstruct;
GPIO_initstruct.GPIO_Mode=GPIO_Mode_IPU; //默认设置为高电平
GPIO_initstruct.GPIO_Pin=GPIO_Pin_1 | GPIO_Pin_0;
GPIO_initstruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_initstruct);
//3.配置AFIO
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);
//4.配置EXTI
EXTI_InitTypeDef EXTI_initstruct;
EXTI_initstruct.EXTI_Line=EXTI_Line1 | EXTI_Line0;//选择中断线
EXTI_initstruct.EXTI_LineCmd=ENABLE;//设置上面选择好的中断线是否使用
EXTI_initstruct.EXTI_Mode=EXTI_Mode_Interrupt;//配置模式,中断模式还是事件模式
EXTI_initstruct.EXTI_Trigger=EXTI_Trigger_Falling;//选择触发方式,上升沿触发还是下降沿触发等等
EXTI_Init(&EXTI_initstruct);//初始化
//5.配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置分组,可放到主函数去分
NVIC_InitTypeDef NVIC_initstruct;
NVIC_initstruct.NVIC_IRQChannel= EXTI0_IRQn ; //选择通道,EXTI通道EXTI15_10_IRQn
NVIC_initstruct.NVIC_IRQChannelCmd=ENABLE;//指定上面通道是否开启,同理
//由于当前只有一个中断,优先级可以根据当前分组随便设置,如果多个的话才涉及这个
NVIC_initstruct.NVIC_IRQChannelPreemptionPriority=1;//设置抢占优先级
NVIC_initstruct.NVIC_IRQChannelSubPriority=1;//设置响应优先级
NVIC_Init(&NVIC_initstruct);
NVIC_initstruct.NVIC_IRQChannel= EXTI1_IRQn ; //选择通道,EXTI通道EXTI15_10_IRQn
NVIC_initstruct.NVIC_IRQChannelCmd=ENABLE;//指定上面通道是否开启,同理
//由于当前只有一个中断,优先级可以根据当前分组随便设置,如果多个的话才涉及这个
NVIC_initstruct.NVIC_IRQChannelPreemptionPriority=1;//设置抢占优先级
NVIC_initstruct.NVIC_IRQChannelSubPriority=2;//设置响应优先级
NVIC_Init(&NVIC_initstruct);
}
//返回转动方向的值,0表示逆时针,1表示顺时针
int16_t Turn_get(){
return turn;
}
//返回数值的变化值
int16_t Encode_get(){
int16_t temp=count;
count=0;
return temp;
}
void EXTI0_IRQHandler(){
if(EXTI_GetITStatus(EXTI_Line0)==SET){
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0&&GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0){
count--;
turn=0; //0表示逆时针旋转
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
void EXTI1_IRQHandler(){
if(EXTI_GetITStatus(EXTI_Line1)==SET){
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0&&GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0){
count++;
turn=1; //1表示顺时针
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
Encode.h代码:
cpp
#ifndef __ENCODE_H
#define __ENCODE_H
void Encode_init();
int16_t Encode_get();
int16_t Turn_get();
#endif // !__ENCODE_H
main.c代码:
cpp
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encode.h"
int16_t num;
int main(void)
{
OLED_Init();
Encode_init();
OLED_ShowString(1,1,"time:");
while(1){
num+=Encode_get();
OLED_ShowSignedNum(1,6,num,4);
OLED_ShowSignedNum(2,1,Turn_get(),1);
}
}
相关的问题可查看:问题详情 (jiangxiekeji.com)
以上就是本期的全部内容了,我们下次见!
今日壁纸: