标题: 一定位一脉冲的EC11旋转编码器最简洁的单片机驱动代码 [打印本页]

作者: lkc8210    时间: 2021-7-2 19:11
标题: 一定位一脉冲的EC11旋转编码器最简洁的单片机驱动代码
先上代碼為敬
  1.             if(!PinA && PinA_O && PinB) {
  2.                 Now++;
  3.             }PinA_O = PinA;               
  4.             if(!PinB && PinB_O && PinA) {
  5.                 Now--;
  6.             }PinB_O = PinB;        
复制代码
只有六行代碼就能用EC11對Now進行加減操作

为什么这样写呢?
上时序图
顺时针转:


逆时针转:


我们看到,当顺时针转时
Pin A会早于Pin B 转低电平,反之亦然

代码解读:
!PinA && PinA_O && PinB//当Pin A 为低电平而之前为高电平(即下降沿)并且Pin B为高电平
这一句就捕捉到顺时针转时序图中箭指着的那一刹那的情况
于是Now加1

!PinB && PinB_O && PinA//当Pin B 为低电平而之前为高电平(即下降沿)并且Pin A为高电平
这一句就捕捉到逆时针转时序图中箭指着的那一刹那的情况
于是Now减1



如果编码器不加电容消抖
就用软件消抖

  1.         if(ScanCount++ > 50) {        //其数值按单片机速度加减
  2.             ScanCount = 0;
  3.             if(PinA && !PinA_O && PinB) {
  4.                 Now++;
  5.             }PinA_O = PinA;               
  6.             if(PinB && !PinB_O && PinA) {
  7.                 Now--;
  8.             }PinB_O = PinB;                        
  9.             Now>9? Now = 0:_nop_();
  10.             Now<0? Now = 9:_nop_();
  11.         }
复制代码
现附上小应用实例一则
基如STC15F104E的EC11软串口六位密码检查程序
如发现顺逆时针相反,对调PinA/PinB 定义脚即可


上图.c文件51hei下载: Encoder3_PW_Lock.zip (1.7 KB, 下载次数: 467)


作者: haokey    时间: 2021-7-3 09:46
假如顺时针扭了一点但没到位之前逆时针扭回去了呢?
作者: lkc8210    时间: 2021-7-3 12:37
不考慮任何不正常操作
作者: zenghl    时间: 2021-7-3 14:49
代码简洁易懂
作者: dzbj    时间: 2021-7-3 17:04
haokey 发表于 2021-7-3 09:46
假如顺时针扭了一点但没到位之前逆时针扭回去了呢?

我觉得"ScanCount++"解决了你说的问题 就是说这之后得到的是一个确定值 要么转了要么没转 即便按你说的哆嗦一下 那也是下一次监测的事了

感觉这代码写的不错
作者: lkc8210    时间: 2021-7-3 23:41
dzbj 发表于 2021-7-3 17:04
我觉得"ScanCount++"解决了你说的问题 就是说这之后得到的是一个确定值 要么转了要么没转 即便按你说的哆 ...

然而,并不能
之前的回覆草率了

先上时序图



青色虚线之间是一次完整的脉冲
Now++是发生在第一条青色虚线之后少少的时间
但一定早于哆嗦X 和 哆嗦Y
而Now++或Now--的先要条件是: 之前高电平+现在低电平(下降沿发生)
无论哆嗦X 还是 哆嗦Y所产生的都是之前低电平+现在高电平(上升沿发生)
不满足Now++或Now--的先要条件

haokey兄所提出的问题,真正答案是:没事发生


而"ScanCount++"所做的事是加长扫描时间去消抖
再上时序图:



这是一个不干净的脉冲
如果没有"ScanCount++"



單片機會在黃色虚线那里判斷
結果在3,4,6判為成立

如果加上"ScanCount++"


就只会在2那里成立,避开了抖动

当然"ScanCount++ >"的数值要进行调节
如果数值太大如图



在2看到的就是B之前高电平+B现在低电平(B下降沿发生)+A高电平 = 逆时针转了!
                       
       
                                                        

作者: haokey    时间: 2021-7-7 11:25
lkc8210 发表于 2021-7-3 23:41
然而,并不能
之前的回覆草率了

能不能帮忙分析一下有没有其他不正常的情况?
作者: haokey    时间: 2021-7-7 12:20
lkc8210 发表于 2021-7-3 23:41
然而,并不能
之前的回覆草率了

探讨一下,假如我改成这样会怎样:
if(!PinA&&PinB)
{PinA_O= 1;}
if(!PinB&&PinA)
{PinB_O= 1;}
                               
                               
if(PinA_O&& PinA)
{
        PinA_O = 0;
        if(!PinB)
        {
                Now++;
        }
}
                               
if(PinB_O&& PinB)
{
        PinB_O= 0;
        if(!PinA)
        {
                Now--;
        }
}                                               
作者: lkc8210    时间: 2021-7-7 17:20
haokey 发表于 2021-7-7 12:20
探讨一下,假如我改成这样会怎样:
if(!PinA&&PinB)
{PinA_O= 1;}

这样改是为了什么?
作者: yxlitol    时间: 2021-8-25 19:52
小白请教:请问,IF最后面大括号外面的PinA_O = PinA是什么意思?
if(PinA && !PinA_O && PinB) {
                Now++;
            }PinA_O = PinA;


作者: yxlitol    时间: 2021-8-25 19:57
PinA_O是怎么定义为下降沿的啊?真心请教
作者: lovexiazai    时间: 2022-1-27 11:10
代码简洁
作者: cdhigh    时间: 2022-1-28 06:16
按中键时如果有移位,可能会误触发一次加减
作者: lkc8210    时间: 2022-1-28 13:54
yxlitol 发表于 2021-8-25 19:52
小白请教:请问,IF最后面大括号外面的PinA_O = PinA是什么意思?
if(PinA && !PinA_O && PinB) {
       ...

记录A脚电平
作者: lkc8210    时间: 2022-1-28 14:00
yxlitol 发表于 2021-8-25 19:57
PinA_O是怎么定义为下降沿的啊?真心请教

不是单单用PinA_O去看
要结合PinA
if(!PinA && PinA_O && PinB)的文字表达就是:
当"A脚现在是低电平" 与 "A脚前一次是高电平" 与 "B脚现在是高电平" 时成立

!PinA && PinA_O 这俩合起来才能判断是否下降沿
作者: lkc8210    时间: 2022-1-28 14:03
cdhigh 发表于 2022-1-28 06:16
按中键时如果有移位,可能会误触发一次加减

如果编码器太松就有可能
作者: xiexugang    时间: 2022-2-3 22:59
这个代码写的不错 简明有效 很实用
作者: wasonking    时间: 2022-2-5 01:12
代码简洁易懂
作者: bhjyqjs    时间: 2022-2-7 10:49
楼主代码及其精简,不错!
实际上,只检测A引脚的下降沿,由B引脚状态决定加减更高效。
经实验,无漏脉冲和多加多减现象。
void key()   //按键处理
{
      if((PinA_O== PinA)||(PinA ==1))  //不理会A引脚上升沿,低电平每个脉冲只做一次处理
         {
                 PinA_O= PinA;
                  return;
         }                                                         
       (PinB == 0) ? NUM-- : NUM++;   //根据引脚B的值,判断正反转
        PinA_O = PinA;                         //存储引脚A状态
}
采样A引脚下降沿时B引脚状态,同相减、异相加。
作者: lzzasd    时间: 2022-2-7 16:45
你这种处理方法我都试过了   用在EC11旋转编码器上勉强够用  也存在其他处理占用时间而掉脉冲的情况   但如果脉冲要达到1K的速度   就掉脉冲严重
EC11还是可以用下的   
作者: lkc8210    时间: 2022-2-7 17:20
bhjyqjs 发表于 2022-2-7 10:49
楼主代码及其精简,不错!
实际上,只检测A引脚的下降沿,由B引脚状态决定加减更高效。
经实验,无漏脉冲 ...

妙啊~
作者: cdhigh    时间: 2022-2-8 01:32
lkc8210 发表于 2022-1-28 14:03
如果编码器太松就有可能

为什么我知道是因为我以前就是用类似的算法,后来全部换成更复杂的算法了。
看应用,如果误加减影响不大则可以用,否则需要更鲁棒性的算法
作者: cn_zhx    时间: 2022-2-8 10:18
本帖最后由 cn_zhx 于 2022-2-8 14:43 编辑

其实,这里AB数据线产生的是格雷码,如果我们采集时采用判断AB两线的变化,即,A或B来下降沿时,作出4次判断,可以避免楼上所说的哆嗦,但是,要求采样频率要跟得上,可以采用加减速器的方法,
作者: lkc8210    时间: 2022-2-8 13:29
cn_zhx 发表于 2022-2-8 10:18
其实,这里AB数据线产生的是格雷码,如果我们采集时采用判断AB两线的变化,即,A或B来下降沿时,作出4次判 ...

什么是加减速器的方法?
可以详细说说吗?
作者: cn_zhx    时间: 2022-2-8 14:40
齿轮传动,小齿轮带大齿轮,用大齿轮带动编码器旋转,即可降低转速
作者: hewayking    时间: 2022-2-16 14:19
个人做法硬件加104电容  一个接外部中断一个接普通IO    中断后读普通IO高低     正转高或低   反转低或高控制++ --      可靠高效无敌  一般人我不告诉他
作者: guojizhou    时间: 2022-2-17 14:27
hewayking 发表于 2022-2-16 14:19
个人做法硬件加104电容  一个接外部中断一个接普通IO    中断后读普通IO高低     正转高或低   反转低或高 ...

难打别人不都是这样吗?
作者: datouyuan    时间: 2022-6-14 15:48
hewayking 发表于 2022-2-16 14:19
个人做法硬件加104电容  一个接外部中断一个接普通IO    中断后读普通IO高低     正转高或低   反转低或高 ...

我也认为这种方法更好。
http://www.51hei.com/bbs/dpj-221520-1.html
这是用十速51mcu做的直流电机定位功能,非常可靠准确,用于EC11要加104电容。
一定要用软件消抖,要增加2个全局bit变量用于存储AB引脚之前的状态,但这样增加了不少mcu开销。
作者: 微笑的小小    时间: 2022-11-8 17:51
这个代码我在STC15W408AS上调试通过。 为什么在STC8H1K08上不行,就是没有操作EC11旋转编码器,电脑串口    不断收到数据。                                                                                                                                                                                          
作者: 微笑的小小    时间: 2022-11-8 20:55
//00准双向  01推挽输出  10高阻输入 11开漏输出高阻输入
P3M1 = B0000_0000;
P3M0 = B1010_0000;

增加这个后就可以了
作者: 微笑的小小    时间: 2022-11-8 20:55
stc8h默认是高阻
作者: hi等你    时间: 2023-4-17 10:59
不用这么复杂,只要判断两个脚是11,然后延时毫秒多少。忘了,再判断是不是10,就说明

它旋转了,如果判断出来是01就是反方向旋转了,中断和定时器都不需要,主程序留在

等待的时候加一丢丢延时再执行就ok了
作者: 17735215296    时间: 2023-6-26 22:21
hi等你 发表于 2023-4-17 10:59
不用这么复杂,只要判断两个脚是11,然后延时毫秒多少。忘了,再判断是不是10,就说明

它旋转了,如果判 ...

[em17
作者: lkc8210    时间: 2023-6-27 11:30
hi等你 发表于 2023-4-17 10:59
不用这么复杂,只要判断两个脚是11,然后延时毫秒多少。忘了,再判断是不是10,就说明

它旋转了,如果判 ...

看到"延时毫秒"和"中断和定时器都不需要"
就知道你还没弄懂
作者: hi等你    时间: 2023-6-28 16:05
lkc8210 发表于 2023-6-27 11:30
看到"延时毫秒"和"中断和定时器都不需要"
就知道你还没弄懂



我就是不用定时器和中断,这个资源用在更重要的地方,只需要判断10和11就行,反转判断01和11.

已经成品用了好久了,手感也很好

作者: cnfloatleaf    时间: 2023-6-30 21:18
EC11不需要用延时,放在中断程序中,占用资源很少,用的很稳定。
作者: cnfloatleaf    时间: 2023-6-30 21:21
微笑的小小 发表于 2022-11-8 17:51
这个代码我在STC15W408AS上调试通过。 为什么在STC8H1K08上不行,就是没有操作EC11旋转编码器,电脑串口     ...

STC15W的引脚默认是准双向口,STC8H的引脚默认是高阻,初始化的时候需要设置为准双向口。
作者: joyb    时间: 2023-7-1 09:36
hi等你 发表于 2023-6-28 16:05
我就是不用定时器和中断,这个资源用在更重要的地方,只需要判断10和11就行,反转判断01和11.

已 ...

能否共享一下
作者: herui2128    时间: 2023-9-22 15:41
谢谢楼主分享,我用的STC15W408AS。用楼主的例程,采用两个外部中断来检测脉冲。能正常检测到正转和反转。但是旋转编码器的旋转速度稍微快点,就容易丢脉冲(脉冲速度快了,连成一片了),导致单片机采不到或者误采到B相。求一下速度快点的解决办法。count1和count2是正转和反转的脉冲计数,以后用于计算角度使用。
void exint0() interrupt 0       //INT0中断入口
{       
        if(!P32 && PinA_O && P33)
        {
                count1++;
               
        }       
        PinA_O = P32;
}
//外部中断服务程序1
void exint1() interrupt 2       //INT1中断入口
{
        if(!P33 && PinB_O && P32)
        {
                count2++;
        }
        PinB_O = P33;
}
作者: lkc8210    时间: 2023-9-22 16:14
herui2128 发表于 2023-9-22 15:41
谢谢楼主分享,我用的STC15W408AS。用楼主的例程,采用两个外部中断来检测脉冲。能正常检测到正转和反转。 ...

用一个外中断即可
  1. uint Delay_XD = 0;
  2. bit Encoder_EN = 1;
  3. void exint0() interrupt 0       //INT0中断入口
  4. {
  5.         if(Encoder_EN)
  6.         {
  7.                 if(!P33)
  8.                 {
  9.                         count1++;
  10.                 }else{
  11.                         count2++;
  12.                 }
  13.                 Encoder_EN = 0;
  14.         }
  15. }
  16. void main()
  17. {
  18.         //your code
  19.         while(1)
  20.         {
  21.                 //your code
  22.                 if(!Encoder_EN)
  23.                 {
  24.                         if(P32)
  25.                         {
  26.                                 if(Delay_XD++>=1000)//按主循环周期调节
  27.                                 {
  28.                                         Delay_XD = 0;
  29.                                         Encoder_EN = 1;
  30.                                 }
  31.                         }else{
  32.                                 Delay_XD = 0;
  33.                         }
  34.                 }
  35.         }
  36. }
复制代码




作者: wkman    时间: 2023-9-22 19:34
一般方法:先判断跳变(同时触发抖动计时连续判断),再判断另一个io的高低,
作者: 君工创    时间: 2023-9-22 23:34
我用一个外中断,使用正常。
作者: 刘佑红    时间: 2023-9-25 15:48
以下是我之前采用拿来主义得到的,只对判断后执行部分稍作修改,应用还不错。
/************************参数设置***************************/
void        canshu()                                //EC11旋转编码器一定位一脉冲
{
        static bit LastA = 0;                 //EC11旋转编码器的A引脚上一次的状态
        static bit LastB = 0;                //EC11旋转编码器的B引脚上一次的状态
               
  if(KA != LastA)                         //判断EC11旋转编码器A引脚是否等于上一次的状态
  {
    if(KA == 0)                                //EC11旋转编码器旋转后,判断KA是否是低电平状态
         {
                if(KB)                         //判断KB引脚当前状态,高电平则为正转
                        {num++;}                                                
                else
                        {num--;}                                                       
         }       
     LastA = KA;                         //更新编码器上一个状态暂存变量
     LastB = KB;                         //更新编码器上一个状态暂存变量
   }
}

现在看来LsaB变量似乎没有用,有空了去掉它试试。对于正反向不同的EC11,我是通过调换num变量的加减方向来解决的。
作者: zhxzhx    时间: 2023-9-28 09:49
haokey 发表于 2021-7-7 12:20
探讨一下,假如我改成这样会怎样:
if(!PinA&&PinB)
{PinA_O= 1;}

你这样是不行的,会重复的加或减
作者: zhxzhx    时间: 2023-9-28 09:57
  unsigned char key=0;
  static bit nextA;
  if (KA()!=nextA)
    {nextA=KA();
      if (nextA==1)
        {if (KB()==1) key=6;else key=5;}
      else
        {if (KB()==0) key=6;else key=5;}
    }
return key;
作者: diyage    时间: 2023-11-25 19:55
刘佑红 发表于 2023-9-25 15:48
以下是我之前采用拿来主义得到的,只对判断后执行部分稍作修改,应用还不错。
/************************ ...

我跟你用的一样,但是发现转快了丢码,慢很好
作者: hhp008    时间: 2023-11-29 14:12
下载学习一下,正准备用这个一定位一脉冲编码器。
作者: 刘佑红    时间: 2023-11-29 15:01
diyage 发表于 2023-11-25 19:55
我跟你用的一样,但是发现转快了丢码,慢很好

你说的这种情况,确实存在,也不能旋转过快,且在部分EC11上表现明显,即存在挑EC11现象,,后来我尝试将KB对地接的104电容换成105的就改善了,也不挑EC11了,你可试试。
作者: skyrusher    时间: 2023-11-29 16:40
很好的优化方法,学习了!
作者: zyhlove813    时间: 2023-12-14 17:37
楼主的代码很不错,我这边有另一种算法,也很精简EC11编码器基于运算解码的算法(原创),汇编后大小也基本一样,有一个算法甚至更小
作者: shennognshi    时间: 2024-1-19 14:17
完美的代码效果应该是转动一下只计数一下,不能多增也不能不增,最重要的是!不论转得多快还是多慢,都如此,那才是好代码!从这个标准来说,我试验了上10款作者自诩为非常不错的代码,均不合格!当然我自己也编不出合格的代码来,一度怀疑是我买的EC11编码器在硬件方面不合格,因为只有上拉电阻。没有消抖电容。但符合这样标准的编码器效果是普遍存在的,比如十几、二十年前的进口功放机,就采用了旋转编码器调整音量,那编码器的使用效果才是我追求的,现在我的一个项目卡在编码器上,怎么都不好用,肯定采用中断来实现,还没找到合适的代码,下一步寄加消抖电容看看谁的代码最理想,再来汇报。
作者: nearsea    时间: 2024-1-19 14:51
这东西还是需要用状态机来写,只要描述好正常状态迁移的关系,异常状态处理,硬件上消抖不消抖都是可以处理好的。
作者: cooleaf    时间: 2024-1-19 14:56
要想多快都不丢步(相对哈,快到中断处理都来不及不算哈),必须在中断里来处理。这种中断+轮询判断的方式,依然会丢步的。
作者: shennognshi    时间: 2024-1-26 16:36
之前测试很多代码,效果均不理想,表现为正转、反转有误判,从而导致计数不对。我想到的办法是得到方向数据后(比如正转定义为1,反转定义为0),先修正方向,再结合中断和方向二者,决定是否累加或累减计数值。方案是建立一个数组,包括10个元素,用来存储获得的方向,任何时刻记录的是最近十次获得的方向数据,对数组的10个元素进行累加,和超过5时,强制为正方向,反之为负方向。实测效果有所改善,计数值绝不会忽大忽小、重复出现等情况了。遗憾的是如果你反复正转、反转1下,那计数还是会有问题,即不会保持不变。但这种使用场景很少见吧?真要微调,你多转几下,再反过来多转几下就行。有人会说,实际卡顿感与计数反映会不同步吧?理论上讲是这样,但单片机速度很快,实际感觉不到不同步。
作者: hewayking    时间: 2024-7-2 17:06
个人做法是a下降沿中断     中断后   判断b高低   确定正反转
作者: shennognshi    时间: 2024-9-20 15:54
hewayking 发表于 2024-7-2 17:06
个人做法是a下降沿中断     中断后   判断b高低   确定正反转

试了,很棒!史上最简洁的代码。
作者: zyhlove813    时间: 2024-9-27 08:22
http://www.51hei.com/bbs/dpj-233399-1.html
这个也是很简洁,又新颖的EC11驱动
作者: w645053932    时间: 2025-1-16 12:31
bhjyqjs 发表于 2022-2-7 10:49
楼主代码及其精简,不错!
实际上,只检测A引脚的下降沿,由B引脚状态决定加减更高效。
经实验,无漏脉冲 ...

高手啊!
作者: youlinys    时间: 2025-2-15 15:21
相当简洁的代码。需要时间消化。
作者: KJSD    时间: 2025-6-4 18:49
有在PIC单片机上使用编码开关的程序代码,小白想借鉴下





欢迎光临 (http://www.51hei.com/bbs/) Powered by Discuz! X3.1