【发布于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旋转编码器解码程序
bikey 密钥