EC11的中断实验——NVIC&EXTI

本文隶属于《GD32 示波器项目软件部分重难点及相关疑问解决》

EC11的中断实验——NVIC&EXTI

1 EC11编码器引脚

实物图

封装图

引脚介绍:

  • A、B、C:编码器数据引脚,C为公共端,A、B为信号端。
  • D、E:按键引脚,相当于按键开关。
  • 6,7:固定编码器引脚,不接入电路。

2, 模块原理

公共端接线:

简单的原理如上图所示,C作为公共端,我们可以根据实际的需求来设置,一般情况下我们都接地,这样方便我们进行代码调试。

如何区分方向:

AB为数据端,AB转动存在相位差,所以通过AB的关系来获取编码器的转向信息,一般来说A在前B在后是正转即顺时针(这里的前后是指电平的变化前后)。

一般来说,正转时A相位快于B,反转时A相位慢于B这样我们就可以通过A的跳变沿结合B的电平来确定正转与反转了。

与电位器的区别

相比较于电位器读取AD值,编码器的特点在于可以不停的旋转,而且只有发生旋转的时候,AB才会发生改变。

编码器的档数(或者说定位):

正常的EC11编码器有15档、20档等,这里的档可以理解为,我们转动一圈,开关内部闭合与打开多少次,由原理图可知,我们编码器的内部实际上是一个单刀开关,转动一次之后,开关改变一次状态,A与B的电平就发生翻转。

输出脉冲数

EC11按旋转的输出动作可以分为两种:

  • 一种是两定位一脉冲(即每个定位是90°),即转两格A、B对C端输出一个完整脉冲(转一格就只是由低电平->高电平或由高电平->低电平);
  • 另一种一定位一脉冲(即每个定位是180°),即就是转一格,A、B对C端输出一个完整脉冲。

大多数的编码器都是一定位一脉冲。

 

3. 两类应用电路——有上拉电阻&无上拉电阻

两类应用电路通用部分:

AB引脚其中一个接入外部中断,另一个接入普通的IO口,C引脚直接接地。

DE引脚其中一个接入普通IO口,另一个接地。

无上拉电阻:初始化的时候,IO要配置内部上拉电阻,否则无法编码(图中KEY_L, KEY_R 的GPIO应该配置为上拉输入模式);本试验原理图中为无上拉电阻的模式。

有上拉电阻:初始化的时候,IO不需要要配置内部上拉电阻,即配置普通模式(准双向、双向模式)即可。(图中R4、R5、R6非必要,可以不用接入电路)

 

4 GD32(STM32)平台实验与代码

GD32E230外部中断EXTI(中断/事件控制器)包括21个相互独立的边沿检测电路并且能够向处理器内核产生中断请求或唤醒事件。
EXTI有三种触发类型:
  • 上升沿触发
  • 下降沿触发
  • 任意沿触发
本次项目需要通过板载EC11旋转编码器来点亮&熄灭LED灯。正转一格点亮LED灯,反转一格熄灭LED灯。

图 本项目EC11接线

从图中可以看出,EC11并没有接上拉电阻,因此需要让GPIO自己配置为上拉输入。

4-1 旋转编码器原理——"旋转编码器"协议

旋转编码器一般有效的线是3根线,一根按键,两根用于输出信号判断顺时针和逆时针。我们分别称之为K1,K2,K3。

除去K3(按键),另外两根在旋转的时候的波形应该是这样的。

 

从图中可得抽象的"编码器”协议——

1. 由两根信号线组成。

2. 当旋转到一定位置的时候,某根信号电平由默认电平变为动作电平,从而产生上升沿或者下降沿。
 (注:可计数单位时间上升沿/下降沿次数,从而实现速度的测量)

3. 两信号线形状相同,但是相位相差一定角度,从而可以判断方向(正转/反转)。

 

4-2 EC11 的编码原理

EC11编码器原理上相当于两个“按键"开关,只是按键的方式很特殊——是通过旋转实现的;两个开关之间被”按“下去的时刻有相位差。

图 编码器等效原理图

而A和B上的信号在时间刻度上如下所示:

图 A、B两相电平图

关键点:

  • 当向着箭头方向旋转的时候我们发现:A在下降沿的时刻B是高电平,A在上升沿的时刻B是低电平,
    这可以表述为(A 下 B 1,A 上 B 0),记作表述1;
  • 当方向取图中反向的时候,可以看出当A在下降沿的时刻B是低电平,A在上升沿的时刻B是高电平,
    这可以表述为(A 下 B 0,A 上 B 1),记作表述2

通过区别 表述1 和 表述2 ,就可以区别编码器的转向。

 

4-3 GD32(STM32)防抖程序(软件消抖):

EC11没防抖几乎不能工作, 因为其抖动很厉害。在中断里写上串口打印语句,可以看到短时间内即使没有转动编码器,也会进入中断。

因此我们可以锁定A作为跳变沿检测中断引脚,在中断中记录下同一方向两个不同组合的状态,当完成一个【完整的表述】后我们就改变一次用于确定最终是否旋转的全局变量。

此处复习一下前面的内容:

A在下降沿的时刻B是高电平,A在上升沿的时刻B是低电平,这可以表述为(A 下 B 1,A 上 B 0),记作表述1;

A在下降沿的时刻B是低电平,A在上升沿的时刻B是高电平,这可以表述为(A 下 B 0,A 上 B 1),记作表述2

所谓完整的表述,就是不仅仅检查A下降沿B的电平,也检查A上升沿B的电平。

例如,我们定义一个全局变量flag用于告诉主函数是否发生了旋钮旋转;定义一个全局变量val,当正向旋转编码器发生一次咔哒声后,我们将val++;当反向旋转编码器发生一次咔哒声后,我们将val--操作一次。

这样为了记录下完整的表述1和表述2,我们定义两个全局变量flag1和flag2,当没有进入中断的时候,默认我们将其初始化为0,当发生了属性为“下1”的中断后,我们将flag1=1;当发生了属性为“下0”的操作后我们记录flag2=1;这样下次再次进入中断后那结果只能是对应的“上0”或“上1”,这个时候我们将对val进行操作,同时将flag1和flag2清零,并记flag为1,这样我们可以在主函数识别出刚才发生了旋转操作,并在主函数中对其清零。

好了,思路有了,上程序的环节到了:

先上没消抖的程序——仅仅检查A下降沿(几乎无法使用)

if(RESET != exti_interrupt_flag_get(EXTI_4))  //A相下降沿触发一次
{
    if((gpio_input_bit_get(GPIOB,GPIO_PIN_4) == RESET))//B相低电平,属于表述2
    {
        LED2(ON);
        LED3(OFF);
    }
    else//B相高电平,属于表述1
    {
        LED2(OFF);
        LED3(ON);
    }
    exti_interrupt_flag_clear(EXTI_4);
}
//不检查A相上升沿

再上消了抖的程序:

//全局变量

int flag1;
int flag2;
int val = 0;

void EXTI4_15_IRQHandler(void)
{
    //消抖处理(需要首先配置成双向触发)
	if(gpio_input_bit_get(PHASE_A_GPIO,PHASE_A_PIN) == RESET)//如果是下降沿
	{
		//判断状态
		if(gpio_input_bit_get(PHASE_B_GPIO,PHASE_B_PIN) == RESET)
		{
			flag1 = 1;
		}
		else if(gpio_input_bit_get(PHASE_B_GPIO,PHASE_B_PIN) == SET)
		{
			flag2 = 1;
		}
	}
	else //如果是上升沿 gpio_input_bit_get(PHASE_A_GPIO,PHASE_A_PIN) == SET
	{
		if((flag1 == 1)&&(gpio_input_bit_get(PHASE_B_GPIO,PHASE_B_PIN) == SET))
		{
			val++;
			LED2(ON);
			LED3(OFF);
			flag1 = 0;//用于下一次检测
			flag2 = 0;//附加的一句,程序运行到这里,说明flag2==1是无效的,将之清零,防止其干扰下一步结果。
		}
		else if((flag2 == 1)&&(gpio_input_bit_get(PHASE_B_GPIO,PHASE_B_PIN) == RESET))
		{
			val--;
			LED2(OFF);
			LED3(ON);
			flag2 = 0;
			flag1 = 0;
		}
		printf("val =%d\r\n",val);//调试
	}
}

其他:采样法

 

5 硬件消抖的评论区讨论

为了提高抗干扰能力(硬件消抖),可以在A和B引脚分别使用一个0.1uF电容接地。

1. 并联的电容应该小,或者串联一个小限流电阻

2. 电容容值的考虑

 

 

 

6 STC15平台实验与代码

使用STC15单片机为例,将A引脚接到P32,B引脚接到P33,开始外部中断1,选择下降沿触发,使用P32作为中断输入。

基本的时序:

工作流程是:旋转编码器\RightarrowP32口下降沿触发外部中断1\Rightarrow判断P33的极性\Rightarrow确定正反转

 
unsigned char num; 
 
void INT0_Init()
{
	IT0=1;    //下降沿触发
	IE0=0;    //清楚中断标志位
	EX0=1;    //打开外部中断请求
	EA=1;     //打开中断总开关
	
}
  
void INTO_Routine() interrupt 0
{
	if(P33) 		//逆时针
	{
		num--;
	}
	else			//顺时针
	{
		num++;
	}		
}

这里的顺时针和逆时针可由自己调试确定,毕竟不同的结构正反也不同。

值得一提的是,P32、P33引脚不是唯一的,只需要将A接入外部中断触发即可,P33则可以换到其他引脚使用。

STC15 软件消抖

一般是使用延时的办法,避开机械抖动的那段时间,从而达到滤波的目的,例如我们的独立按键,就经常使用软件滤波的办法。

消抖思路

①设置定时器中断,定时周期为1ms

②EC11进入一次外部中断,暂时关闭50ms(或者更小,例如20ms),并激活延时变量,使其开始计时。

③计时完成之后,清除中断标志位和延时变量,并重新使能外部中断。

 
unsigned int ec11_delay;
unsigned char ec11_num; 
 
void Timer0_Init(void)		//1毫秒@24.000MHz
{
	AUXR &= 0x7F;			//定时器时钟12T模式
	TMOD &= 0xF0;			//设置定时器模式
	TL0 = 0x30;				//设置定时初始值
	TH0 = 0xF8;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
	ET0=1;
	EA=1;
	
}
 
void INT0_Init()
{
	IT0=1;    //下降沿触发
	IE0=0;    //清楚中断标志位
	EX0=1;    //打开外部中断请求
	EA=1;     //打开中断总开关
	
}
 
 
void INTO_Routine() interrupt 0
{
	if(P33) 		//逆时针
	{
		ec11_num--;
	}
	else			//顺时针
	{
		ec11_num++;
	}
    EX0=0;//关闭外部中断
    ec11_delay=1;//激活延时		
}
  
 
void Timer0_Routinue() interrupt 1 //1ms
{
    if(ec11_delay)
    {
        if(++ec11_delay>=50)  //延时50ms
        {
            IE0=0;              //清楚外部中断标志位        
            EX0=1;              //重新使能外部中断
            ec11_delay=0;       //退出延时
        }
    }
}
    

 

 

关于 EC11的后续——做找羊加热台

找羊加热台的旋钮就是EC11实现的:其要求在C、E之间飞线链接:目的就是两者都接地。这样只要接C或者E就可以了

其余A、B、D引脚则是三个单掷开关的信号线。

参考资料

1. 关于旋转编码器(EC11)的使用(判断旋转方向,按键处理):https://blog.csdn.net/Simon13_11/article/details/136049719

2. 一篇文章带你了解——EC11编码器:https://blog.csdn.net/Xhw3f586/article/details/131183406

3.  EC11编码器高效驱动方法: https://bbs.21ic.com/icview-3325360-1-1.html

 

posted @ 2024-05-10 19:15  FBshark  阅读(157)  评论(0编辑  收藏  举报