Отсутствуют импульсы 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;
}
@Leslie Rhorer, 👍0
Обсуждение1 ответ
Лучший ответ:
Хорошо, у меня есть ответ. 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 нет флэш-памяти? Если нет, то что?
- Светодиод не мигает - Arduino UNO+WiFi R3 ATmega328P+Плата ESP8266
- ATmega328P - проблема с использованием таймера 2 для генерации тона
- Точность синхронизации Arduino nano
- Интервальный таймер на Arduino: Сомнения по поводу библиотеки TimerOne
- Можно ли отсоединить прерывание на определенное время
- Заставить TCNT оставаться ниже OCRxA на ATmega328P
- Помощь с прерыванием режима ожидания Arduino и ватчдог таймера
- Эмуляция Arduino Uno с помощью QEMU: прерывания не работают
попробуйте это... используйте прерывание только для фазы_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