矿石收音机论坛

 找回密码
 加入会员

QQ登录

只需一步,快速开始

搜索
123
返回列表 发新帖
楼主: changjianguo

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

  [复制链接]
     
发表于 2019-8-20 14:57:19 | 显示全部楼层
忘了说一点,51单片机太慢。仿真上面这个得时候不能加消抖以及延时。不然测不正确。。。
回复 支持 反对

使用道具 举报

     
发表于 2019-8-20 16:01:03 | 显示全部楼层
双击元件修改属性,减少惯性质量
回复 支持 反对

使用道具 举报

     
发表于 2019-8-20 17:10:47 | 显示全部楼层
示波器调到DC
回复 支持 反对

使用道具 举报

     
发表于 2019-8-20 19:29:23 | 显示全部楼层
刚弄了个实物编码器测试了一下,发现需要选用克力比较大的,否则如果来回一格一格的扭(就是顺时针扭一格,逆时针扭一格,如此反复)会有没反应的现象。好像编码器没扭到位,触发不了。不过这样也证明了一点,如果编码器扭到一半不扭了,又给他转回去是不会触发的。。。其他旋转方式正常。。。
回复 支持 反对

使用道具 举报

     
发表于 2020-4-7 00:33:37 | 显示全部楼层
学习了。正在用编码器,居然在矿坛上找到了例程,真是意外。标记一下。等实物测试完成再来报告
回复 支持 反对

使用道具 举报

     
发表于 2020-4-7 08:03:07 | 显示全部楼层
本帖最后由 iffi123 于 2020-4-7 08:06 编辑

编码器最好还是加上硬件消抖,厂家的推荐电路

1.jpg

加上后就看不到抖动,然后不论中断还是查询都很可靠, 分别判断2相(A和B)相互电平关系即可判断旋转方向, 如果用查询,千万不要用while等到电平翻转,意外的话就死循环, 每次扫描,记录基准相(推荐A相)的电平,然后退出, 下次查询再检查上次电平状态,如果翻转,再判断另一相电平就可以判断出方向

中断的话,在中断程序里修改触发极性,也就是上升沿下降沿都触发

所以无论极慢旋转,还是快速旋转,或者来回旋转,都很灵敏,不会误判漏判,旋转一周都是30变化(30定位15脉冲)
回复 支持 反对

使用道具 举报

     
发表于 2020-4-8 11:26:51 | 显示全部楼层
这是以前我写的检测旋转编码器的代码,跟楼主的思想是一样的,
sbit FSA   = P3^7; //飞梭右旋
sbit FSB   = P3^5; //飞梭左旋
uint8_t fsss=0;//飞梭按钮状态
//检测飞梭
void feisuo_check(void)
{
        if(FSA==1 && FSB==1) {//飞梭没有旋转
                fsss=0;
                return;
        } else if(FSA==0 && FSB==1 && fsss==0) {
                fsss='R'; //可能是右旋
        } else if(FSA==1 && FSB==0 && fsss==0) {
                fsss='L'; //可能是左旋
        } else if(FSA==0 && FSB==0 && fsss!=0xFF) {
                if(fsss=='L') {
                        //处理左旋
                        fsss = 0xFF;
                } else if(fsss=='R') {
                        //处理右旋
                        fsss = 0xFF;
                }
        }
}
回复 支持 反对

使用道具 举报

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

使用道具 举报

     
发表于 2020-10-26 16:20:21 | 显示全部楼层
本帖最后由 iffi123 于 2020-10-26 16:22 编辑

谁又翻上来了。。。
回复 支持 反对

使用道具 举报

     
发表于 2020-10-26 20:11:22 | 显示全部楼层
本人已经快50岁了,刚开始学习软件编程,感觉有些吃力了,所以想请大家帮忙分析一下,自己写解码程序目前的水平还达不到,谢谢大家
回复 支持 反对

使用道具 举报

     
发表于 2020-11-24 16:09:18 | 显示全部楼层
不错!谢谢楼主分享!刚好要用到这一块。感谢!
回复 支持 反对

使用道具 举报

     
发表于 2021-6-27 02:42:42 | 显示全部楼层
本帖最后由 dbxzjq 于 2021-6-27 03:01 编辑
天天爱玛丽 发表于 2020-4-8 11:26
这是以前我写的检测旋转编码器的代码,跟楼主的思想是一样的,
sbit FSA   = P3^7; //飞梭右旋
sbit FSB    ...


还是这程序精简强大啊!简单几句,一个变量即可解决编码器的正确解码,只要扫描速度够,就非常准确,楼主的程序即使把脉冲的上升沿下降沿都检测了一次,但这程序存在严重的问题,正转时序11 01 00 10 11,如果把反转部分的代码取消掉后,只留下正转这部分的代码,哪么只要反转3格,就会触发一次正转输出return('R');原因是反转的时序11 10 00 01 11 10 00 01 11 10 00 01 11,只要反转三格,刚好可以让正转的检测代码正常进入,首先看
if(EncOld==0x00&&EncNow==0x02||EncOld==0x03&&EncNow==0x01)EncX=EncNow; 01 11 完美配合这一句EncOld==0x03&&EncNow==0x01说=01 11与11 01完全是一样的,因为只是同时比较两个值是不是相等,与实际的编码器转动时的先后顺序不一样啊,程序没了先后顺序的11才到01,与01才到11这个顺序

再当EncNow执行到00后就可以进入一次进行 EncOld=EncNow=00,把旧值更新掉,哪么后面的11 10也同样完全配合到这一句了
if(EncOld==0x00&&EncX==0x02&&EncNow==0x03||EncOld==0x03&&EncX==0x01&&EncNow==0x00)
EncOld==0x00&&EncX==0x02&&EncNow==0x03 00 10 11
这是严重的时序移动的错误
代码修改为如下可以解决此问题,不过这代码得非常高的扫描频率才可以在快速转动时不丢步,因为这代码得扫描多次才完成上升沿和下降沿的检测
        static Enc_IF0=0,Enc_IF1=0;
        static unsigned char Enc_Old,Enc_Ago=0,dir=0;
        unsigned char Enc_Now;
        if(Enc_Old != Enc_Now)
        {
                if(Enc_Old == 0x03 && Enc_Now == 0x01)
                        dir=1;
                else if(Enc_Old == 0x03 && Enc_Now == 0x02)
                        dir=0;
                if(dir)
                        {
                                if(Enc_Old == 0x00 && Enc_Now == 0x02 || Enc_Old == 0x03 && Enc_Now == 0x01) Enc_Ago = Enc_Now;
                                if(Enc_Old == 0x00 && Enc_Ago == 0x02 && Enc_Now == 0x03 || Enc_Old == 0x03 && Enc_Ago == 0x01 && Enc_Now == 0x00)
                                {
                                        Enc_Old = Enc_Now;
                                        if(EC11_Type == 1)
                                        {
                                                ScanResult =1;
                                        }
                                        else
                                        {       
                               
                                                if(Enc_IF1 == 0) Enc_IF1=1;
                                                else
                                                {
                                                        Enc_IF1=0;
                                                        ScanResult =1;
                                                }
                                        }
                       
                                }
                        }
                        else
                        {
                                if(Enc_Old == 0x00 && Enc_Now == 0x01 || Enc_Old == 0x03 && Enc_Now == 0x02) Enc_Ago = Enc_Now;
                                if(Enc_Old == 0x00 && Enc_Ago == 0x01 && Enc_Now == 0x03 || Enc_Old == 0x03 && Enc_Ago == 0x02 && Enc_Now == 0x00)
                                {
                                        Enc_Old = Enc_Now;
                                        if(EC11_Type == 1)
                                        {
                                                ScanResult =-1;
                                        }
                                        else
                                        {       
                               
                                                if(Enc_IF1 == 0) Enc_IF1=1;
                                                else
                                                {
                                                        Enc_IF1=0;
                                                        ScanResult =-1;
                                                }
                                        }
                       
                                }                               
                        }
        }

还是37楼的这代码牛逼,2次扫描即可以正确检测,而且变量就一个,非常精简,在A和B都在低电平状态时执行处理,果然高手
37#这是以前我写的检测旋转编码器的代码,跟楼主的思想是一样的,
sbit FSA   = P3^7; //飞梭右旋
sbit FSB   = P3^5; //飞梭左旋
uint8_t fsss=0;//飞梭按钮状态
//检测飞梭
void feisuo_check(void)
{
        if(FSA==1 && FSB==1) {//飞梭没有旋转
                fsss=0;
                return;
        } else if(FSA==0 && FSB==1 && fsss==0) {
                fsss='R'; //可能是右旋
        } else if(FSA==1 && FSB==0 && fsss==0) {
                fsss='L'; //可能是左旋
        } else if(FSA==0 && FSB==0 && fsss!=0xFF) {
                if(fsss=='L') {
                        //处理左旋
                        fsss = 0xFF;
                } else if(fsss=='R') {
                        //处理右旋
                        fsss = 0xFF;
                }
        }
}
回复 支持 反对

使用道具 举报

     
发表于 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

使用道具 举报

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

本版积分规则

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

蒙公网安备 15040402000005号

GMT+8, 2024-4-26 22:43

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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