Отсутствуют импульсы ATTiny88 - возможно

Мне дали понять, что ATTiny88 очень похож на микроконтроллер Arduino Uno / ATMega328. Похоже, что для ATTiny88 нет тега, поэтому я выбрал теги, которые сделал. У меня пока недостаточно высокая репутация для создания тега. Благодаря щедрой помощи timemage, Эдгара Бонета и других, моя плата MCU теперь реагирует, хотя и очень беспорядочно, на импульсы на контактах 12 и 13, обозначенных как Phase_B и Phase_A, соответственно, в приведенном ниже коде. Я еще не полностью исключил проблему с оборудованием (я так и сделаю), но проблема с программным обеспечением все еще вероятна. Я не совсем знаю, что. MCU регистрирует импульсы, но очень часто пропускает большое количество витков в любом направлении. Более того, он часто сообщает о неправильном направлении, сильно отдавая предпочтение вращению по часовой стрелке, но иногда сообщая о вращении против часовой стрелки, когда ручка поворачивается по часовой стрелке. Это определенно не проблема переполнения, поскольку аномальные эффекты проявляются в изобилии, когда переменные находятся в среднем диапазоне.

#include <Arduino.h>
#include <TM1637Display.h>
#include "PinChangeInterrupt.h"

volatile uint8_t counter = 0;
volatile bool Update = false;
volatile int currentStatePhase_B;
volatile int lastStatePhase_B;
volatile int pulses = 0;

// Выход отключения
#define SW 11

// Входы поворотного энкодера
#define Phase_A 13
#define Phase_B 12
// Порты ввода/вывода TM1637
#define DIO 3
#define CLK 4
TM1637Display display(CLK, DIO);

void setup() {

  // Установить выводы энкодера в качестве входных данных
  pinMode(Phase_A,INPUT);
  pinMode(Phase_B,INPUT);
  pinMode(SW, OUTPUT);

    // Читаем начальное состояние Phase_B
    lastStatePhase_B = digitalRead(Phase_B);
    
    // Вызываем updateEncoder() при изменении максимума/минимума
    // по прерыванию 0 (вывод 2) или прерыванию 1 (вывод 3)
    attachPCINT(digitalPinToPCINT(Phase_A), updateEncoder, RISING);
    attachPCINT(digitalPinToPCINT(Phase_B), updateEncoder, RISING);

  display.setBrightness(0x0f);
  display.clear();
  display.showNumberDec(0, false);
}

void loop() {
    if (Update){
        // Показать десятичные числа с ведущими нулями или без них
    display.showNumberDec(pulses, false);
    delay(1000);
    display.showNumberDec(counter, false);
        Update = false;
    }
}

void updateEncoder(){
    // Читаем текущее состояние Phase_B
    currentStatePhase_B = digitalRead(Phase_B);

    // Если последнее и текущее состояние Phase_B различаются, то произошел импульс
    // Реагировать только на 1 изменение состояния, чтобы избежать двойного счета
    if (currentStatePhase_B != lastStatePhase_B  && currentStatePhase_B == 1){
        // Если состояние Phase_A отличается от состояния Phase_B, то
        // энкодер вращается против часовой стрелки, поэтому уменьшаем
        if (digitalRead(Phase_A) != currentStatePhase_B) {
            counter --;
        } else {
            // Энкодер вращается по часовой стрелке, поэтому увеличивается
            counter ++;
        }
    }
    if (counter > 1023){
      counter = 1023;
    }
    if (counter < 0){
      counter = 0;
    }  
    pulses ++;
    // Запоминаем последнее состояние Phase_B
    lastStatePhase_B = currentStatePhase_B;
    Update = true;
}

, 👍0

Обсуждение

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

Ответы на большинство этих вопросов см. на странице https://arduinoprosto.ru/q/92354/change-interrupts-on-the-attiny-88., @Leslie Rhorer

@jsotola Это EC11 на плате, которую я создал сам. Я не знаю, какой тип кодировщика вы можете себе представить, но это настолько просто, насколько это возможно. Что посоветуете для дебаунза? Примечание (я пробовал несколько вещей, включая опрос, а не использование прерываний., @Leslie Rhorer

Я видел энкодер, который многократно щелкает по одному контакту при повороте в одну сторону, и многократно щелкает по второму контакту при повороте в другую сторону... Думаю, что EC11E0B такой, @jsotola

Хорошо, это точно не то. На самом деле, было бы здорово, если бы это было так., @Leslie Rhorer

https://www.arduino.cc/reference/en/libraries/rotaryencoder/, @jsotola

Ему совсем не нравится эта библиотека. Он выдает более десятка всевозможных ошибок со всеми примерами., @Leslie Rhorer

Поворотные энкодеры с [кодировкой Грея](https://en.wikipedia.org/wiki/Rotary_encoder#Gray_encoding) или [квадратурной кодировкой](https://en.wikipedia.org/wiki/Rotary_encoder#Incremental_encoder) по своей кодировке невосприимчив к прыжкам. Не могли бы вы [отредактировать] свой вопрос и предоставить ссылку на техпаспорт кодировщика?, @the busybee

Нет, это определенно не так. См. https://www.pinteric.com/rotary.html Это правда, что дребезг можно легко обнаружить и игнорировать, но ни один механический переключатель не застрахован. Я только что купил их на Amazon: https://www.amazon.com/gp/product/B07DM2YMT4/ref=ppx_yo_dt_b_asin_title_o08_s02?ie=UTF8&psc=1, @Leslie Rhorer

Я не говорил "они не отскакивают". Я утверждаю, что кодировка неуязвима в том смысле, что у вас всегда будут правильные углы поворота. Я должен был добавить это. Ваш документ, на который вы ссылаетесь, подтверждает мое утверждение., @the busybee

У вас есть необходимые подтягивающие/подтягивающие резисторы на контактах 12 и 13?, @Edgar Bonet

Да, конечно, но мне нужно поставить прицел на энкодер, чтобы подтвердить его дребезг, и, конечно, добавить пару конденсаторов., @Leslie Rhorer


1 ответ


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

0

Хорошо, у меня есть ответ. MCU по-прежнему пропускает много импульсов, но я совершенно уверен, что это аппаратная проблема, или, по крайней мере, в основном так. Я нашел код Ральфа С. Бэкона и Марко Пинтерика. Видео YouTube можно найти здесь

Как и всегда, ISR краток. Здесь это единственная строка, которая просто устанавливает логическую переменную в значение true. В секции цикла код неоднократно проверяет, произошло ли прерывание. Затем функция checkRo taryEncoder проверяет значения выводов, чтобы убедиться, что кодировщик находится в допустимом состоянии (не дребезжит). Если это так, то значение счетчика обновляется результатом.

#include <Arduino.h>
#include <TM1637Display.h>
#include "PinChangeInterrupt.h"

uint8_t lrmem = 3;
int lrsum = 0;
int num = 0;
unsigned int counter = 0;

// Выход отключения
#define SW 11
// Входы поворотного энкодера
#define Phase_A 12
#define Phase_B 13
// Ввод/вывод дисплея TM1637
#define DIO 3
#define CLK 4
TM1637Display display(CLK, DIO);

// Счетчик оборотов для поворотного энкодера (отрицательное значение = против часовой стрелки)
int rotationCounter = 200;
// Флаг из подпрограммы прерывания (moved=true)
volatile bool rotaryEncoder = false;

// Поворотный энкодер сдвинулся (об этом нам сообщает прерывание), но что произошло?
// См. https://www.pinteric.com/rotary.html
int8_t checkRotaryEncoder()
{
    // Сбрасываем флаг, который привел нас сюда (из ISR)
    rotaryEncoder = false;

    static uint8_t lrmem = 3;
    static int lrsum = 0;
    static int8_t TRANS[] = {0, -1, 1, 14, 1, 0, 14, -1, -1, 14, 0, 1, 14, 1, -1, 0};

    // Прочитать состояния ОБЕИХ выводов, чтобы определить правильность вращения (т.е. не просто отскок переключателя)
    int8_t l = digitalRead(Phase_A);
    int8_t r = digitalRead(Phase_B);

    // Перемещаем предыдущее значение на 2 бита влево и добавляем новые значения
    lrmem = ((lrmem & 0x03) << 2) + 2 * l + r;
    // Преобразование битового паттерна в индикатор движения (14 = невозможно, т.е. переключить отскок)
    lrsum += TRANS[lrmem];
    /* encoder not in the neutral (detent) state */
    if (lrsum % 4 != 0)
    {
        return 0;
    }
    /* encoder in the neutral state - clockwise rotation*/
    if (lrsum == 4)
    {
        lrsum = 0;
        return 1;
    }
    /* encoder in the neutral state - anti-clockwise rotation*/
    if (lrsum == -4)
    {
        lrsum = 0;
        return -1;
    }
    // Обнаружено невозможное вращение - игнорируем движение
    lrsum = 0;
    return 0;
}

void setup() {
  // Установить выводы энкодера в качестве входных данных
  pinMode(Phase_A,INPUT);
  pinMode(Phase_B,INPUT);
  // Установить выключатель питания в качестве выхода
  pinMode(SW, OUTPUT);
  // Инициализировать отображение
  display.setBrightness(0x0f);
  display.clear();
  display.showNumberDec(0, false);
  // Настраиваем пины прерывания и вектор
  attachPCINT(digitalPinToPCINT(Phase_A), rotary, CHANGE);
  attachPCINT(digitalPinToPCINT(Phase_B), rotary, CHANGE);
}

void loop()
{
  // Сдвинулся ли поворотный регулятор?
  if (rotaryEncoder) {
    // Получить движение (если допустимо)
    int8_t rotationValue = checkRotaryEncoder();
    // Если допустимое движение, делаем что-то
    if (rotationValue != 0) {
      counter += rotationValue;
      if (counter > 1023){
        counter = 1023;
      }
      else if (counter < 0){
        counter = 0;
      }
      display.showNumberDec(counter, false);
    }
  }
}

// Процедура прерывания просто устанавливает флаг при обнаружении поворота
void rotary()
{
    rotaryEncoder = true;
}

Приветствуются любые комментарии. Кроме того, Ральф Бэкон использовал директиву IRAM_ATTR для создания ISR rotate(). Я понимаю эту директиву так, что в большинстве случаев в ней нет необходимости. С другой стороны, предположительно код будет выполняться быстрее, если он будет помещен во флэш-память. Я не мог сделать эту работу. Компилятор жалуется: "ожидаемый инициализатор перед 'rotary'"; Случайно ли это, потому что у ATTiny88 нет флэш-памяти? Если нет, то что?

,