吃樱桃不吐胡 发表于 2017-1-15 15:56:14

解决旋转编码抖动的方案

本帖最后由 吃樱桃不吐胡 于 2017-1-15 16:06 编辑

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

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

void attachPinInterrupt_fun_encoderPinA() {
if (int_num == 0 && mixly_digitalRead(encoderPinA) == LOW) {
    CW_1 = mixly_digitalRead(encoderPinB);
    int_num = 1;
}

if (int_num == 1 && mixly_digitalRead(encoderPinA) == HIGH) {
    CW_2 = !mixly_digitalRead(encoderPinB);
    if (CW_1 == true && CW_2 == true) {
      count = count + 1;
    }

    if (CW_1 == false && CW_2 == false) {
      count = count - 1;
    }
    int_num = 0;
}
}

void setup()
{
num = 0;
count = 0;
encoderPinA = 3;
encoderPinB = 4;
int_num = 0;
CW_1 = 0;
CW_2 = 0;
pinMode(encoderPinA, INPUT);
PCintPort::attachInterrupt(encoderPinA,attachPinInterrupt_fun_encoderPinA,CHANGE);
Serial.begin(9600);
}

void loop()
{
while (num != count) {
    num = count;
    Serial.println(num);
}
}
自动生成的代码有点傻,可能读起来比较麻烦,我把mixly文件放在附件里,大家用mixly看,就很容易了。
经测试,输出非常稳定,没有一点抖动。
另外受这个方案启发,我发现按键防抖用这种双触发方式效果也很好,EC11+DS3231一键调时间的程序也已经编好,一起发上来。
#include <DS3231.h>
#include <Wire.h>
#include <PinChangeInt.h>
long settime;
long hour;
long minute;
long second;
long TEMP_hour;
long TEMP_minute;
long TEMP_second;
boolean h12;
boolean pm;
long num;
long count;
long switchPin;
boolean switchPushed;
long encoderPinA;
long encoderPinB;
long time;
long int_num0;
long int_num;
boolean CW_1;
boolean CW_2;
DS3231 clock;
int mixly_digitalRead(uint8_t pin) {
pinMode(pin, INPUT);
return digitalRead(pin);
}

void attachInterrupt_fun_switchPin() {
if (int_num0 == 0 && mixly_digitalRead(switchPin) == LOW) {
    int_num0 = 1;
}
if (int_num0 == 1 && mixly_digitalRead(switchPin) == HIGH) {
    switchPushed = true;
    time = millis();
    settime = settime + 1;
    count = 0;
    int_num0 = 0;
}
}



void attachPinInterrupt_fun_encoderPinA() {
if (switchPushed) {
    if (int_num == 0 && mixly_digitalRead(encoderPinA) == LOW) {
      CW_1 = mixly_digitalRead(encoderPinB);
      int_num = 1;
    }

    if (int_num == 1 && mixly_digitalRead(encoderPinA) == HIGH) {
      CW_2 = !mixly_digitalRead(encoderPinB);
      if (CW_1 == true && CW_2 == true) {
       count = count + 1;
      }

      if (CW_1 == false && CW_2 == false) {
      count = count - 1;
      }
      int_num = 0;
    }
    time = millis();
}
}
void setup()
{
settime = 0;
hour = clock.getHour(h12, pm);
minute = clock.getMinute();
second = clock.getSecond();
TEMP_hour = 0;
TEMP_minute = 0;
TEMP_second = 0;
h12 = false;
pm = true;
num = 0;
count = 0;
switchPin = 2;
switchPushed = false;
encoderPinA = 3;
encoderPinB = 4;
time = millis();
int_num0 = 0;
int_num = 0;
CW_1 = 0;
CW_2 = 0;
pinMode(switchPin, INPUT);
pinMode(encoderPinA, INPUT);
Wire.begin();
attachInterrupt(digitalPinToInterrupt(switchPin),attachInterrupt_fun_switchPin,CHANGE);
PCintPort::attachInterrupt(encoderPinA,attachPinInterrupt_fun_encoderPinA,CHANGE);
Serial.begin(9600);
}

void loop()
{

if (TEMP_second != clock.getSecond() && !switchPushed) {
    TEMP_hour = clock.getHour(h12, pm);
    TEMP_minute = clock.getMinute();
    TEMP_second = clock.getSecond();
    Serial.println(String(TEMP_hour) + String(String(":") + String(String(TEMP_minute) + String(String(":") + String(TEMP_second)))));
}
if (switchPushed) {
    if (millis() - time > 10000) {
      switchPushed = false;
      settime = 0;
      Serial.println("Time OUT!");
    }
}

if (switchPushed) {
    switch (settime) {
   case 1:
      hour = TEMP_hour + count;
      if (hour >= 24) {
      hour = (long) (hour) % (long) (24);
      }
      if (hour < 0) {
      hour = (long) (hour) % (long) (24) + 24;
      }
      if (num != hour) {
      num = hour;
      Serial.println(String("SEThour:") + String(num));
      }
      break;
   case 2:
      clock.setHour(hour);
      minute = TEMP_minute + count;
      if (minute >= 60) {
      minute = (long) (minute) % (long) (60);
      }

      if (minute < 0) {
      minute = (long) (minute) % (long) (60) + 60;
      }
      if (num != minute) {
      num = minute;
      Serial.println(String("SetMinute:") + String(num));
      }
      break;
   case 3:
      clock.setMinute(minute);
      second = TEMP_second + count;
      if (second >= 60) {
      second = (long) (second) % (long) (60);
      }
      if (second < 0) {
      second = (long) (second) % (long) (60) + 60;
      }
      if (num != second) {
      num = second;
      Serial.println(String("SetSecond:") + String(num));
      }
   break;
   case 4:
      clock.setSecond(second);
      switchPushed = false;
      settime = 0;
      Serial.println("Set time successed!");
      break;
    }
}



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

jinweimin 发表于 2018-10-29 22:43:43

正需要,可惜刚注册积分不够,再等等吧:Q

ldy2370 发表于 2019-2-6 21:23:25

用一个中断的原因是要把另一个中断留给其他东西用,毕竟uno nano这类就只有两个外部中断,实测编码器AB相加电容接地再用一个中断很稳定

haiyang4060 发表于 2019-4-15 13:02:57

积分 不够啊郁闷 下载不了

gltai 发表于 2021-1-3 21:47:52

答复。研究中

shuai0458 发表于 2021-1-30 06:29:02

我也想参考一下,不过积分不够!:hug:

47okey 发表于 2021-2-9 22:14:19

Highnose 发表于 2021-2-4 13:59
void ChkBMQ(){
    BAKT=T;



:victory::victory::victory:
试了您的代码,很好用。
请教一个问题:BAKT和T分别是什么?

47okey 发表于 2021-2-10 14:40:09

Highnose 发表于 2021-2-10 11:40
这是我程序里的一个变量,当时没去掉

谢谢,明白了

大卫1999 发表于 2021-2-20 13:24:58



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

gltai 发表于 2021-2-28 21:00:03

正需要,可惜刚注册积分不够,再等等吧

dtxy101 发表于 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自动生成的一段程序,很多冗余的,当时偷懒没修改。这个帖子只是提供给大家一个思路哈,理解第一段程序就可以编自己的程序了,思路很简单

jinweimin 发表于 2021-8-13 09:52:19

感谢分享!

dtxy101 发表于 2021-9-8 07:57:36

积分 不够啊  郁闷 下载不了

hilgz 发表于 2021-9-8 15:32:15

正需要,可惜刚注册积分不够,再等等吧
页: [1] 2
查看完整版本: 解决旋转编码抖动的方案