Используем Энкодер

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

отстал от Ваших опытов :( .....

а спросить вот что хотел :

- в теле процедуры обработки прерывания первым же оператором отключить прерывание

        получится программный RS-триггер, по каналу А этим устраняется весь дребезг

        на канале В дребезга в этот момент не может быть (каналы сдвинуты на 180 гр.)

- анализ канала В, вычисления

- разрешить прерывание

?

 

leshak
Offline
Зарегистрирован: 29.09.2011

SU-27-16 пишет:

отстал от Ваших опытов :( .....

        на канале В дребезга в этот момент не может быть (каналы сдвинуты на 180 гр.)

Ага. Я тоже так думал. На прошлой странице расписывал эту логику. С картинками. Реальность оказалось суровей.

К сожалению скетч не сохранил. Жду выходных. Повторю - выложу лог. Может вы сообразите как такая порнография выходит.

leshak
Offline
Зарегистрирован: 29.09.2011

SU-27-16 пишет:

отстал от Ваших опытов :( .....

Ну опыты с экнодером плавно перетекли в жонглирование байтами. Но это тоже весело и захватывающе ;)

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

пойду-ка на антресоли залезу - за своим любимым С1-77.... будем посмотреть.... даааааа, захватывает :)

leshak
Offline
Зарегистрирован: 29.09.2011

SU-27-16 пишет:

пойду-ка на антресоли залезу - за своим любимым С1-77.... будем посмотреть.... даааааа, захватывает :)

SU-27 и "лезть на андресоли"? Выбиваетесь из образа :)

Вообщем реанимировал я свой гиморой

#define PIN_A 2
#define PIN_B 3

volatile unsigned long enabledA_time=0;
volatile bool fl=false; // флаг что нужно вывести
volatile bool value_b=0;
void setup(){
  Serial.begin(57600);
  digitalWrite(PIN_A,HIGH);
  digitalWrite(PIN_B,HIGH);
  
  attachInterrupt(0,handler_a,RISING);
  
  Serial.println("Ready");
}

void handler_a(){
  if(enabledA_time<millis()){
    value_b=digitalRead(PIN_B);
    fl=true;
    enabledA_time=millis()+500;// уж включили антидребезг так включили
  }
}

void loop(){
  if(fl){
    Serial.print(value_b);
    Serial.print(" ");
    fl=false;
  }
}

 

Кручу вправо (паузу между щелчками деалю секунду, две):

1 1 1 1 1 1 1 1 0 0 0 1 1 0 1 1 1 1 1 0 1 1 1 

Кручу влево

0 0 1 0 1 0 0 0 0 1 0 0 0 0 

Другой энкодер (другая модель):

Право "0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 "

Лево "1 0 0 1 1 0 0 1 0 0 0 0 0 1"

Ну вот спрашивается, какого фига? Где обещанная стабильность? 

Прямое чтение из порта - та же картина. Более того даже если я буду делать digitalRead(PIN_A) - и нули и единицы в вводе. Несмотря на то что RISING при аттаче. И прямое чтение из порта - тоже не помогает.

 

leshak
Offline
Зарегистрирован: 29.09.2011

 Полный бред.

Сделал антидребезг через детач.

#define PIN_A 2
#define PIN_B 3

volatile unsigned long enabledA_time=0;
volatile bool fl=false; // флаг что нужно вывести
volatile bool value_b=0;
volatile bool isAttached=false;
void setup(){
  Serial.begin(57600);
  digitalWrite(PIN_A,HIGH);
  digitalWrite(PIN_B,HIGH);
  
  
  Serial.println("Ready");
}

void handler_a(){
  detachInterrupt(0);
  fl=true;
  isAttached=false;
  value_b=digitalRead(PIN_B);

}

void loop(){
  if(fl){
    Serial.print(value_b);
    Serial.print(" ");
    fl=false;
    enabledA_time=millis()+3000;// уж включили антидребезг так включили
  }
  
    if(!fl && !isAttached && enabledA_time<millis()){
      isAttached=true;
      Serial.print("A ");
      attachInterrupt(0,handler_a,RISING);      
    }
}

 

Кручу в одну сторону "A 0 A 1 A 1 A "

На один щелчек выскакивает 1 (или ноль), потом, через три секунды A, потом, иногда(!) опять 1 (или 0). Хотя к тому времени даже рука с энкодера убрана и спрятана за спину. То есть ни о каком "восходящем фронте" не может быть речи. Вообще не может быть сработки. 

Как будто при attachInterrupt оно вспомнило о фронте которые были во время детача.

 

leshak
Offline
Зарегистрирован: 29.09.2011

 Вообщем если кто-то еще "боится библиотек", я вот вариант скетча AlexFisher на состояниях.

Выкинул из него LCD, вывожу в Serial. Добавил поддержку MEGA платы и чуток даунгруйднул что-бы не только с последней версией ArduinoIDE работало.


volatile uint8_t enc=0;
long pos=-999;
volatile long newpos=0;
char action[] PROGMEM ={
    0, //0000
    -1, //0001
    1, //0010
    0, //0011
    1, //0100
    0, //0101
    0, //0110
    -1, //0111
    -1, //1000
    0, //1001
    0, //1010
    1, //1011
    0, //1100
    1, //1101
    -1, //1110
    0  //1111
    
  };

void ISR_ENC() //прерывание энкодера одно для входа 2 и 3
{
 #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
  enc=(enc>>2) | ( (PINE >>2)  & 0b1100); //берем 2 бита входов, поправил для меги
#else
  enc=(enc>>2) | (PIND & 0b1100); //берем 2 бита входов 
#endif
  

  newpos+=(signed char)pgm_read_byte(&action[enc]);
}


void setup() {
  Serial.begin(57600);
  
  digitalWrite(2, HIGH); // сюда подключен энкодер, средний вывод на землю
  digitalWrite(3, HIGH);
  attachInterrupt(0, ISR_ENC, CHANGE);
  attachInterrupt(1, ISR_ENC, CHANGE);

}

void loop() {
  if (newpos != pos)
  {
    pos=newpos;
    Serial.print(pos);
    Serial.print(" ");
  }
}

Вообщем "условно работает". Подшумливает правда.

То есть сделать там "ругулятор громкости" - можно, а вот ловить команды типа "сделали 3 щелчка" - уже нет :( Задача "когда нам каждый тик и мил и дорог" - пока не решена :(

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

 Пишу в 3-й раз :( Что-то фокс сбрасывает, пишу из оперы...

От "подшумливания" так и не смог избавиться. Пытался вставить задержку в начало прерывания на _delay_ns() - она без прерываний, но не помогло. Вот только не пробовал бороться "железными" методами. Может 2 конденсатора по 0.1 мкФ спасут отца русской демократии? Под рукой не оказалось - непроверил.

А тем, кому нужно ловить каждый шаг, все равно "контактные" энкодеры не помогут, им нужны безконтактные, а там дребезга не будет. Там другие подводные камни.

Вот что еще интересно, у меня энкодер заявлен как 24 шага на оборот, по механике дает 24 "щелчка", а считает при этом до 96... Может имеет смысл считать целыми сигнатурами (а-ля синхронизация по началу сигнатуры) или четверть-шагами? Или у меня уже крыша рассинхронизировалась?

leshak
Offline
Зарегистрирован: 29.09.2011

AlexFisher пишет:

 Вот только не пробовал бороться "железными" методами. Может 2 конденсатора по 0.1 мкФ спасут отца русской демократии? Под рукой не оказалось - непроверил.

Та же история. "Железно" - религия не позволяет (по идее то что делает железо должно и програмно быть возможно) и "нет под рукой.

AlexFisher пишет:

А тем, кому нужно ловить каждый шаг, все равно "контактные" энкодеры не помогут, им нужны безконтактные, а там дребезга не будет. Там другие подводные камни.

Не хочется в это верить. Бесконтактные - стоят кучеряво. Если для "разово" это еще покатит, то для "серии" это уже ОЧЕНЬ ОЩУТИМО. Поэтому и хочется научится стабильно работать с самыми дешевыми.

Да и фиг с ним дребезгом при смене. Но ведь, по идее, когда идет фронт A, при этом B должен "стоять как влитой". Уже закончить все свои "трепыхания" к тому времени. Получает он дребезжит вообще при любом механическом действии, а не только когда собрался менятся?

AlexFisher пишет:

Вот что еще интересно, у меня энкодер заявлен как 24 шага на оборот, по механике дает 24 "щелчка", а считает при этом до 96... 

Да нет. Это как раз и есть "самый обычный энкодер". На один шаг выдает 4-ре импульса (можно еще и "полущелчок ловить").

Вообщем-то и было в мыслях, следующих шагом смотреть на вот эту серии из 4-рех. Смотреть "кого из них больше". Ну и считать их все "одним счелчком". Правда нужно, опя-ть таки из за шума, не всегда на получается "счелчек 4-ре". Так что нужно,опять-таки, как-то по времени разделять "на серии", но при быстром вращении, не уверен что "это получится".

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

leshak пишет:

Та же история. "Железно" - религия не позволяет (по идее то что делает железо должно и програмно быть возможно) и "нет под рукой.

Мне как раз религия позволяет - я больше электронщик, чем программист, мне не позволяет лень :) Завтра обязательно попробую. Если не забуду кондерчиков из дома прихватить :)

leshak
Offline
Зарегистрирован: 29.09.2011

AlexFisher пишет:

Мне как раз религия позволяет - я больше электронщик, чем программист

Может тогда у вас осцил есть? Можете посмотреть что там на B происходит в момент смены A?

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Осцил дома... ардуина на работе... попробую в кучу собрать :) 

step962
Offline
Зарегистрирован: 23.05.2011

leshak пишет:

Да и фиг с ним дребезгом при смене. Но ведь, по идее, когда идет фронт A, при этом B должен "стоять как влитой". Уже закончить все свои "трепыхания" к тому времени. Получает он дребезжит вообще при любом механическом действии, а не только когда собрался менятся?

Вот так выглядит механический энкодер изнутри (стырено с robocraft'a):

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

leshak
Offline
Зарегистрирован: 29.09.2011

 

AlexFisher пишет:

Осцил дома... ардуина на работе... попробую в кучу собрать :)

Ну достаточно познакомить энкодер и осцил. Дуина - не обязательно.

Да и вообще "не обязательно", ваша картинка уже дала ответ.
Хотя вообщем-то, после всех мук, я уже в голове себе именно такую картину и нарисовал. Другого объяснения "как такое может быть" - я не видел. Хотел подтверждения. Вроде получил.

Значит, таки нужно либо ставить кондеры, либо изображать их програмно. Причем на обоих пинах.

Только наверное не через

> _delay_ns()

Что-то я не уверен что она "без прерываний". Неужели эна "тупо крутит цикл"? Точно на счетчики времени не смотрит?

К тому же "тупой делей" скорее всего не поможет. Он предпологает что "пошумело и успокоилось". А у нас "шумит все время".

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

 _delay_ns() тупо крутит цикл. Количество интерраций расчитывает компиллятор по тактовой процессора. Именно поэтому аргументом функции может быть только константа. _delay_ms() уже вызывает в цикле _delay_ns().

По поводу механики энкодера - я с ней знаком. "плохой контакт" ползунков лечится 2-мя способами:

  1. Больший ток за счет внешней подтяжки (ом эдак 100-200)
  2. RC-фильтр (в "минимальном" варианте - просто конденсатор параллельно энкодеру).
maksim
Offline
Зарегистрирован: 12.02.2012

А почему тогда потенциометры не шумят? Там такие же контакты....

leshak
Offline
Зарегистрирован: 29.09.2011

maksim пишет:

А почему тогда потенциометры не шумят? Там такие же контакты....

Наверное потому что они уже немного изображают из себя RC-цепочу. R - сам потенциометр. C- какое-то паразитное имеется.

Хотя вообще не факт что "не шумят". В районе своего нуля (когда сопротивление 0) вполне могут и шуметь так же.

Да и банално, в потенциометре площадь контакта может быть на порядок больше.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

leshak пишет:

Хотя вообще не факт что "не шумят". В районе своего нуля (когда сопротивление 0) вполне могут и шуметь так же.

Да и банално, в потенциометре площадь контакта может быть на порядок больше.

Еще как шумят!!! Особенно, когда крутишь ручку! Особенно советсткие! Два раза особенно китайские!!!. Для качественного усилка приходится покупать регуляторы (переменные резистроры) фирмы Alps за 50$

PS Извините, но давайте про резисторы не будем, оффтоп, все же. Итак уходили в программирование сильно...

leshak
Offline
Зарегистрирован: 29.09.2011

leshak пишет:

 

В "теории", на прерываниях вообще все очень просто.

Повесили канал A, на пин 2. Сделали attachInterupt(0,pinAHandler,RISING);  

Красные стрелки это момент когда вызовется функция pinAHandler, в зависимости от того в какую сторону крутим ручку. 

В самом функции pinAHandler прочитали пин на который подсоеденан канал B. Если там 1 - значит щелкнули против часовой, если 0 - значит по часовой (ну или наоборот, смотря как подключили A,B).

Ну в теории "все просто", на практике - сложней (но тоже просто когда знаешь где собака зарыта:)

Купить осцил - жаба давить. У знакомых - через пол города ехать.
В итоге собрал на дуинке простейшик Logic Analizer и "посмотрел че-там".
Ожидал увидеть шум на обоих каналах просто в момент любого движения.
Но все оказалось проще.
Шум позникает только в момент переключения состояния канала. Когда он "установился" - стоит как влитой.
То есть при смене канала A - все-таки можно уверенно смотреть на B. К тому моменту у него никакого дребезга нет и в помине.
"Собака" была, все-таки в определении смены канала А. Неправильно выбран момент когда определяется "он поменялся".
Так как у нас есть шум "при смене", то вот на этой картинке красные стерочки, наш триггер на FRONT может сработать на любой из этих красных стрелок, вне зависимости от того куда мы крутим. Как повезет. Не будет шума при "падении в ноль" - поймаем RISING в правильный момент (и правильно поймем направление глядя на B, четко в соотвествии теории), "зашумит"" при переходе канала в ноль - все, мы ошиблись в направлении. Глядим на B в неправильное время.
Вот и получается, что "по теории" - без раницы на что вешать обработчик, на RISING или FALLING, а в реальности - нужно на FALLING :) Первое же падение A в ноль - четко означет "щелчок", а последующие - это шум.

Вот мой скетч из #55. Отличие только RISING->FALLING

 

#define PIN_A 2
#define PIN_B 3

volatile unsigned long enabledA_time=0;
volatile bool fl=false; // флаг что нужно вывести
volatile bool value_b=0;
void setup(){
  Serial.begin(57600);
  digitalWrite(PIN_A,HIGH);
  digitalWrite(PIN_B,HIGH);
  
  attachInterrupt(0,handler_a,FALLING); // одно слово разницы, а сколько крови попортило :)
  
  Serial.println("Ready");
}

void handler_a(){
  if(enabledA_time<millis()){
    value_b=digitalRead(PIN_B);
    fl=true;
    enabledA_time=millis()+500;// уж включили антидребезг так включили
  }
}

void loop(){
  if(fl){
    Serial.print(value_b);
    Serial.print(" ");
    fl=false;
  }
}

Но, в резульатате. При неспешных щелчках (дебоунс-то 500 милисекунд) - в одном направлении ошибок вообще 0, в другом 2-3% (думаю и это задавлю). При уменьшении дебоунса - ошибки начинают "появлятся", но теперь есть идеи "как это победить" :) И снизить время дебоунса до юзабельных значений.

P.S. Вообщем смотреть нужно когда красная стелочка "вниз", а не "вверх" как на картинке.

leshak
Offline
Зарегистрирован: 29.09.2011

 Фух. Я опять чуствую себя полноценным девелопером. :)
Победил!!!
Четко ловится каждый "щелчек". Без ошибок направления, без пропусков (даже если "крутить" достаточно быстры).

Общий подход такой
1. На нисходящем фронте A, смотрим состояние B и запоминаем его. А так же время "когда это произошло" (падение A)
2. На восходящем фронте A, смотрим сколько времени прошло с пукнта 1. Если меньше определенного значения (5 msec опытно подобрал) - игнорируем, если больше - меняем счетчик энкодра. Направление определяем по состоянию канала B, запомненного на пункте 1.

 

#define PIN_A 2
#define PIN_B 3
#define PULSE_PIN_LEN 5 // минимальная длинна импульса в миллесекундах на которую мы обращаем внимание


volatile unsigned long failingTime=0;
volatile bool fl=false; // флаг что нужно вывести
volatile bool value_b=0;
volatile byte prevA=0;
volatile int encValue=0;
volatile unsigned long pulseLen=0;
void setup(){
  Serial.begin(57600);
  digitalWrite(PIN_A,HIGH);
  digitalWrite(PIN_B,HIGH);
  
  attachInterrupt(0,handler_a,CHANGE);
  
  Serial.println("Ready");
}

void handler_a(){
 // byte portValue=PINE ; // для меги  
//  byte A= (portValue & B10000)>0 ;// digitalRead(PIN_A); PE4
   byte A=digitalRead(PIN_A);
  
  if(!fl){ // пока не отчитались ничего больше не делаем

      if(prevA && !A){ // фронт упал
        //value_b=(portValue & B100000)>0; // digitalRead(PIN_B); PE5 // определили направление, но пока только "запомнили его"
        value_b=digitalRead(PIN_B); // определили направление, но пока только "запомнили его"
        failingTime=millis(); // и запомнили когда мы "упали в ноль", начали отсчет длины импульса
      }
      
    
      if(!prevA && A && failingTime){ // восходящий фронт и мы в режиме "отсчет времени импульса
         pulseLen=millis()-failingTime;
        if( pulseLen>PULSE_PIN_LEN){ // импульс бы достаточно длинный что-бы поверить тому что мы прочитали в его начале
          if(value_b)encValue++; else encValue--;
          fl=true; // включаем пометку что нужно отчитатся в Serial
        }
        failingTime=0; // больше не ведем осчет времени импульса
      }
 }

  prevA=A;
}

void loop(){
  if(fl){
    Serial.print("Enc=");Serial.print(encValue);  
    Serial.print(",Dir="); Serial.print(value_b?"R":"L"); // выводим направление
    Serial.print(",PulseLen=");Serial.println(pulseLen); // выводим длину импульса (удобно для первоначальной настройки)
    fl=false;
  }
}

 

Выдает (сделал 10-ть щелчков по часовой и 10-ть против):

Ready
Enc=1,Dir=R,PulseLen=45
Enc=2,Dir=R,PulseLen=71
Enc=3,Dir=R,PulseLen=58
Enc=4,Dir=R,PulseLen=83
Enc=5,Dir=R,PulseLen=70
Enc=6,Dir=R,PulseLen=30
Enc=7,Dir=R,PulseLen=72
Enc=8,Dir=R,PulseLen=46
Enc=9,Dir=R,PulseLen=97
Enc=10,Dir=R,PulseLen=78
Enc=9,Dir=L,PulseLen=44
Enc=8,Dir=L,PulseLen=28
Enc=7,Dir=L,PulseLen=40
Enc=6,Dir=L,PulseLen=27
Enc=5,Dir=L,PulseLen=14
Enc=4,Dir=L,PulseLen=33
Enc=3,Dir=L,PulseLen=20
Enc=2,Dir=L,PulseLen=27
Enc=1,Dir=L,PulseLen=47
Enc=0,Dir=L,PulseLen=20

Количество щелчков четко совпадет с тем что "чувствуется рукой". Крутил в темпе "что-бы самому успевать считать щелчки".

 

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

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

"совпадает с тем, что чувствует рука"... не гуд, очень хочется ловить все 96 состояний, а не 24.

Хочу собрать тестовую установку из шаговика и энкодера :) Потетсить разные подходы :) Где бы время взять...

leshak
Offline
Зарегистрирован: 29.09.2011

 

AlexFisher пишет:

 

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


Да. В принципе вообще ничто не мешает не расходовать дефицитное внешние прерывание. Взять Pin Change interrupts. Один черт front/down определяем програмно.

А можно, все-таки от таймера. Просто регулярно смотреть на пины. Я когда мерял-смотрел что там происходит, то на частоте опроса ниже 10 килогерц - никаких шумов уже не видно было вообще. Сигналы "чистые и пушистые". Тогда можно будет еще больше упростить логику. И "на каких пинах" - вообще не важно станет (правда таймер займем и кусочек процессорного времени)

AlexFisher пишет:

"совпадает с тем, что чувствует рука"... не гуд, очень хочется ловить все 96 состояний, а не 24.


Ну у меня цель была именно "ручная крутилка". Как устройство ввода "ввести нужное количество тиков". А "сколько состояний" - ну это уже от энкодера зависит :) Или вы "полу-щелчки" ловить хотите? Все-таки длина самих импульсов намного меньше чем интервалы между ними. Там "дребезг" уже трудней будет отфильтровать.
Но возможно это сильно зависит от энкодера. У меня "щелчковый", а "без щелчковый" может быть и более "равномерную картину дает". Тогда там и половинные состояния и имеет смысл и легче ловить.

AlexFisher пишет:

Хочу собрать тестовую установку из шаговика и энкодера :) Потетсить разные подходы :) Где бы время взять...


Как тестовая - да. Как "следить за мотором" - механический быстро сотрется. Тут явно оптический нужен. А с ним и танцев этих, с дребезгом, не будет. Так что IMHO все-таки "чувствует рука" - это гуд. Так как именно только для руки и имеет смысл это использовать.

 

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

leshak пишет:

Как тестовая - да. Как "следить за мотором" - механический быстро сотрется. Тут явно оптический нужен. А с ним и танцев этих, с дребезгом, не будет. Так что IMHO все-таки "чувствует рука" - это гуд. Так как именно только для руки и имеет смысл это использовать.

 

Согласен. Но установку все равно сделаю - со сменным энкодером - с оптическими тоже нужно поэкспериментировать (я имею ввиду самодельные энкодеры).

Jacks_d
Offline
Зарегистрирован: 31.12.2011

Я конечно не дорос до AlexFisher и leshak, т.к. потестил последний код  leshak-а то он всеже не стабилен, в одну сторону считает нормально, а в обратную проскакивают ошибки (вместо вычитания еденицы проскакивает прибавление) к томуже в одну сторону считает на один меньше чем в другую,  вот мой вариант без millis:


#define PIN_A 19
#define PIN_B 48

volatile bool PinAH = 0;
volatile bool PinBH = 0;
volatile bool PinAB = 0;
volatile bool PinBA = 0;
int Enc_v = 0;
int encValue=0;

void setup(){
  Serial.begin(9600);
//  digitalWrite(PIN_A,HIGH);
  digitalWrite(PIN_B,HIGH);
 // attachInterrupt(4,loop,CHANGE);
  Serial.println("Ready");
}
 
void loop(){
if(digitalRead(PIN_A)== HIGH && PinBH == 0 && PinBA == 0)
  {PinAH = 1;PinAB = 1;
   if(digitalRead(PIN_B)== HIGH)
      {PinBH = 1; Enc_v--;}
   }
if(digitalRead(PIN_A)== LOW && digitalRead(PIN_B)== LOW){PinAH = 0; PinBH = 0;PinAB = 0;PinBA = 0;}   

if(digitalRead(PIN_B)== HIGH && PinAH == 0 && PinAB == 0)
  {PinBH = 1; PinBA = 1;
   if(digitalRead(PIN_A)== HIGH)
      {PinAH = 1; Enc_v++;}
   }
if(digitalRead(PIN_A)== LOW && digitalRead(PIN_B)== LOW){PinAH = 0; PinBH = 0;PinAB = 0;PinBA = 0;} 

if(encValue != Enc_v){
Serial.print("Enc=");Serial.println(Enc_v);

encValue = Enc_v;
}
}

плюсы: работает без привязки ко времени(все зависит теперь от скорости работы МК и кода), в обе стороны считает одинаково, можно наверно без внутренней подтяжки, 

этот подход родил когда мучился с длительным нажатием (жмакнули кнопку, переменная приравнялась к millis, если в заданном промежутке времени кнопку отпустили то сбрасываем вход в длительное нажатие, в противном случае можно последовательно нажимать копку и это прокатит за длительное нажатие)

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

Также хочу задать вопрос(офтоп), как стоит строить код если в проекте есть енкодер и графический экран, суть такая,  правильно ли я сделал, что запихал энкодер в while с условием выхода: изменение переменной которое изменяется энкодером, а в конце  кода после отрисовки на экране и прочим делам, приравниваю это значение к предыдущее запомненному которое входит в условие выхода из while, и через break выхожу из while при нажатии других кнопок... если коротко, то если енкодер изменил переменную на еденицу то выходим из цикла (как бы софтовое прерывание, сори не шарю в программировании, возможно есть внятное название) правилен ли  такой подход, т.к. прерывания тут приводили к артефактам на экране?

vworld
vworld аватар
Offline
Зарегистрирован: 26.09.2011

тоже начал пробовать освоить энкодер

пока реализован скетч по управлению яркостью светодиода

int brightness = 0; // яркость LED, начинаем с половины

int fadeAmount = 5; // шаг изменения яркости LED

unsigned long currentTime;

unsigned long loopTime;

const int pin_A = 11; // pin 12

const int pin_B = 10; // pin 11

unsigned char encoder_A;

unsigned char encoder_B;

unsigned char encoder_A_prev=0;

void setup() { // declare pin 9 to be an output:

pinMode(9, OUTPUT); // устанавливаем pin 9 как выход

pinMode(pin_A, INPUT);

pinMode(pin_B, INPUT);
digitalWrite(11, HIGH); 
digitalWrite(10, HIGH);


currentTime = millis();

loopTime = currentTime; }

void loop() {

currentTime = millis();

if(currentTime >= (loopTime + 5)){ // проверяем каждые 5мс (200 Гц)

encoder_A = digitalRead(pin_A); // считываем состояние выхода А энкодера

encoder_B = digitalRead(pin_B); // считываем состояние выхода А энкодера

if((!encoder_A) && (encoder_A_prev)){ // если состояние изменилось с положительного к нулю

if(encoder_B) { // выход В в полож. сост., значит вращение по часовой стрелке

// увеличиваем яркость, не более чем до 255

if(brightness + fadeAmount <= 255) brightness += fadeAmount; }

else { // выход В в 0 сост., значит вращение против часовой стрелки

// уменьшаем яркость, но не ниже 0

if(brightness - fadeAmount >= 0) brightness -= fadeAmount; }

}

encoder_A_prev = encoder_A; // сохраняем значение А для следующего цикла

analogWrite(9, brightness); // устанавливаем яркость на 9 ножку

loopTime = currentTime; }

}

а вот примеры уважемых форумчан, которые приведены выше в теме не заработали, вернее в сериал идет вывод..Redy и все больше ничего не выводится...Ready и все...больше ничего не выводит...

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

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

В порядке бреда: а если повесить выходы B трех энкодеров на одно прерывание и по срабатыванию срочно смотреть какое состояние из трех А изменилось? Думаю, одновременное использование нескольких энкодеров явление редкое и можно это не учитывать в обработке.

Michal
Michal аватар
Offline
Зарегистрирован: 26.04.2013

не получится, будут "считать" все енкодеры сразу, причем 2 точно бред покажут

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

почему 2 остальных бред покажут?

Произошло прерывание, сравниваем B1 и A1, B2 и A2, B3 и A3 (B2 == B1, B3 == B1). 

А1тек != A1пред
А2тек == A2пред
А3тек == A3пред

Следовательно, произошло изменение состояния энкодера 1.

Или это слишком поверхностно?

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Бред - не то слово!

В последнее время не брезгую пользоваться штатной библиотекой. Там можно легко подключать 2 энкодера. Но лучше всего 1 - оба канала на прерывания. А если нужно много энкодеров, то дорога лежит в освоение прерывания по изменению любой ноги одного порта (есть такое в AVR). Прерывание там одно. При возникновении нужно сначала прочитать весь порт и посмотреть, где изменился бит. Новый SoftSerial как раз использует такое прерывание (из-за него есть ограничение, на какие ноги нельзя программировать RX).

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Tomasina пишет:

почему 2 остальных бред покажут?

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

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

Michal
Michal аватар
Offline
Зарегистрирован: 26.04.2013

посмотрите что получается с формой сигнала с енкодера при изменении скорости вращения....

если один датчик будет стоять, а другие вращаться

енкодеры с кодом Грея и инкрементальные енкодеры разные вещи

на счет прерываний по фронту/спаду/изменению... берите старшие меги, у них почти все выводы такую фишку имеют.

Да и чо мудрить,есть готовые библиотеки с поддрежкой софт/хард. опроса енкодера, с выбором количества тактов и .т.п.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Michal пишет:

енкодеры с кодом Грея и инкрементальные енкодеры разные вещи

на счет прерываний по фронту/спаду/изменению... берите старшие меги, у них почти все выводы такую фишку имеют.

Да и чо мудрить,есть готовые библиотеки с поддрежкой софт/хард. опроса енкодера, с выбором количества тактов и .т.п.

Энкодеры с кодом Грея - это разновидность инкрементальных энкодеров.

Библиотеки есть, я уже писал, что такой пользуюсь. Смысл в том, что на ногах без прерываний библиотекой пользоваться очень неудобно - нужно "ручками" опрашивать энкодер часто-часто... Вот и получается, что 2-мя энкодерами худо-бедно можно пользоваться на Arduino UNO, 4-мя на Leonardo и 6-ю на MEGA - по количеству внешних прерываний.

 

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

В моей долгой и не всегда праведной жизни били такие  железные правила:

1. Всегда ставить демпферный диод на индуктивную нагрузку.

2. Разделять силовые и сигнальные землю и питание.

3. Ставить хоть маленький кондёр на входе прерывания.

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

Вот еще один из вариантов, вроде у нас не упоминался: http://avrhobby.ru/index.php?option=com_content&view=article&id=100:2011-10-16-08-23-54&catid=34:projectsmk&Itemid=71

smW
Offline
Зарегистрирован: 24.11.2013

AlexFisher пишет:

Энкодеры с кодом Грея - это разновидность инкрементальных энкодеров.

Библиотеки есть, я уже писал, что такой пользуюсь. Смысл в том, что на ногах без прерываний библиотекой пользоваться очень неудобно - нужно "ручками" опрашивать энкодер часто-часто... Вот и получается, что 2-мя энкодерами худо-бедно можно пользоваться на Arduino UNO, 4-мя на Leonardo и 6-ю на MEGA - по количеству внешних прерываний.

у Leonardo выведены наружу 2 прерывания ?

уточните пожалуйста - можно пользоваться четырьмя выводами энкодера или 4-мя (4 шт) энкодерами ?

 

Цитата:
Arduino Leonardo

http://arduino.ru/Hardware/ArduinoBoardLeonardo

Внешнее прерывание: 2 и 3. Данные выводы могут быть сконфигурированы на вызов прерывания либо на младшем значении, либо на переднем или заднем фронте, или при изменении значения. Подробная информация  ...

 

smW
Offline
Зарегистрирован: 24.11.2013

Michal пишет:
есть готовые библиотеки с поддрежкой софт/хард. опроса енкодера, с выбором количества тактов и .т.п.
здесь такты - внешнее тактирование ?

Michal
Michal аватар
Offline
Зарегистрирован: 26.04.2013

не... это как считывать инфу с датчика, по 1 каналу, по 2, по фронту, по фронту+спаду и т.п., использовать прерывания или программно слушать датчик

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

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

В чем может быть причина? Вроде библиотеки LCD прерывания не используют.

//прерывания ArduinoMega 2560 на пинах 2(0), 3(1), 18(5), 19(4), 20(3), 21(2)

#define DEBUG

#define PIN_ENCODER_A 2           // вывод ENA энкодера
#define PIN_ENCODER_B 3           // вывод ENB энкодера
#define PIN_BUZZER 46             // пин для пищалки

volatile bool PinAH = 0;
volatile bool PinBH = 0;
volatile bool PinAB = 0;
volatile bool PinBA = 0;
int EncoderValue = 0;
int encValue=0;

//************************ Display 
// Подключаем стандартную библиотеку LiquidCrystal
#include <LiquidCrystal.h>
// Задаем размерность индикатора, т.к. эти параметры в коде используются несколько раз
#define LCD_WIDTH 20
#define LCD_HEIGHT 4
// Задаем соответствие пинов индикатора
// VSS — это земля. 
// VDD — питание +5 В
// V0 — контрастность 
#define LCD_PIN3_CONTRAST   45     // он же V0
#define LCD_PIN4_RS         18
#define LCD_PIN6_E          17
#define LCD_PIN11_DB5       38     // он же D4
#define LCD_PIN12_DB6       39     // он же D5
#define LCD_PIN13_DB7       40     // он же D6
#define LCD_PIN14_DB8       41     // он же D7
#define LCD_PIN15_BACKLIGHT 44     // он же A
byte contrastValue = 127;          // задаем контрастность 0...253
byte backlightValue = 253;         // задаем яркость подсветки 0...253
// Инициализируем дисплей в порядке: RS, E, DB5, DB6, DB7, DB8
LiquidCrystal lcd(LCD_PIN4_RS, LCD_PIN6_E, LCD_PIN11_DB5, LCD_PIN12_DB6, LCD_PIN13_DB7, LCD_PIN14_DB8);
//************************ Display end

void setup(){
  Serial.begin(9600);
  digitalWrite(PIN_ENCODER_A,HIGH);
  digitalWrite(PIN_ENCODER_B,HIGH);
  attachInterrupt(0,loop,FALLING);//CHANGE пропускает щелчки

  lcd.begin(LCD_WIDTH, LCD_HEIGHT); 
  pinMode(LCD_PIN15_BACKLIGHT, OUTPUT);      
  pinMode(LCD_PIN3_CONTRAST, OUTPUT);       
  analogWrite (LCD_PIN15_BACKLIGHT, backlightValue); 
  analogWrite (LCD_PIN3_CONTRAST, contrastValue); 

  Serial.println("Ready");
}

void loop()
{
  if(digitalRead(PIN_ENCODER_A) && !PinBH && !PinBA)
  {
    PinAH = 1;
    PinAB = 1;
    if(digitalRead(PIN_ENCODER_B))
    {
      PinBH = 1; 
      EncoderValue--;
    }
  }
  if(!digitalRead(PIN_ENCODER_A) && !digitalRead(PIN_ENCODER_B))
  {
    PinAH = 0; 
    PinBH = 0;
    PinAB = 0;
    PinBA = 0;
  }   

  if(digitalRead(PIN_ENCODER_B) && !PinAH && !PinAB)
  {
    PinBH = 1; 
    PinBA = 1;
    if(digitalRead(PIN_ENCODER_A))
    {
      PinAH = 1; 
      EncoderValue++;
    }
  }
  if(!digitalRead(PIN_ENCODER_A) && !digitalRead(PIN_ENCODER_B))
  {
    PinAH = 0; 
    PinBH = 0;
    PinAB = 0;
    PinBA = 0;
  } 

  if(encValue != EncoderValue)
  { 
    lcd.setCursor(0, 0);
    lcd.print("EncoderValue = ");
    lcd.print(EncoderValue);
    lcd.print("   ");

#ifdef DEBUG
    Serial.print("EncoderValue = ");
    Serial.println(EncoderValue); 
#endif

    tone(PIN_BUZZER, 888);
    delay(16);
    noTone(PIN_BUZZER);
    encValue = EncoderValue;
  }
}
maksim
Offline
Зарегистрирован: 12.02.2012

Tomasina пишет:

В чем может быть причина? 

  attachInterrupt(0,loop,FALLING);

 

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

Если вынести обработчик из loop, то вобще пропускает 70% щелчков:

#define PIN_ENCODER_A 2//49           // вывод ENA энкодера
#define PIN_ENCODER_B 3//48           // вывод ENB энкодера
#define PIN_BUZZER 46              // пин для пищалки

volatile bool PinAH = 0;
volatile bool PinBH = 0;
volatile bool PinAB = 0;
volatile bool PinBA = 0;
int EncoderValue = 0;
int encValue=0;

//************************ Display 
// Подключаем стандартную библиотеку LiquidCrystal
#include <LiquidCrystal.h>
#define LCD_WIDTH 20
#define LCD_HEIGHT 4
#define LCD_PIN3_CONTRAST   45     // он же V0
#define LCD_PIN4_RS         18
#define LCD_PIN6_E          17
#define LCD_PIN11_DB5       38     // он же D4
#define LCD_PIN12_DB6       39     // он же D5
#define LCD_PIN13_DB7       40     // он же D6
#define LCD_PIN14_DB8       41     // он же D7
#define LCD_PIN15_BACKLIGHT 44     // он же A

byte contrastValue = 127;          // задаем контрастность, 0...253
byte backlightValue = 253;         // задаем яркость подсветки, 0...253

// Инициализируем дисплей в порядке: RS, E, DB5, DB6, DB7, DB8
LiquidCrystal lcd(LCD_PIN4_RS, LCD_PIN6_E, LCD_PIN11_DB5, LCD_PIN12_DB6, LCD_PIN13_DB7, LCD_PIN14_DB8);
//************************ Display end

void setup()
{
  Serial.begin(9600);
  digitalWrite(PIN_ENCODER_A,HIGH);
  digitalWrite(PIN_ENCODER_B,HIGH);
  attachInterrupt(0,encoderRead,FALLING);//CHANGE пропускает щелчки

  lcd.begin(LCD_WIDTH, LCD_HEIGHT); 
  pinMode(LCD_PIN15_BACKLIGHT, OUTPUT);      
  pinMode(LCD_PIN3_CONTRAST, OUTPUT);       
  analogWrite (LCD_PIN15_BACKLIGHT, backlightValue); 
  analogWrite (LCD_PIN3_CONTRAST, contrastValue); 

  Serial.println("Ready...");
  lcd.print("Ready...");
}

void encoderRead()
{
  if(digitalRead(PIN_ENCODER_A) && !PinBH && !PinBA)
  {
    PinAH = 1;
    PinAB = 1;
    if(digitalRead(PIN_ENCODER_B))
    {
      PinBH = 1; 
      EncoderValue--;
    }
  }
  if(!digitalRead(PIN_ENCODER_A) && !digitalRead(PIN_ENCODER_B))
  {
    PinAH = 0; 
    PinBH = 0;
    PinAB = 0;
    PinBA = 0;
  }   

  if(digitalRead(PIN_ENCODER_B) && !PinAH && !PinAB)
  {
    PinBH = 1; 
    PinBA = 1;
    if(digitalRead(PIN_ENCODER_A))
    {
      PinAH = 1; 
      EncoderValue++;
    }
  }
  if(!digitalRead(PIN_ENCODER_A) && !digitalRead(PIN_ENCODER_B))
  {
    PinAH = 0; 
    PinBH = 0;
    PinAB = 0;
    PinBA = 0;
  } 
}

void loop()
{
  if(encValue != EncoderValue)
  { 
    lcd.setCursor(0, 0);
    lcd.print("EncoderValue = ");
    lcd.print(EncoderValue);
    lcd.print("   ");

    Serial.print("EncoderValue = ");
    Serial.println(EncoderValue); 

    tone(PIN_BUZZER, 888);
    delay(16);
    noTone(PIN_BUZZER);
    encValue = EncoderValue;
  }
  
}

 

Клапауций
Offline
Зарегистрирован: 10.02.2013

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

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

ну надо же понять как оно изнутри работает.
А что за библиотека?

Клапауций
Offline
Зарегистрирован: 10.02.2013

Tomasina пишет:
ну надо же понять как оно изнутри работает. А что за библиотека?

забыл ссылку воткнуть http://www.pjrc.com/teensy/td_libs_Encoder.html

там единственный ньюанс при считывании значения энкодера и записи переменной в енкодер нужно делить и умножать на 4 - так понимаю, что от аппаратной реализации энкодера зависит:

n = Enc.read()/4;

Enc.write(n*4);

Adno
Offline
Зарегистрирован: 21.09.2012

Я отдельно тупил над этим вопросом, и дарю не очень элегантное решение для квадратичного энкодера (есть ещё суммирующие и прочие):

int FA,FB;
int Counter, Arrow;

attachInterrupt(3, interrupt3, CHANGE); // привязываем pin20 к функции interrupt0()
attachInterrupt(2, interrupt2, CHANGE); // привязываем pin21 к функции interrupt1()

void interrupt2() { CounterAB(); }
void interrupt3() { CounterAB(); }

void CounterAB()
{
int A;
int B;
A=digitalRead(PinA);
B=digitalRead(PinB);
Arrow=0;
if (((FA==HIGH)&&(A==LOW)&&(FB==HIGH)&&(B==HIGH))||((FA==LOW)&&(A==LOW)&&(FB==HIGH)&&(B==LOW))||((FA==LOW)&&(A==HIGH)&&(FB==LOW)&&(B==LOW))||((FA==HIGH)&&(A==HIGH)&&(FB==LOW)&&(B==HIGH)))
{ Counter++; Arrow=1; }
if (((FA==LOW)&&(A==HIGH)&&(FB==HIGH)&&(B==HIGH))||((FA==HIGH)&&(A==HIGH)&&(FB==HIGH)&&(B==LOW))||((FA==HIGH)&&(A==LOW)&&(FB==LOW)&&(B==LOW))||((FA==LOW)&&(A==LOW)&&(FB==LOW)&&(B==HIGH)))
{ Counter--; Arrow=-1; }
FA=A;FB=B; // запомнить состояние
}

Это позволяет реагировать на каждое изменение импульсов

P.S.
не пытайтесь понять логику условий - это опасно для мозга 8=)

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

Если уж сжимать, так по полной, заодно никто не разберется, да и память сэкономим :)

bool FA,FB;
int Counter, Arrow;

void setup(){  
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
attachInterrupt(0, interrupt0, CHANGE); // привязываем pin2 к функции interrupt0()
attachInterrupt(1, interrupt1, CHANGE); // привязываем pin3 к функции interrupt1()
Serial.begin(9600);   
Serial.println("Ready");
}

void interrupt0() {CounterAB();}
void interrupt1() {CounterAB();}

void CounterAB(){
bool A,B;
A=digitalRead(2);
B=digitalRead(3);
Arrow=0;
if((FA&&!A&&FB&&B)||(!FA&&!A&&FB&&!B)||(!FA&&A&&!FB&&!B)||(FA&&A&&!FB&&B)){Counter++;Arrow=1;}
if((!FA&&A&&FB&&B)||(FA&&A&&FB&&!B)||(FA&&!A&&!FB&&!B)||(!FA&&!A&&!FB&&B)){Counter--;Arrow=-1;}
FA=A;FB=B; // запомнить состояние
    Serial.print(A);
    Serial.print("\t");
    Serial.print(B);
    Serial.print("\t");
    Serial.println(Counter);
}

void loop()
{
}

Проблема в том, что с моим экондером что этот код, что код с библиотекой td_libs_Encoder - оба срабатывают четырежды при каждом щелчке энкодера. А если делить на 4, то получаем 4 одинаковых значения подряд.

P.S. и опять виснем при быстром вращении энкодера (рукой!)

Adno
Offline
Зарегистрирован: 21.09.2012

Tomasina пишет:

Проблема в том, что с моим экондером что этот код, что код с библиотекой td_libs_Encoder - оба срабатывают четырежды при каждом щелчке энкодера. А если делить на 4, то получаем 4 одинаковых значения подряд.

P.S. и опять виснем при быстром вращении энкодера (рукой!)


Всё правильно! Это алгоритм 4-кратный. Его писал на прибор с 200 импульсов/оборот, что было мало.
Для получения 1 импульса необходимо установить флаг HА по фронту импульса А, установить флаг HB по фронту импульса B, и по спаду импульса A будет событие счёта, соответствено все флаги сбрасываются и цикл повторяется. Спад B игнорируется
Это сделано для повышения надежности счёта. Нарушение последовательности сразу сообщают контрольному устройству неисправность энкодера.

не знаю получится ли одновременно использовать
attachInterrupt(3, interrupt3, HIGH);
attachInterrupt(3, interrupt4, LOW);

Клапауций
Offline
Зарегистрирован: 10.02.2013

Tomasina пишет:

Проблема в том, что с моим экондером что этот код, что код с библиотекой td_libs_Encoder - оба срабатывают четырежды при каждом щелчке энкодера. А если делить на 4, то получаем 4 одинаковых значения подряд.

P.S. и опять виснем при быстром вращении энкодера (рукой!)

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

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

#define ENCODER_USE_INTERRUPTS // одна нога на прерывании.
#define ENCODER_OPTIMIZE_INTERRUPTS // обе ноги на прерываниях.
#define ENCODER_DO_NOT_USE_INTERRUPTS // без обработки прерываний, подключено к любой ноге.

в сомпорт пуляем изменённое значение переменной энкодера, а не сырые данные - откуда там возьмутся повторы, если читаем изменение состояния?

авторы библиотеки аппаратно проверили максимальную производительность - 127 кГц.

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

иначе, #define ENCODER_OPTIMIZE_INTERRUPTS

пример использования:

#define ENCODER_DO_NOT_USE_INTERRUPTS // енкодер не использует прерывания.
#include <Encoder.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 10, 11, 12, 13);

int n; // переменная, хранящая значение энкодера.

Encoder Enc_00(18, 19); // объявляем, куда подключен энкодер Enc_00.

void setup() {
lcd.begin(16, 2);
n = 0; // начало отсчёта энкодера.
Enc_00.write(n*4);
}

void loop() {
int nn = Enc_00.read()/4;
if (nn != n) {
n = nn;
lcd.clear(); lcd.setCursor(0, 0); lcd.print(n);
}
}

 

Proton
Offline
Зарегистрирован: 06.04.2011

Здравствуйте!

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

Спасибо.

ascon
ascon аватар
Offline
Зарегистрирован: 14.08.2013
/*
** Энкодер
** 
*/
 const int A=4 ;       // порт А
 const int B=5;          // порт В
 int val = 0;              // начало отсчета с нуля
 int fadeAmount = 1;             // шаг изменения 
 unsigned char encoder_A;
 unsigned char encoder_B;
 unsigned char encoder_A_prev=0;
void setup()  {
  Serial.begin(9600);
  pinMode(A,INPUT);
  pinMode(B,INPUT); 
} 
void loop()  
{
    encoder_A = digitalRead(A);     // считываем состояние выхода А энкодера 
    encoder_B = digitalRead(B);     // считываем состояние выхода B энкодера   
    if ((!encoder_A) && (encoder_A_prev)) //  если состояние изменилось с положительное к нулю
    {
       if (encoder_B)
       {
         // выход В в полож. сост., значит вращение по часовой стрелке
        // увеличиваем число, до 60
        if (val + fadeAmount <= 60) val += fadeAmount; 
       }
       else
       {
         // выход В в 0 сост., значит вращение против часовой стрелки     
        // уменьшаем число, но не ниже 0
         if(val - fadeAmount >= 0) val -= fadeAmount; 
       }
    }
      encoder_A_prev = encoder_A; // сохраняем значение А для следующего цикла 
      Serial.println (val);      // устанавливаем значения
      delay(5);
   
   }
   

    
    
    
  Как то вот так я переделал скейтч под себя половино лишнее удалил. подумал что оно не нужное и все работает)))
  

 

Vittorio
Offline
Зарегистрирован: 02.02.2015

Если медленно вращать, стабильно отрабатывает или проскакивает?