Не используем delay

Babaiko
Offline
Зарегистрирован: 18.12.2011

 Доброго времени суток.

затеял проект прогенератора.

железки простые 

датчик давления mpx 5050

пару тэнов через медленый шим 

клапан на долив воды ( пока нет )

 

простенький кодик 

 

#include <LiquidCrystal.h>  //бибилиотека экрана  


LiquidCrystal lcd(78, 79, 80, 81, 82, 83); //распиновка дисплея

/* распиновка */
int   mpxPin = 9; //вход MPX

// переменные
int   mpx;                   // датчик давления
int   mpx_a[2] = {120,130}; // массив для мин и макс значений
int   temp;        // pt 100
float pkPa;               // Давление в kPa 

unsigned long currentTime;
unsigned long loopTime;


void setup() {
  
  lcd.begin(16, 2);   // подключаем дисплей (col,row):
  Serial.begin(9600); // подключаем серийый порт
  pinMode(9, OUTPUT); // устанавливаем 9 ногу как выход тэна 
   pinMode(10, OUTPUT); // устанавливаем 10 ногу как выход тэна
}


void loop() {

  // Время задерки и круга
  currentTime = millis();        // считываем время, прошедшее с момента запуска программы
  if(currentTime >= (loopTime + 500)){   // задержа на 0.5 mc
	loopTime = currentTime;
    
    /* считываем датчики */
    mpx = (float)analogRead(mpxPin); // читаем данные с MPX
    pkPa = (mpx/(float)1023-0.04)/0.018;//персчитываем в kPa
    
    digitalWrite(9, LOW);    // устанавливаем значение на 9 ножке
    digitalWrite(10, HIGH);    // устанавливаем постоянный выход на 10 ножке
   

    /* управление нагревом */
    if (mpx < mpx_a[0]){  // если на датчике меньше чем в первое значение массива
		full_power(); //полный газ
    }

    if (mpx >= mpx_a[0] && mpx < mpx_a[1]){ //если на датчике больше второго значения массива
      low_power(); //даём ограничение по времени 
      }
   
   if (mpx > mpx_a[1]){ //если на датчике больше третьего значения массива
    off_power(); // отключаем тен
      }


    /* показываем значения на экране */
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(pkPa + ' kPa ' + mpx);
    lcd.setCursor(0,1);
    //lcd.setCursor(9,1);
    //lcd.print(power);
    //lcd.print(" br");

    /* Шлём в серийник */
    //Serial.print(pkPa + 'kPa ' + mpx + 'mpx ' + power);
    //Serial.println(' '); 
    Serial.print(pkPa); 
    Serial.print("kPa ");
    Serial.print(mpx);
    Serial.println("mpx ");
    //Serial.println(temp); 
  }
}

void full_power() {
	digitalWrite(9, HIGH);
}

void low_power(){
    digitalWrite(9, HIGH);
    delay(700);
    digitalWrite(9, LOW);
    delay(300);
}

void off_power() {
    digitalWrite(9, LOW);
}

 

вродебы всё работает но не нравится  использование функции delay 

void low_power(){
    digitalWrite(9, HIGH);
    delay(700);
    digitalWrite(9, LOW);
    delay(300);
}

 

 

система подвисает. в данном конкретном случее вроде-бы не важно. 

но если установить долив воды задержки могут быть по 3-5 секунд.

 

все найденные примеры с использованием millis  имеют равную длинну вкл и выкл, а это не мой случай.

leshak
Offline
Зарегистрирован: 29.09.2011

 Чутье вас не обмануло :) delay хорош в простеньких системах, когда не нужно "изображать многозадачность".

Без delay() это делается примерно так:

1. Объявлем глобальную переменную unsigned long offTime; // время когда датчик нужно выключить

2. Когда включили нагрев, делаем offTime=millis()+700; // высчитали время когда датчик нужно будет выключить. Через 700 милисекунд

3. Между строчкой 31 и 32 вставляем if(currentTime>=offTime){digitalWrite(9, LOW);} // время пришло - выключаем

В функции full_power, кроме digitalWrite(9, HIGH) делаем offTime=4294967295; // выключить в далеком будущем

Что-бы не происходило выключения "по времени".

Ну и в функции off_power, можно, но не обязательно вместо digitalWrite(9,LOW) написать просто offTime=0;// давно пора выключить

Ну или, опять-таки, по вкусу, я бы вместо трех функций full_power(), low_power(),off_power()

написал одну, принимающую параметр на сколько времени нужно включить power.

Что-то типа такого

#define OFF_DELAY 0
#define LOW_DELAY 700
#define FULL_DELAY 86400000  //сутки

unsigned long offTime=0;

void power(unsigned long delay){
 digitalWrite(9,HIGH);
 offTime=millis()+delay;
}

void loop(){
  currentTime=millis();
  if(offTime>=currentTime){ digitalWrite(9,LOW);}

....
   power(FULL_DELAY); // вместо full_power();
   power(LOW_DELAY);//  вместо low_power();
   power(OFF_DELAY);// вместо power_off();
....
}

Ну и еще, если устройство будет работать, не выключаясь долго. Нужно не забыть предусмотреть переполнение millis(). Примерно 49 дней и он переполнится. Логика "поломается". Так что где-то на 40-вой - нужно думать как его ресетить, или усложнять логику для "перехода через 0".

Babaiko
Offline
Зарегистрирован: 18.12.2011

Большое спасибо за ответ

по первому варианту скеч получался рабочий но есть нюанс
offTome не может быть больше или равно loopTime
иначе не работает

может я не правильно строки расположил?

со вторым вариантом пока пытаюсь понять

 

 

#include <LiquidCrystal.h>  //бибилиотека экрана  


LiquidCrystal lcd(78, 79, 80, 81, 82, 83); //распиновка дисплея

/* распиновка */
int   mpxPin = 9; //вход MPX

// переменные
int   mpx;                   // датчик давления
int   mpx_a[2] = {70,130}; // массив для мин и макс значений
int   temp;        // pt 100
float pkPa;               // Давление в kPa 

unsigned long currentTime;
unsigned long loopTime;
unsigned long offTime=0;



void setup() {
  
  lcd.begin(16, 2);   // подключаем дисплей (col,row):
  Serial.begin(9600); // подключаем серийый порт
  pinMode(9, OUTPUT); // устанавливаем 9 ногу как выход тэна 
   pinMode(10, OUTPUT); // устанавливаем 10 ногу как выход тэна
}


void loop() {

  // Время задерки и круга
  currentTime = millis();        // считываем время, прошедшее с момента запуска программы
  if(currentTime>=offTime){digitalWrite(9, LOW);}
  if(currentTime >= (loopTime + 500)){   // задержа на 0.5 mc
	loopTime = currentTime;
    
    /* считываем датчики */
    mpx = (float)analogRead(mpxPin); // читаем данные с MPX
    pkPa = (mpx/(float)1023-0.04)/0.018;//персчитываем в kPa
    
    digitalWrite(9, HIGH);    // устанавливаем значение на 9 ножке
    offTime=millis()+100; // высчитали время когда датчик нужно будет выключить. Через 700 милисекунд
   

    /* управление нагревом */
    if (mpx < mpx_a[0]){  // если на датчике меньше чем в первое значение массива
		full_power(); //полный газ
    }

    if (mpx >= mpx_a[0] && mpx < mpx_a[1]){ //если на датчике больше второго значения массива
      low_power(); //даём ограничение по времени 
      }
   
   if (mpx > mpx_a[1]){ //если на датчике больше третьего значения массива
    off_power(); // отключаем тен
 
  }
}

void full_power() {
	digitalWrite(9, HIGH);
        offTime=4294967295;
}

void low_power(){
    digitalWrite(9, HIGH);
 
}

void off_power() {
    digitalWrite(9, LOW);
    offTime=0;
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

>offTome не может быть больше или равно loopTime

Честно говоря, не вижу для этого причин. Они друг-друга вообще никак не касаются. Нигде не сравниваются. If-ы зависящие от них - не вложены друг в друга.

>может я не правильно строки расположил?

Да нет. Вроде правильно, только в low_power(), ради которой все и затевалось, вы включили нагреватель, а установить время "когда его нужно будет выключить" - забыли (второй пункт моих объяснений). Следовательно, если перед ней вызывался full_power - нагреватель включится "навсегда", а если предыдущим вызовом был off_power() - выключится сразу, при следующем же проходе loop().

Туда  (в 68-ю строку) еще нужно добавить строку

 offTime=millis()+700; // высчитали время когда датчик нужно будет выключить. Через 700 милисекунд

 

Babaiko
Offline
Зарегистрирован: 18.12.2011

offTime=millis()+700; // высчитали время когда датчик нужно будет выключить. Через 700 милисекунд

добавил, работает красиво 

но 

low_Power срабатывает правильно только если  offTime  меньше loopTime

чудеса

 

leshak
Offline
Зарегистрирован: 29.09.2011

 "и кроме мордобития - никаких чудес" (С) В.С.Высоцкий ;)

Все должно работать правильно. 

Смотрите что происходит.

Если вы ставите задержку 300. А датчики у вас опрашиваются раз в 500. То нагреватель успевает отработать положенное ему время до следующего опроса датчиков.

Если же вы задержку выставили в 3000. То он включится. Но через 500 милисекунд, произойдет опрос датчиков, и, например, например сработает условие в строчке 55. Произойдет вызов power_off(), который "затрет" наше предыдущие решение "греть 3000". Он скажет "уже пора выключать, температура слишком большая".

IMHO это наиболее правильно-безопастное поведение. Текущие состоянии датчиков имеет приоритет над "прошлыми решениями".

Ну можно, разве что добавить в строчку 51 условие, что-бы она срабатывала только если нагреватель выключен, и не "обновляла время" если он уже включен.

if (offTime==0 && (mpx >= mpx_a[0] && mpx < mpx_a[1]))

Если же вы хотите "раз решили греть 3000, то греем 3000 что-бы не происходило на датчиках", вам нужно будет завести еще одну переменную "флаг". Которой можно временно отключать опрос датчиков. В lowpower ее "взводить" offMpx=1

в строчке 34 сбрасывать ее offMpx=0, а в строчке 35 добавить еще одно условие if(!offMpx && .....