LCD + энкодер
- Войдите на сайт для отправки комментариев
Пнд, 20/10/2014 - 08:51
Приветствую всех. Возникла проблема.
Подключаю энкодер (KY-040) и экранчик 20*4. По отдельности все отлично работает. Экран все показывает, энкодер крутится и значения меняются. Но как только я объединяю два устройства в одном скетче - проблема. Энкодер начинает пропускать шаги, причем прилично. Понимаю, что как-то связано с прерываниями, но что сделать - не понимаю. Подскажите, пожалуйста.
Вот код:
//#include <OneWire.h> // Библиотека для Dallas 1-wire
//#include <DallasTemperature.h> // Библиотека для DS18B20
#include <Wire.h> // Библиотека для I2C
#include <LiquidCrystal_I2C.h> // Библиотека для LCD
LiquidCrystal_I2C lcd(0x27,2,4); // Адрес модуля LCD
//#define ONE_WIRE_BUS 10
//OneWire oneWire(ONE_WIRE_BUS);
//DallasTemperature sensors(&oneWire);
// Используем энкодер с кнопкой
enum PinAssignments {
encoderPinA = 2, // Вращение вправо
encoderPinB = 3, // Вращение влево
clearButton = 8 // Кнопка
};
volatile unsigned int encoderPos = 0; // Счетчик для энкодера
unsigned int lastReportedPos = 1; // Предыдущее значение энкодера для сравнения
static boolean rotating=false; // Антидребезг
// Переменные направления вращения
boolean A_set = false;
boolean B_set = false;
void setup() {
pinMode(encoderPinA, INPUT_PULLUP); // Режим порта
pinMode(encoderPinB, INPUT_PULLUP);
pinMode(clearButton, INPUT_PULLUP);
// Прерывания
attachInterrupt(0, doEncoderA, CHANGE);
attachInterrupt(1, doEncoderB, CHANGE);
// Инициализируем LCD
lcd.init();
lcd.backlight();
lcd.home ();
Serial.begin(9600); // Монитор порта
}
// Основной цикл
void loop() {
rotating = true; // Инициализируем "антидребезг"
// Сравниваем состояние энкодера с предыдущим и если есть изменение - печатаем его
if (lastReportedPos != encoderPos) {
Serial.print("Index:");
Serial.println(encoderPos, DEC);
lastReportedPos = encoderPos;
}
// Обнуляем значение переменной по нажатию кнопки
if (digitalRead(clearButton) == LOW ) {
encoderPos = 0;
}
// Выводим значение переменной на LCD
lcd.setCursor ( 0, 0 );
lcd.print ("Index: ");
lcd.print (encoderPos);
}
// Прерывание 1
void doEncoderA(){
// Включаем "антидребезг"
if ( rotating ) delay (10);
// Проверяем значение порта
if( digitalRead(encoderPinA) != A_set ) { // debounce once more
A_set = !A_set;
// Узнаем направление вращения
if ( A_set && !B_set )
encoderPos += 1;
rotating = false; // выключаем "антидребезг"
}
}
// Все то же самое для прерывания 2
void doEncoderB(){
if ( rotating ) delay (1);
if( digitalRead(encoderPinB) != B_set ) {
B_set = !B_set;
if( B_set && !A_set )
encoderPos -= 1;
rotating = false;
}
}
Заранее спасибо.
Попробуй CyberLib использовать, чтобы от медленной digitalRead избавиться.
дело не в скорости работы digitalRead. Дело скорее в delay(10)... ИМХО, лучше аппаратно антидребезг для энкодера сделать...
https://www.pjrc.com/teensy/td_libs_Encoder.html - посмотрите, там очень неплохая библиотечка была.
А как именно у вас пропуски происходят ? быстро крутите, а знечение не меняется ?
Аппаратный пока нет возможности сделать, но про данный метод в курсе, спасибо.
А пропуски: Кручу на один "щелчок", а значение меняется на 5.. или на 3.. или на -2..
Вообще, если вставить вывод на экран внутрь проверки изменения сигнала, то все отлично. Но мне еще температуру постоянно мониторить, поэтому не совсем поможет.
К сожалению не знаю точно, какая у вас задача, но если вам необходимо на 1 шелчек энкодера изменять значение на 1 еденицу, то:
1) прерывание на переход из 0 в 1 для выхода А
2) в прерывании делайте антидребезг, после этого читайте выход Б (Б можно уже без антидребезга, так как он 100% уже стабилен). Если Б = 1 то увеличте переменную энкодера, если Б = 0 то уменьшите.
Задача банальна. Меню с использованием энкодера с кнопкой и 4-х строчного LCD
Упростите код как я писал, должно взлететь...
В общем я провери код. У ТС не срабатывает антидребезг.
Рабочий упрощенный код (протестировано). Я понимаю, что delay() в прерывании не очень желателен, однако это самый простой способ. главное прарывание на момент delay() отключить.
#include <LiquidCrystalRus.h> LiquidCrystalRus lcd(12, 11, 7, 6, 5, 4); volatile int encoder_register = 0; void setup() { pinMode(2,INPUT_PULLUP); pinMode(3,INPUT_PULLUP); attachInterrupt(1, encoder, RISING); lcd.begin(20, 4); } void loop() { lcd.setCursor(0, 0); lcd.print(encoder_register); lcd.print(" "); } void encoder() { detachInterrupt(1); delay(10); if (digitalRead(3)){ if(digitalRead(2)) ++encoder_register; else --encoder_register; } attachInterrupt(1, encoder, RISING); }Не работает этот код, к сожалению. Так и проскакивает значения. 0-2-4-6-7-9-11-13-15 и т.д. Я думаю проблема именно в том, что вывод на экран тоже использует прерывания.
#include <LiquidCrystal_I2C.h> // Библиотека для LCD #include <OneWire.h> // Библиотека для Dallas 1-wire #include <Wire.h> // Библиотека для I2C LiquidCrystal_I2C lcd(0x27,2,4); // Адрес модуля LCD #define ONE_WIRE_BUS 10 #define D2_Read ((PIND & B00000100)>>2) #define D3_Read ((PIND & B00001000)>>3) OneWire oneWire(ONE_WIRE_BUS); volatile int encoder_register = 0; void setup() { pinMode(2,INPUT_PULLUP); pinMode(3,INPUT_PULLUP); attachInterrupt(1, encoder, RISING); lcd.init(); lcd.backlight(); lcd.home (); } void loop() { lcd.setCursor(0, 0); lcd.print(encoder_register); lcd.print(" "); } void encoder() { noInterrupts(); delay(1); if (D3_Read){ if(D2_Read) ++encoder_register; else --encoder_register; } interrupts(); }код точно рабочий, что ваш что мой. Только у меня дисплей напрямую подключен.
в I2C прерывания точно используются. Очень похоже что у вас срабатывает оба прерывания по фронту (2 и 3 нога). Попробуйте явно отключить прерывание на 2 ноге (detachInterrupt(0)), и в обработчике прерываний явно отключайте и включаете прерывания на 3 ноге. (detachInterrupt(1); и attachInterrupt(1, encoder, RISING);). Задержку до 10 поднимите.
Увы...
А может у вас энкодер 2 перехода в + на 1 клик дает ?
Вот рабочий код, только мой дисплей 16х2. Для 20х4 заменить lcd.begin (16,2); на lcd.begin (20,4);
#include <Wire.h> #include <LCD.h> #include <LiquidCrystal_I2C.h> #define LCD_I2C_ADDR 0x20 #define LCD_BACKLIGHT_PIN 6 #define LCD_EN 4 #define LCD_RW 5 #define LCD_RS 6 #define LCD_D4 0 #define LCD_D5 1 #define LCD_D6 2 #define LCD_D7 3 LiquidCrystal_I2C lcd(LCD_I2C_ADDR,LCD_EN,LCD_RW,LCD_RS,LCD_D4,LCD_D5,LCD_D6,LCD_D7); int encoderPin1 = 2; int encoderPin2 = 3; volatile int lastEncoded = 0; volatile long encoderValue = 0; long lastencoderValue = 0; int lastMSB = 0; int lastLSB = 0; void setup() { lcd.begin (16,2); analogWrite(LCD_BACKLIGHT_PIN, 200); pinMode(encoderPin1, INPUT); pinMode(encoderPin2, INPUT); digitalWrite(encoderPin1, HIGH); digitalWrite(encoderPin2, HIGH); attachInterrupt(0, updateEncoder, CHANGE); attachInterrupt(1, updateEncoder, CHANGE); } void loop() { lcd.setCursor( 0, 0); lcd.print(encoderValue); } void updateEncoder() { int MSB = digitalRead(encoderPin1); int LSB = digitalRead(encoderPin2); int encoded = (MSB << 1) |LSB; int sum = (lastEncoded << 2) | encoded; if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue ++; if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue --; lastEncoded = encoded; }Правда обработка значений энкодера считает по 2 значения , но я в это не лез.
А вот leshak предложил работу с энкодером - четко показывает
#include <Wire.h> #include <LCD.h> #include <LiquidCrystal_I2C.h> #define LCD_I2C_ADDR 0x20 #define LCD_BACKLIGHT_PIN 6 #define LCD_EN 4 #define LCD_RW 5 #define LCD_RS 6 #define LCD_D4 0 #define LCD_D5 1 #define LCD_D6 2 #define LCD_D7 3 LiquidCrystal_I2C lcd(LCD_I2C_ADDR,LCD_EN,LCD_RW,LCD_RS,LCD_D4,LCD_D5,LCD_D6,LCD_D7); #define PIN_A 2 #define PIN_B 3 #define PULSE_PIN_LEN 5 // минимальная длинна импульса в миллисекундах на которую мы обращаем внимание volatile unsigned long failingTime = 0; volatile bool flag = false; // флаг что нужно вывести volatile bool value_b = 0; volatile byte prevA = 0; volatile int encValue = 0; volatile unsigned long pulseLen = 0; void setup() { lcd.begin (16,2); analogWrite(LCD_BACKLIGHT_PIN, 200); digitalWrite(PIN_A,HIGH); digitalWrite(PIN_B,HIGH); attachInterrupt(0, handler_a, CHANGE); } void loop() { if(flag) { lcd.setCursor( 0, 0); lcd.print(encValue); flag = false; } } void handler_a() { byte A = digitalRead(PIN_A); if(!flag) { // пока не отчитались ничего больше не делаем if(prevA && !A) { // фронт упал 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--; flag = true; // включаем пометку что нужно отчитаться в Serial } failingTime = 0; // больше не ведем отсчет времени импульса } } prevA = A; }Вот ещё в loop() небольшое изменение - стираем на дисплее старые значения
void loop() { if(flag) { lcd.setCursor( 0, 0); lcd.print(encValue); lcd.print(" "); flag = false; } }Тогда проще через sprintf пропустить и всё время буфер выводить.
вот рабочий кусок кода опроса 2-ух энкодеров (взято из рабочего проэкта)
union { struct { unsigned a0:1; unsigned b0:1; unsigned a1:1; unsigned b1:1; unsigned bt2:1; unsigned bt3:1; unsigned m:2; } b; byte sig; }mask,old; uint8_t oldb; // Старое нажатие void myItoa(uint16_t i,char *a,uint8_t len); extern Flags enFlags; extern byte regim; void encInit() { DDRB = 0; PORTB = 0x3F; // занято 6 ног порта B DDRD &= 0x3F; PORTD |= 0xC0; old.sig = PINB & 0x3F; oldb = PIND >> 6; enFlags.en0 = 0; enFlags.way0 = 0; enFlags.en1 = 0; enFlags.way1 = 0; enFlags.bt0 = 0; enFlags.bt1 = 0; PCMSK0 = 0x3F; PCMSK2 = 0xC0; PCICR |= 5; } ISR(PCINT0_vect) { unsigned char c1; mask.sig = PINB & 0x3F; c1 = mask.sig ^ old.sig; if (c1 & 1) { if (mask.b.a0 ^ mask.b.b0) enFlags.way1 = 0; else enFlags.way1 = 1; // Определение направления вращения enFlags.en1 = 1; // флаг события - что энкодер повернулся } if (c1 & 4) { if (mask.b.a1 ^ mask.b.b1) enFlags.way0 = 0; else enFlags.way0 = 1; enFlags.en0 = 1; } if (c1 & 16) { if (!mask.b.bt2) enFlags.bt2 = 1; // кнопка энкодера } if (c1 & 32) { if (!mask.b.bt3) enFlags.bt3 = 1; } old.sig = mask.sig; }Вот мой скеч рабочий никаких пропусков у меня мега2650 кидать нужно пины на контакты прерывания #include <Encoder.h> #include <Wire.h> #include <LiquidCrystal_I2C.h> Encoder knobLeft(2, 3); Encoder knobRight(18, 19); LiquidCrystal_I2C lcd(0x27,16,2); void setup() { lcd.init(); lcd.backlight(); } long positionLeft = -9999999; long positionRight = -9999999; void loop() { long newLeft, newRight; newLeft = knobLeft.read(); newRight = knobRight.read(); if (newLeft != positionLeft || newRight != positionRight) { lcd.setCursor(0, 0); lcd.print("Llne X: "); lcd.print(newLeft); lcd.setCursor(0, 1); lcd.print("Llne Z: "); lcd.print(newRight); positionLeft = newLeft; positionRight = newRight; } if (Serial.available()) { Serial.read(); lcd.println("Reset both knobs to zero"); knobLeft.write(0); knobRight.write(0); } }Ты идиот? За такой код надо пальцы ломать.
Жёстко ответил...
Ты идиот? За такой код надо пальцы ломать.
Прокоментируй, пожалуйста.
А что тут комментировать?
Оп начал делать всё асинхронно через прерывания, а этот идиот предлагает вместо этого часто опрашивать порты, авось там чего повится и авось попадем в импульс. Любой минимальный дополнительный функционал приведет к неработоспособности этого говнокода.
Экий ты кровожадный...
Ты идиот? За такой код надо пальцы ломать.
Из-за таких как ты муд_____ков, у нас вся Рооссия в жопе каждый прыщ начинает понты корячить,а твои ответы равносильно таму "Как построить самолет? Сделать крылья машине!" а потом стоять и кричать что никто ничего не умеет делать!
Вот код но есть небольшая проблема на лсд иногда путаются показания,а паралельно включеный ком порт все в идеале,
мож че посоветуете
и тысячах путаются а потом сами становятся как на ком порту.
#define encoder0PinA 2 #define encoder0PinB 3 #include <Wire.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27, 16, 2); volatile long encoder0Pos; void setup () { lcd.init(); lcd.backlight(); lcd.setCursor(0, 0); Serial.begin(9600); pinMode(encoder0PinA, INPUT); pinMode(encoder0PinB, INPUT); digitalWrite(encoder0PinA, HIGH); digitalWrite(encoder0PinB, HIGH); attachInterrupt(0, encoderPinChangeA, CHANGE); // прерывание 0 (вывод 2) attachInterrupt(1, encoderPinChangeB, CHANGE); // прерывание 0 (вывод 3) encoder0Pos = 0; // положение энкодера для 0 } void loop() { lcd.setCursor(0, 0); lcd.print(encoder0Pos); Serial.println(encoder0Pos); delay(100); } void encoderPinChangeA() { if (digitalRead(encoder0PinA) == HIGH) { if (digitalRead(encoder0PinB) == LOW) { encoder0Pos = encoder0Pos + 1; } else { encoder0Pos = encoder0Pos - 1; } } else { if (digitalRead(encoder0PinB) == HIGH) { encoder0Pos = encoder0Pos + 1; } else { encoder0Pos = encoder0Pos - 1; } } } void encoderPinChangeB() { if (digitalRead(encoder0PinB) == HIGH) { if (digitalRead(encoder0PinA) == HIGH) { encoder0Pos = encoder0Pos + 1; } else { encoder0Pos = encoder0Pos - 1; } } else { if (digitalRead(encoder0PinA) == LOW) { encoder0Pos = encoder0Pos + 1; } else { encoder0Pos = encoder0Pos - 1; } } }На лсд остаются иногда последние цыфры а показывает правельно
нашол проблему надо очистить от старых показаний lcd.print(" ")
Ой! А кто это там со стороны параши тяфкает?
Да это же димсобакансемдесятвосемь!
А под чем это он? Под лсд конечно! Да он код научился писать! Только грамоте разучился при этом. Какая незадача! А еще этот говнокодер в полите разбирается!
Только код у него всё равно говно, хотя воняет уже меньше.
Сдаётся мне, джентльмены, это был комплимент...
Ты бы вафлю свою прикрыл, а то кишочки простудиш. Я больше чем уверен ты в жизни ни кто,и зовут тебя ни как.
Ты домашний,подьюбешный,саплежуй.
В реальной жизни такие как ты прыщавые хуесосы которые дрожат на остановках'
Слив защитан. Следущий.
Radjah ты то чего до грубости дошел?
И еще вопросик нужно ли делать подтяжку к земле на энкодерах оптических?
Шо за взаимные оскорбления. Знает-не знает, умеет-не умеет - не причина паскудить форум.
Согласен.он сам зацепил
Ну если кто то и нахамил - можно и не ответить. Это единственный форум из всех, где я сижу, который без мата и скотства. Жалко будет, если и он опаскудится. Есть простая жизненая мудрость -БЕРЕГИ ТО ЧТО ЕСТЬ. ПОТЕРЯТЬ МОЖНО - ВЕРНУТЬ - НЕТ! Это касается и женщин и общения.
Прошу у всех извинения!