энкодер и лсд1602

anon
Offline
Зарегистрирован: 23.05.2013

добрый день. есть у меня энкодер, и хочу я данные с него выводить на дисплей ( в серийный порт скетч все пишет исправно). только вот  когда дописываю lcd.print (nc) в функцию encoderTurn - контроллер виснет, успевая в серийник отправить только Tu

/*
Если кому нужно, выкладываю скетч подключения дешманского китайского энкодера KY-040 с прерыванием по вращению и прерыванием по нажатию.
Двойной шаг вращения (с фиксацией ручки экнодера) считается за один. Немного отрегулировал чувствительность, протестировал на нескольких энкодерах KY-040, все работают.
*/

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);

//Скетч для подключения энкодера KY-040 к Ардуино Мега с прерыванием по вращению и нажатию 

//----------------------------— 
//Указываем пины энкодера 
#define CLK 2 //Прерывание1! 
#define DT 4 
#define SW 3 //Прерывание2! 
//----------------------------— 
#define NORM_STEP 1 // шаг изменения переменной norm_counter при вращении 
#define HOLD_STEP 1 // шаг изменения переменной hold_counter при нажатии, удерживании и вращении 

//#define Beeper 12 //Указываем пин Пищалки 

//================================================================================================== 
//Объявляем переменные 

int norm_counter, hold_counter, nc, hc, d; 
boolean DT_now, DT_last, SW_state, hold_flag, butt_flag, turn_flag; 
unsigned long debounce_timer; 

//================================================================================================== 
void setup() 
{ 
Serial.begin(9600); 
pinMode(CLK, INPUT); 
pinMode(DT, INPUT); 
pinMode(SW, INPUT_PULLUP); 


  lcd.init(); 
  lcd.backlight();
  

attachInterrupt(0, encoderTick, CHANGE);//подключаем прерывание для вращения 
attachInterrupt(1, encoderTick, CHANGE);//подключаем прерывание для нажатия 
DT_last = digitalRead(CLK); // читаем начальное положение CLK (энкодер) 
//pinMode(Beeper, OUTPUT); //Подключаем пищалку 
//tone(Beeper, 3000, 5);// Пищим 
} 

//================================================================================================== 
void loop() 
{ 
Encoder_Hold(); //Опрос кнопки энкодера на предмет ее удержания в нажатом положении (Hold) 
 
} 

//=================================================================================================== 
//Функции Энкодера 
void encoderTick() 
{ 
DT_now = digitalRead(CLK); // читаем текущее положение CLK 
SW_state = !digitalRead(SW); // читаем положение кнопки SW 
// отработка нажатия кнопки энкодера 
//-------------------------------------------------------------------------------------------------— 
//Нажали кнопку, держим 
if (SW_state && !butt_flag) //Если: 
{ 
butt_flag = 1; //Кнопку точно нажали 
hold_flag = 0; //Немного удерживали 
turn_flag = 0; //Не крутили 
debounce_timer = millis(); //Засекаем таймер 
//tone(Beeper, 3000, 5); //Пищим 
encoderClick(); //Выполняем команды в функции Нажатие (Клик) 
} 
//-------------------------------------------------------------------------------------------------— 
if (!SW_state && butt_flag && hold_flag) //Если держали долго: 
{ 
butt_flag = 0; //Кнопку отпустили 
debounce_timer = millis(); //Сравниваем время 
} 
//-------------------------------------------------------------------------------------------------— 
if (!SW_state && butt_flag && millis() - debounce_timer > 10 && millis() - debounce_timer < 500) 
{ 
butt_flag = 0; 
if (!turn_flag && !hold_flag) 
{ // если кнопка отпущена и ручка не поворачивалась 
turn_flag = 0;
 encoderPress(); 
} 
debounce_timer = millis(); 
} 
//-------------------------------------------------------------------------------------------------— 
if (DT_now != DT_last) 
{ // если предыдущее и текущее положение CLK разные, значит был поворот 
if (digitalRead(DT) != DT_now) 
{ // если состояние DT отличается от CLK, значит крутим по часовой стрелке 
if (SW_state) 
{ // если кнопка энкодера нажата 
d = hc; 
hold_counter -= HOLD_STEP; 
hc=hold_counter/2; 
if (hc < d) 
{ 
//tone(Beeper, 3000, 5); 
encoderHoldTurn(); 
} 
} 
else 
{ // если кнопка энкодера не нажата 
d = nc; 
norm_counter -= NORM_STEP; 
nc=norm_counter/2; 

if (nc < d) 
{ 
encoderTurn(); 
} 
} 
} 
else 
{ // если совпадают, значит против часовой 
if (SW_state) 
{ // если кнопка энкодера нажата 
d = hc; 
hold_counter += HOLD_STEP; 
hc=hold_counter/2; 
if (hc > d) 
{ 
//tone(Beeper, 3000, 5); 
encoderHoldTurn(); 
} 
} 
else 
{ // если кнопка энкодера не нажата 
d = nc; 
norm_counter += NORM_STEP; 
nc=norm_counter/2; 
if (nc > d) 
{ 
//tone(Beeper, 3000, 5); 
encoderTurn(); 
} 
} 
} 
turn_flag = 1; // флаг что был поворот ручки энкодера 
} 
DT_last = DT_now; // обновить значение для энкодера 
} 

//=================================================================================================== 


//Опрос кнопки энкодера на предмет ее удержания в нажатом положении (Hold) 
void Encoder_Hold() 
{ 
if (SW_state && butt_flag && millis() - debounce_timer > 500 && !hold_flag) //Если: 
{ 
hold_flag = 1; //Долго удерживали в нажатом состоянии (более 500мс) 
if (!turn_flag) //Но при этом не крутили 
{ 
turn_flag = 0; 
//tone(Beeper, 3000, 5);//Пищим 
encoderHold(); //Выполняем команды в функции Холд 

} 
} 
} 

//=================================================================================================== 
void encoderClick() 
{ 
Serial.print("Click, "); 
Serial.print("butt_flag: "); 
Serial.print(butt_flag); 
Serial.print(", hold_flag: "); 
Serial.print(hold_flag); 
Serial.print(", millis(): "); 
Serial.println(millis()); 
} 

//=================================================================================================== 
void encoderPress() 
{ 
Serial.print("Press, "); 
Serial.print("butt_flag: "); 
Serial.print(butt_flag); 
Serial.print(", hold_flag: "); 
Serial.print(hold_flag); 
Serial.print(", millis(): "); 
Serial.println(millis()); 
} 

//=================================================================================================== 
void encoderHold() 
{ 
Serial.print("Hold, "); 
Serial.print("butt_flag: "); 
Serial.print(butt_flag); 
Serial.print(", hold_flag: "); 
Serial.print(hold_flag); 
Serial.print(", millis(): "); 
Serial.println(millis()); 
} 

//=================================================================================================== 
void encoderTurn() 
{
Serial.print("Turn: "); 
Serial.print("butt_flag: "); 
Serial.print(butt_flag); 
Serial.print(", hold_flag: "); 
Serial.print(hold_flag); 
Serial.print(", NORM_STEP: "); 
Serial.print(NORM_STEP); 
Serial.print(", nc: "); 
Serial.println(nc);

//lcd.print(nc);


} 

//=================================================================================================== 
void encoderHoldTurn() 
{ 
Serial.print("HTurn: "); 
Serial.print("butt_flag: "); 
Serial.print(butt_flag); 
Serial.print(", hold_flag: "); 
Serial.print(hold_flag); 
Serial.print(", HOLD_STEP: "); 
Serial.print(HOLD_STEP); 
Serial.print(", hc: "); 
Serial.println(hc); 

}

anon
Offline
Зарегистрирован: 23.05.2013

не знаю, как код под кат убрать, извините

ВН
Offline
Зарегистрирован: 25.02.2016

вопрос

"Двойной шаг вращения (с фиксацией ручки экнодера) считается за один."

сам не смог придумать ради чего за такое биться =) расскажите зачем такая функция понадобилась

IVAN222
Offline
Зарегистрирован: 19.04.2017

Попробуйте в функции encoderTurn()  закоментируйте все Serial.print(), оставьте там только lcd.

lcd.clear();  //очистить дисплей
lcd.setCursor(0,0);  //устанавливаем курсор
lcd.print(nc);
при этом ЛСД что нибудь напишет?
anon
Offline
Зарегистрирован: 23.05.2013

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

anon
Offline
Зарегистрирован: 23.05.2013

неа. виснет так же, мробовал уже. при чем, если в лупе, то все норм.

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

Нечего все в обработчик прерывания совать. Напхали - ловите глюки.

anon
Offline
Зарегистрирован: 23.05.2013

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

IVAN222
Offline
Зарегистрирован: 19.04.2017

Если Вы не пишете lcd.print(nc);  , а только сериал, у Вас код работает, все функции выполняются. Как только запихиваете в функцию ЛСД получается глюк. Я правильно понял.

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

Вся работа с сериалами-дисплеями - только в лупе. В обработчике только фиксация факта проворота энкодера.

anon
Offline
Зарегистрирован: 23.05.2013

ясно, спасибо

anon
Offline
Зарегистрирован: 23.05.2013

да, именно. 

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

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

anon
Offline
Зарегистрирован: 23.05.2013

эт не я, это добрый самаритянин) 
не скажите, с чем связано такое нежелание работать с дисплеем в обработчике?

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

Полагаю с тем, что он подключен через I2C, Wire хочет прерывания и не имеет выхода из функций по таймауту, а прерывания на время выполнения обработчика запрещены.

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