Skip to content

Фикс: разворот направления при пропуске сэмпла в pollRaw#1

Open
dkxmercury wants to merge 1 commit into
GyverLibs:mainfrom
dkxmercury:fix/encoder-reversed-click-on-missed-sample
Open

Фикс: разворот направления при пропуске сэмпла в pollRaw#1
dkxmercury wants to merge 1 commit into
GyverLibs:mainfrom
dkxmercury:fix/encoder-reversed-click-on-missed-sample

Conversation

@dkxmercury

Copy link
Copy Markdown

Привет! Ковырялся в либе и наткнулся на неприятную штуку в pollRaw: при вращении в одну сторону энкодер иногда выдаёт щелчок в обратную.

В чём дело

Вот этот guard:

if (!(_e0 ^ _e1 ^ e0 ^ e1)) return State::Idle;

Он ловит два разных случая сразу: «ничего не изменилось» и «изменились обе ноги». Второй случай - это пропущенный сэмпл (цикл не успел поймать переход). И вот тут выходим по return, не обновив _e0/_e1.

В итоге они отстают на два шага, и следующий переход считается уже от протухших значений. Направление выходит противоположным.

Например, крутим по часовой 11 -> 01 -> 00 -> 10, и 01 проморгали:

  • 11 -> 00: обе ноги изменились, выходим по guard, в _e0/_e1 осталось 11
  • 00 -> 10: сравниваем с 11, а не с 00. (_e1 ^ e0) даёт 0 вместо 1, и вместо ++_pos получаем --_pos

Щелчок уезжает в обратную сторону. В Step1 это видно сразу, а в Step2/Step4 сбивается счёт до детента, и щелчки просто теряются.

Бьёт в первую очередь по опросному режиму (tick() в loop()), где пропуск сэмпла - обычное дело при небыстром цикле.

Фикс

Если изменились обе ноги, направление честно неизвестно, гадать нельзя. Поэтому просто синхронизируемся с текущими ногами и щелчок не выдаём. Шаг теряем, но разворота нет:

if (!(_e0 ^ _e1 ^ e0 ^ e1)) {
    _e0 = e0, _e1 = e1;
    return State::Idle;
}

Ветка «ничего не изменилось» от этого не страдает: там присваивание вырождается в no-op.

Как проверял

Гонял uEncoderVirt.h нативно на десктопе (g++ 13, мок Arduino.h с millis()). Ручка крутится только по часовой, во втором прогоне один сэмпл выбрасывается:

Было:

--- Step1 ---
  все сэмплы видны  : 12 LEFT  0 RIGHT
  один сэмпл потерян:  9 LEFT  1 RIGHT   <-- разворот
--- Step2 ---
  все сэмплы видны  :  6 LEFT
  один сэмпл потерян:  4 LEFT            <-- потеряно 2 щелчка
--- Step4Low ---
  все сэмплы видны  :  3 LEFT
  один сэмпл потерян:  2 LEFT            <-- потерян щелчок

Стало:

--- Step1 ---
  один сэмпл потерян: 10 LEFT  0 RIGHT   <-- разворота нет
--- Step2 ---
  один сэмпл потерян:  5 LEFT            <-- потерян только 1
--- Step4Low ---
  один сэмпл потерян:  3 LEFT            <-- вообще ничего не потеряно

Заодно видно, что счёт до детента чинится сам собой: Step4Low перестал глотать щелчок.

Тест в PR не тащу, чтобы не ломать структуру репо, но если надо - вот он целиком:

test.cpp
#include <cstdio>
#include <vector>
#include "uEncoderVirt.h"

struct P { bool e0, e1; };
static const P CW[4] = { {1, 1}, {0, 1}, {0, 0}, {1, 0} };  // по часовой, детент на 11

static void spin(uEType type, int skipAt, int& left, int& right) {
    uEncoderVirt enc;
    enc.setEncType(type);
    enc.initEnc(CW[0].e0, CW[0].e1);
    left = right = 0;
    int idx = 0;
    for (int step = 1; step <= 12; step++) {
        idx = (idx + 1) & 3;                  // крутим всегда в одну сторону
        if (step == skipAt) continue;         // цикл проморгал сэмпл
        g_millis += 100;                      // медленно, чтобы не ловить FAST
        uEncoderVirt::State st = enc.pollRaw(CW[idx].e0, CW[idx].e1, false);
        if (st == uEncoderVirt::Idle) continue;
        (uint8_t(st) & UE_DIR) ? right++ : left++;
    }
}

int main() {
    for (uEType t : { uEType::Step1, uEType::Step2, uEType::Step4Low }) {
        int l1, r1, l2, r2;
        spin(t, -1, l1, r1);
        spin(t, 2, l2, r2);
        printf("clean: %2d LEFT %2d RIGHT | lost sample: %2d LEFT %2d RIGHT\n", l1, r1, l2, r2);
    }
}

Мок Arduino.h: stdint.h, millis() поверх g_millis, пустые noInterrupts()/interrupts().

Если что-то делаю не так или это задумано - скажи, поправлю.

Если между опросами изменились обе ноги, направление определить нельзя,
но _e0/_e1 не обновлялись. Следующий переход считался от старых значений
и давал щелчок в обратную сторону при вращении в одну сторону, а в
Step2/Step4 сбивался счёт до детента и щелчки терялись.

Теперь при пропуске сэмпла состояние ног синхронизируется, щелчок не
выдаётся. Шаг теряется, но разворота нет.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant