Энкодер + 1-wire

kosmas
Offline
Зарегистрирован: 16.04.2016

Уважаемы коллеги!

Есть проблема. Три дня назад купил ардуину (что само по себе проблема - кончилось свободное время). Начал осваивать. Прицепил температурный датчик 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", то начинает работать градусник, но перестает работать энкодер. :(

Куда бечь?

 

vde69
Offline
Зарегистрирован: 10.01.2016

концевик посадить на другие пины (например А2, А3)

kosmas
Offline
Зарегистрирован: 16.04.2016

vde69 пишет:

концевик посадить на другие пины (например А2, А3)

Что-то не сильно помогло. Ввод стал более ощутим ( на цифовых входах на 8-10 оборотов счетчик менялся на 1-2, теперь надо сделать 3-4 оборота энкодера, чтобы изметить счетчик на 1-2).  

Замечено, что реакция на обороты энкодера более заметна при неизменной температуре. Как только начинают передаваться данные о температуре, так не реагирует на энкодер. Т.е. надо функцию опроса датчика температуры запускать раз в 5-10 сек., чаще не чему. Вопрос - как это сделать? Насколько я понял, delay() приостанавливает всю программу? 

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

Думаю вам надо покурить "блинк без делай", должно помочь.

kosmas
Offline
Зарегистрирован: 16.04.2016

Arhat109-2 пишет:

Думаю вам надо покурить "блинк без делай", должно помочь.

Должно. Но помогает только 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;
  }



// КОНЕЦ МЕГАЦИКЛА  
}

 

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

вычитание двух беззнаковых длинных и сравнение со знаковым интервалом .. а так, в целом все работает как вы задумали. :)

сделайте интервал тоже беззнаковым и будет вам щастье.

Я бы на вашем месте, всеж таки вынес строчки определения статических переменных из loop() .. некомильфо.

Да и вывод на дисплей тоже бы загнал в everyMillis() также как съем температуры .. не шустро он отрисовывает вообще-то. На каждый символ ставит задержку в 4-5мсек. Вполне можно отрисовывать раз в 100мсек. (каждую 0.1сек) для отображения - вполне достаточно. Зато энкодер будет работать пошустрей.

kosmas
Offline
Зарегистрирован: 16.04.2016

Спасибо! Заработало!! :)

Осталось разобраться с длинным-коротким нажатием кнопки для задания уставки... :)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Arhat109-2 пишет:

Я бы на вашем месте, всеж таки вынес строчки определения статических переменных из loop() .. некомильфо.

С какого перепугу? По всем ресурсам - одинаково, а вот по возможности случайного изменения - в loop они локализованы, а снаружи - меняй, кто хочет.

После истории с define, думал, что Вы уже не сможете меня удивить своей безграмотностью, но я Вас явно недооценивал :)))

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

Мне надоело уже ваше загубленное в молодости ЧСВ, поэтому стараюсь не комментировать ваши перлы. В частности этот тоже. Ещё один Клапауций.. :)

kosmas
Offline
Зарегистрирован: 16.04.2016

Продолжаем разговор. Допилил скетч до задания уставки. Начал ломать голову над мигающим тестом. Начал с мигающего курсора. Файл примера из библиотеки LiquidCrystal работает на "УРА". Поэксперементировал с положением курсора - тоже работает. В моем скетче куда бы не написал 

lcd.setCursor(x,y);

lcd.blink();

Этого самого блинка и не происходит. Только рябь проходит по строке, где блинк.

Куда копать?

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

Там в классе есть метод отправки команды дисплею, попробуйте его так: lcd.command(9);

kosmas
Offline
Зарегистрирован: 16.04.2016

Arhat109-2 пишет:

Там в классе есть метод отправки команды дисплею, попробуйте его так: 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(" "); }

 

Так?

На экране просто все пропадает и все. После отпускания кнопки не появляется.. :(