LCD+Encoder - глюк?

AsNik
Offline
Зарегистрирован: 24.10.2020

Здравствуйте. Подскажите пожалуйста почему данный ниже код работает с глюком.

Глюк заключается в том, что если выводить на LCD дисплей так: lcd.print("Param: "); lcd.print(myparam); то в этих строках:

if (encoder.isRight() && myparam < 35) myparam++;

  if (encoder.isLeft()  && myparam > 10) myparam--;

myparam меняется, но не так как нужно. Я пробовал в сериал выводить myparam сразу после этих строк для сравнения с отображением на LCD но и там и там одинаково и неправильно. Если так: lcd.print(myparam); (без предварительного lcd.print("Param: ");) то все нормально.

Неправильно - это меняется ели ели, как-то медленно, т.е. несколько щелчков энкодера что бы сменилось на единицу. И еще при повороте ручки в одну сторону значение myparam может(или нет) меняться и в ту и в другую сторону).

Там в коде последние четыре строки, если оставлять одну любую для проверки то две работают нормально а две нет.

Из-за чего такой неприятный глюк? Может это связано с конкретным моим железом или библиотеками. Название библиотек и их версии я в коде указал.

  Скрыть содержимое
#include <LiquidCrystal_I2C.h>
//name=LiquidCrystal_I2C
//version=1.1.4
//author=Frank de Brabander
//maintainer=Marco Schwartz <marcolivier.schwartz@gmail.com>
//sentence=A library for I2C LCD displays.
//paragraph= The library allows to control I2C displays with functions extremely similar to LiquidCrystal library. THIS LIBRARY MIGHT NOT BE COMPATIBLE WITH EXISTING SKETCHES.
//category=Display
//url=https://github.com/marcoschwartz/LiquidCrystal_I2C
//architectures=avr
#include <GyverEncoder.h>
//name=GyverEncoder
//version=4.8
//author=AlexGyver <beragumbo@ya.ru>
//maintainer=AlexGyver <beragumbo@ya.ru>
//sentence=Advanced library for encoder.
//paragraph=Allows to get maximum uses of encoder: turns, holded turns, fast turns and more.
//category=Sensors
//url=https://github.com/AlexGyver/GyverLibs
//architectures=*

#define CLK 4
#define DT 5
#define SW 3

Encoder encoder(CLK, DT, SW);
LiquidCrystal_I2C lcd(0x27, 20, 4);

void setup() {
  encoder.setType(TYPE2);
  lcd.init();
  lcd.backlight();
}

byte myparam = 10;
#define FLASH_MS 400
uint32_t ms = 0;
bool f = true;
void loop() {

  encoder.tick();
  if (encoder.isRight() && myparam < 35) myparam++;
  if (encoder.isLeft()  && myparam > 10) myparam--;

  if (millis() - ms >= FLASH_MS) {
    f = !f; ms += FLASH_MS;
  }

  lcd.setCursor(0, 0);
  //      lcd.print(myparam);                                 // Работает
        if (!f) lcd.print("   "); else lcd.print(myparam);    // Работает
  //      lcd.print("Param: "); lcd.print(myparam);           // НЕ работает
  //      lcd.print("Param: "); if (!f) lcd.print("   "); else lcd.print(myparam); // НЕ работает

}

 

Или я чего-то не знаю про связку lcd.print и encoder :(

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

а чо de Brabander с Гайвером говорят?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Первая Ваша ошибка - использование библиотек от Гайвера. Гайвер - талантливый блогер, но никудышний программист.

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

Правильно подключать энкодер - через прерывания. Ну либо заботиться, чтобы между вызовами encoder.tick() не было длительных задержек.

sadman41
Offline
Зарегистрирован: 19.10.2016

Попробуйте работу с LCD временно выкинуть из loop() вообще.

AsNik
Offline
Зарегистрирован: 24.10.2020

andriano пишет:

Если я правильно понял Вашу проблему, при подключении вывода на дисплей энкодер начинает чудить, а без дисплея работает нормально. 

Не совсем так. Глючить начинает когда перед выводом параметра на дисплей (lcd.print(myparam);) я вывожу строку (lcd.print("Param: ");)

Если выводить просто:

  encoder.tick();
  if (encoder.isRight() && myparam < 35) myparam++;
  if (encoder.isLeft()  && myparam > 10) myparam--;
 
  lcd.setCursor(0, 0);
  lcd.print(myparam);   
то все нормально работает.
AsNik
Offline
Зарегистрирован: 24.10.2020

sadman41 пишет:

Попробуйте работу с LCD временно выкинуть из loop() вообще.

Код выше - это максимально упрощенный и сохранивший этот глюк.

Вообще изначально код был больше и опрос энкодера и вывод на экран были в разных функциях и не в loop обе.

Я долго искал ошибку в своей логике. Думал это я чего намудрил. Но когда нашел причину и сделал код минимальным для глюка.

AsNik
Offline
Зарегистрирован: 24.10.2020

andriano пишет:

Первая Ваша ошибка - использование библиотек от Гайвера. Гайвер - талантливый блогер, но никудышний программист.

Неожиданно :(

Ок. Попробую другую библиотеку для энкодера. У Гайвера удобная работа с энкодером... вот и остановился на ней. И у него в этой библиотеке есть вариант использования энкодера по прерываниям. Тоже попробую.

Я надеялся что это известный глюк и есть стандартное его решение. Спасибо.

sadman41
Offline
Зарегистрирован: 19.10.2016

С другой тоже весьма вероятно упретесь в то, что при наличии функций, работающих с LCD, энкодер в линейном алгоритме точно так же будет работать непредсказуемо. Чуть побольше объёмы вывода и всё, приплыли. Вместо lcd.*() просто delay поставьте и получите схожую картину.

AsNik
Offline
Зарегистрирован: 24.10.2020

sadman41 пишет:

С другой тоже весьма вероятно упретесь в то, что при наличии функций, работающих с LCD, энкодер в линейном алгоритме точно так же будет работать непредсказуемо. Чуть побольше объёмы вывода и всё, приплыли. Вместо lcd.*() просто delay поставьте и получите схожую картину.

Хм... как же тогда с ними(энкодерами) работать.... Как понять "энкодер в линейном алгоритме"?

Или это как раз намек на "попробовать с энкодером работать через прерывания"?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

AsNik пишет:

Хм... как же тогда с ними(энкодерами) работать....

Перечитайте последний абзац поста №2.

AsNik
Offline
Зарегистрирован: 24.10.2020

andriano пишет:

Перечитайте последний абзац поста №2.

В общем так и поступил. Но с Гайверской библиотекой.

#include <LiquidCrystal_I2C.h>
//name=LiquidCrystal_I2C
//version=1.1.4
//author=Frank de Brabander
//maintainer=Marco Schwartz <marcolivier.schwartz@gmail.com>
//sentence=A library for I2C LCD displays.
//paragraph= The library allows to control I2C displays with functions extremely similar to LiquidCrystal library. THIS LIBRARY MIGHT NOT BE COMPATIBLE WITH EXISTING SKETCHES.
//category=Display
//url=https://github.com/marcoschwartz/LiquidCrystal_I2C
//architectures=avr
#include <GyverEncoder.h>
//name=GyverEncoder
//version=4.8
//author=AlexGyver <beragumbo@ya.ru>
//maintainer=AlexGyver <beragumbo@ya.ru>
//sentence=Advanced library for encoder.
//paragraph=Allows to get maximum uses of encoder: turns, holded turns, fast turns and more.
//category=Sensors
//url=https://github.com/AlexGyver/GyverLibs
//architectures=*

#define CLK 2
#define DT 3
#define SW 4

Encoder encoder(CLK, DT, SW);
LiquidCrystal_I2C lcd(0x27, 20, 4);

void setup() {
  encoder.setType(TYPE2);
  lcd.init();
  lcd.backlight();
  attachInterrupt(0, isrCLK, CHANGE);    // прерывание на 2 пине! CLK у энка
  attachInterrupt(1, isrDT, CHANGE);    // прерывание на 3 пине! DT у энка
}

void isrCLK() {
  encoder.tick();  // отработка в прерывании
}
void isrDT() {
  encoder.tick();  // отработка в прерывании
}

#define FLASH_MS 400
uint32_t ms = 0;
byte myparam = 10;
bool f = true;
void loop() {

  encoder.tick();
  if (encoder.isRight() && myparam < 35) myparam++;
  if (encoder.isLeft()  && myparam > 10) myparam--;

  if (millis() - ms >= FLASH_MS) {
    f = !f; ms += FLASH_MS;
  }

  lcd.setCursor(0, 0);
  //      lcd.print(myparam);                               // Работает
  //      if (!f) lcd.print("   "); else lcd.print(myparam);  // Работает
  //      lcd.print("Param: "); lcd.print(myparam);           // Работает
        lcd.print("Param: "); if (!f) lcd.print("   "); else lcd.print(myparam); // Работает

}

Вроде нормально. Буду переносить "идею" в основной код и смотреть как это все заработает. Спасибо

Или все же рекомендуете использовать другую библиотеку? А какую лучше?

sadman41
Offline
Зарегистрирован: 19.10.2016
ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016
andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

AsNik пишет:

Вроде нормально. Буду переносить "идею" в основной код и смотреть как это все заработает. Спасибо

Или все же рекомендуете использовать другую библиотеку? А какую лучше?

Лично я остановился на "коде от Леонида Ивановича". Где-то на форуме есть вариант для Uno/Nano/Mini, но я сходу обнаружил только свою модификацию для Меги: http://arduino.ru/forum/proekty/midi-boxmidi-sintezator#comment-531342

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AsNik пишет:

Здравствуйте. Подскажите пожалуйста почему данный ниже код работает с глюком.

Потому что

AsNik пишет:

//author=AlexGyver

AsNik
Offline
Зарегистрирован: 24.10.2020

Попробовал библиотеку из #11 но что-то она у меня с наскока не пошла. Я в код не вникал, запустил пример. Сменил только пины, куда реально у меня подключен энк. Возможно, как говорит Гайвер, имеются двух типов энкодеры, и может как-то можно сменить тип в данной либе... Слишком чувствительно работает. Ручку просто "шатаешь", даже щелчка не делаешь, а значения уже меняются, хотя энкодер новый, не расшатанный.

Из 12 скачал, но не смотрел.... Также не разобрался с "Лионидом Ивановичем".... И как я понял, все эти либы без обработки кнопки энкодера. Не критично, но...

В общем остановился пока на "гайвере" энкодер на двух прерываниях. Приемлимо. Посмотрю как будет дальше. У него(Гайвера) есть еще минилиба и просто "куски кода" для работы с энкодерами... Пока выявил небольшой (а может это и норма) косяк с пропуском шагов если ручку чуть побыстрее(не сильно) крутить...

ЕвгенийП пишет:

AsNik пишет:

Здравствуйте. Подскажите пожалуйста почему данный ниже код работает с глюком.

Потому что

AsNik пишет:

//author=AlexGyver

Не пугайте меня) Неужели совсем так плохо?

Спасибо всем отвечавшим.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

AsNik пишет:

И как я понял, все эти либы без обработки кнопки энкодера.

Вам еще либа для обработки кнопки нужна?

Воспользуйтесь "титановым велосипедом".

sadman41
Offline
Зарегистрирован: 19.10.2016

AsNik пишет:

Попробовал библиотеку из #11 но что-то она у меня с наскока не пошла. Я в код не вникал, запустил пример. Сменил только пины, куда реально у меня подключен энк. Возможно, как говорит Гайвер, имеются двух типов энкодеры, и может как-то можно сменить тип в данной либе... Слишком чувствительно работает. Ручку просто "шатаешь", даже щелчка не делаешь, а значения уже меняются, хотя энкодер новый, не расшатанный.

    int32_t newEncoderPos = controlEncoder.read() / 4;
 
А Гайвер там что-то про двухимпульсные пишет или типа того? У меня один клиент тоже ссылался про эти волшебные Гайверовские мануалы, когда перевесил энкодер с D2/D3 на те пины, которые ему показались более удобными и поймал тот же самый баг с неуправляемостью энкодера. Вернулся обратно на External Interrupt pins и всё как рукой сняло с тем же самым энкодером. 
 
b707
Offline
Зарегистрирован: 26.05.2017

Да у гайвера там и на прерываниях написана какаято ересь...

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

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

Kakmyc
Offline
Зарегистрирован: 15.01.2018

А я вообще так до сих пор и не понял, зачем нужна библиотека энкодера, там кода 2-3 строки достаточно.

AsNik
Offline
Зарегистрирован: 24.10.2020

sadman41 пишет:

А Гайвер там что-то про двухимпульсные пишет или типа того? 

Ну типа того, вот с его сайта:

Бывает два типа энкодеров, я назвал их одноимпульсные и двухимпульсные, тип энкодера можно определить по внешнему виду самого энкодера:
 

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

int32_t newEncoderPos = controlEncoder.read() / 4; - Это помогло.

В общем когда я в основном коде поменял использование энкодера с прерываниями с гайверовской либой, стало лучше, но пропуски шагов стали ужасно... т.е. ручку нужно медленно крутить, тогда норм и то может и пропустить.... Хотя я только начал писать скеч и он не такой большой, да собс-но там работа только с lcd и энкодером... Странно. Буду пробовать с другими либами...

И еще такой непонятный глюк появился. Если сразу как только запустить Arduino IDE 1.8.10 и в нем уже по умолчанию загружен скеч. Нажимаем "компиляция" или "Загрузка" не важно - то сразу вылазит:

E:\...\ARDUINO\libraries\LiquidCrystal_I2C-master\LiquidCrystal_I2C.cpp:71:39: warning: unused parameter 'cols' [-Wunused-parameter]
 
 void LiquidCrystal_I2C::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
 
                                       ^
Но если ничего не менять и сразу повторно нажать туже "компиляцию" или "загрузку" - то все без варнинга компилируется
Я посмотрел LiquidCrystal_I2C.cpp и там действительно вроде не используется cols. Но почему при повторной компиляции он не выдает этот варнинг?
sadman41
Offline
Зарегистрирован: 19.10.2016

Потому что файл не перекомпилируется, если он не изменялся.

AsNik
Offline
Зарегистрирован: 24.10.2020

andriano пишет:

Вам еще либа для обработки кнопки нужна?

Воспользуйтесь "титановым велосипедом".

Ну это по большей части из-за лени... Если есть готовый код, то почему бы и не воспользоваться...

Так-то можно вообще не использовать библиотек и самому все делать с нуля :)

А у меня не просто энкодер, а модуль уже с RC (китайский), поэтому хотелось как проще и меньше писанины, но.... получается как всегда). Вообще первый раз с энкодерами связался...

AsNik
Offline
Зарегистрирован: 24.10.2020

sadman41 пишет:
Потому что файл не перекомпилируется, если он не изменялся.

Все равно не понятно.

Вот я открыл IDE с уже загруженном скечем. Нажимаю первый раз компиляция. Варнинг. Сразу второй раз. Чисто. Ведь я же его не менял.

Но даже если что-то изменить, сохранить. Закрыть ИДЕ. И заново открыть. То опять первый раз варнинг, второй четко.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

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

А мой код на энкодер выглядит так:

#define ENC_B 3

volatile int encTick=0;

void enc_ISR(){
				digitalRead(2)==digitalRead(ENC_B)?encTick=1:encTick=-1;
}

void setup(){
				attachInterrupt(0,enc_ISR,CHANGE);
				pinMode(ENC_B,INPUT_PULLUP);
}
void loop(){
				
}

 

 

Зачем тут библиотека ?

AsNik
Offline
Зарегистрирован: 24.10.2020

Kakmyc пишет:

Зачем тут библиотека ?

Ну данный код по большому счету и ничего и не делает. Это как бы ядро.

Но даже так:

#define ENC_B 3

volatile int encTick = 0;

void enc_ISR() {
  digitalRead(2) == digitalRead(ENC_B) ? encTick = 1 : encTick = -1;
}

void setup() {
  attachInterrupt(0, enc_ISR, CHANGE);
  pinMode(ENC_B, INPUT_PULLUP);
  Serial.begin(9600);
}

int cnt = 0;
void loop() {
  if (encTick != 0) {
    cnt += encTick;
    encTick = 0;
    Serial.println(cnt);
  }
}

(Давно я не программировал... Можно ли так менять волатайловскую переменную?)

у меня получается два тика на один щелчок. 

Может у меня энкодер дибильный :(

В прочем если дальше с этим ядром делать то что нужно + обработка кнопки, короткое нажатие, длинное удержание... Получится кода на еще одну библиотеку :)

Kakmyc
Offline
Зарегистрирован: 15.01.2018

AsNik пишет:

(Давно я не программировал... Можно ли так менять волатайловскую переменную?)

у меня получается два тика на один щелчок. 

Может у меня энкодер дибильный :(

В прочем если дальше с этим ядром делать то что нужно + обработка кнопки, короткое нажатие, длинное удержание... Получится кода на еще одну библиотеку :)

А кнопка вообще к энкодеру никакого отношения не имеет.
Там должен быть отдельный обработчик.
attachInterrupt() может быть не только CHANGE(с ним и будет два тика), а ещё и FALLING/RISING.
В коде был показан обработчик энкодера, как дальше использовать результат обработки, это уже отдельная песня.
Смысл приведения данного кода в том, что бы показать, что библиотека (любая,не говоря уже про библиотеку гивера) в принципе нафиг не нужна

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

в русском языке прижилось слово - волатильность )))
самое сложное как раз - как это использовать

sadman41
Offline
Зарегистрирован: 19.10.2016

AsNik пишет:

sadman41 пишет:
Потому что файл не перекомпилируется, если он не изменялся.

Все равно не понятно.

Вот я открыл IDE с уже загруженном скечем. Нажимаю первый раз компиляция. Варнинг. Сразу второй раз. Чисто. Ведь я же его не менял.

Но даже если что-то изменить, сохранить. Закрыть ИДЕ. И заново открыть. То опять первый раз варнинг, второй четко.


1) Открыл скетч (исходник), нажал "компиляция", компилятор обнаружил в исходнике проблему - отсигнализировал, но объектный код создал, положил в каталог кэша.
2) Не закрывая IDE нажал опять "компиляция" - компилятор обнаружил, что объектный код уже есть, исходник не менялся, компиляцию делать не стал - ошибку не вывел.
3) Закрыл IDE - потерял пути к кэшированным файлам. Goto 1.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

AsNik пишет:

Странно. Буду пробовать с другими либами...

Вы, похоже, не читаете, что Вам пишут.

Вам нужно устранять проблему, а не перебирать библиотеки.

AsNik пишет:

Ну это по большей части из-за лени... Если есть готовый код, то почему бы и не воспользоваться...

Потому, что Вы не понимаете, как этот код работает.

Цитата:

Так-то можно вообще не использовать библиотек и самому все делать с нуля :)

Я обычно так и делаю. И, поверьте, у меня во много раз реже появляются вопросы "А почему не работает?".

AsNik
Offline
Зарегистрирован: 24.10.2020

sadman41 пишет:
AsNik пишет:

sadman41 пишет:
Потому что файл не перекомпилируется, если он не изменялся.

Все равно не понятно.

1) Открыл скетч (исходник), нажал "компиляция", компилятор обнаружил в исходнике проблему - отсигнализировал, но объектный код создал, положил в каталог кэша. 2) Не закрывая IDE нажал опять "компиляция" - компилятор обнаружил, что объектный код уже есть, исходник не менялся, компиляцию делать не стал - ошибку не вывел. 3) Закрыл IDE - потерял пути к кэшированным файлам. Goto 1.

Это все понятно, но... Если на втором шаге, после второй компиляции ничего не закрывать(из IDE не выходить) а поменять что-то в тексте и нажать еще раз(третий, четвертый...) компиляцию, то варнинга больше нет.

Меня вот этот момент и задел. Как так, что варнинг показывается только при первой компиляции, а потом типа просто помни про него :( Я смотрел код библиотеки дисплея и его функцию begin и в теле begin действительно не используется передаваемый в нее параметр cols.

b707
Offline
Зарегистрирован: 26.05.2017

AsNik пишет:

Это все понятно, но... Если на втором шаге, после второй компиляции ничего не закрывать(из IDE не выходить) а поменять что-то в тексте и нажать еще раз(третий, четвертый...) компиляцию, то варнинга больше нет.

И что в этом непонятного? Компилятор пересобирает только те обьектные файлы, исходники которых изменились. Вы меняете только текст своего скетча, текст библиотеки (той в которой возникает предупреждение) - не меняется, поэтому он не пересобирается и варнинга нет.

AsNik
Offline
Зарегистрирован: 24.10.2020

andriano пишет:

Вы, похоже, не читаете, что Вам пишут.

Я Вас понимаю и Вы в общем-то говорите правильно. Но как всегда есть но...

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

В третьих ну не зря же делаются эти библиотеки, Да, есть грамотно сделанные, есть не очень, ну и совсем фуфло тоже есть.... Может быть Вы себя считаете лучше всех в программировании, тогда да, вопросов нет. Лучше делать все с нуля и самому.

Но я например не уверен, что сделаю лучше даже чем гайвер, или даже уверен что многое не сделаю лучше чем сделано уже другими. Поэтому не считаю зазорным "пользоваться чужим" ибо это для этого и предназначено.

В конкретном моем случае я не уверен где основная ошибка. В библиотека энкодера гайвера, в библиотеке LCD, в версии IDE(ага, оказывается и такое может быть (но это не совсем так, новые компиляторы не совместимы со "старыми библиотеками" т.е. по сути дело не в IDE а в либах) или в моем коде. Хотелось бы разобраться.

Даже если самому с нуля делать можно так же напороться на грабли и долго искать проблему.

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

AsNik
Offline
Зарегистрирован: 24.10.2020

b707 пишет:

И что в этом непонятного? Компилятор пересобирает только те обьектные файлы, исходники которых изменились. Вы меняете только текст своего скетча, текст библиотеки (той в которой возникает предупреждение) - не меняется, поэтому он не пересобирается и варнинга нет.

Все теперь понятно, вопросов больше по данному вопросу не имею. Спасибо.

AsNik
Offline
Зарегистрирован: 24.10.2020

На данный момент с библиотекой гайвера работа с экодером даже по двум прерываниям происходит ужасно. Очень сильно пропускаются шаги. Ручку нужно крутить ну очень медленно, что бы работало... Поэтому буду пробовать может быть самому обрабатывать этот энкодер, например за основу возьму #25.

Но как-то странно получается. Я пересмотрел(поверхностно) код всех либ, которые тут озвучены. В некоторых много чего используется... А по сути на выходе тоже самое что и в #25 Не все так просто как кажется и не все так сложно как есть на самом деле :)

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

AsNik пишет:

На данный момент с библиотекой гайвера работа с экодером даже по двум прерываниям происходит ужасно. Очень сильно пропускаются шаги. Ручку нужно крутить ну очень медленно, что бы работало... Поэтому буду пробовать может быть самому обрабатывать этот энкодер, например за основу возьму #25.

Но как-то странно получается. Я пересмотрел(поверхностно) код всех либ, которые тут озвучены. В некоторых много чего используется... А по сути на выходе тоже самое что и в #25 Не все так просто как кажется и не все так сложно как есть на самом деле :)

попробуйте библиотеку из поста #12, она точно не пропускает, числа правда даёт в относительных, от начала старта единицах, вот пример как устанавливаю частоту:
 

static int pos = 0;
  encoder.tick();

  int newPos = encoder.getPosition();
  if (pos != newPos) {
      if(max(newPos,pos) > pos){freq = freq +incr;}else{freq = freq - incr;}
      
    Serial.print("Freq = ");
    Serial.println(freq);
    ftFreq = freq/10;
    Serial.println(freq/1000.0f, 2);
    Radio.SetVfoFreq(ftFreq);
    pos = newPos;
  }

 

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014

Пробуйте

bool f_two = 0;

void two() {// Прерывание Энодера В
  f_two = 1;
}

void one() {// Прерывание Энодера А
  if (f_two) { //Если было Прерывание Энодера B
    f_two = 0;
    digitalRead(2)==digitalRead(ENC_B)?encTick=1:encTick=-1
  }
}

void setup() {
  attachInterrupt(0, one, FALLING);
  attachInterrupt(1, two, FALLING);

}

 

Kakmyc
Offline
Зарегистрирован: 15.01.2018

А вообще,что бы дисплей с энкодером не конфликтовали, дисплей нужно обновлять только тогда, когда обновляются выводимые данные, да и в принципе если это будет происходит с частотой достаточной для восприятия (скажем 5-10Гц), таких проблем не будет.

sadman41
Offline
Зарегистрирован: 19.10.2016

Накладки и задержки всегда будут. Выводи хоть раз в секунду, хоть раз в 10 секунд. Не с дисплеем, так с другим чем-то всплывёт задержка.

AsNik
Offline
Зарегистрирован: 24.10.2020

ua6em пишет:

попробуйте библиотеку из поста #12, она точно не пропускает, числа правда даёт в относительных, от начала старта единицах....

  if (pos != newPos) {
      if(max(newPos,pos) > pos){freq = freq +incr;}else{freq = freq - incr;}

Попробовал. Там в этом RotaryEncodor'е есть кстати getDirection()... Правда я со своими познаниями си сходу не понял как ее использовать. Но вот:

#include <RotaryEncoder.h>

// Setup a RoraryEncoder for pins:
RotaryEncoder encoder(2, 3);

void setup()
{
  Serial.begin(9600);
  Serial.println("SimplePollRotator example for the RotaryEncoder library.");
} // setup()


// Read the current position of the encoder and print out when changed.
int pos = 0;
void loop()
{
  encoder.tick();
  delay(10);
  int n = (int)encoder.getDirection();
  if (n == 1) {pos++; Serial.println("Rotate Right");}
  if (n == -1) {pos--; Serial.println("Rotate Left");}
  if (n != 0) {Serial.println(pos);}
} // loop ()

Если убрать delay(10) - (довольно большая в принципе задержка, но все же) то да, пропусков нет.

В общем наверное не все так просто с этими энкодерами(

PS:

#include <RotaryEncoder.h>

// Setup a RoraryEncoder for pins:
RotaryEncoder encoder(2, 3);

void setup()
{
  Serial.begin(9600);
  Serial.println("SimplePollRotator example for the RotaryEncoder library.");
} // setup()


// Read the current position of the encoder and print out when changed.
int pos = 0;
void loop()
{
  encoder.tick();
  delay(10);
  switch (encoder.getDirection()) {
    case RotaryEncoder::Direction::CLOCKWISE:
    pos++; Serial.println("Rotate Right"); Serial.println(pos);
    break;
    case RotaryEncoder::Direction::COUNTERCLOCKWISE:
    pos--; Serial.println("Rotate Left"); Serial.println(pos);
    break;
    case RotaryEncoder::Direction::NOROTATION:
    break;
  }
} // loop ()

 

AsNik
Offline
Зарегистрирован: 24.10.2020

Kakmyc пишет:
А вообще,что бы дисплей с энкодером не конфликтовали, дисплей нужно обновлять только тогда, когда обновляются выводимые данные, да и в принципе если это будет происходит с частотой достаточной для восприятия (скажем 5-10Гц), таких проблем не будет.

Дык они и обновляются при вращении ручки энкодера :( Но вообще полностью согласен. Здесь по ходу нужно какую-то "хитрую" логику делать...

Но тут по всей видимости не только в дисплее дело :(

 

sadman41
Offline
Зарегистрирован: 19.10.2016

В библиотеке Стоффрегена прерывания подключаются в бэкграунде, если пины, на которые терминалы заведены, их поддерживают.

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

AsNik
Offline
Зарегистрирован: 24.10.2020

vosara пишет:

Пробуйте

.....

Самый лучший результат пока получился. Спасибо.

В прочем это практически код из 25. Моя лень и низкие знания си не дали мне сразу сообразить это:


#define ENC_B 3
volatile int encTick=0;
bool f_two = 0;

void two() {// Прерывание Энодера В
  f_two = 1;
}

void one() {// Прерывание Энодера А
  if (f_two) { //Если было Прерывание Энодера B
    f_two = 0;
    digitalRead(2)==digitalRead(ENC_B) ? encTick=1 : encTick=-1;
  }
}

void setup() {
  attachInterrupt(0, one, FALLING);
  attachInterrupt(1, two, FALLING);
  Serial.begin(9600);
}

int cnt = 0;
void loop() {
  if (encTick != 0) {
    cnt += encTick;
    encTick = 0;
    Serial.println(cnt);
  }
  delay(50);
}

Но vosara мне дал хорошего пинка)

Как видно задержку я увеличил в пять раз (по сравнению с кодом использования библиотеки RotaryEncoder в #40) и все равно пропусков нет.

Да, если ручку вертеть слишком быстро, то может и пропускаются шаги, но они идут (в данном случае cnt увеличивается/уменьшается) строго на 1

В общем остановлюсь я пока на данном варианте

AsNik
Offline
Зарегистрирован: 24.10.2020

Немного подправил. Сделал для теста вот такой код:

#include <LiquidCrystal_I2C.h>

#define ENC_A  2
#define ENC_B  3

LiquidCrystal_I2C lcd(0x27, 20, 4);

bool f_isrA = false;
volatile char encTick = 0;
void isrA() {
  f_isrA = true;
}

void isrB() {
  if (f_isrA) {        //Если было Прерывание Энодера A
    f_isrA = false;
    digitalRead(ENC_A) == digitalRead(ENC_B) ? encTick = 1 : encTick = -1;
  }
}

void setup() {
  lcd.init(); lcd.backlight();
  attachInterrupt(0, isrA, FALLING);    // прерывание на 2 пине! A у энка
  attachInterrupt(1, isrB, FALLING);    // прерывание на 3 пине! B у энка
  Serial.begin(9600);
}

int cnt = 0;
void loop() {
  if (encTick != 0) {
    cnt += encTick;
    encTick = 0;
    Serial.println(cnt);
  }
  UpdateScreen();
  delay(50);    //Специальная задержка для теста
}

#define PERIODMS 400
uint32_t msscr = 0;
void UpdateScreen() {
  lcd.setCursor(0, 0);
  lcd.print("Test Encodera");  //Специальная задержка для теста
  static bool f = true;

  if (millis() - msscr >= PERIODMS) {
    f = !f; msscr += PERIODMS;
  }
  lcd.setCursor(0, 2);
  lcd.print("Value: "); if (!f) lcd.print("    "); else lcd.print(cnt);
}

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

Вопрос: переменную bool f_isrA = false; не нужно делать валотайловой(или валотильной, как правильно, вообщем volatile)?

Она хоть и используется только в прерываниях, но всеж...

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

AsNik пишет:

Но я например не уверен, что сделаю лучше даже чем гайвер, или даже уверен что многое не сделаю лучше чем сделано уже другими. Поэтому не считаю зазорным "пользоваться чужим" ибо это для этого и предназначено.

Нет, концепция Ардуино именно это и подразумевает: Вы берете 2-3 библиотеки и "соединяете" их собственными 5-10 строчками. Но, увы, работает это только в самых простых случаях и идеальных условиях.

Цитата:

В конкретном моем случае я не уверен где основная ошибка. В библиотека энкодера гайвера, в библиотеке LCD, в версии IDE(ага, оказывается и такое может быть (но это не совсем так, новые компиляторы не совместимы со "старыми библиотеками" т.е. по сути дело не в IDE а в либах) или в моем коде. Хотелось бы разобраться.

Еще раз обращаю Ваше внимание на последний абзац сообщения №2. Проблема не в самих библиотеках, а в том, как Вы их используете. Вы допускаете неприемлемо большие паузы между вызовами encoder.tick(), из-за чего происходят пропуски сигналов энкодера. Со всеми вытекающими последствиями.

Logik
Offline
Зарегистрирован: 05.08.2014

//Да, если ручку вертеть слишком быстро, то может и пропускаются шаги

разумеется будут пропуски если cnt суммировать в лупе, а не там, где encTick получен.

Если это поправить, то код будет работать не плохо пока контакты энкодера не окислятся. Потом на один шаг начнет несколько единиц прибавлять. Чтоб это поправить надо задуматься над несоответствием - контакты енкодкра выдают в общем одинаковый сигнал несколько сдвинутый по фазе. А обработчики 2-х прерываний сильно разные. Потому давят "дребезг" только в половине случаев. Обработчики тоже надо сделать с одинаковой логикой для спада обоих входных сигналов. 

И еще. "Дребезг" вызывает частые вызовы обработчиков прерывания, что плохо. Потому в качестве f_isrA лучше использовать флаги разрешения прерываний. Т.е. всегда разрешено только одно из прерываний. Например разрешено сразу isrA. При срабатывании isrA разрешаем isrB и запрещаем isrA. Потом при срабатывании isrB разрешаем isrA и запрещаем isrB, ну и encTick присваиваем (или может увеличиваем или уменьшаем? ;)

// Вопрос: переменную bool f_isrA = false; не нужно делать валотайловой(или валотильной, как правильно, вообщем volatile)?

Тут пофиг оно.

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

AsNik пишет:

Может быть Вы себя считаете лучше всех в программировании, тогда да, вопросов нет.

Вопрос не в том, лучше всех или нет. Вопрос в том, что Вы должны понимать, как работает ЛЮБОЙ кусок используемого Вами кода, вне зависимости от того, написан он Вами или кем-то другим. Вы же хотите использовать библиотеки, не разбираясь, как они работают. И проблема именно в этом.

AsNik
Offline
Зарегистрирован: 24.10.2020

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

#46 - спасибо. Т.е. пока у меня новый энкодер, код из #44 работает нормально. Но скоро придет проблема. Правильно я понял?

Буду учитывать замечания по дребезгу и прерываниям. Всем спасибо.

AsNik
Offline
Зарегистрирован: 24.10.2020

andriano пишет:

Вы должны понимать, как работает ЛЮБОЙ кусок используемого Вами кода, вне зависимости от того, написан он Вами или кем-то другим. Вы же хотите использовать библиотеки, не разбираясь, как они работают.

 

В логике я еще разберусь, но для меня сложна в разбирании бинарная математика :( Если Вы обратили внимание, то я иногда тут поправляю код. Стараюсь его оптимизировать. Может и не получается у меня, но я ...стараюсь) И вы(все) мне в этом очень помогаете. Спасибо

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

andriano пишет:

 Вы допускаете неприемлемо большие паузы между вызовами encoder.tick(), из-за чего происходят пропуски сигналов энкодера. Со всеми вытекающими последствиями.

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