Вопросы по библиотеке GyverEncoder

Старик Похабыч

★★★★★★★
14 Авг 2019
4,198
1,283
Москва
Ну если важно делать именно постоянное вычисление, то стоит посмотреть пример из библиотеки энкодера "enc_pinChangeInt"
 
  • Лойс +1
Реакции: nik45ru

poty

★★★★★★✩
19 Фев 2020
3,003
898
@nik45ru, я, конечно, всё понимаю, но мне интересно, кто будет читать экран даже 50 раз в секунду при "постоянных расчётах и выводе"? Вы ставьте реальные задачи, не знаю, цели или параметры какие-то. Какой смысл выводить данные чаще, чем они изменяются? Кто будет улавливать значение в мелькающих цифрах?
 

nik45ru

✩✩✩✩✩✩✩
7 Фев 2021
21
0
@poty, про экран конечно согласен, мне важней непрерывный расчёт, (получение данных с ацп, расчёт с плавающей точкой) просто не думал, что библиотека так чувствительна к времени. 18 лет назад, занимался чем то подобным с платками НВЛ-1 и ЛА-70. После женитьбы всё забросил, два года назад ностальгия замучила, взялся за паяльник, а вот пару недель назад взялся за ардуинку. Как тяжко всё вспоминать и навёрстывывать.
 

nik45ru

✩✩✩✩✩✩✩
7 Фев 2021
21
0
Это я уже понял :) Вот так
C++:
ADMUX  = 0b01000000;  // 0B0100000 10 bit A0
ADCSRA = 0b11110011;  // Делитель частоты на 8

val = (ADCL|ADCH << 8);
меня в принципе устраивает и скорость и точность.
А кто-нибудь ЦАП резистивный на Ардуике делал? На мега в принципе достаточно выводов и для подключения энкодера и 10бит ЦАП и на управление ещё останется. Или лучше внешний. Я немного пролетел, думал, что аналоговые входы/выходы (АЦП/ЦАП), а оказалось только АЦП. Сейчас блин в лучшем случае 30 дней с китая ждать.
P.S. Так то это уже флуд. :)
 

poty

★★★★★★✩
19 Фев 2020
3,003
898
@nik45ru, давайте быть объективными: расчёт без использования - время на ветер.
Расчетная часть Ардуино - не самая сильная её часть. Я бы ещё перевёл все вычисления на целочисленные "рельсы", если другого выхода нет. А в целом - использовал бы другой МП.
Если количество входных датчиков велико и они капризные (типа энкодера), самым правильным является использование нескольких маленьких Ардуинок, принимающих данные от датчиков в реальном времени, обрабатывающие их для минимизации объема и передающие в асинхронном режиме центральному МП. То же касается выходных потоков: большие объемы требуют больших затрат в коде и во времени, приложения реального времени здесь уже получаются не очень.
 
  • Лойс +1
Реакции: Старик Похабыч

nik45ru

✩✩✩✩✩✩✩
7 Фев 2021
21
0
Если ваш код
снабдить выводом по изменению значения, например так:
C++:
int n=0;
int old_n=0;

void loop() {
enc1.tick();
if (enc1.isRight()) n++;
if (n!=old_n)
  {
   old_n=n;
  Serial.print(n);
  }
ShowFPS();
}
То скорость будет около 63 000 циклов loop в секунду.
Однако самое правильное было выводить это все в условии там, где меняется n, как в 1-ом посте работающие примеры, скорость будет 66000 lps
Если в условие добавить ещё одну изменяющююся переменную, всё перестаёт работать. Через прерывания ситуация чуть получше, если очень медленно вращать энкодер, то показания успевают считаться.
Похоже единственный рабочий и правильный выход это вывод по таймеру на millis.
C++:
if (millis() - Timer >= 300) {
  Timer = millis(); 
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("U=");
  lcd.print(U);
  lcd.print(L"в");
  lcd.setCursor(8,0);
  lcd.print("P=");
  lcd.print(P);
  lcd.print(L"Вт");
  lcd.setCursor(0,1);
  lcd.print("I=");
  lcd.print(I);
  lcd.print(L"А");
}
так у меня всё работает. :)
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,198
1,283
Москва
@nik45ru, Так тут смотря что делать надо. В том виде , что я пробовал, я менял только n, т.к. она целочисленная, то отслеживать мне проще ее изменение.
А если меняются несколько данных, то возможно проще отслеживать изменение результата. Если результат имеет тип float, то без жесткого сравнения на равенство.
А так.. я не знаю что Вы и куда добавили. Но видя как Вы с одной переменной сделали , то верю Вашу способность убивать рабочие коды! :D
 

nik45ru

✩✩✩✩✩✩✩
7 Фев 2021
21
0
@Старик Похабыч,
С двумя это к примеру так
C++:
int n=0;
int old_n=0;

void loop() {
a=analogRead(A0);
enc1.tick();
if (enc1.isRight()) n++;
b=a*n;
if (n!=old_n || a!=old_a)
  {
    old_n=n;
    old_a=a;
   lcd.clear();
   lcd.print(b);
  }
На входе А0 50Гц - всё, энкодер умер. Эта конструкция конечно бессмысленна, чисто так, для проверки. Если через прерывание, то данная конструкция работает, при очень медленном вращении энкодера. Ну а через таймер работает даже то, что привёл выше :)
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,198
1,283
Москва
Тут уже немного другая фигня.
Задержка будет складываться из 1) времени получение аналогового сигнала 2) дребезга самого сигнала. Ну и опять же код куском, где прописано old_а непонятно.

@poty, абсолютно верно все написал. Что надо делать с показаниями ? что надо делать с изображением ?
у меня есть пульт РУ, я могу обрабатывать в нем для передачи 4 потенциометра, 8 кнопок, поворот энкодера, это все считывается и передается, и еще какая то информация идет на экран. Так что надо ставить четкие задачи.
В другом проекте мне надо максимально быстро считывать показание за показанием в память с аналоговго или цифровых входов и потом уже обрабатывать. Читаю все в буфер, сиречь массив, и естественно там никакого вывода нет и в помине.
С какой частотой надо выводить данные на экран ? 20 раз в секунду достаточно для глаза.. Но экран может иметь свои ограничения, например в графическом виде не более 10 раз, а чаще меньше. Если поставить 20 раз, то МК только и будет, что выводить на экран и все.
Теперь к вашему коду. Да, он будет работать, отображение (для контроля) на экране 3 раза в секунду. Будет ли считывание показаний при этом равномерным ? увы нет. Надо ли равномерно ? я не знаю.
А так может и раз в секунду хватит.
 

nik45ru

✩✩✩✩✩✩✩
7 Фев 2021
21
0
Делаю ЛБП. Нужно измерять температуру на двух радиаторах, падение напряжения на шунте (ток) и напряжение на выходе, для выходного напряжения точности штатного АЦП не хватит,так как нужно измерять 50в с шагом 0.01в, также нужно два аналоговых выхода для управления ШИМ микросхемой. Выводится на экран должно: напряжение, ток, мощность, количество залитых ампер, температуры радиаторов. Энкодер регулирует напряжение и ток, по одинарному нажатию переключается между напряжением и током, по двойному нажатию сбрасывает выход на 0. нагрев должен отслеживаться по dT/t, если дельта стала быстро возрастать, то автоматом снижать выходную мощность. В идеале конечно лучше замерять температуру на подложке каждого силового транзистора, бывает при высыхании конденсатора базы, транзистор быстро начинает разогреваться, не успевая отдавать тепло радиатору, подплавляет пластиковую шайбу, отваливается от радиатора, отправляется на тот свет. Вроде слишком много хочу :)
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,198
1,283
Москва
нагрев должен отслеживаться по dT/t, если дельта стала быстро возрастать,
dt какое предполагается ?
У Гайвера есть хорошая библиотека, гайвертаймер. Может снимать показания с датчиков температуры по таймеру ? И , кстати, чем измерять температуру предполагается ? с какой точностью?

В наливаторе я например по кручению энкодера выставлял угол сервы или шагоивика. Так вот если на каждое действие энкодера менять значение угла на экране получается фигня. Но угол на экран выводиться только для информации, поэтому я сделал так: если энкодер не крутили боле 0.5 (или сколько то там, не помню) сек, то обновить экран, если надо конечно. Да, есть задержка, но углы выставляют не по значению, а визуально, и уже потом смотрят на экран
 

nik45ru

✩✩✩✩✩✩✩
7 Фев 2021
21
0
Температура должна будет измеряться не менее 10 рас в секунду, лучше чаще. Но это всё будет подстраиваться эмпирически, так как часто теория не совпадает с практикой. Температуру конечно лучше измерять термопарой, но тогда придётся усилитель делать, наверное, все таки придётся терморезисторы использовать, как раз есть два, мало инертных, даже не знаю откуда взялись.
 

никта

✩✩✩✩✩✩✩
23 Апр 2021
1
0
помогите пожалуйста , сделал гавнокода , а энкодер не откликается , вообще 0 движений , проверил на примере пашет ,
вопрос в чём я накосячил ?
гавнокод:
#include <Wire.h>
#include <LCD_1602_RUS.h>
#include <GyverEncoder.h>
#include <nRF24L01.h>
#include <printf.h>
#include <RF24.h>
#include <RF24_config.h>
uint32_t myTimer2;
LCD_1602_RUS lcd(0x27, 20, 4);
byte poza, nov_poza = 2 ;
Encoder enc1(A0, A1, 13);
 bool NO, INT = 1;

void setup() {
  pinMode(A3, INPUT_PULLUP);
  Serial.begin(9600);
     enc1.setType(TYPE2);
}

void loop() {
 enc1.tick();
  if (millis() - myTimer2 >= 500) {   // ищем разницу (500 мс)
    myTimer2 = millis();              // сброс таймера
    NO = !(digitalRead(A3));   // если a больше b, то c = 10

  }
  if (NO) {
    if (INT) {
      INT = 0;
      lcd.init() ;
      lcd.backlight();
      lcd.display();
  
    }
    
    if (enc1.isRight())nov_poza = constrain(nov_poza + 1, 1, 4);
    if (enc1.isLeft())nov_poza = constrain(nov_poza - 1, 1, 4);
    if (poza != nov_poza) {
      lcd.setCursor(0, poza - 1);
      lcd.print(" ");
      lcd.setCursor(0, nov_poza - 1);
      lcd.print(">");
      poza = nov_poza;
    }

  }
  Serial.print(INT);
  Serial.println(NO);
}
 

bort707

★★★★★★✩
21 Сен 2020
2,911
865
в вашем коде энкодер будет работать, только если нажать на кнопку А3 и удерживать ее
 

xa0c

✩✩✩✩✩✩✩
26 Апр 2021
7
0
Что-то не могу понять смысла вот этого куска кода:
GyverEncoder.cpp:
        if (!SW_state && flags.butt_flag && (debounceDelta > ENC_DEBOUNCE_BUTTON)) {
            if (!flags.turn_flag && !flags.hold_flag) {  // если кнопка отпущена и ручка не поворачивалась
                flags.turn_flag = false;
                flags.isRelease_f = true;               
            }
Ещё и с комментарием. Почему условие !flags.turn_flag?
Как при таком отлавливать релиз энкодера после окончания прокрутки?
Другими словами, вот этот код без удаления !flags.turn_flag работать не будет при отпускании энкодера после прокрутки.
sample:
  if (enc.isReleaseHold()) {
    Serial.println("encoder released after hold");
  }
 
  if (enc.isRelease()) {
    Serial.println("encoder released");
  }

  if (enc.isRightH()) {
    Serial.println("R");
  }
При дефолтных настройках холда в 700мс и незакоментированном условии !flags.turn_flag отлавливается только релиз долгого нажатия, всё, что короче 700мс отрабатывает isRightH, но без какого-либо релиза.

P.S. Ну и использование слова "holded" — это забавно :)
 

poty

★★★★★★✩
19 Фев 2020
3,003
898
@xa0c, ну так, видимо, нажатие-удержание-отпускание и дифференцируется от нажатия-отпускания периодом в 700мс. Т.е., всё, что короче 700мс считается click-ом. Наверняка саму цифру в 700 мс можно настроить.
 

xa0c

✩✩✩✩✩✩✩
26 Апр 2021
7
0
Ну так тогда и isRightH() не должен бы срабатывать, если "H" было меньше 700мс? :)
Я к тому, что "поворот с удержанием" работает всегда, а "отпускание кнопки" срабатывает только если крутили кнопку дольше 700мс (isReleaseHold()), либо если не крутили вообще (isRelease()), то есть нельзя сделать "громкость по вращению вправо, а перемотка по вращению вправо с удержанием", если "мотал" быстрее, чем 700мс.
 

poty

★★★★★★✩
19 Фев 2020
3,003
898
В описании библиотеки, в принципе, это расписано.
Кнопки имеют состояния и генерят события. Состояния: не нажато, нажато (isHold()), удержано (isHolded()). События - переходы между состояниями. События кнопки отслеживаются отдельными функциями (isPress(), isRelease() и т.п.).
Событие isReleaseHold() описано как "возвращает true при отпускании кнопки после удержания, сама сбрасывается в false" (выделено мной). Т.е., должно быть зафиксировано событие нажатия, состояние нажатия продолжается больше 700мс, превращаясь в состояние удержания, затем - выход из этого состояния индицируется событием isReleaseHold(). Если вращение начато ранее 700 мс, то перехода в состояние удержания не происходит, это интерпретируется как "нажатие с кручением". Я так понимаю сделано это для использования в меню: просто кручение изменяет один параметр, нажатие с кручением - другой, просто нажатие без кручения - переход между пунктами меню, удержание - ввод, двойной клик, например, отмена.
isRightH() - "возвращает true при удержании кнопки и повороте направо, сама сбрасывается в false" - описывает как раз ситуацию нажал-повернул, без контроля входа в состояние удержания, лишь с контролем того, нажата ли кнопка. Соответственно контроль кнопки реализуется isPress(), isRelease(), isClick().
 

xa0c

✩✩✩✩✩✩✩
26 Апр 2021
7
0
Если вращение начато ранее 700 мс
Если вращение закончено ранее 700мс.

В комментарии кода явно говорится "не генерировать событие отпускания кнопки, если энкодер крутился", вот этот момент неясен. Потому как "если энкодер крутился, но дольше 700мс, то событие отпускания кнопки генерить".
Я думаю, тут ошибка. Т.е такой код, например, позволит отлавливать и isRelease(), и isReleaseHold() без каких-либо последствий, как мне кажется (см. коммент):
GyverEncoder.cpp:
        if (!SW_state && flags.butt_flag && (debounceDelta > ENC_DEBOUNCE_BUTTON)) {
            if (/*!flags.turn_flag && */!flags.hold_flag) {
                //flags.turn_flag = false; // зачем флаг устанавливается в false, если 'false' — это часть условия? :)
                flags.isRelease_f = true;               
            }
В текущей же реализации "поймать" момент, когда пользователь перестал крутить нажатый энкодер невозможно.
 

kDn

★★★★★✩✩
18 Ноя 2019
1,103
437
@xa0c, ежели считаете что баг, то просто правьте и пул-реквест в гит. Баги есть, можете ни секунды не сомневаться
* Holded на WasHeld тоже можно поправить, чтобы два раза не вставать)))
 

poty

★★★★★★✩
19 Фев 2020
3,003
898
@xa0c, я думаю, Вы просто не до конца разобрались в библиотеке. Такой функции, как удержание+кручение не предусмотрено. Предусмотрено отдельно удержание и кручение или детектируется кручение при нажатой кнопке, с или без ситуации удержания.
 

xa0c

✩✩✩✩✩✩✩
26 Апр 2021
7
0
Такой функции, как удержание+кручение не предусмотрено.
Ну вот же :)
C++:
    boolean isRightH();                        // возвращает true при удержании кнопки и повороте направо, сама сбрасывается в false
    boolean isLeftH();                        // возвращает true при удержании кнопки и повороте налево, сама сбрасывается в false
Ещё раз, если крутить нажатый энкодер дольше 700мс, то isReleaseHold() возникает, если меньше 700мс, то isRelease() не возникает.
Если бы "отпускание кнопки после кручения" не было предусмотрено, то и isReleaseHold() не возникал бы после 700мс кручения нажатой кнопки.
...ну и оригинальный код (упрощённо) намекает на то, что здесь не всё чисто:
C++:
if (!flagSet) {
  flagSet = false;
}