Поворотный энкодер KY-040 пропускает шаги

Вчера я задал вопрос об устранении дребезга этого поворотного энкодера, и Джсотола предложил мне использовать такой метод, как Это для определения предыдущего состояния и последующего подсчета. используя этот метод, каждый оборот считается дважды за один оборот, поэтому я придумал свою версию устранения дребезга поворотного энкодера:

int s1, s2;
int count = 0;
boolean right = 0;
boolean left = 0;
int rotary = 0;
unsigned long timer = 0;
unsigned int bounce = 50;

void setup() {
  Serial.begin(9600);
  pinMode(6, INPUT);
  pinMode(7, INPUT);

}

void loop() {

  s1 = digitalRead(6);
  s2 = digitalRead(7);
  if (s1 == 1 && s2 == 1) {
    right = 0;
    left = 0;
    rotary = 0;
  }
  if (s1 == 0 && s2 == 1) {
    left = 1;
  }
  if (left == 1 && (s1 == 0 && s2 == 0)) {
    rotary = 2;
  }
  if (s1 == 1 && s2 == 0) {
    right = 1;
  }
  if (right == 1 && (s1 == 0 && s2 == 0)) {
    rotary = 1;
  }

  if (rotary == 1) {
    if ((millis() - timer) > bounce) {
      count++;
    }
    right = 0;
    left = 0;
    timer = millis();
  }
  if (rotary == 2) {
    if ((millis() - timer) > bounce) {
      count--;
    }
    right = 0;
    left = 0;
    timer = millis();
  }
  Serial.print(s1);
  Serial.print(s2);
  Serial.print(", ");
  Serial.print(count);
  Serial.println(" ");
  delay(15);
}

Код работает нормально. При повороте поворотного диска я получаю следующие шаги:

10-00-01 < (11) > 10-00-01

проблема в том, что если я быстро поверну ручку, поворотный энкодер пропустит один или два шага, например: 01-01-01 < (11) или 10-00-00 < (11).

Эта проблема существует во всех методах/библиотеках (я протестировал их все), можно ли как-то исправить это с помощью программного/аппаратного обеспечения, или этот поворотный энкодер просто хлам, и я должен найти лучший?


Еще один способ устранения отказов по запросу Jsotola:

 int pinA = 3;  // Подключен к CLK на KY-040
 int pinB = 4;  // Подключен к DT на KY-040
 int encoderPosCount = 0; 
 int pinALast;  
 int aVal;
 boolean bCW;

 void setup() { 
   pinMode (pinA,INPUT);
   pinMode (pinB,INPUT);
   /* Read Pin A
   Whatever state it's in will reflect the last position   
   */
   pinALast = digitalRead(pinA);   
   Serial.begin (9600);
 } 

 void loop() { 
   aVal = digitalRead(pinA);
   if (aVal != pinALast){ // Означает, что ручка вращается
     // если ручка вращается, нам нужно определить направление
     // Мы делаем это, считывая вывод B.
     if (digitalRead(pinB) != aVal) {  // Означает, что вывод A изменен первым — мы вращаемся по часовой стрелке
       encoderPosCount ++;
       bCW = true;
     } else {// В противном случае B изменится первым, и мы движемся против часовой стрелки
       bCW = false;
       encoderPosCount--;
     }
     Serial.print ("Rotated: ");
     if (bCW){
       Serial.println ("clockwise");
     }else{
       Serial.println("counterclockwise");
     }
     Serial.print("Encoder Position: ");
     Serial.println(encoderPosCount);

   } 
   pinALast = aVal;
 } 

, 👍1

Обсуждение

Вы удалили свой 1-й вопрос? Я не могу найти это. 1) Было бы лучше, если бы вы сохранили автомат с 4 состояниями, чтобы вы могли различать слева направо на основе текущего кода и предыдущего четырехъядерного кода. Я не думаю, что вижу это в вашем коде выше. 2) При работе с выборкой прошивки можно предположить, что некоторые выборки будут пропущены. Таким образом, программное обеспечение должно компенсировать или вам нужно лучшее оборудование. Обычно дешевое оборудование и хорошее программное обеспечение побеждают из-за стоимости. Итак, я хотел бы, чтобы программное обеспечение составляло левую и правую базу на основе истории незаконных последовательностей кода., @st2000

@ st2000 удалил первый вопрос, потому что я не мог объяснить, в чем на самом деле была проблема, и вопрос был беспорядочным. | Как я могу отличить один шаг от другого, когда, как в примере в вопросе, он просто пропускает все 3 или 2 шага? мне нужен хотя бы первый шаг..., @ElectronSurf

Я предполагаю, что все эти serial.prints действительно замедляют ваш код, делая его слишком медленным для обнаружения быстрых вращательных движений. Например, вы можете только распечатать счет, затем быстро повернуть вправо, а затем медленно повернуть назад, пока он не достигнет исходного положения. Затем проверьте, вернулся ли счетчик к 0., @Gerben

@Gerben удалил delay () и последовательные отпечатки, без разницы, что когда-либо ..., @ElectronSurf

у вас почти получилось... программа определяет переход, а вы перескакиваете пытаясь определить направление вращения.... забудьте о направлении и сосредоточьтесь на последовательном обнаружении либо нарастающего, либо спадающего фронта сигнала.. ... когда вы видите переход, подождите 50 мс и проверьте снова ... если переход подтвержден, то проверьте уровень сигнала .... если он высокий, то произошел нарастающий фронт ... если он низкий, то произошел спадающий фронт .... в этот момент не беспокойтесь о направлении ... просто увеличивайте счетчик только на нарастающем фронте ... наблюдайте за результатами, @jsotola

@jsotola, пожалуйста, если возможно, опубликуйте ответ с примером., @ElectronSurf


1 ответ


Лучший ответ:

2

Я аплодирую вам за упорство в идее "устранения дребезга", но для этого поворотного энкодера существует отличная библиотека, и автор также написал о ней статью: Бен Бакстон - поворотные энкодеры сделаны правильно. Вот ссылка на его библиотеку: GitHub - buxtronix.

Я уже использовал более старую версию библиотеки с прерываниями, и она отлично работала для моего приложения. Вижу автор обновил библиотеку и есть 2 примера скетча; один использует прерывания, а другой использует опрос. Я попробовал оба из них сегодня на Uno, и я был очень впечатлен производительностью скетча опроса. Казалось, что это эквивалентно методу прерывания.

Если вы удалите важные биты из файлов библиотеки, а затем добавите их в скетч, вы увидите, как мало кода требуется для этого «метода» обработки вывода кодировщика. Вот тестовый скетч с битами библиотеки.

#define R_START 0x0
#define R_CW_FINAL 0x1
#define R_CW_BEGIN 0x2
#define R_CW_NEXT 0x3
#define R_CCW_BEGIN 0x4
#define R_CCW_FINAL 0x5
#define R_CCW_NEXT 0x6
#define DIR_NONE 0x0
#define DIR_CW 0x10
#define DIR_CCW 0x20

const byte pin1 = 5;  // DT
const byte pin2 = 6;  // CLK
int state = R_START;
int counter = 0;

const unsigned char ttable[7][4] = {
  // R_START
  {R_START,    R_CW_BEGIN,  R_CCW_BEGIN, R_START},
  // R_CW_FINAL
  {R_CW_NEXT,  R_START,     R_CW_FINAL,  R_START | DIR_CW},
  // R_CW_BEGIN
  {R_CW_NEXT,  R_CW_BEGIN,  R_START,     R_START},
  // R_CW_NEXT
  {R_CW_NEXT,  R_CW_BEGIN,  R_CW_FINAL,  R_START},
  // R_CCW_BEGIN
  {R_CCW_NEXT, R_START,     R_CCW_BEGIN, R_START},
  // R_CCW_FINAL
  {R_CCW_NEXT, R_CCW_FINAL, R_START,     R_START | DIR_CCW},
  // R_CCW_NEXT
  {R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START},
};

unsigned char process(){

  // Grab state of input pins.
  unsigned char pinstate = (digitalRead(pin2) << 1) | digitalRead(pin1);

  // Determine new state from the pins and state table.
  state = ttable[state & 0xf][pinstate];

  // Return emit bits, ie the generated event.
  return state & 0x30;
}

void setup(){
  pinMode(pin1, INPUT_PULLUP);
  pinMode(pin2, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop(){
  unsigned char result = process();
  if(result == DIR_CW){
    counter++;
    Serial.println(counter);
  }
  else if(result == DIR_CCW){
    counter--;
    Serial.println(counter);
  }
}

Вот упрощенный скетч (он работает так же, но мне не нравятся шестнадцатеричные числа, и я не использую определение):

const byte pin1 = 5;  // Подключен к DT на KY-040
const byte pin2 = 6;  // Подключен к CLK на KY-040
int state = 0;
int counter = 0;

const unsigned char ttable[7][4] = {
  {0, 2, 4, 0},
  {3, 0, 1, 0 | 16},
  {3, 2, 0, 0},
  {3, 2, 1, 0},
  {6, 0, 4, 0},
  {6, 5, 0, 0 | 32},
  {6, 5, 4, 0},
};

void setup(){
  pinMode(pin1, INPUT_PULLUP);
  pinMode(pin2, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop(){

  unsigned char result = process();

  // Вращение по часовой стрелке.
  if(result == 16){
    counter++;
    Serial.println(counter);
  }

  // Вращение против часовой стрелки.
  else if(result == 32){
    counter--;
    Serial.println(counter);
  }

}

unsigned char process(){

  // Захват состояния входных контактов.
  unsigned char pinstate = (digitalRead(pin2) << 1) | digitalRead(pin1);

  // Определяем новое состояние из пинов и таблицы состояний.
  state = ttable[state & 0xf][pinstate];

  // Возвращаем испускаемые биты, т.е. сгенерированное событие.
  return state & 48;
}
,

ЧУВАК! это магия! почему я не смог найти эту библиотеку! Спасибо., @ElectronSurf