一段简单的EC11旋转编码器解码程序

【发布于2019-02-18】最近更新:2022-03-07,请在页面尾部查看更新内容。

最近翻出来一个用于音量调节的旋转编码器,我以为这玩意儿不同方向旋转只会导通其中一个引脚。后来发现并不是这么回事,每旋转一个刻度会先后导通两只脚,你需要通过判断两只脚的信号顺序来判断正向旋转还是反向旋转。

通过对网上部分资料的学习,发现没有几个能符合实际的,大多数通过定时器来判断,这样的一个问题就是旋转速度过快时,定时器未能采集到信号导致误码,再就是定时器与原有的串口发送数据功能冲突。后来经过两天自己研究,通过死循环调用该函数采集信号,测试完美,无论旋转多快或者多慢都不会误码,由于是死循环,占用CPU较高,适合要求不高的场合,比如控制LED亮度,电机无极调速等等。

这是年前写的代码,年后竟然忘了变量是干嘛的,算了,大家复制粘贴稍微改改就能使。

//AT:正向引脚,BT:反向引脚,BTN:按下。
sbit AT = P1^2;
sbit BT = P1^3;
sbit BTN = P1^4;//这里无用
unsigned char BC,ZX;

void Delay100us()		//@11.0592MHz
{
	unsigned char i, j;

	_nop_();
	_nop_();
	i = 2;
	j = 15;
	do
	{
		while (--j);
	} while (--i);
}

void Test()					 
{
	if(AT==1 & BT==1){	
	        Delay100us();//延时100微秒,根据实际情况去掉或缩短延时时间或增加延时时间
		if(AT==1 & BT==1){
			BC = 0;
			ZX = 1;
		}
	}
	if(AT==0 & BT==0){	
                Delay100us();
		if(AT==0 & BT==0){
			BC = 0;
			ZX = 0;
		}
	}
	if(AT==1 & BC == 0){	
	        Delay100us();
		if(AT==1 & BC == 0){
			if(BT==0){	
			        Delay100us();
				if(BT==0){
					BC = 1;
					if(ZX==1){	
						SendString("-"); //串口发送数据,可以不用
					}
					else{	
						SendString("+"); 
					}
				}
			}
		}
	}
	if(AT==1 & BT==1){//这两个判断和前面那段一样的代码,只是为了确保稳定,实际去掉这一段也能正常使用。
		Delay100us();
		if(AT==1 & BT==1){
			BC = 0;
			ZX = 1;
		}
	}
	if(AT==0 & BT==0){
		Delay100us();
		if(AT==0 & BT==0){
			BC = 0;
			ZX = 0;
		}
	}
	if(BT==1 & BC == 0){
		Delay100us();
		if(BT==1 & BC == 0){
			if(AT==0){
				Delay100us();
				if(AT==0){
					BC = 1;
					if(ZX==0){		
						SendString("-"); 
					}
					else{	
						SendString("+"); 
					}
					
				}
			}
		}
	}	 
}

【2020年06月04日更新】

最近写个数控电源驱动,用到旋编,以上代码测试发现在旋编损坏(用久了的旋编不一定刚好卡在停止位)时会误触发,特地优化代码,放在中断里即可,代码如下。

unsigned char BC,ZX;//时隔两年,仍想不起当时写的这俩变量干嘛的,ZX应该是判断正向还是反向旋转的,BC完全想不起来。
void Encoder()
{
	if(AT==1 && BT==1)//AT为旋编A脚,BT为旋编B脚,旋编C脚接地。
	{
		BC = 0;
		ZX = 1;
	}
	if(AT==0 && BT==0)
	{
		BC = 0;
		ZX = 0;
	}
	if(AT==1 && BC == 0)
	{			
		if(BT==0)
		{
			BC = 1;
			if(ZX==1)
			{
				temp--;//反向--
			}
			else
			{
				temp++;//正向++
			}
		}			
		if(AT==0)
		{				
			BC = 1;
			if(ZX==0)
			{
				temp--;//反向--
			}
			else
			{
				temp++;//正向++
			}				
		}
	}
}

代码很简单,比网上一堆神秘莫测的代码简单多了,佩服自己瞎折腾出来的,将该函数放在10us-100us的中断里调用即可,如果放在主函数死循环里要加延时防抖,参考最顶上的代码。(垃圾代码编辑框缩进自动给去了)

【2020年11月18日更新】

闲来无事,再次研究旋编,没想到让我写出更精简的检测代码,放在10us的中断里调用即可。经测试误码率极低,除非特别快旋转,测试正常使用没有发现误码,理论上中断检测时间越快,就越不会出现误码,而且测试旋编用久了接触不良也能正常解码,短短几行代码,吊打全网的解码程序,看得懂的就知道为什么如此简单了。

当然,这里还是稍微说一下程序的思想,一般旋编转一格会输出以下四个信号:11 10 00 01 11(这个是回到下一个11了),同理反过来旋转就是11 01 00 10 11。

发现没有,正向和反向实际上是10和01的检测,于是就有了如下代码。

首先编码器锁定时AT BT一定会同时为1,就从11开始判断,如果第一时刻AT为0(此时为AT:BT为01),可以假设是正向(不同的编码器不同,也可能是反向),但这个信号有可能是接触不良抖了一下,先插个ZX标记,如果第二时刻BT为0(此时为AT:BT为00),就可以确定一定是正向旋转了,所以输出正向。实际上旋编的旋转过程并没有完成,还会有第三时刻AT:BT为10信号,最后回到第四时刻AT:BT为11信号。完整的解码程序会在第四时刻AT:BT为11时才响应,中途需要记录3个状态再加以判断,如果旋编用久了坏了接触不良,产生各种毛刺,基本上就无法解码了。再精简一点的会在第三时刻响应,这样容错就很高了,我之前的代码就是在这一时刻响应。而现在最新的程序仅仅在第二时刻就做出响应,程序更加精简,而且再烂的旋编都能有反应,不管你转到一半往回转还是各种花式旋转,至少都有响应,为自己的创新加个鸡腿,吊打全网不是吹的。

void EncodeScan()
{	
	static unsigned char BC,ZX=0,FX=0;//2022年03月07日勘误,如果把这3个变量放在函数内,请加上static作为静态变量,或者将这3个变量放在函数外作为全局变量。
	if(AT==1 && BT==1)        //AT和BT即旋编AB两脚
	{
		BC = 0;		  //保持
		ZX = 0;		  //正向标志
		FX = 0;		  //反向标志
	}
	if(AT==0 && FX == 0)
	{
		ZX = 1;
		if(BT==0 && BC == 0)
		{
			BC = 1;
			Forward();//正向旋转
		}
	}
	if(BT==0 && ZX == 0)
	{
		FX = 1;
		if(AT==0 && BC == 0)
		{
			BC = 1;
			Reverse();//反向旋转
		}
	}
}

【2022年03月07日更新】

最近从老五那里淘来了一些编码器,竟然是30定位15脉冲的半波ec11编码器,这种编码器相对于普通20定位20脉冲的全波ec11编码器没有什么本质区别,全波编码器A、B通道定位时一定在高电平11,转一格产生4个信号11 10 00 01 11(见上一个程序分析),半波编码器A、B通道定位时有可能在高电平11,也有可能在低电平00,正向转一格只产生2个信号11 01 00或00 10 11,反向旋转就是11 10 00或00 01 00,共4种情况,所以程序要分2步解码,话不多说直接上代码,还是放在中断内调用,中断速度看个人需求了,中断速度越快采样速度越快响应速度也越快,但滤波能力降低,如果需要过滤毛刺信号不要太快。

void EncodeScan()
{	
	static unsigned char HL,BC=1,ZX=0,FX=0;  //对比全波解码程序多了个HL变量用来区分初始状态为高电平或低电平,特别注意BC变量初始化为1,全波时不必指定,如果把这4个变量放在函数内,请加上static作为静态变量,或者将这4个变量放在函数外作为全局变量。
	if(HL==1)
			{
				if(AT==0 && FX == 0)			   //A通道超前置0时如果不是反向旋转标志
				{
					ZX = 1;						   //正向标志置1
					if(BT==0 && BC == 0)		   //B通道随后置0时如果没有保持使能
					{							   
						BC = 1;					   //保持使能防止多触发
						Forward();//正向旋转
					}
				}
				if(BT==0 && ZX == 0)			   //B通道超前置0时如果不是正向旋转标志
				{								   
					FX = 1;						   //反向标志置1
					if(AT==0 && BC == 0)		   //A通道随后置0时如果没有保持使能
					{							   
						BC = 1;					   //保持使能防止多触发
						Reverse();//反向旋转
					}
				}
			}
			else
			{
				if(AT==1 && FX == 0)			   //A通道超前置1时如果不是反向旋转标志
				{
					ZX = 1;						   //正向标志置1
					if(BT==1 && BC == 0)		   //B通道随后置1时如果没有保持使能
					{							   
						BC = 1;					   //保持使能防止多触发
						Forward();//正向旋转
					}
				}
				if(BT==1 && ZX == 0)			   //B通道超前置1时如果不是正向旋转标志
				{								   
					FX = 1;						   //反向标志置1
					if(AT==1 && BC == 0)		   //A通道随后置1时如果没有保持使能
					{							   
						BC = 1;					   //保持使能防止多触发
						Reverse();//反向旋转
					}
				} 
			}
			if(AT==1 && BT==1)				   //两个触点归1时检测下一个循环    相比较全波解码程序归1或归0判断放在旋转过程判断之后了,不要交换程序流程
			{
				HL = 1;		  //高电平定位
				BC = 0;		  //保持
				ZX = 0;		  //正向标志
				FX = 0;		  //反向标志
				
			}
			if(AT==0 && BT==0)				   //两个触点归0时检测下一个循环
			{
				HL = 0;		  //低电平定位
				BC = 0;		  //保持
				ZX = 0;		  //正向标志
				FX = 0;		  //反向标志		
			}
}


转载请注明来源:bikey 密钥 » 一段简单的EC11旋转编码器解码程序

评论 沙了个发

换个身份

取消评论