ARDUINO Виртуальный энкодер EncButton и сдвиговый входной регистр 74HC165

CYBorg16SR

✩✩✩✩✩✩✩
29 Апр 2022
5
1
Доброго времени суток, форумчане!
Прошу помощи у вас, так как уже не знаю как решить свою проблему и уже просто руки опускаются...
Я новичок в программировании Ардуино, поэтому мои познания очень небольшие в этом плане.
Решил я построить свой собственный проект на Ардуино. Устройство представляет из себя простой принцип - приходит аналоговый сигнал в ардуино, обрабатывается по определенным алгоритмам и с помощью внешнего ЦАП выдает измененный сигнал. Основной смысл и сложность кроется в необходимости использования большого количества внешних регуляторов (порядка 13 энкодеров и с пятак простых кнопок). Так как я на примерах и уроках AlexGyver познал прелесть вариации режимов работ энкодера, подключенного напрямую к МК с помощью библиотеки EncButton, мне бы хотелось применить весь этот функционал ко всем 13 используемым энкодерам + кнопкам.
Насколько я знаю, все их можно подключить только с помощью входных сдвиговых регистров. Для этого в библиотеке EncButton есть режим виртуального энкодера.

Я пишу тут, потому что из интернета я понял одно: в этом вопросе все якобы очень просто, поэтому никто ничего толком не расписывает и не спрашивает... К сожалению я тут не догоняю(((

1) Первая проблема с которой я столкнулся - из примеров и описания библиотеки - я не понимаю как выглядит простая работа такого виртуального энкодера и как будет выглядеть такой скетч. Ибо в описании 2 библиотеки EncButton и EncButton2, для каждой свои значения и примеры опроса и как все это организовать - непонятно (лично мне). Например энкодер который подключен напрямую в МК у меня отлично работает так:
Часть скетча...:
#include <EncButton.h>                        // <Библиотека общего энкодера с кнопкой <A, B, KEY>
EncButton<EB_TICK, 3, 4, 5>   encx4;      

int Dval, Rval, x4;
int E1, E2;

void setup() {
Serial.begin(9600);         // порт для связи
}

void loop() {
if (encx4.tick() && Dval==1)   {      
  if (encx4.isRight()  && Rval!=0)   {x4 += E1;}  
  if (encx4.isRightH() && Rval!=0)   {x4 += E2;}  
  if (encx4.isLeft()   && Rval!=0)   {x4 -= E1;}  
  if (encx4.isLeftH()  && Rval!=0)   {x4 -= E2;}  
encx4.resetState();             }
Может конечно что-то можно упростить, ибо часть переменных я не описывал, но главное, что я понимаю как и что тут работает. Как опрашивается энкодер, условия и последующие изменения....
Но как организовать скетч виртуального энкодера и что в какой последовательности будет стоять в скетче - я не понимаю. Первое что прошу помочь объяснить, а лучше показать, как будет выглядеть этот же самый скетч, но в виртуальном режиме. В библиотеке есть пример "Виртуал", но меня там в виртуальной кнопке смутила строчка:
pinMode(4, INPUT_PULLUP); // подтянем пин
насколько я понимаю это пин 4 МК, если да - то причем тут вообще режим виртуальный? непонятно....
Вообщем буду признателен в помощи решения этой проблемы!

2) Вторая проблема это сдвиговый регистр ввода 74HC165.
История аналогичная - в инете простейшие примеры с выводом инфы о битах в монитор порта. Все рассказывают, что потом эти пины можно использовать как хочешь... А как именно - непонятно.... Как я понимаю - нужно опрашивать каждый пин регистра, потом как-то применять значения пина (1/0) для последующей обработки, как-то это собрать в каскад один за другим. Механически собрать понятно как, но как это все дело опрашивать в программе для своих нужд - непонятно.
Более менее понятный скетч я нашел:

Скетч обработки сдвигового регистра:
#define NUMBER_OF_SHIFT_CHIPS   1 // количество регистров
#define DATA_WIDTH   NUMBER_OF_SHIFT_CHIPS * 8 // количество входов
#define PULSE_WIDTH_USEC   5 // задержка при считывании данных

// для хранения считаных байт
// если регистров больше двух, то int меняется на long
#define BYTES_VAL_T unsigned int

// пины для подключения регистра
int pL = 10;                                    // pL - ploadPin
int cEP = 16;                                   // cEP - clockEnablePin
int dP = 14;                                    // dP - dataPin
int cP = 15;                                    // cP - clockPin

BYTES_VAL_T pinValues; // текущее значение пинов
BYTES_VAL_T oldPinValues; // предыдущее значение пинов

// функция для считывания пинов
BYTES_VAL_T read_shift_regs() {
    long bitVal;
    BYTES_VAL_T bytesVal = 0;

    // опрашиваем регистр о состоянии пинов
    digitalWrite(cEP, HIGH);
    digitalWrite(pL, LOW);
    delayMicroseconds(PULSE_WIDTH_USEC);
    digitalWrite(pL, HIGH);
    digitalWrite(cEP, LOW);

    // считываем полученные данные о пинах
    for(int i = 0; i < DATA_WIDTH; i++){
        bitVal = digitalRead(dP);
        bytesVal |= (bitVal << ((DATA_WIDTH-1) - i));
        digitalWrite(cP, HIGH);
        delayMicroseconds(PULSE_WIDTH_USEC);
        digitalWrite(cP, LOW);
    }
   
    // возвращяем результат опроса регистра
    return(bytesVal);
}

// функция для вывода состояния пинов
void display_pin_values(){
    // перебор всех пинов
    for(int i = 0; i < DATA_WIDTH; i++){
        Serial.print("  Button-");
        Serial.print(i);
        Serial.print(": ");
        if((pinValues >> i) & 1){
          Serial.print("ON");
        }else{
          Serial.print("OFF");
        }  
        Serial.println();
    }
    Serial.println();
}

void setup(){
    // для вывода данных в монитор порта
    Serial.begin(9600);

    // установка режима работа пинов
    pinMode(pL, OUTPUT);
    pinMode(cEP, OUTPUT);
    pinMode(cP, OUTPUT);
    pinMode(dP, INPUT);
    digitalWrite(cP, LOW);
    digitalWrite(pL, HIGH);

    // считываем значения с пинов
    pinValues = read_shift_regs();
    // выводим результат
    display_pin_values();
    // сохраняем текущее значение
    oldPinValues = pinValues;
}

void loop(){
    // считываем значения с пинов
    pinValues = read_shift_regs();
    // если значения изменились, то выводим их
    if(pinValues != oldPinValues){
        // выводим результат в монитор порта
        display_pin_values();
        // сохраняем текущее значение
        oldPinValues = pinValues;
    }
    delay(50);
}
Схема работает, при нажатии подключенных кнопок - в порт выдается инфа о нажатых или отпущенных кнопках...
Но я не понимаю: как здесь обратиться к нужному пину? Как его использовать для своих нужд? каждому значению пина при опросе присваивать свою отдельную переменную чтобы потом на нее ссылаться при дальнейших манипуляциях?
На сайте АлекГайвер есть статья про опрос байта и бита, но как я это понял - опрос в пределах МК. Как опросить сдвиговый регистр? (может быть так же, но я что-то не понимаю?)
Вообщем буду признателен в помощи решения и этой проблемы тоже!

3) Ну и самый коронный и главный вопрос: Как заставить их работать вместе (энкодеры и сдвиговый регистр)?))))))
Я понимаю это так: куча механических энкодеров подключается физически к пинам сдвигового регистра, происходит периодический опрос регистра. Потом каким-то образом (надеюсь я пойму с вашей помощью) значения (1/0) пинов регистра передаются (или вернее будет сказать используются) в качестве виртуального состояния виртуальных энкодеров, для которых есть удобная библиотека EncButton. Я просто хочу разобраться в этом вопросе....

Вообщем, спасибо тем кто всё это дочитал, понимаю что много букв, но для меня ситуация патовая, потому что я не знаю уже как решить эту проблему и к кому обратиться(( Буду безумно благодарен каждому за посильную помощь в объяснении того, что я делаю не так(((
 

Svod

✩✩✩✩✩✩✩
22 Апр 2022
15
7
Можно поинтересоватся, зачем столько энкодеров и энкодеры с кнопками или без?
 
  • Лойс +1
Реакции: CYBorg16SR

Kir

★✩✩✩✩✩✩
28 Мар 2020
69
16
@CYBorg16SR,
1) Судя по исходникам, в метод tick нужно передавать состояния сигналов, которые далее будут обрабатываться
C++:
#define encSigPin_A     2
#define encSigPin_B     3

// Типовое использование из примера tickMode
EncButton<EB_TICK, encSigPin_A, encSigPin_B> enc;     // просто энкодер <A, B>

void loop() {
    enc.tick(); 
    ...
}


// Виртуальный эквивалент типового использования
EncButton<EB_TICK, VIRT_BTN> vEnc;

void setup() {
    pinMode(encSigPin_A, INPUT_PULLUP);
    pinMode(encSigPin_B, INPUT_PULLUP);
}

void loop() {
    // WARNING: передавать значения в tick отличные от 0 и 1
    // чревато не определенным поведением
    vEnc.tick(
        !digitalRead(encSigPin_A),
        !digitalRead(encSigPin_B),
    ); 
    ...
}
Т.е. в типовом использование, считывание состояния пинов происходит внутри клвасса, в виртуальном эквиваленте считывание происходит "вручную", и результат передается на обработку в метод tick().

2) Функция read_shift_regs() возвращает битовую маску, каждый бит в которой соответствует состоянию (прошлому) входа 74HC165.
3) проще показать простейший пример для 1-го энкодера:

C++:
#define NUMBER_OF_SHIFT_CHIPS   1 // количество регистров
#define DATA_WIDTH   NUMBER_OF_SHIFT_CHIPS * 8 // количество входов
#define PULSE_WIDTH_USEC   5 // задержка при считывании данных

// для хранения считаных байт
// если регистров больше двух, то int меняется на long
#define BYTES_VAL_T unsigned int

// пины для подключения регистра
int pL = 10;                                    // pL - ploadPin
int cEP = 16;                                   // cEP - clockEnablePin
int dP = 14;                                    // dP - dataPin
int cP = 15;                                    // cP - clockPin


// функция для считывания пинов
BYTES_VAL_T read_shift_regs() {
    // см Скетч обработки сдвигового регистра:
    return(bytesVal);
}

#define SN74HC165_INPUT_A   (0)
#define SN74HC165_INPUT_B   (1)
#define SN74HC165_INPUT_C   (2)
#define SN74HC165_INPUT_D   (3)
#define SN74HC165_INPUT_E   (4)
#define SN74HC165_INPUT_F   (5)
#define SN74HC165_INPUT_G   (6)
#define SN74HC165_INPUT_H   (7)


#define ENC_SIGNAL_MASK_A   (1 << SN74HC165_INPUT_A)
#define ENC_SIGNAL_MASK_B   (1 << SN74HC165_INPUT_B)

EncButton<EB_TICK, VIRT_BTN> vEnc;

void setup(){
    // установка режима работа пинов для
    // реализации програмного SPI, для SN74HC165
    // см Скетч обработки сдвигового регистра:
    ...
}


void loop(){
    // считываем значения с пинов
    BYTES_VAL_T const inputsMask = read_shift_regs();

    // Выделяем логические состояния входов к которым подключен энкодер
    bool encSignal_A = !(inputsMask & ENC_SIGNAL_MASK_A);
    bool encSignal_B = !(inputsMask & ENC_SIGNAL_MASK_B);
    
    // обработка сигналов энкодера
    vEnc.tick(encSIgnal_A, encSIgnal_B); 
    ...
}
 
  • Лойс +1
Реакции: CYBorg16SR

poty

★★★★★★✩
19 Фев 2020
2,956
886
Опасаюсь, что обслуживать столько энкодеров и делать какую-то ещё полезную работу будет невозможно. Для энкодера крайне важно, чтобы опрос состояния длился не более десятых долей миллисекунды. 13 энкодеров - это 26 выводов (и это если не считать встроенных и других кнопок). Чтобы воспользоваться результатами нужно увеличить эту частоту в такое же количество раз. Ну, скажем, получится 260кГц. Такой опрос нужно производить постоянно и успевать обрабатывать.
С моей точки зрения для таких задач больше подойдёт децентрализация обработки энкодеров и соединение их по какой-нибудь шине типа I2C.
 
  • Лойс +1
Реакции: CYBorg16SR

Svod

✩✩✩✩✩✩✩
22 Апр 2022
15
7
Не большой пример на пальцах :)
C++:
#define clockEnablePin 9  // CE
#define ploadPin 8        // PL
#define dataPin 11        // Q7 MISO
#define clockPin 12       // CP
#define pulseWidth 5  // Задержка сдвига
#define chipCount 6   // Количество микросхем

#include "GyverEncoder.h"
Encoder enc1;
Encoder enc2;
Encoder enc3;
Encoder enc4;
Encoder enc5;
Encoder enc6;
Encoder enc7;
Encoder enc8;
Encoder enc9;
Encoder enc10;
Encoder enc11;
Encoder enc12;
Encoder enc13;

bool Button1;
bool Button2;
bool Button3;
bool Button4;
bool Button5;

uint8_t dataWidth = chipCount * 8;
uint64_t inputs;

void setup() {
  Serial.begin(9600);
  pinMode(clockEnablePin, OUTPUT);
  pinMode(ploadPin, OUTPUT);
  pinMode(dataPin, INPUT);
  pinMode(clockPin, OUTPUT);
  enc1.setType(TYPE2);
  enc2.setType(TYPE2);
  enc3.setType(TYPE2);
  enc4.setType(TYPE2);
  enc5.setType(TYPE2);
  enc6.setType(TYPE2);
  enc7.setType(TYPE2);
  enc8.setType(TYPE2);
  enc9.setType(TYPE2);
  enc10.setType(TYPE2);
  enc11.setType(TYPE2);
  enc12.setType(TYPE2);
  enc13.setType(TYPE2);

}

void loop() {
  inputs = Read_inputs();
  enc1.tick(bitRead(inputs, 0),bitRead(inputs, 1),bitRead(inputs, 2)); // считанные сигналы с первой микросхемы с первых входов
  enc2.tick(bitRead(inputs, 3),bitRead(inputs, 4),bitRead(inputs, 5));
  enc3.tick(bitRead(inputs, 6),bitRead(inputs, 7),bitRead(inputs, 8));
  enc4.tick(bitRead(inputs, 9),bitRead(inputs, 10),bitRead(inputs, 11));
  enc5.tick(bitRead(inputs, 12),bitRead(inputs, 13),bitRead(inputs, 14));
  enc6.tick(bitRead(inputs, 15),bitRead(inputs, 16),bitRead(inputs, 17));
  enc7.tick(bitRead(inputs, 18),bitRead(inputs, 19),bitRead(inputs, 20));
  enc8.tick(bitRead(inputs, 21),bitRead(inputs, 22),bitRead(inputs, 23));
  enc9.tick(bitRead(inputs, 24),bitRead(inputs, 25),bitRead(inputs, 26));
  enc10.tick(bitRead(inputs, 27),bitRead(inputs, 28),bitRead(inputs, 29));
  enc11.tick(bitRead(inputs, 30),bitRead(inputs, 31),bitRead(inputs, 32));
  enc12.tick(bitRead(inputs, 33),bitRead(inputs, 34),bitRead(inputs, 35));
  enc13.tick(bitRead(inputs, 36),bitRead(inputs, 37),bitRead(inputs, 38));
  Button1 = bitRead(inputs, 39);
  Button2 = bitRead(inputs, 40);
  Button3 = bitRead(inputs, 41);
  Button4 = bitRead(inputs, 42);
  Button5 = bitRead(inputs, 43);

// ------------------- примеры AlexGyver
  if (enc1.isTurn()) {     // если был совершён поворот (индикатор поворота в любую сторону)
    // ваш код
  }

  if (enc1.isRight()) Serial.println("Right");         // если был поворот
  if (enc1.isLeft()) Serial.println("Left");

  if (enc1.isRightH()) Serial.println("Right holded"); // если было удержание + поворот
  if (enc1.isLeftH()) Serial.println("Left holded");

  if (enc1.isPress()) Serial.println("Press");         // нажатие на кнопку (+ дебаунс)
  if (enc1.isClick()) Serial.println("Click");         // отпускание кнопки (+ дебаунс)
  //if (enc1.isRelease()) Serial.println("Release");     // то же самое, что isClick

  if (enc1.isHolded()) Serial.println("Holded");       // если была удержана и энк не поворачивался
  //if (enc1.isHold()) Serial.println("Hold");         // возвращает состояние кнопки
//-------------------------------------------
}

uint64_t Read_inputs(){
  uint64_t result = 0;
  digitalWrite(clockEnablePin, HIGH);
  digitalWrite(ploadPin, LOW);
  delayMicroseconds(pulseWidth);
  digitalWrite(ploadPin, HIGH);
  digitalWrite(clockEnablePin, LOW);

  for(uint16_t i = 0; i < dataWidth; i++) {
    bitWrite(result, i, digitalRead(dataPin));
    digitalWrite(clockPin, HIGH);
    delayMicroseconds(pulseWidth);
    digitalWrite(clockPin, LOW);
  }

  return result;
}
Но я бы порекомендовал лучше использовать микросхему CD74HC4067 для нее не требуется задержка и будет работать быстрее.
Код написан без теста!
 
  • Лойс +1
Реакции: CYBorg16SR

poty

★★★★★★✩
19 Фев 2020
2,956
886
@Старик Похабыч, это всё равно опрос состояния, хоть и менее интенсивный. А надо именно изолировать "расшифровку" тиков и передавать готовое количество и направление тиков, прошедшее с прошлого опроса.
 

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

★★★★★★★
14 Авг 2019
4,159
1,267
Москва
@poty,Так там тики считает сам энкодер (стоит стм8 кажется), т.е. пропусков не должно быть. И если будет задержка в коде , то потом получаешь число тиков. У меня где то подобный валяется, только не i2c.
Но вот нужны ли там энкодеры или можно обойтись потенциометром ?
 

poty

★★★★★★✩
19 Фев 2020
2,956
886
@Старик Похабыч, а, ну если так, то это как раз то, что я предлагал. Можно, думаю, и на более дешёвой микросхеме сделать. Или по паре-тройке энкодеров на микросхему поставить.
 

CYBorg16SR

✩✩✩✩✩✩✩
29 Апр 2022
5
1
Можно поинтересоватся, зачем столько энкодеров и энкодеры с кнопками или без?
Приветствую!
Я планирую сделать устройство, которое будет изменять различными подходами и регулировками входящий аналоговый сигнал: например, он приходит линейной зависимости и дальше идет такой же (то есть пришло 3 вольта - вышло 3, пришло 1 - вышло 1 и тд). Мне нужно чтобы была неравномерная зависимость (например пришло 1вольт-на выходе 0,5в, потом 2-1, 3-2 , 4-5 и тд и тп). С логической точки зрения расчетов изменения сигнала нет проблем. Проблема в том что эту зависимость нужно еще и регулировать и чтобы не каждый раз перезаливать прошивку с новыми данными - а изменять ее в "прямом эфире" так сказать) поэтому я хочу еще подрубить сюда дисплей для отображения этой самой зависимости. Выглядеть это будет в форме графика:
График.JPG

Так вот регулировка энкодерами будет каждой из 4 координат (х1, х2..... y1, y2 .... и тд). Помимо этого регулировка этими энкодерами планируется порядка 4 подобных графиков. Затея большая и да, возможно от чего то придется отказаться или же упростить так сказать по ходу пьесы. Но это уже война план покажет))
Планируется использование энкодеров и как вращения и как кнопки, так как ими же я планирую сохранять или восстанавливать значения координат. Помимо этого есть еще кнопки переключения режимов и тд, вообщем или я этот проект сделаю, или же в запой уйду)))
 

CYBorg16SR

✩✩✩✩✩✩✩
29 Апр 2022
5
1
@CYBorg16SR,
1) Судя по исходникам, в метод tick нужно передавать состояния сигналов, которые далее будут обрабатываться
Огромное спасибо за пример! Посидел я потыкал разные режимы работы и пришел к такому выводу:
Или я опрашиваю не правильно, или же опрос виртуального энкодера занимает серьезного времени (относительно конечно же)
В Вашем примере с виртуальным энкодером я подключил физическую кнопку, чтобы она эмулировала нажатие энкодера.

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

А что касается примера работы с регистром, то при опросе вывод в порт состояния виртуального энкодера по типу:
Serial.println("вправо");
вообще нет никакой реакции. Я про этот момент:

отсутствие реакции энкодера:
if (vEnc.tick(encSIgnal_A, encSIgnal_B)) {
        if (vEnc.right())   {Serial.println("вправо");  }
        if (vEnc.left())    {Serial.println("влево");   }
        vEnc.resetState();

Сам код получился такой:

Измененный код регистра и энкодера:
#include <EncButton.h>

#define NUMBER_OF_SHIFT_CHIPS   1 // количество регистров
#define DATA_WIDTH   NUMBER_OF_SHIFT_CHIPS * 8 // количество входов
#define PULSE_WIDTH_USEC   5 // задержка при считывании данных

// для хранения считаных байт
// если регистров больше двух, то int меняется на long
#define BYTES_VAL_T unsigned int

EncButton<EB_TICK, VIRT_ENC> vEnc;     // A, B: номера пинов

boolean encSIgnal_A;
boolean encSIgnal_B;

// пины для подключения регистра
int pL = 10;                                    // pL - ploadPin
int cEP = 16;                                   // cEP - clockEnablePin
int dP = 14;                                    // dP - dataPin
int cP = 15;                                    // cP - clockPin

BYTES_VAL_T pinValues; // текущее значение пинов
BYTES_VAL_T oldPinValues; // предыдущее значение пинов

// функция для считывания пинов
BYTES_VAL_T read_shift_regs() {
    long bitVal;
    BYTES_VAL_T bytesVal = 0;

    // опрашиваем регистр о состоянии пинов
    digitalWrite(cEP, HIGH);
    digitalWrite(pL, LOW);
    delayMicroseconds(PULSE_WIDTH_USEC);
    digitalWrite(pL, HIGH);
    digitalWrite(cEP, LOW);

    // считываем полученные данные о пинах
    for(int i = 0; i < DATA_WIDTH; i++){
        bitVal = digitalRead(dP);
        bytesVal |= (bitVal << ((DATA_WIDTH-1) - i));
        digitalWrite(cP, HIGH);
        delayMicroseconds(PULSE_WIDTH_USEC);
        digitalWrite(cP, LOW);
    }
  
    // возвращяем результат опроса регистра
    return(bytesVal);
}

#define SN74HC165_INPUT_A   (0)
#define SN74HC165_INPUT_B   (1)
#define SN74HC165_INPUT_C   (2)
#define SN74HC165_INPUT_D   (3)
#define SN74HC165_INPUT_E   (4)
#define SN74HC165_INPUT_F   (5)
#define SN74HC165_INPUT_G   (6)
#define SN74HC165_INPUT_H   (7)


#define ENC_SIGNAL_MASK_A   (1 << SN74HC165_INPUT_A)
#define ENC_SIGNAL_MASK_B   (1 << SN74HC165_INPUT_B)



void setup(){
    // установка режима работа пинов для
    // реализации програмного SPI, для SN74HC165
    // см Скетч обработки сдвигового регистра:
    // ...
        // для вывода данных в монитор порта
    Serial.begin(9600);
                           pinMode(encSIgnal_A, INPUT);
                           pinMode(encSIgnal_B, INPUT);
    // установка режима работа пинов
    pinMode(pL, OUTPUT);
    pinMode(cEP, OUTPUT);
    pinMode(cP, OUTPUT);
    pinMode(dP, INPUT);
    digitalWrite(cP, LOW);
    digitalWrite(pL, HIGH);

    // считываем значения с пинов
    pinValues = read_shift_regs();
    // выводим результат
//    display_pin_values();
    // сохраняем текущее значение
    oldPinValues = pinValues;

}


void loop(){
    // считываем значения с пинов
    BYTES_VAL_T const inputsMask = read_shift_regs();

    // Выделяем логические состояния входов к которым подключен энкодер
    bool encSignal_A = !(inputsMask & ENC_SIGNAL_MASK_A);
    bool encSignal_B = !(inputsMask & ENC_SIGNAL_MASK_B);
  
    // обработка сигналов энкодера
  if (vEnc.tick(encSIgnal_A, encSIgnal_B)) {
        if (vEnc.right())   {Serial.println("вправо");  }
        if (vEnc.left())    {Serial.println("влево");   }
        vEnc.resetState();
    }
Serial.print(encSignal_A);
Serial.println(encSignal_B);
}
При всем при этом вывод в порт логического состояния пинов виртуального энкодера показывает быстрый опрос через сдвиговый регистр и правильную работу контактов А и В при поворотах физического энкодера:

Serial.print(encSignal_A);
Serial.println(encSignal_B);

Но вот сам виртуальный энкодер молчит....
Я уже пробовал добавлять в опрос tick() к виртуальным пинам !digitalRead() как в первом примере, реакции тоже нет.. Получается что видимо я неправильно опрашиваю или обращаюсь к виртуальному энкодеру?

За примеры еще раз большое спасибо! Сижу разбираюсь дальше как эти примеры работают, век живи - век учись)
 
Изменено:

Kir

★✩✩✩✩✩✩
28 Мар 2020
69
16
@CYBorg16SR,
Или я опрашиваю не правильно, или же опрос виртуального энкодера занимает серьезного времени (относительно конечно же)
В Вашем примере с виртуальным энкодером я подключил физическую кнопку, чтобы она эмулировала нажатие энкодера:
В данном случае у вас двойная обработка, поэтому в этом нет ничего удивительного, в реальном применение так, конечно, делать смысла нет.
Может быть у меня корявый код опроса?
Увы, но да. Вот код с пояснением в комментариях:

C++:
#include <EncButton.h>

// Виртуальный эквивалент типового использования
EncButton<EB_TICK, VIRT_ENCBTN> vEnc;     // A, B, KEY: номера пинов
EncButton<EB_TICK, 7>           button;   // просто кнопка <KEY>

void setup() {
    Serial.begin(9600);

    /* Это бессмысленные действия,
     * Pin_A, Pin_B, Pin_C - это переменные которые хранят логическе состояние
     * вы пытаетесь проинициализировать пины в качестве идентификатора
     * используия логическое состояние, это есть бессмыслица */
    // pinMode(Pin_A, INPUT_PULLUP);
    // pinMode(Pin_B, INPUT_PULLUP);
    // pinMode(Pin_C, INPUT_PULLUP);
}

bool virtualSignal_Button = false;
bool virtualSignal_A      = false;
bool virtualSignal_B      = false;

void loop() {
    if( button.tick() ) {
        if (button.press())   { virtualSignal_Button = true;  Serial.print  ("Физ нажат и "); }
        if (button.release()) { virtualSignal_Button = false; Serial.print  ("Физ отжат и "); }
        button.resetState();
    }
   
    /* Вот тут принципиально не верное понимание, вы зачем-то считываете состояние пинов МК,
     * виртуальный экодер тут просто принимает логические состояния, которые может устанавливать
     * что угодно в программе */
    // if( vEnc.tick( !digitalRead(Pin_A),  !digitalRead(Pin_B),  !digitalRead(Pin_C)   ) ) {

    /* Вот тут передается состояние переменных которые в данном случае меняет
     * код if( button.tick() ) { ... }  */
    if( vEnc.tick(virtualSignal_A, virtualSignal_B, virtualSignal_Button) {
        if (vEnc.press())   {  Serial.print("вирт нажат");  Serial.println("");  }
        if (vEnc.release()) {  Serial.print("вирт отжат");  Serial.println("");  }
        vEnc.resetState();
    }
}
Но вот сам виртуальный энкодер молчит....
Опечатки
C++:
#include <EncButton.h>

#define NUMBER_OF_SHIFT_CHIPS   (1)                           // количество регистров
#define DATA_WIDTH              (NUMBER_OF_SHIFT_CHIPS * 8)   // количество входов
#define PULSE_WIDTH_USEC        (5)                           // задержка при считывании данных

// для хранения считаных байт
// если регистров больше двух, то int меняется на long
typedef unsigned int BYTES_VAL_T;

EncButton<EB_TICK, VIRT_ENC> vEnc;     // A, B: номера пинов

// Эти переменные не нужны
// boolean encSIgnal_A;
// boolean encSIgnal_B;

// пины для подключения регистра
/* тут можно применить возможности языка и убрать из ОЗУ */
constexpr byte pL  = 10;    // pL - ploadPin
constexpr byte cEP = 16;    // cEP - clockEnablePin
constexpr byte dP  = 14;    // dP - dataPin
constexpr byte cP  = 15;    // cP - clockPin


/* в данном случае эти переменные не нужны */
// BYTES_VAL_T pinValues; // текущее значение пинов
// BYTES_VAL_T oldPinValues; // предыдущее значение пинов

// функция для считывания пинов
BYTES_VAL_T read_shift_regs() {
    long bitVal;
    BYTES_VAL_T bytesVal = 0;

    // опрашиваем регистр о состоянии пинов
    digitalWrite(cEP, HIGH);
    digitalWrite(pL, LOW);
    delayMicroseconds(PULSE_WIDTH_USEC);
    digitalWrite(pL, HIGH);
    digitalWrite(cEP, LOW);

    // считываем полученные данные о пинах
    for(int i = 0; i < DATA_WIDTH; i++){
        bitVal = digitalRead(dP);
        bytesVal |= (bitVal << ((DATA_WIDTH-1) - i));
        digitalWrite(cP, HIGH);
        delayMicroseconds(PULSE_WIDTH_USEC);
        digitalWrite(cP, LOW);
    }
   
    // возвращяем результат опроса регистра
    return(bytesVal);
}

#define SN74HC165_INPUT_A   (0)
#define SN74HC165_INPUT_B   (1)
#define SN74HC165_INPUT_C   (2)
#define SN74HC165_INPUT_D   (3)
#define SN74HC165_INPUT_E   (4)
#define SN74HC165_INPUT_F   (5)
#define SN74HC165_INPUT_G   (6)
#define SN74HC165_INPUT_H   (7)


#define ENC_SIGNAL_MASK_A   (1 << SN74HC165_INPUT_A)
#define ENC_SIGNAL_MASK_B   (1 << SN74HC165_INPUT_B)


void InitSoftSPI(void) {
    // установка режима работа пинов для
    // реализации програмного SPI, для SN74HC165
    pinMode(pL, OUTPUT);
    pinMode(cEP, OUTPUT);
    pinMode(cP, OUTPUT);
    pinMode(dP, INPUT);
    digitalWrite(cP, LOW);
    digitalWrite(pL, HIGH);
}


void setup(){

    // для вывода данных в монитор порта
    Serial.begin(9600);

                        /* вот эти 2 строчки лишние
                         * вы настраиваете физические пины как входы, а в качестве
                         * идентификаторов используете boolean переменные,
                         * это странно */
                        //    pinMode(encSIgnal_A, INPUT);
                        //    pinMode(encSIgnal_B, INPUT);
    InitSoftSPI();
   
    // считываем значения с пинов,
    // чтобы исключить случайное
    // значение при следующем использовании
    auto temp = read_shift_regs();
}


void loop(){
    // считываем значения с пинов
    BYTES_VAL_T const inputsMask = read_shift_regs();

    // Выделяем логические состояния входов к которым подключен энкодер
    bool encSignal_A = !(inputsMask & ENC_SIGNAL_MASK_A);
    bool encSignal_B = !(inputsMask & ENC_SIGNAL_MASK_B);
   
    // обработка сигналов энкодера
    // тут опечатка, возможно из прошлого примера тянется
    // оставлю закомментированной, чтобы увидеть разницу
//   if (vEnc.tick(encSIgnal_A, encSignal_B)) {
  if (vEnc.tick(encSignal_A, encSignal_B)) {
        if (vEnc.right())   {Serial.println("вправо");  }
        if (vEnc.left())    {Serial.println("влево");   }
        vEnc.resetState();
    }
    Serial.print(encSignal_A);
    Serial.println(encSignal_B);
}
 
Изменено:
  • Лойс +1
Реакции: CYBorg16SR

Svod

✩✩✩✩✩✩✩
22 Апр 2022
15
7
Немного изменил логику программы так как при тесте в Протеусе выявились ошибки.

C++:
#define clockEnablePin  9   // CE
#define ploadPin        8   // PL
#define dataPin         11  // Q7 MISO
#define clockPin        12  // CP
#define pulseWidth      1   // Задержка сдвига
#define chipCount       6   // Количество микросхем

#include "GyverEncoder.h"
Encoder enc1;
Encoder enc2;
Encoder enc3;
Encoder enc4;
Encoder enc5;
Encoder enc6;
Encoder enc7;
Encoder enc8;
Encoder enc9;
Encoder enc10;
Encoder enc11;
Encoder enc12;
Encoder enc13;

bool Button1,Button2,Button3,Button4,Button5;
uint8_t dataWidth = chipCount * 8;
uint64_t inputs;
/* Карта битов в переменной inputs относительно подлюченных сдвиговых регистров 74HС165
 * Bit 0 = U7(D0)  Bit 8  = U6(D0)  Bit 16 = U5(D0)  Bit 24 = U4(D0)  Bit 32 = U3(D0)  Bit 40 = U2(D0)
 * Bit 1 = U7(D1)  Bit 9  = U6(D1)  Bit 17 = U5(D1)  Bit 25 = U4(D1)  Bit 33 = U3(D1)  Bit 41 = U2(D1)
 * Bit 2 = U7(D2)  Bit 10 = U6(D2)  Bit 18 = U5(D2)  Bit 26 = U4(D2)  Bit 34 = U3(D2)  Bit 42 = U2(D2)
 * Bit 3 = U7(D3)  Bit 11 = U6(D3)  Bit 19 = U5(D3)  Bit 27 = U4(D3)  Bit 35 = U3(D3)  Bit 43 = U2(D3)
 * Bit 4 = U7(D4)  Bit 12 = U6(D4)  Bit 20 = U5(D4)  Bit 28 = U4(D4)  Bit 36 = U3(D4)  Bit 44 = U2(D4)
 * Bit 5 = U7(D5)  Bit 13 = U6(D5)  Bit 21 = U5(D5)  Bit 29 = U4(D5)  Bit 37 = U3(D5)  Bit 45 = U2(D5)
 * Bit 6 = U7(D6)  Bit 14 = U6(D6)  Bit 22 = U5(D6)  Bit 30 = U4(D6)  Bit 38 = U3(D6)  Bit 46 = U2(D6)
 * Bit 7 = U7(D7)  Bit 15 = U6(D7)  Bit 23 = U5(D7)  Bit 31 = U4(D7)  Bit 39 = U3(D7)  Bit 47 = U2(D7)
 */

void setup() {
  Serial.begin(9600);
  pinMode(clockEnablePin, OUTPUT);
  pinMode(ploadPin, OUTPUT);
  pinMode(dataPin, INPUT);
  pinMode(clockPin, OUTPUT);
  enc1.setType(TYPE2);
  enc2.setType(TYPE2);
  enc3.setType(TYPE2);
  enc4.setType(TYPE2);
  enc5.setType(TYPE2);
  enc6.setType(TYPE2);
  enc7.setType(TYPE2);
  enc8.setType(TYPE2);
  enc9.setType(TYPE2);
  enc10.setType(TYPE2);
  enc11.setType(TYPE2);
  enc12.setType(TYPE2);
  enc13.setType(TYPE2);

}

void loop() {
  inputs = Read_inputs();
  enc1.tick(bitRead(inputs, 0),bitRead(inputs, 1),!bitRead(inputs, 2)); // Энкодер1 (U7-D0(A), U7-D1(B), !U7-D2(SW))
  enc2.tick(bitRead(inputs, 3),bitRead(inputs, 4),!bitRead(inputs, 5)); // Энкодер2 (U7-D3(A), U7-D4(B), !U7-D5(SW))
  enc3.tick(bitRead(inputs, 6),bitRead(inputs, 7),!bitRead(inputs, 8));
  enc4.tick(bitRead(inputs, 9),bitRead(inputs, 10),!bitRead(inputs, 11));
  enc5.tick(bitRead(inputs, 12),bitRead(inputs, 13),!bitRead(inputs, 14));
  enc6.tick(bitRead(inputs, 15),bitRead(inputs, 16),!bitRead(inputs, 17));
  enc7.tick(bitRead(inputs, 18),bitRead(inputs, 19),!bitRead(inputs, 20));
  enc8.tick(bitRead(inputs, 21),bitRead(inputs, 22),!bitRead(inputs, 23));
  enc9.tick(bitRead(inputs, 24),bitRead(inputs, 25),!bitRead(inputs, 26));
  enc10.tick(bitRead(inputs, 27),bitRead(inputs, 28),!bitRead(inputs, 29));
  enc11.tick(bitRead(inputs, 30),bitRead(inputs, 31),!bitRead(inputs, 32));
  enc12.tick(bitRead(inputs, 33),bitRead(inputs, 34),!bitRead(inputs, 35));
  enc13.tick(bitRead(inputs, 36),bitRead(inputs, 37),!bitRead(inputs, 38));
  Button1 = bitRead(inputs, 39);
  Button2 = bitRead(inputs, 40);
  Button3 = bitRead(inputs, 41);
  Button4 = bitRead(inputs, 42);
  Button5 = bitRead(inputs, 43);


// ------------------- примеры AlexGyver
  if (enc1.isTurn()) {     // если был совершён поворот (индикатор поворота в любую сторону)
    // ваш код
  }

  if (enc1.isRight()) Serial.println("Right");         // если был поворот
  if (enc1.isLeft()) Serial.println("Left");

  if (enc1.isRightH()) Serial.println("Right holded"); // если было удержание + поворот
  if (enc1.isLeftH()) Serial.println("Left holded");

  if (enc1.isPress()) Serial.println("Press");         // нажатие на кнопку (+ дебаунс)
  if (enc1.isClick()) Serial.println("Click");         // отпускание кнопки (+ дебаунс)
  //if (enc1.isRelease()) Serial.println("Release");   // то же самое, что isClick

  if (enc1.isHolded()) Serial.println("Holded");       // если была удержана и энк не поворачивался
  //if (enc1.isHold()) Serial.println("Hold");         // возвращает состояние кнопки
//-------------------------------------------
}

uint64_t Read_inputs(){
  uint64_t result = 0;
  digitalWrite(clockEnablePin, HIGH);
  digitalWrite(ploadPin, LOW);
  delayMicroseconds(pulseWidth);
  digitalWrite(ploadPin, HIGH);
  digitalWrite(clockEnablePin, LOW);
 
  for(int16_t i = 0; i < dataWidth; i++) {
    result |= (((uint64_t)digitalRead(dataPin)) << ((dataWidth-1) - i));
    digitalWrite(clockPin, HIGH);
    delayMicroseconds(pulseWidth);
    digitalWrite(clockPin, LOW);
  }
 
  return result;
}
Принципиальная схема
proteus_74hc165.jpg

В Протеусе подключал мотор с энкодером так как там нет простых энкодеров но это смысла не меняет, все работает как надо и еще я уменьшил паузу в 1 мкс при этом проблем не обнаружил.
 
  • Лойс +1
Реакции: CYBorg16SR

CYBorg16SR

✩✩✩✩✩✩✩
29 Апр 2022
5
1
@Svod, огромнейший респект и моя благодарность за работу и с примером и принципиальной схемой:eek:(y)(y)! Проверил на 3 регистрах, и со всех принимает данные с энкодеров. А за карту битов - прям отдельный респект!

@Kir, спасибо за правки и пояснения! По вашему коду смог подключить 2 регистра, почему-то при подключении третьего регистра в каскад, первый регистр (который, как я понимаю, считывается в последнюю очередь) отказывается определяться. Я немного изменил Ваш код, чтобы разобраться в какой последовательности считываются биты и все их вывел в порт. Подключил 2 энкодера и 1 кнопку к разным регистрам и вот как раз последний по счету для считывания отказался как-либо реагировать на обсчитывание битов (хотя 2 и 3 регистры подключены по одной и той же схеме)
В принципе, меня полностью устраивает и скетч товарища Svod, но может Вы знаете в чем может быть тут проблема? (интересует в целях сличного развития)
Пример проверки битов:
#include <EncButton.h>

#define NUMBER_OF_SHIFT_CHIPS   (3)                           // количество регистров
#define DATA_WIDTH              (NUMBER_OF_SHIFT_CHIPS * 8)   // количество входов
#define PULSE_WIDTH_USEC        (5)                           // задержка при считывании данных

// для хранения считаных байт
// если регистров больше двух, то int меняется на long

typedef unsigned long BYTES_VAL_T;

EncButton<EB_TICK, VIRT_ENCBTN> vEnc1;     // A1, B1, C1: номера пинов
EncButton<EB_TICK, VIRT_ENCBTN> vEnc2;     // A2, B2, C2: номера пинов
EncButton<EB_TICK, VIRT_BTN> vBtn;         //         K3: номера пинов


// пины для подключения регистра
/* тут можно применить возможности языка и убрать из ОЗУ */
constexpr byte pL  = 10;    // pL - ploadPin
constexpr byte cEP = 16;    // cEP - clockEnablePin
constexpr byte dP  = 14;    // dP - dataPin
constexpr byte cP  = 15;    // cP - clockPin



// функция для считывания пинов
BYTES_VAL_T read_shift_regs() {
  unsigned long bitVal;
  BYTES_VAL_T bytesVal = 0;

  // опрашиваем регистр о состоянии пинов
  digitalWrite(cEP, HIGH);
  digitalWrite(pL, LOW);
  delayMicroseconds(PULSE_WIDTH_USEC);
  digitalWrite(pL, HIGH);
  digitalWrite(cEP, LOW);

  // считываем полученные данные о пинах
  for (int i = 0; i < DATA_WIDTH; i++) {
    bitVal = digitalRead(dP);
    bytesVal |= (bitVal << ((DATA_WIDTH - 1) - i));
    digitalWrite(cP, HIGH);
    delayMicroseconds(PULSE_WIDTH_USEC);
    digitalWrite(cP, LOW);
  }

  // возвращяем результат опроса регистра
  return (bytesVal);
}

#define SN74HC165_INPUT_A   (0)
#define SN74HC165_INPUT_B   (1)
#define SN74HC165_INPUT_C   (2)
#define SN74HC165_INPUT_D   (3)
#define SN74HC165_INPUT_E   (4)
#define SN74HC165_INPUT_F   (5)
#define SN74HC165_INPUT_G   (6)
#define SN74HC165_INPUT_H   (7)

#define SN74HC165_INPUT_I   (8)
#define SN74HC165_INPUT_J   (9)
#define SN74HC165_INPUT_K   (10)
#define SN74HC165_INPUT_L   (11)
#define SN74HC165_INPUT_M   (12)
#define SN74HC165_INPUT_N   (13)
#define SN74HC165_INPUT_O   (14)
#define SN74HC165_INPUT_P   (15)

#define SN74HC165_INPUT_Q   (16)
#define SN74HC165_INPUT_R   (17)
#define SN74HC165_INPUT_S   (18)
#define SN74HC165_INPUT_T   (19)
#define SN74HC165_INPUT_U   (20)
#define SN74HC165_INPUT_V   (21)
#define SN74HC165_INPUT_W   (22)
#define SN74HC165_INPUT_X   (23)


#define ENC_SIGNAL_MASK_A   (1 << SN74HC165_INPUT_A)
#define ENC_SIGNAL_MASK_B   (1 << SN74HC165_INPUT_B)
#define ENC_SIGNAL_MASK_C   (1 << SN74HC165_INPUT_C)
#define ENC_SIGNAL_MASK_D   (1 << SN74HC165_INPUT_D)
#define ENC_SIGNAL_MASK_E   (1 << SN74HC165_INPUT_E)
#define ENC_SIGNAL_MASK_F   (1 << SN74HC165_INPUT_F)
#define ENC_SIGNAL_MASK_G   (1 << SN74HC165_INPUT_G)
#define ENC_SIGNAL_MASK_H   (1 << SN74HC165_INPUT_H)

#define ENC_SIGNAL_MASK_I   (1 << SN74HC165_INPUT_I)
#define ENC_SIGNAL_MASK_J   (1 << SN74HC165_INPUT_J)
#define ENC_SIGNAL_MASK_K   (1 << SN74HC165_INPUT_K)
#define ENC_SIGNAL_MASK_L   (1 << SN74HC165_INPUT_L)
#define ENC_SIGNAL_MASK_M   (1 << SN74HC165_INPUT_M)
#define ENC_SIGNAL_MASK_N   (1 << SN74HC165_INPUT_N)
#define ENC_SIGNAL_MASK_O   (1 << SN74HC165_INPUT_O)
#define ENC_SIGNAL_MASK_P   (1 << SN74HC165_INPUT_P)

#define ENC_SIGNAL_MASK_Q   (1 << SN74HC165_INPUT_Q)
#define ENC_SIGNAL_MASK_R   (1 << SN74HC165_INPUT_R)
#define ENC_SIGNAL_MASK_S   (1 << SN74HC165_INPUT_S)
#define ENC_SIGNAL_MASK_T   (1 << SN74HC165_INPUT_T)
#define ENC_SIGNAL_MASK_U   (1 << SN74HC165_INPUT_U)
#define ENC_SIGNAL_MASK_V   (1 << SN74HC165_INPUT_V)
#define ENC_SIGNAL_MASK_W   (1 << SN74HC165_INPUT_W)
#define ENC_SIGNAL_MASK_X   (1 << SN74HC165_INPUT_X)


void InitSoftSPI(void) {
  // установка режима работа пинов для
  // реализации програмного SPI, для SN74HC165
  pinMode(pL, OUTPUT);
  pinMode(cEP, OUTPUT);
  pinMode(cP, OUTPUT);
  pinMode(dP, INPUT);
  digitalWrite(cP, LOW);
  digitalWrite(pL, HIGH);
}


void setup() {

  // для вывода данных в монитор порта
  Serial.begin(9600);

  InitSoftSPI();
  // считываем значения с пинов,
  // чтобы исключить случайное
  // значение при следующем использовании
  auto temp = read_shift_regs();
}


void loop() {
  // считываем значения с пинов
  BYTES_VAL_T const inputsMask = read_shift_regs();

  // Выделяем логические состояния входов к которым подключен энкодер
  bool encSignal_A = !(inputsMask & ENC_SIGNAL_MASK_A);
  bool encSignal_B = !(inputsMask & ENC_SIGNAL_MASK_B);
  bool encSignal_C = !(inputsMask & ENC_SIGNAL_MASK_C);
  bool encSignal_D = !(inputsMask & ENC_SIGNAL_MASK_D);
  bool encSignal_E = !(inputsMask & ENC_SIGNAL_MASK_E);
  bool encSignal_F = !(inputsMask & ENC_SIGNAL_MASK_F);
  bool encSignal_G = !(inputsMask & ENC_SIGNAL_MASK_G);
  bool encSignal_H = !(inputsMask & ENC_SIGNAL_MASK_H);

  bool encSignal_I = !(inputsMask & ENC_SIGNAL_MASK_I);
  bool encSignal_J = !(inputsMask & ENC_SIGNAL_MASK_J);
  bool encSignal_K = !(inputsMask & ENC_SIGNAL_MASK_K);
  bool encSignal_L = !(inputsMask & ENC_SIGNAL_MASK_L);
  bool encSignal_M = !(inputsMask & ENC_SIGNAL_MASK_M);
  bool encSignal_N = !(inputsMask & ENC_SIGNAL_MASK_N);
  bool encSignal_O = !(inputsMask & ENC_SIGNAL_MASK_O);
  bool encSignal_P = !(inputsMask & ENC_SIGNAL_MASK_P);

  bool encSignal_Q = !(inputsMask & ENC_SIGNAL_MASK_Q);
  bool encSignal_R = !(inputsMask & ENC_SIGNAL_MASK_R);
  bool encSignal_S = !(inputsMask & ENC_SIGNAL_MASK_S);
  bool encSignal_T = !(inputsMask & ENC_SIGNAL_MASK_T);
  bool encSignal_U = !(inputsMask & ENC_SIGNAL_MASK_U);
  bool encSignal_V = !(inputsMask & ENC_SIGNAL_MASK_V);
  bool encSignal_W = !(inputsMask & ENC_SIGNAL_MASK_W);
  bool encSignal_X = !(inputsMask & ENC_SIGNAL_MASK_X);

  if (vEnc1.tick(encSignal_I, encSignal_J, encSignal_K)) {
    if (vEnc1.right())   {
      Serial.println("вправо 1");
    }
    if (vEnc1.left())    {
      Serial.println("влево 1");
    }
    if (vEnc1.click())   {
      Serial.println("клик 1");
    }
    vEnc1.resetState();
  }

  if (vEnc2.tick(encSignal_F, encSignal_G, encSignal_E)) {
    if (vEnc2.right())   {
      Serial.println("вправо 2");
    }
    if (vEnc2.left())    {
      Serial.println("влево 2");
    }
    if (vEnc2.click())   {
      Serial.println("клик 2");
    }
    vEnc2.resetState();
  }

  if (vBtn.tick(encSignal_H)) {
    if (vBtn.click())   {
      Serial.println("клик 3");
    }
    vBtn.resetState();
  }
  delay(20);
  Serial.print  (encSignal_A);
  Serial.print  (encSignal_B);
  Serial.print  (encSignal_C);
  Serial.print  (encSignal_D);
  Serial.print  (encSignal_E);
  Serial.print  (encSignal_F);
  Serial.print  (encSignal_G);
  Serial.print  (encSignal_H);
  Serial.print  ("   ");
  Serial.print  (encSignal_I);
  Serial.print  (encSignal_J);
  Serial.print  (encSignal_K);
  Serial.print  (encSignal_L);
  Serial.print  (encSignal_M);
  Serial.print  (encSignal_N);
  Serial.print  (encSignal_O);
  Serial.print  (encSignal_P);
  Serial.print  ("   ");
  Serial.print  (encSignal_Q);
  Serial.print  (encSignal_R);
  Serial.print  (encSignal_S);
  Serial.print  (encSignal_T);
  Serial.print  (encSignal_U);
  Serial.print  (encSignal_V);
  Serial.print  (encSignal_W);
  Serial.println(encSignal_X);


}
В любом случае, я очень много узнал от Вас, ребят! Мое глубочайшее вам всем уважение!🤝💪
 

Kir

★✩✩✩✩✩✩
28 Мар 2020
69
16
@CYBorg16SR, По коду если смотреть, то сигналы обрабатываются только с 2-х сдвиговых регистров, может в этом проблема.
 
  • Лойс +1
Реакции: CYBorg16SR

Svod

✩✩✩✩✩✩✩
22 Апр 2022
15
7
Вот другой пример с правильной растановкой последовательности регистров:
C++:
#define clockEnablePin  9   // CE
#define ploadPin        8   // PL
#define dataPin         11  // Q7 MISO
#define clockPin        12  // CP
#define pulseWidth      1   // Задержка сдвига
#define chipCount       6   // Количество микросхем

#if (chipCount == 1)
typedef uint8_t Type_t;
#elif (chipCount == 2)
typedef uint16_t Type_t;
#elif (chipCount == 3 || chipCount == 4)
typedef uint32_t Type_t;
#elif (chipCount > 4 && chipCount <= 8 )
typedef uint64_t Type_t;
#else
#error "Chips of microcircuits should not be less than 1 or more than 8"
#endif

#include "GyverEncoder.h"
Encoder enc1;
Encoder enc2;
Encoder enc3;
Encoder enc4;
Encoder enc5;
Encoder enc6;
Encoder enc7;
Encoder enc8;
Encoder enc9;
Encoder enc10;
Encoder enc11;
Encoder enc12;
Encoder enc13;

bool Button1,Button2,Button3,Button4,Button5;
Type_t inputs;
Type_t last_inputs;

/* Карта битов в переменной inputs относительно подлюченных сдвиговых регистров 74HС165 (По своей логике и удобству)
 * Bit 0 = U2(D0)  Bit 8  = U3(D0)  Bit 16 = U4(D0)  Bit 24 = U5(D0)  Bit 32 = U6(D0)  Bit 40 = U7(D0)
 * Bit 1 = U2(D1)  Bit 9  = U3(D1)  Bit 17 = U4(D1)  Bit 25 = U5(D1)  Bit 33 = U6(D1)  Bit 41 = U7(D1)
 * Bit 2 = U2(D2)  Bit 10 = U3(D2)  Bit 18 = U4(D2)  Bit 26 = U5(D2)  Bit 34 = U6(D2)  Bit 42 = U7(D2)
 * Bit 3 = U2(D3)  Bit 11 = U3(D3)  Bit 19 = U4(D3)  Bit 27 = U5(D3)  Bit 35 = U6(D3)  Bit 43 = U7(D3)
 * Bit 4 = U2(D4)  Bit 12 = U3(D4)  Bit 20 = U4(D4)  Bit 28 = U5(D4)  Bit 36 = U6(D4)  Bit 44 = U7(D4)
 * Bit 5 = U2(D5)  Bit 13 = U3(D5)  Bit 21 = U4(D5)  Bit 29 = U5(D5)  Bit 37 = U6(D5)  Bit 45 = U7(D5)
 * Bit 6 = U2(D6)  Bit 14 = U3(D6)  Bit 22 = U4(D6)  Bit 30 = U5(D6)  Bit 38 = U6(D6)  Bit 46 = U7(D6)
 * Bit 7 = U2(D7)  Bit 15 = U3(D7)  Bit 23 = U4(D7)  Bit 31 = U5(D7)  Bit 39 = U6(D7)  Bit 47 = U7(D7)
 */

void setup() {
  Serial.begin(9600);
  pinMode(clockEnablePin, OUTPUT);
  pinMode(ploadPin, OUTPUT);
  pinMode(dataPin, INPUT);
  pinMode(clockPin, OUTPUT);
  enc1.setType(TYPE2);
  enc2.setType(TYPE2);
  enc3.setType(TYPE2);
  enc4.setType(TYPE2);
  enc5.setType(TYPE2);
  enc6.setType(TYPE2);
  enc7.setType(TYPE2);
  enc8.setType(TYPE2);
  enc9.setType(TYPE2);
  enc10.setType(TYPE2);
  enc11.setType(TYPE2);
  enc12.setType(TYPE2);
  enc13.setType(TYPE2);
 
}

void loop() {
     // Опрос регитров и запись в переменную inputs
  inputs = Read_inputs();
    // Обработка энкодеров
  enc1.tick(bitRead(inputs, 0),bitRead(inputs, 1),!bitRead(inputs, 2));
  enc2.tick(bitRead(inputs, 3),bitRead(inputs, 4),!bitRead(inputs, 5));
  enc3.tick(bitRead(inputs, 6),bitRead(inputs, 7),!bitRead(inputs, 8));
  enc4.tick(bitRead(inputs, 9),bitRead(inputs, 10),!bitRead(inputs, 11));
  enc5.tick(bitRead(inputs, 12),bitRead(inputs, 13),!bitRead(inputs, 14));
  enc6.tick(bitRead(inputs, 15),bitRead(inputs, 16),!bitRead(inputs, 17));
  enc7.tick(bitRead(inputs, 18),bitRead(inputs, 19),!bitRead(inputs, 20));
  enc8.tick(bitRead(inputs, 21),bitRead(inputs, 22),!bitRead(inputs, 23));
  enc9.tick(bitRead(inputs, 24),bitRead(inputs, 25),!bitRead(inputs, 26));
  enc10.tick(bitRead(inputs, 27),bitRead(inputs, 28),!bitRead(inputs, 29));
  enc11.tick(bitRead(inputs, 30),bitRead(inputs, 31),!bitRead(inputs, 32));
  enc12.tick(bitRead(inputs, 33),bitRead(inputs, 34),!bitRead(inputs, 35));
  enc13.tick(bitRead(inputs, 36),bitRead(inputs, 37),!bitRead(inputs, 38));
    // Состояние кнопок
  Button1 = bitRead(inputs, 39);
  Button2 = bitRead(inputs, 40);
  Button3 = bitRead(inputs, 41);
  Button4 = bitRead(inputs, 42);
  Button5 = bitRead(inputs, 43);

      // Вывести состояние битов от старшего до младшего бит в Serial port
  if(inputs != last_inputs){
    last_inputs = inputs;
    for(int i = chipCount * 8 - 1; i> -1; i--){
      bool Val = bitRead(inputs, i);
      Serial.print(Val);
    }
    Serial.println();
  }

/*
// ------------------- примеры AlexGyver
  if (enc1.isTurn()) {     // если был совершён поворот (индикатор поворота в любую сторону)
    // ваш код
  }

  if (enc1.isRight()) Serial.println("Right");         // если был поворот
  if (enc1.isLeft()) Serial.println("Left");

  if (enc1.isRightH()) Serial.println("Right holded"); // если было удержание + поворот
  if (enc1.isLeftH()) Serial.println("Left holded");

  if (enc1.isPress()) Serial.println("Press");         // нажатие на кнопку (+ дебаунс)
  if (enc1.isClick()) Serial.println("Click");         // отпускание кнопки (+ дебаунс)
  //if (enc1.isRelease()) Serial.println("Release");   // то же самое, что isClick

  if (enc1.isHolded()) Serial.println("Holded");       // если была удержана и энк не поворачивался
  //if (enc1.isHold()) Serial.println("Hold");         // возвращает состояние кнопки
//-------------------------------------------
*/
}

Type_t Read_inputs(){
  Type_t result = 0;
  digitalWrite(clockEnablePin, HIGH);
  digitalWrite(ploadPin, LOW);
  delayMicroseconds(pulseWidth);
  digitalWrite(ploadPin, HIGH);
  digitalWrite(clockEnablePin, LOW);

  for(int8_t a = 1; a < chipCount + 1; a++) {  // (По своей логике и удобству)
    for(int8_t b = a * 8 - 1; b > a * 8 - 8 - 1; b--){
      result |= (((Type_t)digitalRead(dataPin)) << b);
      digitalWrite(clockPin, HIGH);
      delayMicroseconds(pulseWidth);
      digitalWrite(clockPin, LOW);
    }
  }
  return result;
}
Принципиальная схема:
proteus_74hc165.jpg

Но есть еще лучше вариант, чем использовать тип переменной лучше использовать массив, тогда можно будет подключать больше восьми регистров до тех пор пока память МК позволит.
 
  • Лойс +1
Реакции: CYBorg16SR

CYBorg16SR

✩✩✩✩✩✩✩
29 Апр 2022
5
1
Но есть еще лучше вариант, чем использовать тип переменной лучше использовать массив, тогда можно будет подключать больше восьми регистров до тех пор пока память МК позволит.
@Svod, звучит заманчиво и сексуально)))
Спасибо за последний пример, все работает, сохранил как альтернативу(y)
 
  • Лойс +1
Реакции: Svod