极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 94129|回复: 17

解决旋转编码抖动的方案

[复制链接]
发表于 2017-1-15 15:56:14 | 显示全部楼层 |阅读模式
本帖最后由 吃樱桃不吐胡 于 2017-1-15 16:06 编辑

本人菜鸟一枚,虽然注册论坛账号很长时间了,但一般都是看的多,还是第一次在arduino版块发帖。
这段时间对diy时钟产生了很大兴趣,设想的方案是旋转编码器EC11来一键调整时间。编程过程中发现抖动问题很难消除,按论坛上方法折腾了好久,但效果都不是很理想,昨天一个朋友发给我一个方案,试了一下,果然十分有效,所以发上来,供大家参考。
其实这个方案原作者在2012年就已经在别的论坛上发出了,不过原作者用的是单片机,所以大家在网上找的时候可能容易忽略,原程序如下:
  1. void interrupt main_int(void)
  2. {  
  3.   if(RBIF)
  4.     {
  5.        if(int_nu==0 && KEY_A==0)   //第一次中断,并且A相是下降沿
  6.        {
  7.             flag=0;
  8.             if(KEY_B)    flag=1;  //根据B相,设定标志
  9.             int_nu=1;             //中断计数
  10.        }
  11.       if(int_nu && KEY_A)      //第二次中断,并且A相是上升沿
  12.        {
  13.          if(KEY_B==0 && flag==1) --power_m;
  14.          if(KEY_B && flag==0)    ++power_m;
  15.          int_nu=0;               //中断计数复位,准备下一次
  16.        }
  17.   RBIF=0;     
  18.     }
复制代码

仔细学习了一下,发现作者的方法真的很巧妙,大家用旋转编码器判断方向的时候,经常只用了一个触发沿,作者同时使用了两个,一个下降沿,然后在上升沿再检查一遍,如果两次结果一样,则输出数据,否则放弃。
按照作者思路,我编制了arduino下的程序,另外本人比较懒,上次无疑中发现了MIXLY这个编程神器,所以现在基本上编程都用的mixly,下面的程序也是mixly自动生成的:
  1. #include <PinChangeInt.h>
  2. long num;
  3. long count;
  4. long encoderPinA;
  5. long encoderPinB;
  6. long int_num;
  7. boolean CW_1;   // 转向判据1
  8. boolean CW_2;   // 转向判据2
  9. int mixly_digitalRead(uint8_t pin) {
  10.   pinMode(pin, INPUT);
  11.   return digitalRead(pin);
  12. }

  13. void attachPinInterrupt_fun_encoderPinA() {
  14.   if (int_num == 0 && mixly_digitalRead(encoderPinA) == LOW) {
  15.     CW_1 = mixly_digitalRead(encoderPinB);
  16.     int_num = 1;
  17.   }

  18.   if (int_num == 1 && mixly_digitalRead(encoderPinA) == HIGH) {
  19.     CW_2 = !mixly_digitalRead(encoderPinB);
  20.     if (CW_1 == true && CW_2 == true) {
  21.       count = count + 1;
  22.     }

  23.     if (CW_1 == false && CW_2 == false) {
  24.       count = count - 1;
  25.     }
  26.     int_num = 0;
  27.   }
  28. }

  29. void setup()
  30. {
  31.   num = 0;
  32.   count = 0;
  33.   encoderPinA = 3;
  34.   encoderPinB = 4;
  35.   int_num = 0;
  36.   CW_1 = 0;
  37.   CW_2 = 0;
  38.   pinMode(encoderPinA, INPUT);
  39.   PCintPort::attachInterrupt(encoderPinA,attachPinInterrupt_fun_encoderPinA,CHANGE);
  40.   Serial.begin(9600);
  41. }

  42. void loop()
  43. {
  44.   while (num != count) {
  45.     num = count;
  46.     Serial.println(num);
  47.   }
  48. }
复制代码

自动生成的代码有点傻,可能读起来比较麻烦,我把mixly文件放在附件里,大家用mixly看,就很容易了。
经测试,输出非常稳定,没有一点抖动。
另外受这个方案启发,我发现按键防抖用这种双触发方式效果也很好,EC11+DS3231一键调时间的程序也已经编好,一起发上来。
  1. #include <DS3231.h>
  2. #include <Wire.h>
  3. #include <PinChangeInt.h>
  4. long settime;
  5. long hour;
  6. long minute;
  7. long second;
  8. long TEMP_hour;
  9. long TEMP_minute;
  10. long TEMP_second;
  11. boolean h12;
  12. boolean pm;
  13. long num;
  14. long count;
  15. long switchPin;
  16. boolean switchPushed;
  17. long encoderPinA;
  18. long encoderPinB;
  19. long time;
  20. long int_num0;
  21. long int_num;
  22. boolean CW_1;
  23. boolean CW_2;
  24. DS3231 clock;
  25. int mixly_digitalRead(uint8_t pin) {
  26.   pinMode(pin, INPUT);
  27.   return digitalRead(pin);
  28. }

  29. void attachInterrupt_fun_switchPin() {
  30.   if (int_num0 == 0 && mixly_digitalRead(switchPin) == LOW) {
  31.     int_num0 = 1;
  32.   }
  33.   if (int_num0 == 1 && mixly_digitalRead(switchPin) == HIGH) {
  34.     switchPushed = true;
  35.     time = millis();
  36.     settime = settime + 1;
  37.     count = 0;
  38.     int_num0 = 0;
  39.   }
  40. }



  41. void attachPinInterrupt_fun_encoderPinA() {
  42.   if (switchPushed) {
  43.     if (int_num == 0 && mixly_digitalRead(encoderPinA) == LOW) {
  44.       CW_1 = mixly_digitalRead(encoderPinB);
  45.       int_num = 1;
  46.     }

  47.     if (int_num == 1 && mixly_digitalRead(encoderPinA) == HIGH) {
  48.       CW_2 = !mixly_digitalRead(encoderPinB);
  49.       if (CW_1 == true && CW_2 == true) {
  50.        count = count + 1;
  51.       }

  52.       if (CW_1 == false && CW_2 == false) {
  53.         count = count - 1;
  54.       }
  55.       int_num = 0;
  56.     }
  57.     time = millis();
  58.   }
  59. }
  60. void setup()
  61. {
  62.   settime = 0;
  63.   hour = clock.getHour(h12, pm);
  64.   minute = clock.getMinute();
  65.   second = clock.getSecond();
  66.   TEMP_hour = 0;
  67.   TEMP_minute = 0;
  68.   TEMP_second = 0;
  69.   h12 = false;
  70.   pm = true;
  71.   num = 0;
  72.   count = 0;
  73.   switchPin = 2;
  74.   switchPushed = false;
  75.   encoderPinA = 3;
  76.   encoderPinB = 4;
  77.   time = millis();
  78.   int_num0 = 0;
  79.   int_num = 0;
  80.   CW_1 = 0;
  81.   CW_2 = 0;
  82.   pinMode(switchPin, INPUT);
  83.   pinMode(encoderPinA, INPUT);
  84.   Wire.begin();
  85.   attachInterrupt(digitalPinToInterrupt(switchPin),attachInterrupt_fun_switchPin,CHANGE);
  86.   PCintPort::attachInterrupt(encoderPinA,attachPinInterrupt_fun_encoderPinA,CHANGE);
  87.   Serial.begin(9600);
  88. }

  89. void loop()
  90. {

  91.   if (TEMP_second != clock.getSecond() && !switchPushed) {
  92.     TEMP_hour = clock.getHour(h12, pm);
  93.     TEMP_minute = clock.getMinute();
  94.     TEMP_second = clock.getSecond();
  95.     Serial.println(String(TEMP_hour) + String(String(":") + String(String(TEMP_minute) + String(String(":") + String(TEMP_second)))));
  96.   }
  97.   if (switchPushed) {
  98.     if (millis() - time > 10000) {
  99.       switchPushed = false;
  100.       settime = 0;
  101.       Serial.println("Time OUT!");
  102.     }
  103.   }

  104.   if (switchPushed) {
  105.     switch (settime) {
  106.      case 1:
  107.       hour = TEMP_hour + count;
  108.       if (hour >= 24) {
  109.         hour = (long) (hour) % (long) (24);
  110.       }
  111.       if (hour < 0) {
  112.         hour = (long) (hour) % (long) (24) + 24;
  113.       }
  114.       if (num != hour) {
  115.         num = hour;
  116.         Serial.println(String("SEThour:") + String(num));
  117.       }
  118.       break;
  119.      case 2:
  120.       clock.setHour(hour);
  121.       minute = TEMP_minute + count;
  122.       if (minute >= 60) {
  123.         minute = (long) (minute) % (long) (60);
  124.       }

  125.       if (minute < 0) {
  126.         minute = (long) (minute) % (long) (60) + 60;
  127.       }
  128.       if (num != minute) {
  129.         num = minute;
  130.         Serial.println(String("SetMinute:  ") + String(num));
  131.       }
  132.       break;
  133.      case 3:
  134.       clock.setMinute(minute);
  135.       second = TEMP_second + count;
  136.       if (second >= 60) {
  137.         second = (long) (second) % (long) (60);
  138.       }
  139.       if (second < 0) {
  140.         second = (long) (second) % (long) (60) + 60;
  141.       }
  142.       if (num != second) {
  143.         num = second;
  144.         Serial.println(String("SetSecond:  ") + String(num));
  145.       }
  146.      break;
  147.      case 4:
  148.       clock.setSecond(second);
  149.       switchPushed = false;
  150.       settime = 0;
  151.       Serial.println("Set time successed!");
  152.       break;
  153.     }
  154.   }



  155. }
复制代码

忘了说连线方式了:其实都是最基本的联系,看代码也能知道:ec11的A联arduino的3,B联4,按键接的是是arduino的2,AB和按键都接10k的上拉。DS3231也是最基本连线:SCL接A5,SDA接A4.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复

使用道具 举报

发表于 2018-10-29 22:43:43 | 显示全部楼层
正需要,可惜刚注册积分不够,再等等吧
回复 支持 反对

使用道具 举报

发表于 2019-2-6 21:23:25 | 显示全部楼层
用一个中断的原因是要把另一个中断留给其他东西用,毕竟uno nano这类就只有两个外部中断,实测编码器AB相加电容接地再用一个中断很稳定
回复 支持 反对

使用道具 举报

发表于 2019-4-15 13:02:57 | 显示全部楼层
积分 不够啊  郁闷 下载不了
回复 支持 反对

使用道具 举报

发表于 2021-1-3 21:47:52 | 显示全部楼层
答复。研究中
回复 支持 反对

使用道具 举报

发表于 2021-1-30 06:29:02 | 显示全部楼层
我也想参考一下,不过积分不够!
回复 支持 反对

使用道具 举报

发表于 2021-2-9 22:14:19 | 显示全部楼层
Highnose 发表于 2021-2-4 13:59
void ChkBMQ(){
    BAKT=T;  


试了您的代码,很好用。
请教一个问题:BAKT和T分别是什么?
回复 支持 反对

使用道具 举报

发表于 2021-2-10 14:40:09 | 显示全部楼层
Highnose 发表于 2021-2-10 11:40
这是我程序里的一个变量,当时没去掉

谢谢,明白了
回复 支持 反对

使用道具 举报

发表于 2021-2-20 13:24:58 | 显示全部楼层


正想学习学习,谢谢了!
int mixly_digitalRead(uint8_t pin) {
  pinMode(pin, INPUT);
  return digitalRead(pin);
没有搞懂,这段是什么?
回复 支持 反对

使用道具 举报

发表于 2021-2-28 21:00:03 | 显示全部楼层
正需要,可惜刚注册积分不够,再等等吧
回复 支持 反对

使用道具 举报

发表于 2021-3-18 19:00:11 | 显示全部楼层
正需要,可惜刚注册积分不够,再等等吧
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-3-25 15:05:34 | 显示全部楼层
大卫1999 发表于 2021-2-20 13:24
正想学习学习,谢谢了!
int mixly_digitalRead(uint8_t pin) {
  pinMode(pin, INPUT);

就是个digitalRead,这是mixly自动生成的一段程序,很多冗余的,当时偷懒没修改。这个帖子只是提供给大家一个思路哈,理解第一段程序就可以编自己的程序了,思路很简单
回复 支持 反对

使用道具 举报

发表于 2021-9-8 07:57:36 | 显示全部楼层
积分 不够啊  郁闷 下载不了
回复 支持 反对

使用道具 举报

发表于 2021-9-8 15:32:15 | 显示全部楼层
正需要,可惜刚注册积分不够,再等等吧
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-4-20 15:50 , Processed in 0.044111 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

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