矿石收音机论坛

 找回密码
 加入会员

QQ登录

只需一步,快速开始

搜索
查看: 45211|回复: 42

非常稳定的旋转编码器解码程序(C51源代码)

  [复制链接]
     
发表于 2018-4-5 20:35:36 | 显示全部楼层 |阅读模式
    在网上下载过很多种编码器解码程序,使用后感觉都不够稳定,特别是旋转速度稍快时,经常会出现错误解码。为此,经过分析编码器输出波形特点,结合其它解码程序的优点,编写如下代码,不用中断,也可以不用定时器。经过一段时间使用,效果很好,不论旋转速度快慢,都不会出现错误解码,比较稳定。
    此程序在STC15W408AS单片机上调试通过,理论上其它C51单片机也可以;函数可以在循环中调用,也可在定时器中调用(定时器中调用时间间隔最好小于1毫秒,否则旋转快了会丢码);支持一定位一脉冲、两定位一脉冲的编码器解码,函数中可设置编码器类型;PINA和PINB是编码器两个引脚连接的IO口。编码器静止时函数返回0, 正转(向右转)返回单字符'R',反转(向左转)返回单字符'L',也可根据需要自行更改。

调用示例:
#include<reg51.h>
unsigned char Encoder(void);
sbit PINA=P1^1;
sbit PINB=P1^2;
void main(void)
{
    unsigned char enc,x=0,y=0;
    while(1)
    {
       enc=Encoder();    //扫描编码器,取得返回值
       if(enc=='R')x++;  //正转x累加
       if(enc=='L')y++;  //反转y累加
       //以下为显示或数据处理

    }
}


/************************************************************************************************
    函数名称:Encoder
    函数功能:编码器旋转的扫描及处理
    入口参数:无
    出口参数:char型    0-无旋转   'R'-正转(向右转)   'L'-反转(向左转)
************************************************************************************************/
unsigned char Encoder(void)
{
    static bit Enc0=0,Enc1=0;
    static unsigned char EncOld,EncX=0;
    unsigned char EncNow;
    bit EncType=0; //设置编码器类型: 0为一定位一脉冲,1为两定位一脉冲

    PINA=1;        //PINA置高电平
    PINB=1;        //PINB置高电平
    if(Enc0==0)EncOld=(PINA?0x02:0x00)+(PINB?0x01:0x00),Enc0=1;  //记住初次调用时编码器的状态
    EncNow=(PINA?0x02:0x00)+(PINB?0x01:0x00);        //根据两个IO当前状态组合成16进制的0x00|0x01|0x02|0x03
    if(EncNow==EncOld)return(0);                                //如果新数据和原来的数据一样(没有转动)就直接返回0

    if(EncOld==0x00&&EncNow==0x02||EncOld==0x03&&EncNow==0x01)EncX=EncNow;         //00-10|11-01
    if(EncOld==0x00&&EncX==0x02&&EncNow==0x03||EncOld==0x03&&EncX==0x01&&EncNow==0x00)//00-10-11|11-01-00右转
    {
        EncOld=EncNow,EncX=0;
        if(EncType==1)return('R');   //两定位一脉冲
        else       //一定位一脉冲
        {
            if(Enc1==0)Enc1=1;
            else
            {
                //Delayms(60);       //延时降低旋转灵敏度(不能用在定时器中)
                Enc1=0;
                return('R');
            }
        }
    }

    if(EncOld==0x00&&EncNow==0x01||EncOld==0x03&&EncNow==0x02)EncX=EncNow;         //00-01|11-10
    if(EncOld==0x00&&EncX==0x01&&EncNow==0x03||EncOld==0x03&&EncX==0x02&&EncNow==0x00)//00-01-11|11-10-00左转
    {
        EncOld=EncNow,EncX=0;
        if(EncType==1)return('L');   //两定位一脉冲
        else           //一定位一脉冲
        {
            if(Enc1==0)Enc1=1;
            else
            {
                //Delayms(60);       //延时降低旋转灵敏度(不能用在定时器中)
                Enc1=0;
                return('L');
            }
        }
    }
    return(0);           //没有正确解码返回0
}


评分

1

查看全部评分

     
发表于 2020-10-26 13:34:31 | 显示全部楼层
    楼主的编码器解码程序,我在实际制作中采用了,芯片用的也是STC15W408,优点是编码器不论旋转速度快慢,都不会出现错误解码。
    但是有一个问题,就是有时候系统上电后,转动编码器系统没有任何反应,即解码程序没有返回有用值,此时只要系统重新上电(即断电重启)就能恢复正常,这个问题有时使用一天也不会出现,有时一开机就出现,有时测试10次都不会出现,有时测试10次会出现一两次,只要重启就可以恢复正常。
    使用其他的解码程序,不会出现这个问题,但是会出现丢码、误码的情况。
    这个问题很是困扰,不知楼主在长期使用中出现过这样的问题吗?
回复 支持 2 反对 0

使用道具 举报

     
发表于 2021-7-4 20:49:47 | 显示全部楼层
本帖最后由 dbxzjq 于 2021-7-4 21:43 编辑

通过楼上的代码改写出支持一定位一脉冲和两定位一脉冲,经过测试,放进1毫秒的定时器中断服务执行,非常好使
分别测量了一个EC11 20格的一定位一脉冲和EC16 30格,两定位一脉冲,都可以完美操作,代码最好不要放主循环,如果主循环
无法保证少于1毫秒扫描一次,扭快就会丢步,放定时器中断,也得必须少于1毫秒中断一次进行扫描
IO均初始化为高电平,准确双向口,或其它的输入口类型,按单片机的IO类型选择,是否要外接上拉电阻
sbit Enc_A   = P3^7; //Encoder右旋
sbit Enc_S   = P3^6; //Encoder按键
sbit Enc_B   = P3^5; //Encoder左旋
unsigned char Enc_Type=0; //Enc_Type=0 一定位一脉冲,=1两定位一脉冲
//函数返回值  0:无动作; 1:正转;  -1:反转; 2:只按下按键; 3:按下按键同时正转; -3:按下按键同时反转动;
char Encoder_Scan()  
{
        char ScanResult = 0;    //返回编码器扫描结果,用于分析编码器的动作
                                         // 0:无动作;      1:正转;           -1:反转;  
                                         // 2:只按下按键;    3:按下按键同时正转   -3:按下按键同时反转动
        static unsigned char dir=0; //编码器状态

        if(Enc_Type == 0) //一定位一脉冲
        {
                if(Enc_A == 1 && Enc_B == 1) dir = 0; //一定位一脉冲不动作时都只有一种状态,即只要卡在定位的状态A和B都为高电平
                else if(End_A ==0 && Enc_B==1 && dir ==0) dir ='R'; //A和B不相等时证明已经被扭动,通过A和B对应的电平判断出方向,此为第一次扫描
                else if(End_A ==1 && Enc_B==0 && dir ==0) dir ='L';
                else if(End_A ==0 && Enc_B==0 && dir !=0xff)  //当扭到A和B都在低电平状态时执行处理相应的方向,此为第二次扫描
                {
                        if(dir =='L')
                        {
                                ScanResult = -1;
                                dir=0xff;
                        }else if(dir =='R')
                        {
                                ScanResult = 1;
                                dir=0xff;
                        }
                       
                }
        }else if(EC11_Type == 1) //两定位一脉冲
        {        //两定位一脉冲的,定位卡,间隔的定位卡位上的A和B电平是有两种电平状态的,一格A和B都为高电平,一格A和B都为低电平
                if(Enc_A == 1 && Enc_B == 1) dir = 1;   //两定位一脉冲编码的当前定位状态1,A和B同时都为高电平
                else if(Enc_A == 0 && Enc_B == 0) dir = 0;  //两定位一脉冲编码的的当前定位状态2,A和B同时都为高电平
                else if((Enc_A ==0 && Enc_B==1 && dir==1)||(Enc_A ==1 && Enc_B==0 && dir==0)) dir = 'R'; //对应两种定位电平判断方向
                else if((Enc_A ==1 && Enc_B==0 && dir==1)||(Enc_A ==0 && Enc_B==1 && dir==0)) dir = 'L'; //对应两种定位电平判断方向
                else if(dir != 0xFF ) //由于一格只能半个脉冲变化,不能像一定位一脉冲哪样,两状态扫描才进入处理确定方向
                {
                        if(dir =='L')
                        {
                                ScanResult = -1;
                                dir=0xFF;
                        }else if(dir =='R')
                        {
                                ScanResult = 1;
                                dir=0xFF;
                        }
                                               
                }
        }
        if(Enc_S == 0) //按键被按下
        {
                if(ScanResult == 0) ScanResult = 2; //只按下了按键
                else if(ScanResult == 1) ScanResult = 3; //按下按键同时正转
                else if(ScanResult == -1) ScanResult = -3; //按下按键同时反转
        }
         return ScanResult;
}

通过扫描得到编码器上的动作返回值,进行下一步的动作分析处理
按键还得应该加入消抖计数,对于按键还可以进一步处理单击,双击,短按,长按等处理
回复 支持 1 反对 0

使用道具 举报

发表于 2018-4-5 20:47:16 | 显示全部楼层
好段子,在线学习了!
回复 支持 反对

使用道具 举报

     
发表于 2018-4-6 11:30:33 | 显示全部楼层
感谢楼主分享~~~
回复 支持 反对

使用道具 举报

     
发表于 2018-4-7 15:24:19 | 显示全部楼层
感谢楼主无私奉献,我也对此很感兴趣,希望能贴出电路原理图让大家学习下
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2018-4-7 17:28:42 | 显示全部楼层
jinanyuanyue 发表于 2018-4-7 15:24
感谢楼主无私奉献,我也对此很感兴趣,希望能贴出电路原理图让大家学习下

旋转编码器接线图:
20180407172204.jpg
回复 支持 反对

使用道具 举报

     
发表于 2018-4-8 11:17:05 | 显示全部楼层
感谢楼主分享,网上编码开关程序试过,不好用啊,试试楼主的
回复 支持 反对

使用道具 举报

     
发表于 2018-4-10 17:53:32 | 显示全部楼层
多年前做程序猿时,用到过旋转编码器
回复 支持 反对

使用道具 举报

     
发表于 2018-8-25 12:19:40 | 显示全部楼层
不错的设计,楼主能给个判断旋转方向的程序吗?先谢谢了。
回复 支持 反对

使用道具 举报

     
发表于 2018-8-25 12:35:41 | 显示全部楼层
51速度太慢了,工业上快速运转的Quadrature encoder还是要用带专用接口的单片机。
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2018-8-26 09:43:27 | 显示全部楼层
tjwjd 发表于 2018-8-25 12:19
不错的设计,楼主能给个判断旋转方向的程序吗?先谢谢了。

这个就是判断旋转方向的,返回字符'R'表示向右旋转了,返回字符'L'表示向左旋转了,返回0表示没旋转
回复 支持 反对

使用道具 举报

     
发表于 2018-8-26 22:26:11 | 显示全部楼层
changjianguo 发表于 2018-8-26 09:43
这个就是判断旋转方向的,返回字符'R'表示向右旋转了,返回字符'L'表示向左旋转了,返回0表示没旋转

谢谢了,这几天用PLC做个水闸的程序,正好参考一下。
回复 支持 反对

使用道具 举报

     
发表于 2019-2-19 17:08:59 | 显示全部楼层
http://www.crystalradio.cn/forum ... p;extra=&page=1
楼主,箱自制一个DDS信号发生器,想把按钮调节,改成旋转编码开关调节。带按钮的那种,按下按钮调节块一些。
我不会这个,能帮忙吗?
图片传不上。请移步链接
回复 支持 反对

使用道具 举报

     
发表于 2019-2-23 22:30:16 | 显示全部楼层
这个早没看到,有空一定试一下。谢谢楼主分享
回复 支持 反对

使用道具 举报

     
发表于 2019-2-24 21:49:48 | 显示全部楼层
支持一下,顺便mark,正好想做一个用编码器的实验项目,能用的上这段代码
回复 支持 反对

使用道具 举报

     
发表于 2019-2-25 03:10:11 来自手机 | 显示全部楼层
学习了,不错,谢谢分享,有时间实践一下。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 加入会员

本版积分规则

小黑屋|手机版|矿石收音机 ( 蒙ICP备05000029号-1 )

蒙公网安备 15040402000005号

GMT+8, 2024-4-26 07:51

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表