delay() в сложных временнЫх процессах.

Piskunov
Offline
Зарегистрирован: 13.02.2014

Написал программу, в которой используется много временнЫх интервалов, задаваемых оператором delay(). Работает медленно, но уверенно )). Очень хочу избавиться от delay, но не пойму с какой стороны подступиться.

Краткая предыстория создания. Сделал устройство для включения четырех нагрузок 220В. Два-три месяца работало хорошо, а потом стали залипать контакты реле, хотя используются энергосберегающие лампочки. Решил заменить реле на твердотельные. Но у меня их всего два, поэтому придумал схему на четыре нагрузки с одной твердотелкой.

// ***** Define pins *****
// Serial_RX 0
// Serial_TX 1
#define K1_relay 5
#define K2_relay 6
#define K3_relay 9
#define K4_relay 10
#define K5_relay 7
#define K6_relay 8
#define K7_relay 11
#define K8_relay 12
#define K9_SSR 3    // Solid State Zero Crossing Relay
//
#define IOPort Serial        // порт ввода-вывода
#define PortSpeed 9600       // скорость работы порта
// ***** Define relay switching times *****
#define SSR_Switch_Time 11                // in milliseconds
#define RelaySwitchON_Time 10             // in milliseconds
#define RelaySwitchOFF_Time 20            // in milliseconds
//
struct output_power_switch {
  unsigned long sw_time;        // для будущего использования
  byte relay_A;                 // реле А (1..4) подключающее
  byte relay_B;                 // реле В (5..8) перемыкающее
  boolean state;                // текщее состояние канала ВКЛ-ВЫКЛ
} RelayPool [] = {0, K9_SSR, K9_SSR, LOW, \
                  0, K1_relay, K5_relay, LOW, \
                  0, K2_relay, K6_relay, LOW, \
                  0, K3_relay, K7_relay, LOW, \
                  0, K4_relay, K8_relay, LOW};
  const byte RelayPoolMaxN = sizeof(RelayPool)/sizeof(output_power_switch);
//
// Переменные исключительно для тестирования работы
unsigned long interval = 1500;  // интервал включения-выключения канала
unsigned long previousMillis = 0;
unsigned long currentMillis;
//
void setup() 
{
     IOPort.begin (PortSpeed);
     for (byte i = 0; i < RelayPoolMaxN; i++) {
         pinMode (RelayPool[i].relay_A, OUTPUT);
         digitalWrite (RelayPool[i].relay_A, LOW);
         pinMode (RelayPool[i].relay_B, OUTPUT);
         digitalWrite (RelayPool[i].relay_B, LOW);
     }
}
void loop()
{
     currentMillis = millis();
     if(currentMillis - previousMillis > interval) {
         previousMillis = currentMillis;
         ChangeState (1);                //можно любой номер 1..4
     }
}
void SwitchON(byte n){
      if ( !RelayPool[n].state && (boolean)n && (n < RelayPoolMaxN)){
        // если канал вЫключен, его номер не 0 и не больше числа каналов,
        // то будем включать
        noInterrupts();
        digitalWrite (RelayPool[n].relay_A, HIGH);
        delay (RelaySwitchON_Time);
        digitalWrite (RelayPool[0].relay_A, HIGH);
        delay (SSR_Switch_Time);
        digitalWrite (RelayPool[n].relay_B, HIGH);
        delay (RelaySwitchON_Time);
        digitalWrite (RelayPool[0].relay_A, LOW);
        digitalWrite (RelayPool[n].relay_A, LOW);
        delay (RelaySwitchOFF_Time);
        RelayPool[n].state = HIGH;
        interrupts();
        return;
      }
}
void SwitchOFF(byte n){
      if ( RelayPool[n].state && (boolean)n && (n < RelayPoolMaxN)){
        // если канал включен, его номер не 0 и не больше числа каналов,
        // то будем вЫключать
        noInterrupts();
        digitalWrite (RelayPool[n].relay_A, HIGH);
        delay (RelaySwitchON_Time);
        digitalWrite (RelayPool[0].relay_A, HIGH);
        delay (SSR_Switch_Time);
        digitalWrite (RelayPool[n].relay_B, LOW);
        delay (RelaySwitchOFF_Time);
        digitalWrite (RelayPool[0].relay_A, LOW);
        delay (SSR_Switch_Time);
        digitalWrite (RelayPool[n].relay_A, LOW);
        delay (RelaySwitchOFF_Time);
        RelayPool[n].state = LOW;
        interrupts();
        return;
      }
}
void ChangeState(byte n){
      if ((boolean)n && (n < RelayPoolMaxN)){
        RelayPool[n].state ? SwitchOFF(n) : SwitchON(n);
      }
      return;
}

 

Piskunov
Offline
Зарегистрирован: 13.02.2014

Извините, схему забыл.

Looka
Offline
Зарегистрирован: 24.04.2012

delay() в  loop()   это зло. 
Надеюсь не надо писать почему. 

Используйте millis().  

Расчитывайте  моменты времени когда надо выполнить то или иное действие. 
И постоянно в цикле loop()  постоянно проверяйте наступило условие для события или нет.

А для начала смотрите темы как мигать  без delay()

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Piskunov, что делает ваше устройство, по-очереди включает и отключает лампочки? Опишите на словах алгоритм.

Piskunov
Offline
Зарегистрирован: 13.02.2014

dimax пишет:

Опишите на словах алгоритм.

dimax, приветствую!

Задача, которая решена в этой схеме - использование единственного твердотельного реле для включения/выключения четырёх нагрузок.

Описание схемы.
Реле 1..8 - обычные, с током коммутации 5А по паспорту.
Реле 9 - твердотельное, с функцией Zero Crossing.

Алгоритм работы.
Для включения, например, нагрузки №1 сначала включаем реле 1 (задержка на включение 10 мс), затем включаем реле 9 (задержка на включение один полупериод), соответственно ток включения/выключения коммутируется твердотелкой, и контакты обычных реле не подгорают. После этого реле 5 перемыкает замкнутые контакты реле 1 и 9 (задержка на включение 10 мс) и остаётся включеным.
Далее отключаем реле 1 и 9 (задержка 20мс) и готовы к включению другой нагрузки.
Выключение производим в том же порядке, но с обратным знаком ))

Описанный функционал запрограммирован в функциях SwitchON и SwitchOFF

Остальная часть программы особого значения не имеет, так как упрощена до предела - подача команд, оценка освещённости и тд удалены, дабы не загружать сообщение в теме.

Сейчас это решение стоит у меня в комнате и управляет светом, в принципе нормально, но в моменты включения/выключения подтормаживает, и может не воспринимать команды. Думаю, что из-за delay.

PS. Я понимаю, что четыре твердотелки упрощают этот вопрос принципиально ))
но у меня их нет ((

Piskunov
Offline
Зарегистрирован: 13.02.2014

Looka пишет:

А для начала смотрите темы как мигать  без delay()

Эта тема мне знакома ))
См. строки 50..52

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Piskunov, честно говоря я даже не могу начать думать по теме вашего вопроса, т.к. не понимаю зачем такое чудовищное извращение. Если у вас сдохло реле через 2 месяца -значит реле было плохое. Хорошие электромагнитные реле работают по 30 лет. Если ваше реле было на китайском реле-шилде, то туда всё самое дешевое г@вно ставят. Я вот однажды себе  такое брал, правда всего пару лет работает, рано выводы делать. Есть ещё хорошие реле Tyco , они обычно в оранжевых корпусах.

Piskunov
Offline
Зарегистрирован: 13.02.2014

У меня FINDER 34.51.7.012.0010, мне по случаю достались ))
Они тихие, и на лампочки накаливания работают без проблем. А вот с энергосберегайками ерунда вышла, контакты подгорают. Наверное из-за большой емкостной нагрузки, сама мощность ерунда, 6 ламп по 12 ватт на один канал и три по 18 на другой.
Три месяца работают, а потом менять приходится - залипают и не выключают свет, пока отвёрткой не ударишь.
Пробовал поставить мощнее (44.62.9.012.0000), щёлкают громко и время нормальной работы увеличилось раза в полтора.
Зато с твердотельным по такой схеме уже четыре месяца полёт нормальный. Лишь delay() покоя не даёт ))

Logik
Offline
Зарегистрирован: 05.08.2014

Piskunov пишет:

Эта тема мне знакома ))
См. строки 50..52

Видно что знакома, но не прочувствована. Архитектура должна быть переделана. Полный запрет на делеи во всех функциях, вызываемых из лупа. Т.е. выполнение любой функции , из тех у  которых в вашей реализации делей, должно содержать проверку истек ли временной интервал. Если не истек - выходим, в другой раз вызовут иначе выполняем действие. Величину  интервала и выполняемое действие определяет некоторая глобальная переменная.

Типа пример из "раннего", несколько замутненного оптимизацией. Генерит морзянкой "...---..." по кругу.

#define T_HIGH_1 50
#define T_HIGH_2 200
#define T_LOWE   100
#define T_LOWE_P 600

/* Машина состояний управления светодиодом для морзянки */
word StatMashine(word t)
{
  static word status = 0;
  static word time;
  word        l;

  t = t-time;

  switch (status)
  {
  case 0:
    time = 0;
    goto NextStep;
  case 1:
  case 3:
  case 5:
  case 13:
  case 15:
  case 17:
    l= T_HIGH_1;
    goto TestHIGH;
  case 7:
  case 9:
  case 11:
    l= T_HIGH_2;
TestHIGH:   
    if (t>l)
    {
      STAT_MASHINE_OFF;
      goto NextStep;
    }
    break;
  case 18:
    l= T_LOWE_P;
    goto TestHIGH;
  case 19:
    goto NextStepON;
  default:
    if (t>T_LOWE)
    {
NextStepON:
      STAT_MASHINE_ON;
NextStep:
      //перходим к следующему статусу
      status++;
      time+=t;
    }

    if (status==20) //цикл завершен
    {
      status = 1; //переходим к началу цикла
      return true;
    }
  }
  return false;
}

void loop() {
  word t;

  t = millis();
StatMashine(t);

А noInterrupts(); у Вас зачем?

Piskunov
Offline
Зарегистрирован: 13.02.2014

Logik пишет:

А noInterrupts(); у Вас зачем?

Сорри, не убрал. Это датчик движения у меня в полной версии через прерывание на площадке свет включает. А когда сосед долго ключ в замок вставить не может, у меня свет не всегда включался, пока прерывание не запретил ))

Logik
Offline
Зарегистрирован: 05.08.2014

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

Piskunov
Offline
Зарегистрирован: 13.02.2014

Logik пишет:

Видно что знакома, но не прочувствована. 

Возможно так.
Но, понимаете, у меня не линейный процесс, и может быть ситуация, когда один свет будет включаться, на другой придёт команда выключить, а задействованы одни и те же элементы.
Как в этом случае по времени разносить, не понятно...

А с делэями пока не выполнится, к другому не приступит.

Piskunov
Offline
Зарегистрирован: 13.02.2014

Logik пишет:

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

Ну, подумаешь, свет на площадке на полсекунды позже включится. Не критично ))

Logik
Offline
Зарегистрирован: 05.08.2014

Piskunov пишет:

Возможно так.
Но, понимаете, у меня не линейный процесс, и может быть ситуация, когда один свет будет включаться, на другой придёт команда выключить, а задействованы одни и те же элементы.
Как в этом случае по времени разносить, не понятно...

Очевидно блокировать выполнение новых команд до выполнения текущей, складывать их в очереди и выполнять по мере готовности. Или просто игнорить.

Piskunov
Offline
Зарегистрирован: 13.02.2014

Logik пишет:

Очевидно блокировать выполнение новых команд до выполнения текущей, складывать их в очереди и выполнять по мере готовности. Или просто игнорить.

Я так пробовал сделать, но примерно на сотой строке функции включения меня посетило чувство, что я двигаюсь в никуда.
Теперь, по Вашему примеру, подозреваюб что я рано сдался.
Буду пробовать ещё, хотя я надеялся получить ответ типа "есть книга такая-то, читай, неучь, там про это есть"

Logik
Offline
Зарегистрирован: 05.08.2014
Pyotr
Offline
Зарегистрирован: 12.03.2014

Piskunov пишет:

 

Краткая предыстория создания. Сделал устройство для включения четырех нагрузок 220В. Два-три месяца работало хорошо, а потом стали залипать контакты реле, хотя используются энергосберегающие лампочки. 

В момент включения энергосберегаек импулс тока достигает нескольких десятков ампер (под сотню Ампер) и контакты просто привариваются. Поставьте последовательно с каждой лампой NTC Ом на 20-30.
И с лампами накаливания этот трюк полезен - в холодном состоянии сопротивление спирали в 10-15 раз меньше, чем в раскаленном. Номинал NTC поменьше в этом случае.

Logik
Offline
Зарегистрирован: 05.08.2014

Pyotr пишет:

И с лампами накаливания этот трюк полезен - в холодном состоянии сопротивление спирали в 10-15 раз меньше, чем в раскаленном. Номинал NTC поменьше в этом случае.

 

А уж как он полезен для галлогенок на 220В! Там температура рабочая выше и отношенее сопротивлений соответственно больше. NTC исключает их перегорание при включении и продлевает срок службы просто катастрофически.

Piskunov
Offline
Зарегистрирован: 13.02.2014

Pyotr пишет:

Поставьте последовательно с каждой лампой NTC Ом на 20-30.

Спасибо.
Я почему-то думал, что они дорогие, ан нет - вполне по карману.
Обязательно поставлю, по результатам отпишусь (месяца через три :-)

Piskunov
Offline
Зарегистрирован: 13.02.2014

Logik пишет:

Автоматное программирование...

О как!

Я про эту тему ничего не знал, с удовольствием почитаю. Думаю до нового года точно хватит ))

Спасибо

Looka
Offline
Зарегистрирован: 24.04.2012

А не проще будет  Zero Detect использовать?
Эт снимет проблему?   Есть у кого опыт  более менее длительной  эксплуатации? 
 

Примеров реализации много.    Микросхемка с детектором, и соответсвенно прерывание которое обрабатывает. 

Или оптопары  переключающиеся при пересечении нуля. 
 

 

Piskunov
Offline
Зарегистрирован: 13.02.2014

Pyotr пишет:

Поставьте последовательно с каждой лампой NTC Ом на 20-30.

Поставил. Греются как... Ну, в общем сильно греются. Убрал.

Pyotr
Offline
Зарегистрирован: 12.03.2014

Мощность лампы какая? Я то думал ватт по 20. Поставте 2 NTC паралельно. Если NTC не будут нагреваться, то в них смысла не больше чем в простых резисторах. Думаю нагрев при работе до 50С* это нормально.

Piskunov
Offline
Зарегистрирован: 13.02.2014

Pyotr пишет:

Мощность лампы какая? Я то думал ватт по 20. Поставте 2 NTC паралельно. Если NTC не будут нагреваться, то в них смысла не больше чем в простых резисторах. Думаю нагрев при работе до 50С* это нормально.

Pyotr, приветствую.

Стоит 8 шт по 18Вт, итого должно быть меньше 1 ампера.
NTC резистор один на всю группу светильников, вот такой: JNR15S200, http://www.joyin.com.tw/lan_en/product_files/15S200L.pdf

Подплавил мне подрозетник )). То есть я понимаю, они должны греться, возможно даже градусов до 100. Делаю вывод, что лампочки полнейшее г@вно.

Pyotr
Offline
Зарегистрирован: 12.03.2014

Вообще то я писал: "Поставьте последовательно с каждой лампой NTC Ом на 20-30."

У вас получается 1А на 20 Ом = 20 В. Мощность 20в*1А=20 Вт маленький такой паяльничек)

Piskunov
Offline
Зарегистрирован: 13.02.2014

Pyotr пишет:

Вообще то я писал: "Поставьте последовательно с каждой лампой NTC Ом на 20-30."

У вас получается 1А на 20 Ом = 20 В. Мощность 20в*1А=20 Вт маленький такой паяльничек)

"Неправильно ты, Дядя Фёдор, бутерброд ешь." (с) не я.

Если поставить с каждой лампочкой, то сопротивление уменьшится пропорционально количеству, и мы не получим существенное ограничение тока (20 / 8 = 2,5). Поэтому я и поставил на группу.

Мысли, собственно, такие:

Для начального момента Ваш расчёт верен, но в процессе резистор нагреется, его сопротивление упадёт. Этакая система с обратной связью. В25/50 = 3100, по графику сопротивление резистора упадёт до 0,2 от номинала при температуре 80 С, и будет меньше 0,1 при 120 С. Что даёт нам мощность 2 ватта при 1 ампере.

Мне кажется, что при такой мощности пластмассу не расплавить ))

Следовательно ток больше, причём существенно. Почему? Потому что лампочки ...

Pyotr
Offline
Зарегистрирован: 12.03.2014

В нашем непонимании друг друга виноват Интернет)
Внутреннее сопротивление электросети думаю 0.5- 2 Ом. Напрямую не получится измерить, но косвенно можно посчитать. Пусть будет 1 Ом. Включаем сберегайку. Если момент попадёт на вершину синуса почти 300 В, ток в цепи (теоретически, на самом деле меньше из за индуктивности проводов) под 300 А (кондёр разряжен). Диоды такой кратковременный импульс выдерживают, а контакты подгорают. Будет хорошо, если ограничим импульс тока до 10-20 А. Для этого нужно включить последовательно с энергосберегайкой резистор 30-15 Ом. При этом при потребляемой мощности лампы 22 Вт ток =0.1 А. На 20 Ом резисторе падает 2 В и выделяется Р=0.1*2=0.2 Вт. Делить омы на количество ламп не надо. Да, эти цифры для холодного NTC. От такого подогрева его Т поднимется на 10-20С*, но не до 100С* и, при нагреве выделяемая мощность на нем уменьшится вследствие уменьшения его сопротивления.

Померьте ток через лампу и будете знать потребляемую мощность. И 20 Ом резистор на одну лампу или 3 Ом на 8 ламп.
Во всех советских теликах с импульсными БП стояли проволочные резисторы 4.7 Ом. На БП стояли 1-2 кондера по 100 мкф. и всё замечательно работало годами. 

 

Piskunov
Offline
Зарегистрирован: 13.02.2014

Возможно интернет ))

С оценкой "сопротивления сети" согласен.
Если мы ставим NTC последовательно с каждой лампой, то в начальный момент времени эквивалентная схема для NTC будет представлять параллельное включение. Всё-таки настаиваю на 2,5 омах. Что плохо, и не сильно ограничивает ток через управляющие контакты.
Поэтому считаю, что NTC надо включать в общую цепь, но обязательно предусматривать тот факт, что он будет нагреваться и ставить его надо не абы как (типа как я), а по нормальному.

На всякий случай график.

Pyotr
Offline
Зарегистрирован: 12.03.2014

Картинка хорошая, сохранил у себя. Ещё вот про NTC http://aterlux.ru/index.php?page=article&art=ntcresistor
Пусть будет 2.5 Ом не спорю. Тут важно найти компромис между пусковым током и рассеиваемой на резисторе мощностью, что сказывается на КПД устройства.
Идеальный вариант по КПД это замыкание балластного резистора контактами дополнительного реле через 0.5-1 сек после включения.

Piskunov
Offline
Зарегистрирован: 13.02.2014

Pyotr пишет:

Идеальный вариант по КПД это замыкание балластного резистора контактами дополнительного реле через 0.5-1 сек после включения.

Спасибо, статья полезная.

А если для исключения потерь использовать дополнительное реле, то NTC и не обязателен, можно поставить обычный резистор на 15..20 Ом, его за полсекунды не перегреем наверное. В результате приходим к идее, сходной с моей первоначальной. Она хороша на мой взгляд, но размыкать цепь нам придётся обычными контактами под полной нагрузкой.

Резюме: надо продолжать изучение автоматного программирования ))

PS и ещё, в такой схеме NTC будет даже вреден, так как повторное включение через несколько секунд будет при полном токе, NTC остыть не успеет, у него постянная времени большая (125 сек!).

Pyotr
Offline
Зарегистрирован: 12.03.2014

Размыкать нагрузку в Вашем случае контактам не трудно. Им "трудно" размыкать индуктивную нагрузку. При работе выпрямителя на емкость ток течёт в короткий период на самых вершинах синусоиды, а остальное время цепь как будто разомкнута.

Писал я скетч для себя типа: мигаем светиками без делай и без миллис, работает на прерывании по совпадению таимера0. Не трогая настроек Т/С можно независимо переключать хоть все пины с шагом в 1 мсек.