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

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Элементарно. Грязь и окисление контактов. Ну и "пропил" конечно же. Разобрать, зачистить и будет "как новый". или правильный кондер, точнее "НЧ фильтр". :)

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

Arhat109-2 пишет:

Элементарно. Грязь и окисление контактов. Ну и "пропил" конечно же. Разобрать, зачистить и будет "как новый". или правильный кондер, точнее "НЧ фильтр". :)

отсюда вывод: на скоростные балалайки ставим оптические энкодеры, на медленные - механику.

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

**рекомендацию тестировать код на убитом энкодере считаю странной.

 

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

Клапауций 777 пишет:

интересно, нарыть полный даташит на механический энкодер 

Какое странное желание, еще понять дворец или принца на белом коне. Ну ладно.  Трахтибидох -http://www.platan.ru/pdf/datasheets/bourns/pec12.pdf

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

Logik пишет:

Какое странное желание, еще понять дворец или принца на белом коне. Ну ладно.  Трахтибидох -http://www.platan.ru/pdf/datasheets/bourns/pec12.pdf

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

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Я вообще слабо понимаю проблему. Вроде как не гипермассовое производство (для себя любимого, ну на крайняк ещё для 10-100 таких же, кто купит и то "может быть"), чтобы экономить на оптическом энкодере, который либо собирается из 2-х деталек по 3 коп. за ведро, или они же выдираются из старых мышек, коих "завались" на любом офисе. Подойди к любому эникейщику, он тебе этих сдохших мышей прямо в ведро и отсыплет. Себе так наковырял рабочих переключательных кнопок штук 30 для "датчиков касания" (в Лего исполнении продаются по .. 1500руб/шт!) и пар светодиод-транзистор с дырчатыми колесиками .. нафига вообще в наше время нужны эти "скользящие трещётки"?

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

В самом верху: max speed 100rpm Для числовых параметров нормирована 15rpm. Стоит в скобках.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Кстати, писал уже про китайские 130-е моторы с оптическими энкодерами на валу .. Ардуино мега вполне справляется с подсчетом скорости вращения 8000rpm по прерываниям (фронт/срез) как миниум 2-х моторов одновременно. Покупал по $1/шт .. вместе с мотором вот, в конце сентября 2015 и пришли.

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

Arhat109-2 пишет:

В самом верху: max speed 100rpm Для числовых параметров нормирована 15rpm. Стоит в скобках.

да - при более внимательном просмотре увидел, что врщать нужно не чаще одного оборота за 0,6 секунды

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

Arhat109-2 пишет:

нафига вообще в наше время нужны эти "скользящие трещётки"?

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

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

В предыдущем посте слово "владельцы" необходимо читать как "производители".

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

Logik пишет:

В предыдущем посте слово "владельцы" необходимо читать как "производители".

производителю пофиг, где будут гореть владельцы их автомагнитол.

ii_alex
Offline
Зарегистрирован: 28.12.2015

dimax пишет:

Мой вариант обработчика энкодера. Очень простой и совершенно безглючен. Обработчик сидит в теле внешнего прерывания PCINT.   В нижеследующем скетче задействованы входы A0 и A1 как цифровые. Используется подтяжка к питанию (распаянная на платке стандартного энкодера),  и антидребезговые конденсаторы 0,1мкф на землю. Кому нужно на другие ноги - смотреть даташит страница 73 и маскировку битов сделать по аналогии.

volatile int enc;
void setup() {                
Serial.begin(9600);
PCICR=1<<PCIE1; //разрешить пренрывание
PCMSK1=(1<<PCINT9)|(1<<PCINT8); //выбрать входы
}

ISR (PCINT1_vect){
static byte old_n=PINC&3; // маска B00000011 что б читать только нужные 2 бита
byte new_n=PINC&3;
if (old_n==1&&new_n==3||old_n==2&&new_n==0) {enc++;}
if (old_n==2&&new_n==3||old_n==1&&new_n==0) {enc--;}
old_n= new_n;
}

void loop() {
Serial.println(enc); 
}

dimax  Если не затруднит,  расскажите пожалуйста по коду. Только начал осваивать arduino очень многое непонятно. С этим кодом мой энкодер прекрасно работает только в сторону увеличения, в сторону уменьшения никак не хочет. Подскажите пожалуйста в чем может быть проблема. И еще один маленький вопрос, как ограничить максимальное значение переменной enc например до 50 и минимальное до 0. Извините за глупые вопросы, только учусь. 

 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

ii_alex, я потом ещё упростил обработчик энкодера, вот последний вариант (для входов A0 и A1):

volatile int enc;
void setup() {                
Serial.begin(9600);
PCICR=1<<PCIE1; //разрешить прерывание
PCMSK1=1<<PCINT8; //выбрать вход на котором сработает прерывание 
}
ISR (PCINT1_vect){
byte n=PINC&3; // узнать значение суммы битов PC0 и PС1
if (n==1||n==2) {enc++;}
if (n==3||n==0) {enc--;}
}
void loop() {
Serial.println(enc); 
}

По коду что сказать? Тут всё базируется на прерывании PCINT и двоичной математике.  Читается порт, если его младшие биты в сумме своих весов дают те значения, что указаны в скетче то делается вывод, что энкодер крутили в ту или другую сторону. Для этого способа обязательно нужны конденсаторы на землю от обоих выводов энкодера. Ёмкость зависит от конкретного энкодера, 0,1мкф у меня нужно было на очень старом, убитом энкодере. На новом достаточно будет 0,001мкф. Если в одну сторону инкременируется, а в другую нет, то скорее всего недостаточная ёмкость конденсатора. Или его вообще нет :) И подтяжка к питанию на плате энкодера конечно должна быть.

Версия с ограничением от 0 до 50. В первой строке начальное значение.

volatile uint8_t enc=25;
void setup() {                
Serial.begin(9600);
PCICR=1<<PCIE1; //разрешить прерывание
PCMSK1=1<<PCINT8; //выбрать входы
}
ISR (PCINT1_vect){
byte n=PINC&3;
if ( (n==1||n==2)&&enc<50) {enc++;}
if ((n==3||n==0)&&enc>0) {enc--;}
}
void loop() {
Serial.println(enc); 
}

 

ii_alex
Offline
Зарегистрирован: 28.12.2015

dimax Спасибо за ответ ! Пытаюсь вникать. Энкодер обычный, резики подтяжки распаяны, конденсаторы стоят на 0,1 мкф.  Если залить ваш скетч из соседней ветки, где обсуждается (отличная вешь, генератор). То энкодер работает без проблем, в любую сторону. Значит подключение A0 A1, емкости, резисторы, все в порядке. А с эти скетчем нивкакую в сторону уменьшения. Последний, который самый упрощенный, к сожалению смогу попробовать только завтра. А так хочется ! 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

ii_alex, последний -это и есть тот, который из "генератора.." :) Но первый вариант тоже рабочий на 100%, не знаю в чём может быть дело у вас.

maksim
Offline
Зарегистрирован: 12.02.2012
ISR (PCINT1_vect){
  (PINC&1)^((PINC&2)>>1)?enc++:enc--;
}

 

ii_alex
Offline
Зарегистрирован: 28.12.2015

dimax пишет:

ii_alex, последний -это и есть тот, который из "генератора.." :) Но первый вариант тоже рабочий на 100%, не знаю в чём может быть дело у вас.

dimax в скетче генератора немного по другому:

PCICR=1<<PCIE1; //разрешить прерывание
PCMSK1=1<<PCINT9; //выбрать входы
}
 
ISR (PCINT1_vect){
byte n=PINC&6;
if ( (n==4||n==1) {enc++;}
if ((n==6||n==1) {enc--;}
}
 
Вот так у меня работает в обе стороны, отлично.
Alexino
Offline
Зарегистрирован: 29.12.2015

Значит подключено к другим выводам (скорее всего к PC1 и PC2). А в варианте выше - PC0 и PC1.
Только вот совершенно не понятно, зачем ещё условие n==1. Ибо оно может только навредить, т.к. будет читаться ещё и значение с соседнего вывода (PC0).

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

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

ii_alex пишет:

ISR (PCINT1_vect){

byte n=PINC&6;
if ( (n==4||n==1) {enc++;}
if ((n==6||n==1) {enc--;}
}
 
Вот так у меня работает в обе стороны, отлично.

Как верно подметил Alexino так работать вообще не должно. Обратите внимание, при считанной единице с порта получается и enc+ и enc -, а это абсурд. Если у вас работает, значит читаются только 4 или 6, и скорее всего значения меняются не каждый раз, а через один щелчок. В оригинале было 4-2  6-0.

Alexino пишет:

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

Хоть это и нарушение идеологии прерываний, но как нибудь проверю такой вариант из спортивного интереса:)

Alexino
Offline
Зарегистрирован: 29.12.2015

dimax пишет:

Хоть это и нарушение идеологии прерываний, но как нибудь проверю такой вариант из спортивного интереса:)

Можно же это всё сделать и без нарушений :)
Существует системный таймер, который можно читать с помощью millis(). Думаю, об этом Вы в курсе. Ну а дальше дело техники )
Причём, я не зря назвал цифру 2, т.к. при использовании системного таймера, разница в 1 мс = 0...1 мс, и можно нарваться на самое минимально время, при котором дребезг будет учтён.
А при отслеживании разницы в 2 мс., мы гарантированно отсеиваем целую миллисекунду. А диапазон времён будет = 1...2 мс.

ii_alex
Offline
Зарегистрирован: 28.12.2015

dimax в оригинале действительно 4-2 6-0. Только в этом варианте получается, что энкодер срабатывает через 2 значения т.е было 1 вращаем становится 3. Чтобы получить 2 нужно вращать энкодер очень очень медленно и буквально чуть-чуть тогда сработка на 1 если крутить стандартно то обрабатывается 2 значения. Я только учусь по этому экспериментировал. Тот код который выложил работает отлично, все считается по 1 в любую сторону, но код неправильный. Подскажите пожалуйста как правильно обработать. 

 

 

 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

ii_alex, значит у вас энкодер другого типа. На один физический щелчок он дёргает прерывание 2 раза. В теме про генератор обсуждался такой вариант, почитайте сообщение #52 Можно как вариант убрать одну цифру.  if ( n==4) {enc++;} if (n==6) {enc--;}

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

А мааааааааленькую фотографию энкодэра можно посмотреть?
Или название какое у него имеется?
Может просто с кондёрами переборщили? 1-10 нан - я бы больше не ставил.

ii_alex
Offline
Зарегистрирован: 28.12.2015
 
dimax Посмотрел пост 52 в теме про генератор.  Исходя из него получается 
 
ISR (PCINT1_vect){
static boolean tic=1;
tic=!tic;
uint8_t n=PINC&6;
if (tic){
if ((n==4)&&enc<16) {enc++;}
if ((n==6)&&enc>1) {enc--;}
}
}
 
Вообще никак не работает. 
А вот вариант 
 
ISR (PCINT1_vect){
byte n=PINC&6;
if ((n==4)&&enc<16) {enc++;}
if ((n==6)&&enc>1) {enc--;}
}
 
Работает отлично ! 
Огромное спасибо ! 

 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

ii_alex,  Я имел ввиду, что нужно сделать либо как в #52, либо убрать одну цифру. А вы взяли как в 52 + убрали цифру. Но раз второй вариант заработал , то и ладно.

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

До боли похож на "обычный" и обсосанный 100 раз.
Должен работать!

http://www.decada.org.ru/project/lessons/bascom_avr/18/

Посмотрите по "полосатым полоскам".
При смене состояния вверх или вниз любого входа ( а это PinChangeInterrupt)
и при 100%, а это главное, отсутствии дребезга ( как раз лечится кондёрчиками, а гистерезис входов нам в этом помогает)
два входа имеют 4 состояния ( потому-что  это 2 бита).

И эти состояния будут, как писал dimax:  1,2  или 0,3

if (n==1||n==2) {enc++;}
if (n==3||n==0) {enc--;}

Кстати по поводу кондёрчиков очень важное замечание.
Они заряжаются довольно медленно через резистор привязки,
а вот разряжаются очень быстро через металлический контакт.
Я бы при 20 кОм привязки поставил Ом 510-1000  последовательно на контакты- хоть немного затянуть их  разряд.

skodec
Offline
Зарегистрирован: 26.07.2015

Досталась панель от неисправной магнитолы, хочу переделать ее для управления чем нибудь другим. Возникла проблема, в ней стоит 3х фазный энкодер вот такой http://www.alps.com/prod/info/E/HTML/Encoder/Ring/EC35AH/EC35AH240403.html Подскажите как его победить, логика работы другая. Пробовал платой  нано каждые 10мс опрашивать порты и слать в сериал порт - все видит, но при быстрой прокрутке не успевает. Покупать другую плату где есть больше ног для прерываний? или можно как то по другому?

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

Опрашивайте чаще.
Реагируйте на смену уровня
Шлите на 115200.....

miller2212
Offline
Зарегистрирован: 07.03.2016

Приветсвую всех форумчан!!!!

Решил я значит смастерить термомстат на котел в загородном доме.
котел греется дровами и электричесвом
функцианал таков:

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

Аппаратная часть

ардуино уно
I2C LCD16x2
енкодер с кнопкой (общий - земля)
кнопка 

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

 


#include <LiquidCrystal_I2C.h>
#include <RotaryEncoder.h>

RotaryEncoder encoder(12, 13); //устанавливаем 12, 13 порты для энкодера
LiquidCrystal_I2C lcd(0x27,16,2);  // определяем дисплей

int encBtn = 11; // кнопка энкодера
int btnPin = 10; // кнопка

int result = -1;
int subRes = -1;

int encBtnCl = 0;
int encBtnSubMenuCl = 0;
int setMenuCl = 0;

long lcdUpdate;

boolean PUMP = false;
boolean HEATER = false;
boolean SERENA = false;

int PUMP_temp;
int HEATER_temp;
int SERENA_temp;

char* indexMenu[] = { 
  "Setup PUMP",
  "Setup HEATER",
  "Setup ALARM",
  "EXIT Setup"
};


void setup()
{
  pinMode(13, INPUT);
  pinMode(12, INPUT);
  pinMode(11, INPUT);
  pinMode(10, INPUT);
  lcd.init(); // initialize the lcd 
  lcd.backlight();
  lcd.setCursor(2,0);
  lcd.print("ENCODER_test");
  digitalWrite(encBtn, HIGH); digitalWrite(btnPin, HIGH); digitalWrite(13, HIGH); digitalWrite(12, HIGH);

  Serial.begin(9600);

} // setup()

void loop(){
  if (digitalRead(btnPin) == LOW){
     lcd.clear();
     lcd.setCursor(6,0);
     lcd.print("MENU");
     while (setMenuCl != 1) menu();}else {
  lcd.clear();
  lcd.setCursor(2,0);
  lcd.print("ENCODER_test");
  setMenuCl = 0;}
}     

void menu(){
/////////////////////////////----МЕНЮ----///////////////////////////////////////////////////////////////////////////

/////////////////////////////ОПРОС ЭНКОДЕРА  
  static int pos = 0;
  encoder.tick();
  int newPos = encoder.getPosition();
                           //_____
  if (pos < newPos) result++;//  |
  if (pos > newPos) result--;//  | крутим меню по кругу
  if (result > 3) result = 0;//  |
  if (result < 0) result = 3;//  |
                          //_____
  if (pos != newPos){
  pos = newPos;
/////////////////////////////// обновление дисплея
  if (millis() - lcdUpdate >= 100) {
  
  lcd.clear();
  lcd.setCursor(6,0);
  lcd.print("MENU");
///////////////////////////ИНДИКАЦИЯ МЕНЮ   
switch (result){
     case 0: //установка насоса
       
       lcd.setCursor(0,1);
       lcd.print(indexMenu[result]);
     
     break; 
     case 1: //установка нагревателя
       
       lcd.setCursor(0,1);
       lcd.print(indexMenu[result]);

     break;
     case 2: //установка сирены
       
       lcd.setCursor(0,1);
       lcd.print(indexMenu[result]);
        
     break;
     case 3: //выход
       
       lcd.setCursor(0,1);
       lcd.print(indexMenu[result]);
       
     break;
     }                                   
     lcdUpdate = millis();
  } // конец обновления
  } // конец опроса энкодера
/////////////////////////////////////////////////////КОНЕЦ ИНДИКАЦИИ МЕНЮ
/////////////////////////////////////////////////////ВХОД В ПОДМЕНЮ 1
   if ((digitalRead(encBtn) == LOW)&&(result == 0)){                     //ОПРОС КНОПКИ ЭНКОДЕРА
                                                                         //ЕСЛИ КНОПКА НАЖАТА И ПУНКТ МЕНЮ 1, ТО...
            lcd.clear();         
            lcd.setCursor(0,0);
            lcd.print(indexMenu[result]);
            lcd.setCursor(0,1);
            lcd.print("****************");

            Serial.print("encoder btn1 ");
            Serial.println(encBtnCl);
            
            
         if (encBtnCl == 0){
            while (encBtnCl != 3){
/////////////////////////////////////////////////////ИНДИКАЦИЯ ПОДМЕНЮ НАСОС
          encoder.tick();
          newPos = encoder.getPosition();

          if (pos < newPos) subRes++;
          if (pos > newPos) subRes--;
          if (subRes > 2) subRes = 0;
          if (subRes < 0) subRes = 2;

          if (pos != newPos){
             pos = newPos;
//////////////////////////////////////////////////////////////////////////////////////////            
             if (millis() - lcdUpdate >= 100) {              
              lcd.clear();         
              lcd.setCursor(0,0);
              lcd.print(indexMenu[result]); 
              
                switch (subRes){
                  case 0:
                  
                  lcd.setCursor(0,1);
                  lcd.print("ON");
                  
                  break;
                  case 1:
                  
                  lcd.setCursor(0,1);
                  lcd.print("OFF");
                  
                  break;
                  case 2:
                  
                  lcd.setCursor(0,1);
                  lcd.print("<=Back");
                  
                  break;
                }
                lcdUpdate = millis();
             }
          }
          
/////////////////////////////////////////////////////КОНЕЦ ИНДИКАЦИИ ПОДМЕНЮ 1
/////////////////////////////////////////////////////СОХРАНЕНИЕ ПАРАМЕТРОВ ИЛИ ВЫХОД
             
             if ((digitalRead(encBtn) == LOW)&&(subRes == 0)){      //ВКЛЮЧИТЬ НАСОС И УСТАНОВИТЬ ТЕМПЕРАТУРУ
              /*
              encBtnCl = subRes;
              Serial.println(encBtnCl);
              */
              PUMP = true;
                   if (encBtnSubMenuCl == 0){
                      while (encBtnSubMenuCl != 1){
                         pos = 0;
                         encoder.tick();
                         newPos = encoder.getPosition();
                             
                          if (pos != newPos) {                        
                             lcd.setCursor(10, 1); 
                             lcd.print(newPos);
                             lcd.print(" ");
                             pos = newPos;
                             }    
                      }
             }}
             if ((digitalRead(encBtn) == LOW)&&(subRes == 1)){      //ВЫКЛЮЧИТЬ НАСОС
              /*
              encBtnCl = subRes;
              Serial.println(encBtnCl);
              */    
              PUMP = false;            
             }
             if ((digitalRead(encBtn) == LOW)&&(subRes == 2)){       //ВЫХОД

              lcd.clear();
              lcd.setCursor(6,0);
              lcd.print("MENU");
              lcd.setCursor(0,1);
              lcd.print(indexMenu[result]);           
              
              encBtnCl = 3; 
              menu();     
             }
            }  
///////////////////////////////////////////////////////
          encBtnCl = 0;
          subRes = -1;
         }   
     }///////////////////////////////////////////----КОНЕЦ ПУНКТА ПОДМЕНЮ 1----////////////////////////


/////////////////////////////////////////////////////ВХОД В ПОДМЕНЮ 2
   if ((digitalRead(encBtn) == LOW)&&(result == 1)){                     //ОПРОС КНОПКИ ЭНКОДЕРА
                                                                         //ЕСЛИ КНОПКА НАЖАТА И ПУНКТ МЕНЮ 1, ТО...
            lcd.clear();         
            lcd.setCursor(0,0);
            lcd.print(indexMenu[result]);
            lcd.setCursor(0,1);
            lcd.print("****************");

            Serial.print("encoder btn1 ");
            Serial.println(encBtnCl);
            
            
         if (encBtnCl == 0){
            while (encBtnCl != 3){
/////////////////////////////////////////////////////ИНДИКАЦИЯ ПОДМЕНЮ НАГРЕВАТЕЛЬ
          encoder.tick();
          newPos = encoder.getPosition();

          if (pos < newPos) subRes++;
          if (pos > newPos) subRes--;
          if (subRes > 2) subRes = 0;
          if (subRes < 0) subRes = 2;

          if (pos != newPos){
             pos = newPos;
//////////////////////////////////////////////////////////////////////////////////////////            
             if (millis() - lcdUpdate >= 100) {              
              lcd.clear();         
              lcd.setCursor(0,0);
              lcd.print(indexMenu[result]); 
              
                switch (subRes){
                  case 0:
                  
                  lcd.setCursor(0,1);
                  lcd.print("ON");
                  
                  break;
                  case 1:
                  
                  lcd.setCursor(0,1);
                  lcd.print("OFF");
                  
                  break;
                  case 2:
                  
                  lcd.setCursor(0,1);
                  lcd.print("<=Back");
                  
                  break;
                }
                lcdUpdate = millis();
             }
          }
          
/////////////////////////////////////////////////////КОНЕЦ ИНДИКАЦИИ ПОДМЕНЮ 2
/////////////////////////////////////////////////////СОХРАНЕНИЕ ПАРАМЕТРОВ ИЛИ ВЫХОД
             
             if ((digitalRead(encBtn) == LOW)&&(subRes == 0)){      //ВКЛЮЧИТЬ НАГРЕВАТЕЛЬ И УСТАНОВИТЬ ТЕМПЕРАТУРУ
              /*
              encBtnCl = subRes;
              Serial.println(encBtnCl);
              */  
             }
             if ((digitalRead(encBtn) == LOW)&&(subRes == 1)){      //ВЫКЛЮЧИТЬ НАГРЕВАТЕЛЬ
              /*
              encBtnCl = subRes;
              Serial.println(encBtnCl);
              */                
             }
             if ((digitalRead(encBtn) == LOW)&&(subRes == 2)){       //ВЫХОД

              lcd.clear();
              lcd.setCursor(6,0);
              lcd.print("MENU");
              lcd.setCursor(0,1);
              lcd.print(indexMenu[result]);           
              
              encBtnCl = 3; 
              menu();     
             }
            }  
///////////////////////////////////////////////////////
          encBtnCl = 0;
          subRes = -1;
         }   
     }///////////////////////////////////////////----КОНЕЦ ПУНКТА ПОДМЕНЮ 2----////////////////////////

/////////////////////////////////////////////////////ВХОД В ПОДМЕНЮ 3
   if ((digitalRead(encBtn) == LOW)&&(result == 2)){                     //ОПРОС КНОПКИ ЭНКОДЕРА
                                                                         //ЕСЛИ КНОПКА НАЖАТА И ПУНКТ МЕНЮ 1, ТО...
            lcd.clear();         
            lcd.setCursor(0,0);
            lcd.print(indexMenu[result]);
            lcd.setCursor(0,1);
            lcd.print("****************");

            Serial.print("encoder btn1 ");
            Serial.println(encBtnCl);
            
            
         if (encBtnCl == 0){
            while (encBtnCl != 3){
/////////////////////////////////////////////////////ИНДИКАЦИЯ ПОДМЕНЮ СИРЕНА
          encoder.tick();
          newPos = encoder.getPosition();

          if (pos < newPos) subRes++;
          if (pos > newPos) subRes--;
          if (subRes > 2) subRes = 0;
          if (subRes < 0) subRes = 2;

          if (pos != newPos){
             pos = newPos;
//////////////////////////////////////////////////////////////////////////////////////////            
             if (millis() - lcdUpdate >= 100) {              
              lcd.clear();         
              lcd.setCursor(0,0);
              lcd.print(indexMenu[result]); 
              
                switch (subRes){
                  case 0:
                  
                  lcd.setCursor(0,1);
                  lcd.print("ON");
                  
                  break;
                  case 1:
                  
                  lcd.setCursor(0,1);
                  lcd.print("OFF");
                  
                  break;
                  case 2:
                  
                  lcd.setCursor(0,1);
                  lcd.print("<=Back");
                  
                  break;
                }
                lcdUpdate = millis();
             }
          }
          
/////////////////////////////////////////////////////КОНЕЦ ИНДИКАЦИИ ПОДМЕНЮ 3
/////////////////////////////////////////////////////СОХРАНЕНИЕ ПАРАМЕТРОВ ИЛИ ВЫХОД
             
             if ((digitalRead(encBtn) == LOW)&&(subRes == 0)){      //ВКЛЮЧИТЬ СИРЕНУ И УСТАНОВИТЬ ТЕМПЕРАТУРУ
              /*
              encBtnCl = subRes;
              Serial.println(encBtnCl);
              */  
             }
             if ((digitalRead(encBtn) == LOW)&&(subRes == 1)){      //ВЫКЛЮЧИТЬ НАСОС
              /*
              encBtnCl = subRes;
              Serial.println(encBtnCl);
              */                
             }
             if ((digitalRead(encBtn) == LOW)&&(subRes == 2)){       //ВЫХОД

              lcd.clear();
              lcd.setCursor(6,0);
              lcd.print("MENU");
              lcd.setCursor(0,1);
              lcd.print(indexMenu[result]);           
              
              encBtnCl = 3; 
              menu();     
             }
            }  
///////////////////////////////////////////////////////
          encBtnCl = 0;
          subRes = -1;
         }   
     }///////////////////////////////////////////----КОНЕЦ ПУНКТА ПОДМЕНЮ 3
/////////////////////////////////////////////////----ВЫХОД ИЗ МЕНЮ----////////////////////////
    
if ((digitalRead(encBtn) == LOW)&&(result == 3)) {
  Serial.println("exit");  
  setMenuCl = 1; 
}

}  
/////////////////////////////----КОНЕЦ МЕНЮ----///////////////////////////////////////////////////////////////////////////  

 


 

ali_vlad
Offline
Зарегистрирован: 20.02.2015

Здрасьте, люди добрые!

Прошу поправить скетч. Хочу, чтоб считал с шагом 5, но что-то не выходит.

И, если не затруднит, чтобы не выходило из диапазона [0...255]

#define ENCODER_DO_NOT_USE_INTERRUPTS // без прерываний.
#include <Encoder.h>
Encoder Enc_1(9, 8);
long old_pos = 0;

void setup() {
  Serial.begin(9600);
  Enc_1.write (100);
}

void loop() {
  long new_pos;
  new_pos = (Enc_1.read()/4);
  if (new_pos != old_pos) {
    Serial.print(Enc_1.read());
    old_pos = new_pos;
    Serial.print(" ");
    Serial.print(new_pos*4+1);
    Serial.print(" ");
    Serial.println(old_pos);
    Enc_1.write (new_pos*4+1);  
  }
}

 

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

new_pos = (Enc_1.read()/4)*5;

new_pos*4

ali_vlad
Offline
Зарегистрирован: 20.02.2015

Клапауций 232 пишет:

new_pos = (Enc_1.read()/4)*5;

new_pos*4

Безконечный цикл.

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

ali_vlad пишет:

Безконечный цикл.

какой цикл? - я тебе показал куски твоего кода, где тебе нужно исправить.

ali_vlad
Offline
Зарегистрирован: 20.02.2015

Чет я не врубаюсь про исправления

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

ali_vlad пишет:

Чет я не врубаюсь про исправления

заходи через месяц - врубишся

ali_vlad
Offline
Зарегистрирован: 20.02.2015

Очень смешно.

Что так трудно указать, как правильно?

Если нет желания помочь, зачем вообще писать что-то.

BoBo4kA
Offline
Зарегистрирован: 15.01.2016

Решил и я использовать энкодер, купил простой, похожий на KY-040, почитал, посмотрел. Залил стандартный код под него, которого на просторах много, покрутил. Срабатывало с ошибками, крутишь 3 раза в одну, а он 2 в одну, 1 обратно... Решил посмотреть на дребезг... "шумность" за гранью ))

код, и результат поворота на одно деление по обоим выводам:

volatile int x = 0;
volatile int y = 0;
int a = 0;
int b = 0;

void setup()
{
attachInterrupt(0, encoderX, FALLING); //clk
attachInterrupt(1, encoderY, FALLING); //dt
Serial.begin(57600);
}

void loop()
{
if(x!=a)
  {
    Serial.print("X - ");
    Serial.println(x);
    a=0;
    x=0;
  }
if(y!=b)
  {
    Serial.print("Y - ");
    Serial.println(y);
    b=0;
    y=0;
  }  
Serial.println("delay");
delay(5000);
}

void encoderX()
{
x++;
}
void encoderY()
{
y++;
}



left      right
X - 8     X - 34
Y - 52    Y - 3
delay     delay
X - 12    X - 35
Y - 58    Y - 1
delay     delay
X - 20    X - 6
Y - 7     Y - 2
delay     delay
X - 2     X - 3
Y - 29    Y - 1
delay     delay
X - 5     X - 1
Y - 2     Y - 2
delay     delay
X - 4     X - 32
Y - 4     Y - 18
delay     delay
X - 18    X - 2
Y - 16    Y - 1
delay     delay
X - 2     X - 1
Y - 31    Y - 4
delay     delay
X - 60    X - 15
Y - 53    Y - 3
delay     delay
X - 13    X - 36
Y - 13    Y - 3
delay     delay
X - 10    X - 59
Y - 16    Y - 3

Прицепил конденсаторы на землю по 1мкФ, стало чуть лучше, но не принципиально. Вменяемого результата удалось получить при емкости конденсаторов в 4мкФ, но полностью от дребезга всё равно не удалось избавиться. Недостатком большой емкости стало "медленное" срабатывание, т.е. при большой скорости вращения пропускает шаги.

итоговый код:

volatile int x = 0;
int a = 0;

void setup()
{
attachInterrupt(0, encoderX, FALLING); //clk
pinMode(3, INPUT); //dt
Serial.begin(57600);
}

void loop()
{
if(x!=a)
  {
    Serial.print("X - ");
    Serial.println(x);
    a=x;
  }

Serial.println("delay");
delay(5000);
}

void encoderX()
{
  if(digitalRead(3)){x++;}
  else{x--;}
}

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

satelit 2
Offline
Зарегистрирован: 04.12.2016

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

 

Igoreck
Offline
Зарегистрирован: 01.03.2017

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

Пожалуйста помогите!

#include <OLED_I2C.h>
   OLED  myOLED(SDA, SCL, 1);
   extern uint8_t SmallFont[];
 
   volatile int encoderPin1 = 2;      
   volatile int encoderPin2 = 3;      
   volatile int MSB ;
   volatile int LSB ;
   volatile int encoded = 0 ;
   volatile int lastEncoded = 0;
   volatile int sum  ;                
   volatile int i = 0;
   volatile int a = 0;
   volatile int b = 0;
   volatile int c = 0;
   volatile int encoderValue = 0;
      
void setup(){
      Serial.begin (9600);
      myOLED.begin();
      myOLED.clrScr();
      myOLED.setFont(SmallFont);
      
      pinMode(encoderPin1, INPUT);
      pinMode(encoderPin2, INPUT);
      pinMode(4, INPUT);    // Kнопка "Mеню"
      pinMode(13, OUTPUT); 
      
      digitalWrite(encoderPin1, HIGH);
      digitalWrite(encoderPin2, HIGH);
      digitalWrite(4, HIGH);
      
      attachInterrupt(0, updateEncoder, CHANGE);
      attachInterrupt(1, updateEncoder, CHANGE);
}
void loop(){
    MainScreen();
    if(!digitalRead(4)){
      delay(200);Menu();
      }
    if(c==1){
      c=0;Serial.println(encoderValue);
      }
}                     
void updateEncoder(){  
  MSB = digitalRead(encoderPin1);
  LSB = digitalRead(encoderPin2);
  encoded = (MSB << 1) |LSB; 
  sum  = (lastEncoded << 2) | encoded;
    if(encoded==0b0000){
      if(a==0){
        encoderValue = encoderValue+i; c=1; a=1; b=0;
        }
      }
    if(encoded==0b0011){
      if(b==0){
        encoderValue = encoderValue+i; c=1; b=1; a=0;
        }
      }
    if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011){
        digitalWrite(13, HIGH);
        i=1;
      }
    if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000){
        digitalWrite(13, LOW);
        i=-1;
      } 
  lastEncoded = encoded;
}
void MainScreen() {
  myOLED.clrScr();
  myOLED.print("MainScreen", CENTER, 20);
  myOLED.printNumI(encoderValue, CENTER, 40);
  myOLED.update();
}
void Menu(){
  byte menuPos = 1;
  for (i=0; i<=500; i++){
    
   //***************************** Выбор позиции в меню ***********************          
                if(!encoded --){                
                       menuPos = menuPos + 1;
                        }
                       if(menuPos > 4){
                          menuPos = 1;
                        } 
                       
                if(!encoded ++){
                       menuPos = menuPos - 1;
                        }                     
                      if(menuPos < 1){
                      menuPos = 4;
                        }

        switch(menuPos){
  //***************  устанавливаем курсор согласно позиции в меню  ***************
            case 1:
              myOLED.clrScr();             
              myOLED.print(">", 0, 0);              
              myOLED.print("Alarm", 10, 0);         
              myOLED.print("Time", 10, 10);         
              myOLED.print("Setting", 10, 20);      
              myOLED.print("back", 10, 40);  
            if (menuPos == 1 && !digitalRead(4)) MenuSetAlarm();    
               break;                              
            case 2:
              myOLED.clrScr();                             
              myOLED.print(">", 0, 10);             
              myOLED.print("Alarm", 10, 0);        
              myOLED.print("Time", 10, 10); 
              myOLED.print("Setting", 10, 20);     
              myOLED.print("back", 10, 40);   
              if (menuPos == 2 && !digitalRead(4)) MenuSetTime();
               break;
            case 3:
              myOLED.clrScr();                      
              myOLED.print(">", 0, 20);             
              myOLED.print("Alarm", 10, 0);         
              myOLED.print("Time", 10, 10);         
              myOLED.print("Setting", 10, 20);        
              myOLED.print("back", 10, 40);   
              if (menuPos == 3 && !digitalRead(4)) MenuSetting();
               break;      
            case 4:
              myOLED.clrScr();             
              myOLED.print(">", 0, 40);     
              myOLED.print("Alarm", 10, 0);   
              myOLED.print("Time", 10, 10);  
              myOLED.print("Setting", 10, 20);      
              myOLED.print("back", 10, 40);   
            }    
          myOLED.update();     
      if (!digitalRead(4) && menuPos == 4){break;}
         } 
  delay (150);     
}
void MenuSetTime(){  
    for (i=0; i<=500; i++){
    myOLED.clrScr();            
    myOLED.print("MenuSetTime", CENTER, 20);
    myOLED.update();
    if(!digitalRead(4)){break;}
  }
}
void MenuSetAlarm(){
  for (i=0; i<=500; i++){
  myOLED.clrScr();            
  myOLED.print("MenuSetAlarm", CENTER, 20);
  myOLED.update();
  if(!digitalRead(4)){break;}
  }
}
void MenuSetting(){
  for (i=0; i<=500; i++){
  myOLED.clrScr();            
  myOLED.print("MenuSetting", CENTER, 20);
  myOLED.update();
  if(!digitalRead(4)){break;}
  }
}

 

satelit 2
Offline
Зарегистрирован: 04.12.2016

у тебя скобки открыты н в том месте и закрыты

 

Igoreck
Offline
Зарегистрирован: 01.03.2017

satelit 2 пишет:

у тебя скобки открыты н в том месте и закрыты

 

В каком именно ?

 

satelit 2
Offline
Зарегистрирован: 04.12.2016

35,,43,44,

satelit 2
Offline
Зарегистрирован: 04.12.2016

ты не правильно скетч вставил, мало кто подскажет

как и я ошибся в строках

Igoreck
Offline
Зарегистрирован: 01.03.2017

satelit 2 пишет:

35,,43,44,

Там все правильно смотрите внимательно, а лутше загрузите мой скетч и скомпилируйте!

Qanatoz
Offline
Зарегистрирован: 24.05.2017

Начал делать проект паяльной станции (фен+паяльник) и уперся в, казалось бы , элементарную проблему. Ни библиотечная версия, ни куча реализаций работы энкодера на этом форуме меня не устраивают - пропуски шагов, несрабатывание. И энкодер новый, и выводы подтянуты 10кОм резисторами и заблокированы 10нФ кондерами, а не работает энкодер нормально. Единственная реализация которая работала  - это реализация в посте #74 - вот такая реализация работает без сбоев и проскакиваний. Но практическое применение такой реализации не имеет смысла - так просто поиграться с энкодером, не более, а иначе все процессорное время будет занято обработкой энкодера. Все реализации через прерывания  - это для идеального энкодера. Вы все хорошие программисты, но не практики. Да, должно быть так, но в реале  - совсем не так. При вращении энкодера на увеличение, возможны не только несрабатывания (а их может быть 50%), но возможны даже шаги на уменьшение, ну и наоборот! Так, что рано почивать на лаврах, тема не закрыта. Через недельку продолжу свои мучения и отпишусь о своих опытах.

renoshnik
Offline
Зарегистрирован: 11.04.2013

Qanatoz пишет:

Начал делать проект паяльной станции (фен+паяльник) и уперся в, казалось бы , элементарную проблему. Ни библиотечная версия, ни куча реализаций работы энкодера на этом форуме меня не устраивают - пропуски шагов, несрабатывание. И энкодер новый, и выводы подтянуты 10кОм резисторами и заблокированы 10нФ кондерами, а не работает энкодер нормально. Единственная реализация которая работала  - это реализация в посте #74 - вот такая реализация работает без сбоев и проскакиваний. Но практическое применение такой реализации не имеет смысла - так просто поиграться с энкодером, не более, а иначе все процессорное время будет занято обработкой энкодера. Все реализации через прерывания  - это для идеального энкодера. Вы все хорошие программисты, но не практики. Да, должно быть так, но в реале  - совсем не так. При вращении энкодера на увеличение, возможны не только несрабатывания (а их может быть 50%), но возможны даже шаги на уменьшение, ну и наоборот! Так, что рано почивать на лаврах, тема не закрыта. Через недельку продолжу свои мучения и отпишусь о своих опытах.

 

https://www.youtube.com/watch?v=ejbCYs-WJaY

 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

Qanatoz пишет:

Все реализации через прерывания  - это для идеального энкодера. Вы все хорошие программисты, но не практики. Да, должно быть так, но в реале  - совсем не так. При вращении энкодера на увеличение, возможны не только несрабатывания (а их может быть 50%), но возможны даже шаги на уменьшение, ну и наоборот! пытах.

Это нормально, не думайте, что те кто писал свои обработчики мало в этом понимают. Есть один нюанс -все энкодеры разные. Есть три основных вида, которые в свою очередь образуют ещё подвиды. И все они между собой плохо совместимы. У меня есть два типа, и могу о них рассказать. Первый популярный ky-40

При одном щелчке он даёт вот такую осциллограмму :

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

При тех же самых настройках осциллографа делаю с такой же скоростью один щелчок.

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

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

Дополню: есть различные схемы подключения энкодеров. Я могу порекомендовать такую:

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

andriano пишет:

Дополню: есть различные схемы подключения энкодеров. Я могу порекомендовать такую:

конденсаторы, что бы прерывание аппаратно затупить? :D

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

Кстати, насчет конденсаторов: схему естественно, подключать только к тем ногам МК, у которых есть триггера Шмитта.