Энкодер + 1-wire
- Войдите на сайт для отправки комментариев
Чт, 21/04/2016 - 17:42
Уважаемы коллеги!
Есть проблема. Три дня назад купил ардуину (что само по себе проблема - кончилось свободное время). Начал осваивать. Прицепил температурный датчик 1-wire (1DS18B20), экранчик, кнопочки, светодиодики. Все работает. Начал цеплять энкодер. Появилась проблема.
Код:
#include <LiquidCrystal.h>
#include <OneWire.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // описываем экранчег
enum { LCD_WIDTH = 16, LCD_HEIGHT = 2 }; // размеры экрана
enum { ENC_PIN1 = 7, ENC_PIN2 = 10 }; // куда прицеплен энкодер
OneWire ds(6); // DS18B20 on pin 6 (a 4.7K resistor is necessary)
int ledPin = 8; // сетодиод
int btnPin = 13; // кнопка
int val=0; // для кнопки
int enc_counter = 0; // счетчик щелчков энкодера
void setup() {
pinMode(ledPin, OUTPUT); // это выход - светодиод
pinMode(btnPin, INPUT); // а это вход - кнопка
// описываем подключение энкодера
pinMode(ENC_PIN1, INPUT);
pinMode(ENC_PIN2, INPUT);
// запускаем экран
lcd.begin(LCD_WIDTH, LCD_HEIGHT);
// запускаем ком-порт
Serial.begin(9600);
}
/* Функция декодирования кода Грея, взятая с Википедии.
* Принимает число в коде Грея, возвращает обычное его представление.
*/
unsigned graydecode(unsigned gray)
{
unsigned bin;
for (bin = 0; gray; gray >>= 1) bin ^= gray;
return bin;
}
void loop() { // НАЧАЛО МЕГАЦИКЛА
/* НАЧАЛО 1-WIRE
// для 1-wire датчика
byte i;
byte present = 0;
byte data[12];
byte addr[8];
if ( !ds.search(addr)) {
// Serial.print("No more addresses.\n");
ds.reset_search();
// delay(250);
return;
}
if ( OneWire::crc8( addr, 7) != addr[7]) {
Serial.print("CRC is not valid!\n");
return;
}
if ( addr[0] != 0x28) {
Serial.print("Device is not a DS18B20 family device.\n");
return;
}
ds.reset();
ds.select(addr);
ds.write(0x44,1); // запускаем конвертацию
// delay(1000); // скорее всего достаточно 750ms
// we might do a ds.depower() here, but the reset will take care of it.
present = ds.reset();
ds.select(addr);
ds.write(0xBE); // считываем ОЗУ датчика
for ( i = 0; i < 9; i++) { // обрабатываем 9 байт
data[i] = ds.read();
}
// высчитываем температуру :)
int HighByte, LowByte, TReading;
float celsius;
LowByte = data[0];
HighByte = data[1];
TReading = (HighByte << 8) + LowByte;
celsius = TReading/16.0;
lcd.setCursor(0, 0);
lcd.print("T = ");
lcd.print(celsius);
lcd.print(" ");
// простейший термостат
if(celsius > 27 ) // температура выше 27
{ digitalWrite(ledPin, HIGH); } // зажигаем светодиод
else // температура ниже
{ digitalWrite(ledPin, LOW); } // гасим светодиод
КОНЕЦ 1-WIRE
*/
/* НАЧАЛО ОБРАБОТКИ ЭНКОДЕРА */
// Обрабатываем энкодер
static uint8_t previous_code = 0; // предыдущий считанный код
/* gray_code - считанное с энкодера значение
* code - декодированное значение
*/
uint8_t gray_code = digitalRead(ENC_PIN1) | (digitalRead(ENC_PIN2) << 1),
code = graydecode(gray_code);
/* Если считался нуль, значит был произведён щелчок ручкой энкодера */
if (code == 0)
{
/* Если переход к нулю был из состояния 3 - ручка вращалась
* по часовой стрелке, если из 1 - против.
*/
if (previous_code == 3)
{ enc_counter++; }
else if (previous_code == 1)
{ enc_counter--; }
}
/* Сохраняем код и ждём 1 мс - вполне достаточно опрашивать энкодер
* не более 1000 раз в секунду.
*/
previous_code = code;
delay(1);
/* КОНЕЦ ОБРАБОТКИ ЭНКОДЕРА */
// Выводим на экран
lcd.setCursor(0, 1);
lcd.print(enc_counter);
lcd.write(" ");
// Сброс счетчика энкодера
val = digitalRead(btnPin); // узнаём состояние кнопки
if(val==HIGH) // кнопка нажата
{
lcd.clear();
enc_counter = 0;
}
// КОНЕЦ МЕГАЦИКЛА
}
В общем, как только раскоментирую кусок кода от "НАЧАЛО 1-WIRE" до "КОНЕЦ 1-WIRE", то начинает работать градусник, но перестает работать энкодер. :(
Куда бечь?
концевик посадить на другие пины (например А2, А3)
концевик посадить на другие пины (например А2, А3)
Что-то не сильно помогло. Ввод стал более ощутим ( на цифовых входах на 8-10 оборотов счетчик менялся на 1-2, теперь надо сделать 3-4 оборота энкодера, чтобы изметить счетчик на 1-2).
Замечено, что реакция на обороты энкодера более заметна при неизменной температуре. Как только начинают передаваться данные о температуре, так не реагирует на энкодер. Т.е. надо функцию опроса датчика температуры запускать раз в 5-10 сек., чаще не чему. Вопрос - как это сделать? Насколько я понял, delay() приостанавливает всю программу?
Думаю вам надо покурить "блинк без делай", должно помочь.
Думаю вам надо покурить "блинк без делай", должно помочь.
Должно. Но помогает только 1 цикл в первые 10 сек. Мистика какая-то.
Снял захватывающий боевик о работе скетча - https://youtu.be/UgmeN3wiuB4
Ткните носом, где я ошибся?
#include <LiquidCrystal.h> #include <OneWire.h> LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // описываем экранчег enum { LCD_WIDTH = 16, LCD_HEIGHT = 2 }; // размеры экрана enum { ENC_PIN1 = 15, ENC_PIN2 = 16 }; // куда прицеплен энкодер OneWire ds(6); // DS18B20 on pin 6 (a 4.7K resistor is necessary) int ledPin = 8; // сетодиод int btnPin = 13; // кнопка int val=0; // для кнопки int enc_counter = 0; // счетчик щелчков энкодера void setup() { pinMode(ledPin, OUTPUT); // это выход - светодиод pinMode(btnPin, INPUT); // а это вход - кнопка // описываем подключение энкодера pinMode(ENC_PIN1, INPUT); pinMode(ENC_PIN2, INPUT); // запускаем экран lcd.begin(LCD_WIDTH, LCD_HEIGHT); // запускаем ком-порт Serial.begin(9600); } /* Функция декодирования кода Грея, взятая с Википедии. * Принимает число в коде Грея, возвращает обычное его представление. */ unsigned graydecode(unsigned gray) { unsigned bin; for (bin = 0; gray; gray >>= 1) bin ^= gray; return bin; } void loop() { // НАЧАЛО МЕГАЦИКЛА /* * НАЧАЛО 1-WIRE ROBOCRAFT */ // для 1-wire датчика byte i; byte present = 0; byte data[12]; byte addr[8]; unsigned long previousMillis = 0; // здесь будет храниться время предыдущего выполнения const long interval = 10000; // интервал опроса в миллисекундах unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { previousMillis = currentMillis; // запоминаем текущее время if ( !ds.search(addr)) { ds.reset_search(); return; } if ( OneWire::crc8( addr, 7) != addr[7]) { Serial.print("CRC is not valid!\n"); return; } if ( addr[0] != 0x28) { Serial.print("Device is not a DS18B20 family device.\n"); return; } ds.reset(); ds.select(addr); ds.write(0x44,1); // запускаем конвертацию present = ds.reset(); ds.select(addr); ds.write(0xBE); // считываем ОЗУ датчика for ( i = 0; i < 9; i++) { // обрабатываем 9 байт data[i] = ds.read(); } // высчитываем температуру :) int HighByte, LowByte, TReading; float celsius; LowByte = data[0]; HighByte = data[1]; TReading = (HighByte << 8) + LowByte; celsius = TReading/16.0; lcd.setCursor(0, 0); lcd.print("T = "); lcd.print(celsius); lcd.print(" "); // простейший термостат if(celsius > 27 ) // температура выше 27 { digitalWrite(ledPin, HIGH); } // зажигаем светодиод else // температура ниже { digitalWrite(ledPin, LOW); } // гасим светодиод /* * КОНЕЦ 1-WIRE */ } /* НАЧАЛО ОБРАБОТКИ ЭНКОДЕРА */ // Обрабатываем энкодер static uint8_t previous_code = 0; // предыдущий считанный код /* gray_code - считанное с энкодера значение * code - декодированное значение */ uint8_t gray_code = digitalRead(ENC_PIN1) | (digitalRead(ENC_PIN2) << 1), code = graydecode(gray_code); /* Если считался нуль, значит был произведён щелчок ручкой энкодера */ if (code == 0) { /* Если переход к нулю был из состояния 3 - ручка вращалась * по часовой стрелке, если из 1 - против. */ if (previous_code == 3) { enc_counter++; } else if (previous_code == 1) { enc_counter--; } } /* Сохраняем код и ждём 1 мс - вполне достаточно опрашивать энкодер * не более 1000 раз в секунду. */ previous_code = code; delay(1); /* КОНЕЦ ОБРАБОТКИ ЭНКОДЕРА */ // Выводим на экран lcd.setCursor(0, 1); lcd.print(enc_counter); lcd.write(" "); lcd.setCursor(6, 1); lcd.print(previousMillis); lcd.write(" "); // Сброс счетчика энкодера val = digitalRead(btnPin); // узнаём состояние кнопки if(val==HIGH) // кнопка нажата { lcd.clear(); enc_counter = 0; } // КОНЕЦ МЕГАЦИКЛА }вычитание двух беззнаковых длинных и сравнение со знаковым интервалом .. а так, в целом все работает как вы задумали. :)
сделайте интервал тоже беззнаковым и будет вам щастье.
Я бы на вашем месте, всеж таки вынес строчки определения статических переменных из loop() .. некомильфо.
Да и вывод на дисплей тоже бы загнал в everyMillis() также как съем температуры .. не шустро он отрисовывает вообще-то. На каждый символ ставит задержку в 4-5мсек. Вполне можно отрисовывать раз в 100мсек. (каждую 0.1сек) для отображения - вполне достаточно. Зато энкодер будет работать пошустрей.
Спасибо! Заработало!! :)
Осталось разобраться с длинным-коротким нажатием кнопки для задания уставки... :)
Я бы на вашем месте, всеж таки вынес строчки определения статических переменных из loop() .. некомильфо.
С какого перепугу? По всем ресурсам - одинаково, а вот по возможности случайного изменения - в loop они локализованы, а снаружи - меняй, кто хочет.
После истории с define, думал, что Вы уже не сможете меня удивить своей безграмотностью, но я Вас явно недооценивал :)))
Мне надоело уже ваше загубленное в молодости ЧСВ, поэтому стараюсь не комментировать ваши перлы. В частности этот тоже. Ещё один Клапауций.. :)
Продолжаем разговор. Допилил скетч до задания уставки. Начал ломать голову над мигающим тестом. Начал с мигающего курсора. Файл примера из библиотеки LiquidCrystal работает на "УРА". Поэксперементировал с положением курсора - тоже работает. В моем скетче куда бы не написал
lcd.setCursor(x,y);
lcd.blink();
Этого самого блинка и не происходит. Только рябь проходит по строке, где блинк.
Куда копать?
Там в классе есть метод отправки команды дисплею, попробуйте его так: lcd.command(9);
Там в классе есть метод отправки команды дисплею, попробуйте его так: lcd.command(9);
Что-то не получается...
val = digitalRead(btnEnc); // узнаём состояние кнопки энкодера if(val==HIGH) // кнопка нажата { ... крутим энкодер и выставляем уставку ... lcd.setCursor(10, 0); lcd.command(9); lcd.write("("); lcd.print(ustavka); lcd.write(")"); lcd.write(" "); }Так?
На экране просто все пропадает и все. После отпускания кнопки не появляется.. :(