Как работает энкодер?

Я “трачу” своё время, потому что мне стало интересно разобраться, почему у вас такой результат. Про трату времени речь не идёт

Закрыть тему может лишь модератор.
Если кому есть что сказать - может продолжить. Ваше участие не обязательно.

Просто идиот- это совершенно нормально. Все мы идиоты, пока не разберемся в вопросе, хотя бы частично. Хуже - идиот обидчивый. Ему не важно чему его учат, а важно КАК. Его нужно учить почтительно, не задевая тонкую шкурку его души, не дай Б…г поцарапаешь!

По теме - дребез и совершенно ненормальный код. То сериал в прерывании, то печать раз в секунду.
Кстати я видел огромную кучу бракованных китайских энкодеров. В одном фронты каналов А и Б совпадали, спады - разные, а рост одновременный. Эффект понятен? Я неделю думал, что я рехнулся!

надеюсь к психологу не успел попасть?
PS у меня их энкодеры (новые) немного полежали (всего 6 лет) - не работают, даже на 100ком не замыкают, …а и нечего, они жеж фабрика, пора новые заказывать…
PPS новые работали

ВАМ ДЛЯ ПРОВЕРКИ У МЕНЯ ПОХОЖЕЯ ТЕМА
САМ НЕ ПРОВЕРЯЛ НО ДОЛЖЕН РАБОТАТЬ
ЕСЛИ ЧТО СПРАШИВАЙТЕ . ПОМОГУ

volatile long temp, counter;
#define CLK 10
#define DAT 9
void setup() {
    Serial.begin(115200);
    digitalWrite(10, HIGH);
    digitalWrite(9, HIGH);
    pinMode(10, INPUT_PULLUP);     
    pinMode(9, INPUT_PULLUP);
    attachInterrupt(0, ai0, RISING);
    attachInterrupt(1, ai1, RISING);
}

void loop() { if( counter != temp ) { Serial.println (counter); temp = counter; }}
void ai0()  { if (digitalRead(10) == digitalRead(9)) { counter--; } else { counter++; }}
void ai1()  { if (digitalRead(10) != digitalRead(9)) { counter--; } else { counter++; }}

Не понял, в чем похожесть наших тем. Вы привели обычный код для опроса энкодера, в котором отсутствует дребезг. Не считая того, что строчки 6 и 7 не нужны в принципе, а 2 и 3 не нужны, т.к. далее не используются ни CLK, ни DAT. С дребезгом он уже не будет нормально работать, а к объяснению моей ситуации не имеет отношения. Возможно я Вас как-то не так понял. Или Вы меня.

Не мой случай. Т.к. у меня вообще опрашивается только один канал.

Без дребезга с нормальным кодом нет темы для обсуждения.

Сериал убрал в сообщении #41. А печать хоть раз в сутки, печатается все равно только результат считывания пина в первом срабатывании прерывания за эти сутки.

Если ты все еще хочешь учиться, то сделай так:
Никаких прерываний.
в loop() читаешь пин, проверяешь изменился ли (хранишь прошлое состояние) если изменился - печатаешь и рядом счетчик изменений.
Код я тебе писать не буду в расчете на то, что сам справишься.

Если получится интересно, то результат можешь показать, но скорее всего ты просто увидишь все, что происходит и тебе не будет нужна дальнейшая консультация.

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

Вот код:

Да это бесполезно.
Он никак не поймет, что во время щелчка экнодером, прерывание вызывается несколько раз, а выводится только последний результат, потому что в коде задержка на вывод в 1 сек.
А в каком состоянии будет пин CLK в этой ситуации зависит от того в какую сторону вращали энкодер.
Впрочем пусть думает, что это у него энкодер “волшебно сломанный”, главное мы поняли , что там содержимое головы по ГОСТ 26074-84

1 лайк

Состояние CLK никак не может зависеть от направления ИМХО.
Произошёл щелчок - низкий уроень(каое-то время, оговоренное даташитом). А в какую сторону крутим - без разницы . Исключение дребезг и “кривость” самого энкодера.
Вот код для Нано, написал специально так, чтобы ответить на вопрос ТС. WOKWI потому, что нет под рукой энкодера.

Спойлер
#define CLK 2
#define DAT 3

uint32_t last_millis = 0;
volatile int16_t encCount = 0;

void setup() {
  Serial.begin(115200);
  pinMode(CLK, INPUT);
  pinMode(DAT, INPUT);

  attachInterrupt(0, enc_interrupt, FALLING);//int.0 на CLK
}

void enc_interrupt() {//входим в прерывание при смене на CLK 
  sei();              //с высокого на низкий урвень
  delayMicroseconds(500);//задержка для антидребезга 0-3000us
  cli();                 //выше 3000мкс появляются ошибки

 if(!digitalRead(CLK))//!!!! только если на CLK всё так же 
 {                    //низкий уровень, обновляем счётчик !!!
 if(!digitalRead(DAT))
   encCount++;
  else
   encCount--;
 }
}

void loop() {
  if(millis() - last_millis > 500)
  {
  last_millis = millis();
  Serial.print("encCount = ");
  Serial.println(encCount);
  }  
}
Спойлер

P.S. В Proteus тоже норм

Спойлер

delay же не работает в прерывании.
Или sei() / cli() как-то обходит это? Я просто не в курсе, что за функции.

Разрешаем, а затем снова запрещаем прерывания, и всё работает

P.S. Вариант с флагом для вывода в сериал

Спойлер
#define CLK 2
#define DAT 3

uint32_t last_millis = 0;
volatile int16_t encCount = 0;
bool Change = false;

void setup() {
  Serial.begin(115200);
  pinMode(CLK, INPUT);
  pinMode(DAT, INPUT);

  attachInterrupt(0, enc_interrupt, FALLING);//int.0 на CLK
}

void enc_interrupt() {//входим в прерывание при смене на CLK 
  sei();              //с высокого на низкий урвень
  delayMicroseconds(500);//задержка для антидребезга
  cli();                 //выше 3000мкс появляются ошибки

 if(!digitalRead(CLK))//!!!! только если на CLK всё так же 
 {                    //низкий уровень, обновляем счётчик !!!
 if(!digitalRead(DAT))
   encCount++;
  else
   encCount--;
   Change = true;
 }
}

void loop() {
  if(millis() - last_millis > 100 && Change)
  {
  last_millis = millis();
  Serial.print("encCount = ");
  Serial.println(encCount);
  Change = false;
  }  
}

напомните, зачем вы это делаете? (в смысле, пытаетесь читать только один канал на двухканальном энкодере)?
Если уже отвечали - укажите только номер сообщения в ветке.

Меня просто добивает бессмысленность всей этой темы, которую вы сами понимаете:

В скетче, который я пишу, я конечно же опрашиваю оба канала. Просто столкнувшись с дребезгом, я захотел попробовать написать свой алгоритм программной обработки дребезга (Да, есть готовые, работающие примеры, интересно было попробовать свой). В этом алгоритме нужно было считывать в том числе и состояние CLK сразу после срабатывания прерывания на его же (CLK) изменение. Предполагалось, что при изменении состояния с 0 на 1, считывание даст 1. И наоборот, при изменении с 1 на 0, считывание даст 0. В этом месте уже было сомнение, что, возможно, дребезг успеет повлиять на результат считывания, но решил попробовать. Если бы считанное состояние было бы нестабильно, то 0, то 1, то пришлось бы отказаться от этого алгоритма совсем. Однако, оказалось, что при вращении налево при изменении с 1 на 0 считывается стабильно 0 (как и ожидалось), а вот при вращении вправо при изменении с 1 на 0, считывается стабильно 1. Меня этот результат удивил. Подумав и не найдя самостоятельно убедительного объяснения, я выкинул из кода все лишнее и оставил только код, демонстрирующий всю ситуацию. Убедившись, что поведение сохраняется, в том числе и при замене энкодера на другой, аналогичный, решил задать этот вопрос здесь.
Вот как-то так возник код, который считывает только один канал.

Я выше ответил почему такое происходит.
На вывод идёт результат только последнего прерывания.
Соответственно в одном случае это будет прерывание по переднему фронту CLK, а в другом по заднему

почему бы не взять энкодер, присоединить оба вывода к логическому анализатору и не снять диаграмму состояния при вращении вправо и влево. Думаю, все вопросы сразу отпадут.
Только сделать это должны вы (ну или кто-то более продвинутый - но НА ВАШЕМ ЭНКОДЕРЕ).
А выносить мозг форуму тут смысла нет

2 лайка

Да ТС уже ни на чём не настаивает…
Просто интересно разобраться . Вон Евгений Петрович в соседней ветке сам примеры говнокода придумывает, а тут всё уже готово)).

И, всё же непонятно, возможно я туплю
#41

  1. Изначальное состояние CLK == 1, flag == true(устанавливается в loop())
  2. При повороте в любую сторону первое прерывание по падению, читаем 0.
  3. flag сбрасываем, и CLK после rising не читаем , и ничего больше не читаем ,до тех пор, пока не выведем в лупе ранее считанное значение.

Если я не прав, кому не лень - поправьте пжлст

А где ты у него увидел RISING ?
Там у него CHANGE

Это я так назвал второе по счёту прерывание по растущему фронту
Т.е. первое CHANGE 1 → 0, второе 0 → 1

А почему первое именно 1->0 ?
Может быть и 0->1.
Это куда и что крутить