Энкодер и прерывание
- Войдите на сайт для отправки комментариев
Итак, к ардуине (китайская nano) подключено:
1)две штуки DHT22
2)один DS18B20
3) DS3231
4)LCD 1602
5)Энкодер, на 2 и 3 цифровые пины, притянутые через 10 кОм к земле, средний контакт - +5В
Что есть: дефолтом показываем время, влажность и две температуры (засекал - луп со всеми опросами датчиков выполняется за 1260 мс, все нужные данные на один экран не влазят, поэтому делаем без дилея, просто чередуя вывод нужных значений на экран (1.2 секунды показывает температуру внутри, 1.2 секунды - температуру снаружи).
Что нужно - чтобы по энкодеру считывались данные с еепром, выводились на экран, в течении какого-то времени (секунд 30-60) висели на экране, ожидаясь дальнейшего кручения энкодера, если этого не произошло - возвращалось к лупу.
Есть скетч, который просто выводит данные на 1602(кстати,буду рад любым советам по улучшению читаемости кода):
// подключаем все библиотеки #include <OneWire.h> #include "DHT.h" #include <DallasTemperature.h> #include <Time.h> #include <Wire.h> #include <DS1307RTC.h> #include <EEPROM.h> #include <LiquidCrystal.h> #define ONE_WIRE_BUS 13 //номер пина ds18b20 #define DHTPINa 8 // номер пина первого dht22 #define DHTPINb 9 // номер пина второго dht22 OneWire oneWire(ONE_WIRE_BUS); //создаем обьект для ds18b20 DHT dhta(DHTPINa, DHT22); // и для dht22 DHT dhtb(DHTPINb, DHT22); LiquidCrystal lcd(12, 11, 10, 5, 4, 6, 7); //подключаем экран DallasTemperature sensors(&oneWire); //подключаем ds18b20 DeviceAddress Thermometer1 = { 0x28, 0xFF, 0xEE, 0xBB, 0x62, 0x15, 0x01, 0x92 }; //адрес термометра int addressWrite = 0; // переменная для записи int addressRead = 0; // переменная для чтения byte value; // переменная значения ячейки boolean counter_temp = true; int val; unsigned long current_time; byte L_kr_lit[8] = //рисуем нужную кириллицу, буква л { B00000, B00000, B01111, B00101, B00101, B10101, B01001, B00000, }; byte J_kr_lit[8] = //рисуем нужную кириллицу, буква ж { B00000, B00000, B10101, B10101, B01110, B10101, B10101, B00000, }; byte N_kr_lit[8] = //рисуем нужную кириллицу, буква н { B00000, B00000, B10001, B10001, B11111, B10001, B10001, B00000, }; byte P_kr_lit[8] = //рисуем нужную кириллицу, буква п { B00000, B00000, B11111, B10001, B10001, B10001, B10001, B00000, }; byte M_kr_lit[8] = //рисуем нужную кириллицу, буква м { B00000, B00000, B10001, B11011, B10101, B10001, B10001, B00000, }; byte U_kr_lit[8] = //рисуем нужную кириллицу, буква У { B10001, B10001, B10001, B01010, B00100, B01000, B10000, B00000, }; byte Cels_kr_lit[8] = //рисуем значок градуса и С { B11000, B11000, B00010, B00101, B00101, B00100, B00101, B00010, }; void setup() { lcd.createChar(2, L_kr_lit); // создаем чары для наших нарисованных букв и символов lcd.createChar(3, J_kr_lit); lcd.createChar(4, N_kr_lit); lcd.createChar(5, P_kr_lit); lcd.createChar(6, M_kr_lit); lcd.createChar(7, Cels_kr_lit); lcd.createChar(1, U_kr_lit); lcd.begin(16, 2); // инициализируем экран sensors.begin(); // запускаем датчики sensors.setResolution(Thermometer1, 10); // и задаем разрешение датчика(от 8 до 12(бит),при 12 лагает) dhta.begin(); // запускаем оба DHT dhtb.begin(); setSyncProvider(RTC.get); // и модуль часов } void loop() { sensors.requestTemperatures(); // запрашиваем данные с датчиков, пишем в переменные float h1 = dhta.readHumidity(); float h2 = dhtb.readHumidity(); float t3 = sensors.getTempC(Thermometer1); float t1 = dhta.readTemperature(); float t2 = dhtb.readTemperature(); if (isnan(h1) || isnan(t1) || isnan(h2) || isnan(t2) || isnan (t3) ) // на случай если не удалось считать данные с датчика - выдадим моргающее 10 раз сообщение о ошибке { for (int i=0; i < 10; i++) { lcd.clear(); lcd.setCursor(0, 0); lcd.print( "Sensor Error" ); delay(200); lcd.clear(); delay(200); } return; } byte humydity; // переменная для средней влажности humydity = ((h1+h2)/2); // получаем и записываем среднюю влажность int temp; // переменная для средней температуры temp = ((t1+t2)/2); lcd.clear(); // очистим для начала экран lcd.setCursor(0, 0); // зададим координаты lcd.print( "B\2a\3\4.:" ); // пишем "Влаж.:", самое значение влажности и процент lcd.print(humydity); lcd.print("%"); lcd.setCursor(11, 0); // переходим на вторую строку if (hour() < 10) // для красивого отображения часов и минут меньше 10 (например, не 2:5, а 02:05) {lcd.print("0");} lcd.print( hour() ); // пишем часы и минуты lcd.print( ":" ); if (minute() < 10) {lcd.print("0");} lcd.print( minute() ); if (counter_temp) // в зависимости от счетчика показываем либо температуру внутри и меняем счетчик, либо уличную и меняем счетчик { counter_temp = false; lcd.setCursor(0, 1); lcd.print( "Te\6\5.:" ); // пишем "Темп.:" lcd.print(temp); lcd.print( "\7" ); // и наш значок градусов } else { counter_temp = true; lcd.setCursor(0, 1); // аналогично для уличной температуры, только пишем "Темп.Ул.:" lcd.print( "Te\6\5.\1\2.:" ); lcd.print(t3); lcd.print("\7"); } }
Наработки по записи\чтению из еепром тоже наличествуют:
while (buttonState == HIGH) // если нажата кнопка { value = EEPROM.read(addressWrite);// то выводим в ком-порт данные Serial.print("Archive of temperature and humidity: "); Serial.print(value, DEC); // первая ячейка - число Serial.print("."); addressWrite = addressWrite + 1; value = EEPROM.read(addressWrite); Serial.print(value, DEC); Serial.print("."); // вторая - месяц addressWrite = addressWrite + 1; value = EEPROM.read(addressWrite); Serial.print(value + 2000, DEC); // третья - год, чтобы влезло в один байт, при записи отнимаем 2000, при чтении добавляем addressWrite = addressWrite + 1; Serial.print("\t"); value = EEPROM.read(addressWrite); Serial.print(value, DEC); // четвертая - час записи. addressWrite = addressWrite + 1; Serial.print(":00\n"); value = EEPROM.read(addressWrite); Serial.print("The temperature at a height of 1.5 meters: "); Serial.print(value-127, DEC); // пятая - температура с первого DHT22, -127 - чтобы иметь возможность писать отрицательные температуры, при записи Serial.print(" Celsius"); // берем значение 127 за 0 градусов. addressWrite = addressWrite + 1; Serial.print("\n"); value = EEPROM.read(addressWrite); Serial.print("The temperature at a height of 6 meters: "); Serial.print(value-127, DEC); // шестая - температура с второго DHT22 Serial.print(" Celsius"); addressWrite = addressWrite + 1; Serial.print("\n"); value = EEPROM.read(addressWrite); Serial.print("The temperature at a height of 9 meters: "); Serial.print(value-127, DEC); Serial.print(" Celsius"); // седьмая - температура с ds18b20 addressWrite = addressWrite + 1; Serial.print("\n"); value = EEPROM.read(addressWrite); Serial.print("Average humidity: "); Serial.print(value, DEC); // восьмая - средняя влажность Serial.print(" %"); addressWrite = addressWrite + 1; Serial.print("\n"); Serial.print("\n"); if(addressWrite == EEPROM.length()) // если достигли конца EEPROM - начинаем сначала addressWrite = 0; } if (x == 0 && hour() == 8 && minute() == 0 ) // условия для записи, одна запись в нужную минуту { digitalWrite(13, HIGH); //моргнем светодиодом пока пишем, для визуального контроля EEPROM.write(addressWrite, day()); addressWrite = addressWrite + 1; // переход на следующий адрес if (addressWrite == 1024) // если заполнилось - начинаем сначала addressWrite = 0; delay(50); // на всякий случай EEPROM.write(addressWrite, month()); addressWrite = addressWrite + 1; if (addressWrite == 1024) addressWrite = 0; delay(50); EEPROM.write(addressWrite,(byte) year() - 2000 ); addressWrite = addressWrite + 1; if (addressWrite == 1024) addressWrite = 0; delay(50); EEPROM.write(addressWrite, hour()); addressWrite = addressWrite + 1; if (addressWrite == 1024) addressWrite = 0; delay(50); EEPROM.write(addressWrite, (byte) t1 + 127); addressWrite = addressWrite + 1; if (addressWrite == 1024) addressWrite = 0; delay(50); EEPROM.write(addressWrite, (byte) t2 + 127); addressWrite = addressWrite + 1; if (addressWrite == 1024) addressWrite = 0; delay(50); EEPROM.write(addressWrite, (byte) t3 + 127); addressWrite = addressWrite + 1; if (addressWrite == 1024) addressWrite = 0; delay(50); EEPROM.write(addressWrite, (byte) humydity); addressWrite = addressWrite + 1; if (addressWrite == 1024) addressWrite = 0; delay(50); x++; digitalWrite(13, LOW); // выключаем светодиод } if ( x > 0 && hour() == 8 && minute() == 1 ) // когда прошла первая минута - обнуляем счетчик, не сделали это сразу чтобы не было несколько записей x =0; // в одну и ту же минуту delay(100); }
Есть скетч, на котором добился стабильной работы энкодера:
enum { ENC_PIN1 = 2, ENC_PIN2 = 3 }; #include <LiquidCrystal.h> LiquidCrystal lcd(12, 11, 10, 5, 4, 6, 7); volatile int val= 0; volatile int val_prev = 0; void setup() { pinMode(ENC_PIN1, INPUT); pinMode(ENC_PIN2, INPUT); lcd.begin(16, 2); } /* Функция декодирования кода Грея, взятая с Википедии. * Принимает число в коде Грея, возвращает обычное его представление. */ unsigned graydecode(unsigned gray) { unsigned bin; for (bin = 0; gray; gray >>= 1) bin ^= gray; return bin; } void loop() { 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) val = val +1; else if (previous_code == 1) val = val -1; } /* Сохраняем код и ждём 1 мс - вполне достаточно опрашивать энкодер * не более 1000 раз в секунду. */ previous_code = code; if (val != val_prev) { lcd.clear(); lcd.print(val); val_prev = val; } delay(1); }
Теперь вопрос - при добавлении в первый скетч прерывания (attachInterrupt(0, encoder, RISING);) отображение температуры начинает работать нестабильно, функция энкодера как нужно тоже не срабатывает.
Подскажите, как это вообще по-человечески делается? %)
вот здесь я выкладывал код работы энкодера без прерывания, работает до 500КГц
http://arduino.ru/forum/apparatnye-voprosy/opticheskie-enkodery#comment-144045
вот здесь я выкладывал код работы энкодера без прерывания, работает до 500КГц
http://arduino.ru/forum/apparatnye-voprosy/opticheskie-enkodery#comment-144045
Ну, если я правильно понимаю, то без прерывания здесь мне все равно не обойтись
библиотека http://www.pjrc.com/teensy/td_libs_Encoder.html
параметры ползания прерываний:
библиотека http://www.pjrc.com/teensy/td_libs_Encoder.html
параметры ползания прерываний:
Премного благодарствую!
Теперь же вопрос такой, как в этот код прикрутить чтобы данные на экране оставались,допустим, 10 секунд ?
Пробовал весь код по отображению засовывать в while ((Millis() - current_time) < interval)
но получается вообще какая-то неработающая хрень, с дилээм, само собой, тоже, т.к. энкодер не срабатывает.
Какие еще могут быть варианты, чтобы и на экране показывалось, и листалось, если положение энкодера изменилось?
ну, я не знаю, что ты там нагородил - напиши простой скетч, где у тебя просто значение энкодера и остальных переменных будет печататься в указанное место - lcd.setCursor();
и затираться в указанном месте через время печатью пробелов
lcd.clear(); вообще использовать не обязательно, если ты печатаешь по разным событиям в разные месте экрана.
Вот весь код целиком, правда пока что в дико нечитаемом виде:
Вот именно что экран у меня используется по полной и в таком случае даже без lcd.clear архивные данные тут же сотрутся риал-тайм данными с датчиков.
Вот весь код целиком, правда пока что в дико нечитаемом виде:
Вот именно что экран у меня используется по полной и в таком случае даже без lcd.clear архивные данные тут же сотрутся риал-тайм данными с датчиков.
не нужно мне код давать, что ты там наворотил, сам разбирайся - я тебе объясняю общий принцип: печатай с lcd.setCursor();, стирай пробелами с lcd.setCursor(); всё.
и, всё будет работать.
не нужно мне код давать, что ты там наворотил, сам разбирайся - я тебе объясняю общий принцип: печатай с lcd.setCursor();, стирай пробелами с lcd.setCursor(); всё.
и, всё будет работать.
А чем это принципиально лучше, чем lcd.clear, если, допустим, в режиме реал-тайм отображения ин-фы с датчиков обе строки полностью заняты и в режиме считывания записанных данных тоже ? clear же в таком случае даже удобнее использовать, чем две строки пробелами забивать.
В других условиях это может быть актуально, спору нет.
А у меня проблема в том, что я не могу придержать цикл, вызванный кручением энкодера, на нужное кол-во секунд, чтобы он в это же время слушал энкодер и реагировал на него.
В покое отображают реал-тайм данные с датчиков, крутанул энкодером - отображаются данные из еепром за прошлые числа, если 10 секунд энкодером не крутишь - возврат обратно к реал-тайм данным, если крутишь - листаются записи.
А у меня проблема в том, что я не могу придержать цикл, вызванный кручением энкодера, на нужное кол-во секунд, чтобы он в это же время слушал энкодер и реагировал на него.
Смотрите http://arduino.ru/tutorials/BlinkWithoutDelay и этот подход используйте для определения нужного времени.
А чем это принципиально лучше, чем lcd.clear, если, допустим, в режиме реал-тайм отображения ин-фы с датчиков обе строки полностью заняты и в режиме считывания записанных данных тоже ? clear же в таком случае даже удобнее использовать, чем две строки пробелами забивать.
начнём с факта того, что твой алгоритм кода не работает, а мой - работает.
тебе этого достаточно?
подробней: после затирания всего дисплея тебе придётся заново печатать все переменные в дисплей, вместо печати только изменившейся переменной, т.е. крутить весь код в лупе только для затирания дисплея.
вот тебе шаблон печати в дисплей одной переменной без затирания всего дисплея:
печать четырёхзначного значения энкодера в экран
А у меня проблема в том, что я не могу придержать цикл, вызванный кручением энкодера, на нужное кол-во секунд, чтобы он в это же время слушал энкодер и реагировал на него.
Смотрите http://arduino.ru/tutorials/BlinkWithoutDelay и этот подход используйте для определения нужного времени.
Премного благодарен
В итоге все-таки додумал решение