Необходимо сделать терморегулятор. Есть arduino uno, 18b20 lcd 16:2 i2c, и энкодер. Все работает но из-за медленного 18b20 не работает энкодер. Соответственно необходимо использовать аппаратное прерывание. Энкодер должен начать работу после нажатие на кнопку. Помогите вынести работу энкодер в прерывание. Код (C++): #include <EEPROM.h> #include <Wire.h> #include <LiquidCrystal_I2C.h> #include <PID_v1.h> // https://playground.arduino.cc/Code/PIDLibrary #include <OneWire.h> // DS18b20 OneWire ds(11); LiquidCrystal_I2C lcd(0x3F , 16 , 2 ); ////////////////////////////////////////// #define ENCODER_A 2 // вход энкодера #define ENCODER_B 3 #define ENCODER_KEY 8 #define TEMP_MAX 60.0 // Приделы настройки термостата #define TEMP_MIN 25.0 #define RELAY_PIN 12 // выход на реле #define WindowSize 500 // периуд, для симистора можно оставить, для // реле увеличить в ~10 раз ////////////////////////////////////////////// bool encoderPinALast = LOW; bool n = LOW; unsigned long windowStartTime; double Setpoint, Input, Output; double Kp=2, Ki=5, Kd=1.5; // коэффициенты настройки PID PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT); float temperature; void setup() { Serial.begin(9600); lcd.init(); lcd.backlight(); lcd.clear(); pinMode(RELAY_PIN, OUTPUT); pinMode (ENCODER_A, INPUT); pinMode (ENCODER_B, INPUT); pinMode (ENCODER_KEY, INPUT); myPID.SetOutputLimits(0, WindowSize); myPID.SetMode(AUTOMATIC); //// проверка ЕЕПРОМ для первого включения if (EEPROM.read(0) == 255 && EEPROM.read(4) == 255){ EEPROM.write(0, 10); EEPROM.write(1, 0); EEPROM.write(2, 240); EEPROM.write(3, 65); } EEPROM.get(0, Setpoint); } void loop() { // Определяем температуру от датчика DS18b20 byte data[2]; // Место для значения температуры ds.reset(); // Начинаем взаимодействие со сброса всех предыдущих команд и параметров ds.write(0xCC); // Даем датчику DS18b20 команду пропустить поиск по адресу. В нашем случае только одно устрйоство ds.write(0x44); // Даем датчику DS18b20 команду измерить температуру. Само значение температуры мы еще не получаем - датчик его положит во внутреннюю память delay(50); // Микросхема измеряет температуру, а мы ждем. ds.reset(); // Теперь готовимся получить значение измеренной температуры ds.write(0xCC); ds.write(0xBE); // Просим передать нам значение регистров со значением температуры // Получаем и считываем ответ data[0] = ds.read(); // Читаем младший байт значения температуры data[1] = ds.read(); // А теперь старший // Формируем итоговое значение: // - сперва "склеиваем" значение, // - затем умножаем его на коэффициент, соответсвующий разрешающей способности (для 12 бит по умолчанию - это 0,0625) float temperature = ((data[1] << 8) | data[0]) * 0.0625; // Выводим полученное значение температуры в монитор порта //Serial.println(temperature); //temperature = analogRead(LM35_PIN) / 9.31; // забераем температуру Input = temperature; myPID.Compute(); /////////////////////// if(!digitalRead(ENCODER_KEY)){ // если надавили на кнопку, входим в настройки digitalWrite(RELAY_PIN, LOW); // и выключаем нагреватель. lcd.clear(); // очищаем экран и выводим параметр lcd.setCursor(8, 0); lcd.print(Setpoint, 1); lcd.print("C "); // управление энкодером n = digitalRead(ENCODER_A); if ((encoderPinALast == LOW) && (n == HIGH)) { if (digitalRead(ENCODER_B) == LOW) { Setpoint -= .1; if (Setpoint < TEMP_MIN) Setpoint = TEMP_MIN; // проверяем приделы настроек } else { Setpoint += .1; if (Setpoint > TEMP_MAX) Setpoint = TEMP_MAX; } EEPROM.put(0, Setpoint); // пишим в еепром настройку } encoderPinALast = n; delay(5); return; } // if (millis() - windowStartTime >= WindowSize) { // таймер на миллис windowStartTime += WindowSize; if (windowStartTime > millis()) windowStartTime = 0; // защита от переполнения lcd.setCursor(0, 0); lcd.print(temperature); // вывод на экран перенес в таймер lcd.print("C "); // чтение с LM35 происходит быстрее. lcd.setCursor(8, 0); // отсутствует необходимая LCD lcd.print(Setpoint, 1); // экрану зарежка lcd.print("C "); lcd.setCursor(0, 1); lcd.print(map(Output, 0, WindowSize, 0, 100)); lcd.print("% "); lcd.setCursor(8, 1); lcd.print(Output); lcd.print("mS "); } if (Output < millis() - windowStartTime) digitalWrite(RELAY_PIN, LOW); else digitalWrite(RELAY_PIN, HIGH); }
И еще дополнение: 1) За 50 миллисекунд датчик не измерит температуру. Нужно минимум 750 мс, если я не ошибаюсь. 2) Зачем так часто запрашивать температуру? Это нужно делать минимум раз в несколько секунд. Иначе только греете датчик.
Я вас понимаю, но Я даже убрал delay(50); но все рано не работает энкодер. Температура продолжает измерятся. Получается датчик тормозит цикл пока не измеряет температуру?
Посмотрите пример blink without delay, там используется millis() для обеспечения интервала времени. У Вас не успевает датчик получить новую температуру и выдает старую.
На сколько я понял надо опрашивать датчик. Спустя время. Между опросами датчика будет срабатывать энкодер.
Хватит рекламировать свою абдурину! Будет! Не путайте человека! (есть много корректно работающих вариантов) Пускай пока разбирается с блинк без делэй.
Подскажите где ошибка в millis() Код (C++): #include <EEPROM.h> #include <Wire.h> #include <LiquidCrystal_I2C.h> #include <PID_v1.h> // https://playground.arduino.cc/Code/PIDLibrary #include <OneWire.h> // DS18b20 OneWire ds(11); LiquidCrystal_I2C lcd(0x3F , 16 , 2 ); ////////////////////////////////////////// #define ENCODER_A 2 // вход энкодера #define ENCODER_B 3 #define ENCODER_KEY 8 #define TEMP_MAX 60.0 // Приделы настройки термостата #define TEMP_MIN 25.0 #define RELAY_PIN 12 // выход на реле #define WindowSize 500 // периуд, для симистора можно оставить, для // реле увеличить в ~10 раз ////////////////////////////////////////////// bool encoderPinALast = LOW; bool n = LOW; unsigned long windowStartTime; long previousMillis = 0; // в переменной previousMillis будет информация о времени, прошедшем с тех пор, как была измерена температура; long interval = 100; // интервал, после которого быдет измерена температура (измеряется в миллисекундах) double Setpoint, Input, Output; double Kp=2, Ki=5, Kd=1.5; // коэффициенты настройки PID PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT); float temperature; void setup() { Serial.begin(9600); lcd.init(); lcd.backlight(); lcd.clear(); pinMode(RELAY_PIN, OUTPUT); pinMode (ENCODER_A, INPUT); pinMode (ENCODER_B, INPUT); pinMode (ENCODER_KEY, INPUT); myPID.SetOutputLimits(0, WindowSize); myPID.SetMode(AUTOMATIC); //// проверка ЕЕПРОМ для первого включения if (EEPROM.read(0) == 255 && EEPROM.read(4) == 255){ EEPROM.write(0, 10); EEPROM.write(1, 0); EEPROM.write(2, 240); EEPROM.write(3, 65); } EEPROM.get(0, Setpoint); } void loop() { unsigned long currentMillis = millis(); if(currentMillis - previousMillis > interval) { // Определяем температуру от датчика DS18b20 byte data[2]; // Место для значения температуры ds.reset(); // Начинаем взаимодействие со сброса всех предыдущих команд и параметров ds.write(0xCC); // Даем датчику DS18b20 команду пропустить поиск по адресу. В нашем случае только одно устрйоство ds.write(0x44); // Даем датчику DS18b20 команду измерить температуру. Само значение температуры мы еще не получаем - датчик его положит во внутреннюю память //delay(50); // Микросхема измеряет температуру, а мы ждем. ds.reset(); // Теперь готовимся получить значение измеренной температуры ds.write(0xCC); ds.write(0xBE); // Просим передать нам значение регистров со значением температуры // Получаем и считываем ответ data[0] = ds.read(); // Читаем младший байт значения температуры data[1] = ds.read(); // А теперь старший // Формируем итоговое значение: // - сперва "склеиваем" значение, // - затем умножаем его на коэффициент, соответсвующий разрешающей способности (для 12 бит по умолчанию - это 0,0625) float temperature = ((data[1] << 8) | data[0]) * 0.0625; // Выводим полученное значение температуры в монитор порта //Serial.println(temperature); //temperature = analogRead(LM35_PIN) / 9.31; // забераем температуру Input = temperature; } myPID.Compute(); /////////////////////// if(!digitalRead(ENCODER_KEY)){ // если надавили на кнопку, входим в настройки digitalWrite(RELAY_PIN, LOW); // и выключаем нагреватель. lcd.clear(); // очищаем экран и выводим параметр lcd.setCursor(8, 0); lcd.print(Setpoint, 1); lcd.print("C "); // управление энкодером n = digitalRead(ENCODER_A); if ((encoderPinALast == LOW) && (n == HIGH)) { if (digitalRead(ENCODER_B) == LOW) { Setpoint -= .1; if (Setpoint < TEMP_MIN) Setpoint = TEMP_MIN; // проверяем приделы настроек } else { Setpoint += .1; if (Setpoint > TEMP_MAX) Setpoint = TEMP_MAX; } EEPROM.put(0, Setpoint); // пишим в еепром настройку } encoderPinALast = n; delay(5); return; } // if (millis() - windowStartTime >= WindowSize) { // таймер на миллис windowStartTime += WindowSize; if (windowStartTime > millis()) windowStartTime = 0; // защита от переполнения lcd.setCursor(0, 0); lcd.print(temperature); // вывод на экран перенес в таймер lcd.print("C "); // чтение с LM35 происходит быстрее. lcd.setCursor(8, 0); // отсутствует необходимая LCD lcd.print(Setpoint, 1); // экрану зарежка lcd.print("C "); lcd.setCursor(0, 1); lcd.print(map(Output, 0, WindowSize, 0, 100)); lcd.print("% "); lcd.setCursor(8, 1); lcd.print(Output); lcd.print("mS "); } if (Output < millis() - windowStartTime) digitalWrite(RELAY_PIN, LOW); else digitalWrite(RELAY_PIN, HIGH); }
Я не просто тру. Что-то летит в "Столбик для почесать", что-то получает приз в виде предупреждения. Они набираются, набираются, и, как говорил тов. Гегель, количество обязательно перерастёт в качество. А тов. Сталин утверждал, что "количество уже само по себе есть качество".
Не, тут нужно не так. Через millis() определять время старта измерения температуры датчика. Тут все как блинк без делэй. Дальше, когда запустили измерение - взводить флаг и запускать "новый таймер" на чтение результата.
В первом посте данной темы автор пишет,что у ds1820 слишком большой цыкл измерения температуры. И процессор не успевает обрабатывать информацию с энкодера. И решение проблемы видит в использовании прерывания. И весь сыр-бор в этой теме разгорелся из за этого. Можно,конечно изловчится и сделать. И зачем все эти извращения с делей и без делей. И зачем прерывание? Перед входом в цыкл выдаем команду Начать измерение. В основном программном цыкле Опрашиваем энкодер и пр. Опрашиваем шину Wire. Если=1 (измерение закончилось) передаем на индикацию и выдаем очередную команду на измерение. В противном случае в начало цыкла. При этом не важно сколько времени длится измерение. И нет лишних телодвижений