По нажатию кнопки моргнуть 3 раза диодом (без delay)

kukrpavt
Offline
Зарегистрирован: 15.08.2020

День добрый, форумчане!

Никак не могу понять, сам принцип как реализовать следующую задачу:

На нажатие кнопки светодиод должен моргнуть 3 раза (скажем 1 сек горит, 1 сек не горит... и так 3 раза).  При наличии delay всё получается. Но как только заменяю delay на схему с millis и циклами, то выходит одно из двух: или светодиод начинает мигать без остановки, или моргает только один раз.

Вот пример кода:

---------------------------------------------

#define BUTT_PIN 2
#define RELE1_PIN 10
#define INTERVAL_RELE1 1000
#include "GyverButton.h"    

GButton butt2(BUTT_PIN);

uint32_t rs01 = 0;

//----------------------
void setup() {
  Serial.begin(9600);

pinMode(BUTT_PIN,INPUT_PULLUP);
pinMode(RELE1_PIN,OUTPUT);
  butt2.setDebounce(90);        // настройка антидребезга (по умолчанию 80 мс)
  butt2.setTimeout(300);        // настройка таймаута на удержание (по умолчанию 500 мс)
  butt2.setType(HIGH_PULL);
  butt2.setDirection(NORM_OPEN);

//----------------------

void loop() {
butt2.tick();
LED ();}

//----------------------

void LED (){
for (int i=0; i<3; i++) {

   if (butt2.isPress()) {  
   digitalWrite(RELE1_PIN, HIGH);
     rs01 = millis();
 //    Serial.println ("1");
    }
      
   if( rs01 > 0 && (millis() - rs01 >= INTERVAL_RELE1)) {
   digitalWrite(RELE1_PIN, LOW);
     rs01 = 0;
 //    Serial.println ("2");
    }
   }
}

------------------------------

Если отследить при помощи SERIAL то, как работает цикл, то становится понятно, что цикл работает... но диод моргает только один раз... Никак не пойму что не так в коде.

Помогите, кто может. Если есть у кого-нибудь ссылки на подобное решение - буду благодарен.

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

1. Код, естественно, не смотрел (почему "естественно" - читайте правила форума).

2. Для мигания заводится переменная состояния, принимающая, скажем, следующие значения:

- 0 - начальное состояние, при нажатии кнопки - переводим в состояние 1,

- 1 - светодиод горит, по истечении интервала времени переводим в 2,

- 2 - светодиод не горит, по истечении интервала времени переводим в 3,

- 3 - светодиод горит, по истечении интервала времени переводим в 4,

- 4 - светодиод не горит, по истечении интервала времени переводим в 5,

- 5 - светодиод горит, по истечении интервала времени переводим в 0.

 

kukrpavt
Offline
Зарегистрирован: 15.08.2020

Логически я это понимаю.... Т. е. вы предлагаете не цикл сделать, а раз надо 3 раза моргнуть, то создать 6 разных задач, которые будут вызываться через установленный интервал друг за другом?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

1. Цикл всегда один - loop(). Других циклов не нужно.

2. Задача - одна. В этой задаче используется переменная состояния, которая может принимать одно из 6 значений: на нечетные светодиод горит, на четные - нет.

kukrpavt
Offline
Зарегистрирован: 15.08.2020

Советуете воспользоваться:

switch (mode) {
case 0: task_0();
break;
case 1: task_1();
break;
case 2: task_2();
break;
case 3: task_3();
break;
case 4: task_4();
break;
case 5: task_5();
break;
}

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

 

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

ТС, вы задаете вопросы но ответы не читаете. Вас же попросили код оформить правильно. По сути, своими действиями вы хамите тем кто хочет вам помочь. 

Зачем вам эти тухлые библиотеки в решении элементарной задачи ? 

#define LED_PIN     LED_BUILTIN
#define BUTTON_PIN  5

void setup() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP); 
}

void loop() {
  static uint32_t timer=0;
  static uint8_t counter=0;
  if(counter && millis()-timer>1000){
    timer=millis();
    if(digitalRead(LED_PIN)){
      digitalWrite(LED_PIN,LOW);
      counter--;
    } else {
      digitalWrite(LED_PIN,HIGH);
    }
  } else if(digitalRead(BUTTON_PIN==LOW)){ //если кнопка нажата
    counter=3; // мигнуть 3 раза  
  }
}

 

kukrpavt
Offline
Зарегистрирован: 15.08.2020

andriano пишет:

2. Для мигания заводится переменная состояния, принимающая, скажем, следующие значения:

- 0 - начальное состояние, при нажатии кнопки - переводим в состояние 1,

-.........

- 5 - светодиод горит, по истечении интервала времени переводим в 0.

Спасибо вам за подсказку! Попробовал. Реализовал...

Затем мысленный процесс пошёл по пути оптимизации и в результате получилось следующее:

// --------Начальные установки здесь----------

#define DATCHIK1_PIN 2    
#define RELE1_PIN 10 
#define LED_LOW_OIL_PIN 13   
#define INTERVAL_RELE1 250   
#define MODE1 3    // количество срабатываний (миганий) реле1 (от 1 до указанного)

//-----Подключаем библилтеки, объявляем переменные--------

#include "GyverButton.h"     
GButton D1(DATCHIK1_PIN);
uint32_t  RS1 = 0;
bool RELE1flag = false;
    

//=================================================
void setup() {

// Serial.begin(9600);
  pinMode(DATCHIK1_PIN,INPUT_PULLUP);
  pinMode(RELE1_PIN,OUTPUT);
  digitalWrite(RELE1_PIN, LOW);
  D1.setDebounce(90);        
  D1.setTimeout(300);        
  D1.setType(HIGH_PULL);
  D1.setDirection(NORM_OPEN);
  
//---------------------------------------------------    

void loop() {
   D1.tick();
   if (D1.isPress()) {run_rele1();};
} }

//==========================================================

//--------Работа 1-го реле----------
void run_rele1() {
 // Serial.println("Task 1");
  int i=0;
    while (i < MODE1 * 2){
    if (millis() - RS1 >= INTERVAL_RELE1) {
       RS1 = millis(); // сбросить таймер
        digitalWrite(RELE1_PIN, !RELE1flag);  // вкл/выкл
          RELE1flag = !RELE1flag; // инвертировать флаг
    i++;
  }}}
kukrpavt
Offline
Зарегистрирован: 15.08.2020

1) Простите, извините... #5 Я ничего такого не хотел сделать... Это всё - мой первый опыт программирования на Ардуино... А сейчас даже не могу поменять свой первый пост: нет кнопки "Редактировать" или "Изменить"... Ещё раз прошу прощения у вас и других членов форума.

2)  и ещё раз: спасибо за направление.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Это сложно назвать оптимизацией. 

Давайте посмотрим, что вы делаете. Вы вешаете на таймер некое действие, которое по сработке этого таймера выполняется. Ладно... Тут вроде все норм...

А что же за действие вы вешаете ? А вот тут облом. Вы написали блокирующую процедуру. Она выполняется в цикле долго-долго, пока не истечет некоторое время. По сути вы написали тот же delay. 

То есть запутались еще больше. Сделаете это не используя while, loop и goto. Тогда у вас все получится.

kukrpavt
Offline
Зарегистрирован: 15.08.2020

brokly пишет:

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

То есть запутались еще больше. Сделаете это не используя while, loop и goto. Тогда у вас все получится.

Так, теперь вот я точно запутался...(( (ну это и не удивительно, исходя из моего опыта) Дык, я так понимаю, что в том и смысл любого цикла...

И если "всё плохо", то тогда возникает главный вопрос любого новичка к учителю: А как надо было?

И если вам не трудно - подправьте код (тот что созданный мною делей) так, как должно быть с точки зрения вас как профи. Как сделать то это без while?

Green
Offline
Зарегистрирован: 01.10.2015

Ну написал же brokly в #5. Хотя и его можно "оптимизировать").

kukrpavt
Offline
Зарегистрирован: 15.08.2020

Тада я сначала пошёл читать мануалы на тему:  чем while отличается от if с точки зрения блокировок и долгого выполнения...

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

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

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Green пишет:

Ну написал же brokly в #5. Хотя и его можно "оптимизировать").

Покажешь ? :)

kukrpavt
Offline
Зарегистрирован: 15.08.2020

Благодарю за разъяснение. Смысл ваших объяснений мне понятен. Но их сутью ещё не проникся... Не прочувствовал как оно так на самом деле... Нужно помозговать.

Green
Offline
Зарегистрирован: 01.10.2015

brokly пишет:

Green пишет:

Ну написал же brokly в #5. Хотя и его можно "оптимизировать").

Покажешь ? :)

Да, тяжело оптимизировать нерабочий скетч. )

#define LED_PIN     LED_BUILTIN
#define BUTTON_PIN  5

void setup() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
}

void loop() {
  static uint32_t timer;
  static uint8_t counter;
  
  if (counter) {
    if (millis() - timer > 1000) {
      timer = millis();
      if (digitalRead(LED_PIN))
        counter--;
      digitalWrite(LED_PIN, !digitalRead(LED_PIN));
    }
  } 
  else if (digitalRead(BUTTON_PIN) == LOW)
    counter = 3;
}

 

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

А вот эта оптимизация , она что сделала ? Теперь код стал оптимальнее в чем, ну не считая "нерабочести" ? В том что стал еще больше нерабочий ? Стал сложнее читаем ? Стал занимать больше места после компиляции? Стал медленее работать ? :)

Green
Offline
Зарегистрирован: 01.10.2015

Меньше букв, лаконичнее. Только и всего.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

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

Green
Offline
Зарегистрирован: 01.10.2015

Братан, пудри мозги новичкам.)

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

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

Знаю несколько библиотек, которые написаны красиво на си, но тупо сишным программером, который про процессоры знает очень мало. Так вот они работают, но места занимают столько, что становятся бессмысленными. Они так же, как ты говорили, когда им рассказывали об экономии ресурсов :) 

И еще один момент, я в форум пишу не тебе одному. Новички, тут тоже читают.

kukrpavt
Offline
Зарегистрирован: 15.08.2020

Уважаемые знатоки!

Заранее прошу прощения, если что не так скажу или сделаю, у обоих: и у brokly, и у Green... но я проверил на Nano  код brokly:

brokly пишет:

#define LED_PIN     LED_BUILTIN
#define BUTTON_PIN  5

void setup() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP); 
}

void loop() {
  static uint32_t timer=0;
  static uint8_t counter=0;
  if(counter && millis()-timer>1000){
    timer=millis();
    if(digitalRead(LED_PIN)){
      digitalWrite(LED_PIN,LOW);
      counter--;
    } else {
      digitalWrite(LED_PIN,HIGH);
    }
  } else if(digitalRead(BUTTON_PIN==LOW)){ //если кнопка нажата
    counter=3; // мигнуть 3 раза  
  }
}

Он просто моргает раз в секунду светодиодом и на кнопку никак не реагирует.

Код же от Green:

Green пишет:

#define LED_PIN     LED_BUILTIN
#define BUTTON_PIN  5

void setup() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
}

void loop() {
  static uint32_t timer;
  static uint8_t counter;
  
  if (counter) {
    if (millis() - timer > 1000) {
      timer = millis();
      if (digitalRead(LED_PIN))
        counter--;
      digitalWrite(LED_PIN, !digitalRead(LED_PIN));
    }
  } 
  else if (digitalRead(BUTTON_PIN) == LOW)
    counter = 3;
}

работает как надо, т. е. при нажатии на кнопку мигает три раза.

Но это была просто ремарка ( я никого не хотел обидеть, просто констатировал факты).

-----------------------------------------------

Вот теперь то я понял, в чём я никак не могу разобраться, а именно:

1) если сделать так (т. е. код разместить прямо внутри главного цикла):

void setup() {
  pinMode(10, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);}
  static uint32_t timer1, timer2;
  static uint8_t counter1, counter2;

void loop() {
//Работа 1-й кнопки 1-го реле 
  if (counter1) {
    if (millis() - timer1 > 1000) {
      timer1 = millis();
      if (digitalRead(10))
        counter1--;
      digitalWrite(10, !digitalRead(10));
    } } 
  else if (digitalRead(2) == LOW)
    counter1 = 2;
// Работа 2-й кнопки и 2-го реле
  if (counter2) {
    if (millis() - timer2 > 500) {
      timer2 = millis();
      if (digitalRead(12))
        counter2--;
      digitalWrite(12, !digitalRead(12));
    } } 
  else if (digitalRead(3) == LOW)
    counter2 = 4; }

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

2) А вот если сделать так (т. е. тот же функционал, но сделать через вызов функции в главном цикле):

void setup() {
  pinMode(10, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);}
  static uint32_t timer1, timer2;
  static uint8_t counter1, counter2;
    
void loop() {
void run_rele1();
void run_rele2();}

//Работа 1-й кнопки 1-го реле 
 void run_rele1() {
  if (counter1) {
    if (millis() - timer1 > 1000) {
      timer1 = millis();
      if (digitalRead(10))
        counter1--;
      digitalWrite(10, !digitalRead(10));
    } } 
  else if (digitalRead(2) == LOW)
    counter1 = 2;}
    
//Работа 2-й кнопки 2-го реле 
 void run_rele2() {
  if (counter2) {
    if (millis() - timer2 > 500) {
      timer2 = millis();
      if (digitalRead(12))
        counter2--;
      digitalWrite(12, !digitalRead(12));
    }  } 
  else if (digitalRead(3) == LOW)
    counter2 = 4;}

то не работает вообще: при нажатии кнопок нет никакой реакции...

Вроде бы, всё одно и то же. Но похоже, это именно то, чего я и не понимаю: в чём тут разница?

Пожалуйста, "пните" меня в нужном направлении...  Моя цель - разобраться в том, чего я не понимаю и не знаю и понять, как в таком случае надо правильно поступать чтоб и delay не было, и многозадачность сохранялась... Ну и чтоб работало.))

[/quote]

nik182
Offline
Зарегистрирован: 04.05.2015

Проверку кнопок надо делать в основном цикле и вызвать функции моргания по результатам.

kukrpavt
Offline
Зарегистрирован: 15.08.2020

Так разве главный цикл не вызывает функцию, которая всё и должна выполнить? Такая же логика?

Green
Offline
Зарегистрирован: 01.10.2015

Нужно же понимать что вы делаете. void-ы уберите из лупа. И закрывающие фигурные скобки ставьте где положено - смотреть невозможно.

kukrpavt
Offline
Зарегистрирован: 15.08.2020

Убрал void-ы из лупа. Блин, действительно ужасно лопухнулся... Как я проглядел. Опробую - отпишусь. Запроста не работало из-за моего ляпа. И прога при компиляции не ругнулась... Надо ж...

А скобки перенёс - чтоб сократить размер простыни кода.... Извините, буду впредь писать как обычно..

Green
Offline
Зарегистрирован: 01.10.2015

Что бы размер простыни уменьшить оформляйте классом. Тогда и ошибок меньше будет.
Исправил.

kukrpavt
Offline
Зарегистрирован: 15.08.2020

Вот так всегда....(( Начинается с "да это ж плёвая задача! Зачем вам.....", а заканчивается классами, которые, как пелось в песне, "это мы не проходили" (в прямом смысле слова - ещё предстоит поизучать)) А моя сегодняшняя задача не разобраться в классах, а понять сам принцип многозадачности. А понимать его лучше на примерах, причём рабочих. А их то и нет нигде...(  Казалось бы, что может быть проще задачи: есть 2 кнопки и 2 светодиода. При нажатии 1-й кнопки нужно моргнуть 1-м диодом 3 раза за секунду и перстать моргать, а при нажатии 2-й кнопки - моргнуть 4 раза  за секунду вторым диодом и перстать моргать.... и без использования delay.

А по факту выходит: разбирать и разбираться - не чего и не в чем. А без понимания, что ты сделал не так - и обучения нет никакого... Цикл замыкается.

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

А у меня есть мнооого многозадачности, но разобраться тебе в ней задача не стоит, она вся состоит из классов. 

kukrpavt
Offline
Зарегистрирован: 15.08.2020

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

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

kukrpavt пишет:

А по факту выходит: разбирать и разбираться - не чего и не в чем. А без понимания, что ты сделал не так - и обучения нет никакого... Цикл замыкается.

А кто тебя обманул, сказав, что здесь обучающая площадка? 

kukrpavt
Offline
Зарегистрирован: 15.08.2020

А ещё я слышал, что есть FreeRTOS.... Тоже надо будет посмотреть чего это такое.

kukrpavt
Offline
Зарегистрирован: 15.08.2020

А зачем нужен дискуссионный клуб, коим является любой форум, дабы не разъяснить собравшимся свою правоту и не показать остальным правильный путь решения проблем? Форум что, нужен чтоб выяснить - кто круче, а то более всех остальных является недоучкой? А заодно и почесать своё эго, считающее самого себя гуру?

nik182
Offline
Зарегистрирован: 04.05.2015

Прочитайте правила форума, что бы у Вас не было заблуждений. http://arduino.ru/forum/pesochnitsa-razdel-dlya-novichkov/pesochnitsa-dlya-vsekh-novichkov . Форумы разные. Цели и задачи форума определяются создателями. Они могут совершенно не соответствовать Вашим представлениям. Форум не коммерческий. Без рекламы. Вы не платите денег. Ваша плата - следование правилам форума. Если Вам что то не нравиться, то просто уйдите.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

brokly пишет:

 

#define LED_PIN     LED_BUILTIN
#define BUTTON_PIN  5

void setup() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP); 
}

void loop() {
  static uint32_t timer=0;
  static uint8_t counter=0;
  if(counter && millis()-timer>1000){
    timer=millis();
    if(digitalRead(LED_PIN)){
      digitalWrite(LED_PIN,LOW);
      counter--;
    } else {
      digitalWrite(LED_PIN,HIGH);
    }
  } else if(digitalRead(BUTTON_PIN==LOW)){ //если кнопка нажата
    counter=3; // мигнуть 3 раза  
  }
}

 

GREEN же написал, что мой код с ошибкой. Я же в онлайне от руки набросал, могу и ошибиться. Вы же пытаетесь понять, так не списывайте тупо, а анализируйте, что делаете.

20 строка :

 

} else if(digitalRead(BUTTON_PIN)==LOW){ //если кнопка нажата

Обратите внимание на расстановку круглых скобок.

Насчет FreeRTOS. Она есть, но использовать ее при решении вашей задачи, все равно, что стрелять из пушки по воробьям. И "глянуть" просто так не получится. Сначала нужно понять что такое критические секции, семафоры, потоки, а уже потом пытаться глядеть :)

kukrpavt
Offline
Зарегистрирован: 15.08.2020

1) Извините  меня. Но вы не так поняли. Я ещё раз говорю: моя задача не списать, а разобраться и понять. Понять почему МОЙ код не работает; понять то, что через while я сделал тот же deley (кстати, после вашего объяснения я понял мою ошибку с while); понять и "прочувствовать" многозадачность и главное способы её реализации... И тут сильно "открыли мне глаза" (лично мне) 2 поста (первый - ваш, кстати): http://arduino.ru/forum/programmirovanie/mnogozadachnost-s-vyvodom-na-displei#comment-174839 и http://arduino.ru/forum/programmirovanie/mnogozadachnost-s-vyvodom-na-displei#comment-174874

Но я, увы пока, не умею так, как вы: глянуть беглым взглядом на код и понять, что расстановка скобок не в том месте... Мне нужно сесть, чтоб за душой не было "тянущих" дел, собрать в кучу все "исходники", "разложить всё по полочкам", протестировать в живую... И я проконстатировал просто факты: один код работает, согласно поставленной задачи, а другой - нет. Никакой цели другой у меня не было.

А ошибиться, тем более ненароком (как я с void-ами), может каждый. Главное вовремя осознать и исправить ошибку.

2) Вот, опять же вам огромное спасибо про краткое резюме о FreeRTOS... Отзыв профи, краткий и по сути - это очень важно. Особенно для того, кто только название слышал....

kukrpavt
Offline
Зарегистрирован: 15.08.2020

Теперь о главном:

Green пишет:

Нужно же понимать что вы делаете. void-ы уберите из лупа. И закрывающие фигурные скобки ставьте где положено - смотреть невозможно.

И действительно, убрав void-ы всё заработало как надо, то есть:

void setup() {
  pinMode(10, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP); }
  static uint32_t timer1, timer2;
  static uint8_t counter1, counter2;
    
void loop() {
run_rele1();
run_rele2();
}

//Работа 1-й кнопки 1-го реле 
 void run_rele1() {
  if (counter1) {
    if (millis() - timer1 > 1000) {
      timer1 = millis();
      if (digitalRead(10))
        counter1--;
      digitalWrite(10, !digitalRead(10));
    } 
  } 
  else if (digitalRead(2) == LOW)
    counter1 = 3;
}
    
//Работа 2-й кнопки 2-го реле 
 void run_rele2() {
  if (counter2) {
    if (millis() - timer2 > 300) {
      timer2 = millis();
      if (digitalRead(12))
        counter2--;
      digitalWrite(12, !digitalRead(12));
    }  
  } 
  else if (digitalRead(3) == LOW)
    counter2 = 4;
}

Значит, я на правильном пути и предыдущие "темы уроков" я" выучил".  ))

И это хорошо.

 

kukrpavt
Offline
Зарегистрирован: 15.08.2020

Подходим к итогу... 

И снова я хочу высказать благодарность: вам, brokly, за http://arduino.ru/forum/programmirovanie/po-nazhatiyu-knopki-morgnut-3-raza-diodom-bez-delay#comment-576771 и http://arduino.ru/forum/programmirovanie/mnogozadachnost-s-vyvodom-na-displei#comment-174839 2) вам, Green за http://arduino.ru/forum/programmirovanie/po-nazhatiyu-knopki-morgnut-3-raza-diodom-bez-delay#comment-576966 и http://arduino.ru/forum/programmirovanie/po-nazhatiyu-knopki-morgnut-3-raza-diodom-bez-delay#comment-576778 и 3) вам, nik182, за 

nik182 пишет:
Проверку кнопок надо делать в основном цикле и вызвать функции моргания по результатам.

Это я отметил те "вехи", что помогли мне понять происходящее и достичь таки поставленной перед самим собой же цели.

kukrpavt
Offline
Зарегистрирован: 15.08.2020

Итог.

Теперь к поставленной мною задаче " По нажатию кнопки моргнуть 3 раза диодом (без delay)" добавим следующие условия:

1) чтоб система реагировала только на нажатие кнопки (не на удержание, не на нажатие-отпускание, не на отпускание) и учесть дребезг контактов;

2) чтоб система работала в нужном временном интервале, скажем с 13-00 до 15-00,

3) чтоб в системе был индикатор, работющий круглосуточно при наступлении и до окончания некоего события;

4) чтоб количество морганий светодиодов и интервал морганий  были бы произвольно настраиваемыми;

5) чтоб система сама себя контроллировала бы от зависания (WhatchDog; но эту функцию я не реализовал по причине того, что пока не разобрался с темой как и чем перепрошить загрузчик NANO) и

6) чтоб была "многозадачность", т.е. выполнение одних функций системы не должны блокировать остальные.

Я применяя свои, пусть и крохотные, знания а так же благодаря вышеупомянутых (в предыдущем посте) товарищей, в итоге получил следующий код (проверил - работает всё так, как надо на Nano):

// --------Начальные установки здесь----------
#define DATCHIK1_PIN 2        // Первый датчик цепи
#define DATCHIK2_PIN 3        // Второй датчик цепи
#define DATCHIK_LOW_OIL_PIN 6 // Пин датчика уровня масла
#define RELE1_PIN 10 
#define RELE2_PIN 12
#define LED_LOW_OIL_PIN 13   // Пин cветодиода низкого уровня масла
#define INTERVAL_LED 333     // Частота моргания сетодиода низкого уровня масла
#define INTERVAL_RELE1 500    // Интервал между всасываниями масла в реле 1
#define INTERVAL_RELE2 150    // Интервал между всасываниями масла в реле 2

#define MODE1 3    // количество срабатываний (миганий) реле1 (от 1 до указанного)
#define MODE2 10    // количество срабатываний (миганий) реле2 (от 1 до указанного)

//-----Подключаем библиотеки, объявляем переменные--------
#include <microDS3231.h>      // Библиотека часов RTC
MicroDS3231 rtc;
#include "GyverButton.h"      // Библиотека датчиков(кнопок) уровня масла и релюх
GButton LO(DATCHIK_LOW_OIL_PIN);
GButton D1(DATCHIK1_PIN);
GButton D2(DATCHIK2_PIN);
//#include "GyverWDT.h"   // Библиотека WatchDog против зависания
uint32_t LS = 0, RS1 = 0, RS2 = 0;
bool led_stat = true;
bool RELE1flag = false;
bool RELE2flag = false;
static uint8_t count1, count2; 

//--- Настройка временного интервала работы клапанов--------
int H_start = 17;    // Старт работы контроллёра - ЧАСЫ.
    //int M_start = 0;   // Старт работы контроллёра - МИНУТЫ. (всегда должно быть = 0)
    //int S_start = 0;  // Старт работы контроллёра - Секунды.
int H_finish = 20;   // Окончание работы контроллёра - ЧАСЫ.
    //int M_finish = 30;  // Окончание работы контроллёра - МИНУТЫ.
    //int S_finish = 1; // Окончание работы контроллёра - Секунды.

//=================================================
void setup() {
  Serial.begin(9600);
//rtc.setTime(10,25,01,21,12,2020);
//rtc.setTime(SEC, MIN, HOUR, DAY, MONTH, YEAR); // устанвока времени вручную

//if (rtc.lostPower()) {      //  при потере питания
//rtc.setTime(COMPILE_TIME);  //  установить время компиляции
//}
  pinMode(DATCHIK1_PIN,INPUT_PULLUP);
  pinMode(DATCHIK2_PIN,INPUT_PULLUP);
  pinMode(DATCHIK_LOW_OIL_PIN,INPUT_PULLUP);
  pinMode(RELE1_PIN,OUTPUT);
  digitalWrite(RELE1_PIN, LOW);
  pinMode(RELE2_PIN,OUTPUT);
  digitalWrite(RELE2_PIN, LOW);
  pinMode(LED_LOW_OIL_PIN, OUTPUT);
  digitalWrite(LED_LOW_OIL_PIN, LOW); 
      // HIGH_PULL - кнопка подключена к GND, пин подтянут к VCC (PIN --- КНОПКА --- GND)
      // LOW_PULL  - кнопка подключена к VCC, пин подтянут к GND
      // NORM_OPEN - нормально-разомкнутая кнопка
      // NORM_CLOSE - нормально-замкнутая кнопка
  LO.setDebounce(90);        // настройка антидребезга (по умолчанию 80 мс)
  LO.setTimeout(300);        // настройка таймаута на удержание (по умолчанию 500 мс)
  LO.setType(HIGH_PULL);
  LO.setDirection(NORM_OPEN);
  
  D1.setDebounce(90);        // настройка антидребезга (по умолчанию 80 мс)
  D1.setTimeout(300);        // настройка таймаута на удержание (по умолчанию 500 мс)
  D1.setType(HIGH_PULL);
  D1.setDirection(NORM_OPEN);
  
  D2.setDebounce(90);        // настройка антидребезга (по умолчанию 80 мс)
  D2.setTimeout(300);        // настройка таймаута на удержание (по умолчанию 500 мс)
  D2.setType(HIGH_PULL);
  D2.setDirection(NORM_OPEN);

  //Watchdog.enable(RESET_MODE, WDT_PRESCALER_512); // Режим сторжевого сброса , таймаут ~4с
}
//======================================================    
void loop() {
//  printTime(); // проверка внутреннего времени по RTC
 int H = rtc.getHours(); 
// int M = rtc.getMinutes(); 
   LO.tick();
   D1.tick();
   D2.tick(); 
   run_oil(); 
if (H >= H_start && H < H_finish) { 
   run_rele1();
   run_rele2();
}
//Watchdog.reset(); // Переодический сброс watchdog, означающий, что устройство не зависло
}
//=======================================================

//--------Работа 1-го реле----------
void run_rele1() {
  if (count1) {
    if (millis() - RS1 >= INTERVAL_RELE1) {
      RS1 = millis();
      if (digitalRead(RELE1_PIN))
        count1--;
      digitalWrite(RELE1_PIN, !digitalRead(RELE1_PIN));
    } 
  } 
    else if (D1.isPress())
    count1 = MODE1;
}
 //-------Работа 2-го реле---------
void run_rele2() {
  if (count2) {
    if (millis() - RS2 >= INTERVAL_RELE2) {
      RS2 = millis();
      if (digitalRead(RELE2_PIN))
        count2--;
      digitalWrite(RELE2_PIN, !digitalRead(RELE2_PIN));
    } 
  } 
    else if (D2.isPress())
    count2 = MODE2;
}
//------Раздел работы индикаторв LED_LOW OIL-----------
void run_oil(){
  if (LO.isHold()) {led_oil();} 
  else  digitalWrite(LED_LOW_OIL_PIN, LOW);
}  
void led_oil(){
if( ( millis() - LS ) > INTERVAL_LED || millis() < LS ){
    LS = millis();
   digitalWrite(LED_LOW_OIL_PIN, led_stat); 
  led_stat = !led_stat;
   }
}
// --- Вывод внутреннего времени с RTC в COM-порт для контроля
void printTime() {
  Serial.print(rtc.getHours());
  Serial.print(":");
  Serial.print(rtc.getMinutes());
  Serial.print(":");
  Serial.print(rtc.getSeconds());
  Serial.print(" ");
  Serial.print(rtc.getDay());
  Serial.print(" ");
  Serial.print(rtc.getDate());
  Serial.print("/");
  Serial.print(rtc.getMonth());
  Serial.print("/");
  Serial.println(rtc.getYear());
}

Посему, считаю тему закрытой, так как нужный результат был достигнут.

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

Кому нужен этот код - пользуйтесь на здоровье.

И ещё раз: благодарю всех за помощь и кооперацию.

 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

125 строка порадовала ;)) защита от переполнения millis().

kukrpavt
Offline
Зарегистрирован: 15.08.2020

Улучшения - приветствуются! И да... ну а чё бы и не защититься? Пожалуй нужно вставить и в работу реле.... :-)

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Так вам надо изучать как организованы и как Вы сможете написать классы в Си++.

kukrpavt
Offline
Зарегистрирован: 15.08.2020

Вот так вот будет с защитой от переполнения millis() во всех функциях. Думаю, что для профи эта инфа не актуальна, а студентам - в самый раз.   ;-)

// --------Начальные установки здесь----------
#define DATCHIK1_PIN 2        // Первый датчик цепи
#define DATCHIK2_PIN 3        // Второй датчик цепи
#define DATCHIK_LOW_OIL_PIN 6 // Пин датчика уровня масла
#define RELE1_PIN 10 
#define RELE2_PIN 12
#define LED_LOW_OIL_PIN 13   // Пин cветодиода низкого уровня масла
#define INTERVAL_LED 333     // Частота моргания сетодиода низкого уровня масла
#define INTERVAL_RELE1 500    // Интервал между всасываниями масла в реле 1
#define INTERVAL_RELE2 150    // Интервал между всасываниями масла в реле 2

#define MODE1 3    // количество срабатываний (миганий) реле1 (от 1 до указанного)
#define MODE2 10    // количество срабатываний (миганий) реле2 (от 1 до указанного)

//-----Подключаем библиотеки, объявляем переменные--------
#include <microDS3231.h>      // Библиотека часов RTC
MicroDS3231 rtc;
#include "GyverButton.h"      // Библиотека датчиков(кнопок) уровня масла и релюх
GButton LO(DATCHIK_LOW_OIL_PIN);
GButton D1(DATCHIK1_PIN);
GButton D2(DATCHIK2_PIN);
//#include "GyverWDT.h"   // Библиотека WatchDog против зависания
uint32_t LS = 0, RS1 = 0, RS2 = 0;
bool led_stat = true;
bool RELE1flag = false;
bool RELE2flag = false;
static uint8_t count1, count2; 

//--- Настройка временного интервала работы клапанов--------
int H_start = 17;    // Старт работы контроллёра - ЧАСЫ.
    //int M_start = 0;   // Старт работы контроллёра - МИНУТЫ. (всегда должно быть = 0)
    //int S_start = 0;  // Старт работы контроллёра - Секунды.
int H_finish = 20;   // Окончание работы контроллёра - ЧАСЫ.
    //int M_finish = 30;  // Окончание работы контроллёра - МИНУТЫ.
    //int S_finish = 1; // Окончание работы контроллёра - Секунды.

//=================================================
void setup() {
  Serial.begin(9600);
//rtc.setTime(10,25,01,21,12,2020);
//rtc.setTime(SEC, MIN, HOUR, DAY, MONTH, YEAR); // устанвока времени вручную

//if (rtc.lostPower()) {      //  при потере питания
//rtc.setTime(COMPILE_TIME);  //  установить время компиляции
//}
  pinMode(DATCHIK1_PIN,INPUT_PULLUP);
  pinMode(DATCHIK2_PIN,INPUT_PULLUP);
  pinMode(DATCHIK_LOW_OIL_PIN,INPUT_PULLUP);
  pinMode(RELE1_PIN,OUTPUT);
  digitalWrite(RELE1_PIN, LOW);
  pinMode(RELE2_PIN,OUTPUT);
  digitalWrite(RELE2_PIN, LOW);
  pinMode(LED_LOW_OIL_PIN, OUTPUT);
  digitalWrite(LED_LOW_OIL_PIN, LOW); 
      // HIGH_PULL - кнопка подключена к GND, пин подтянут к VCC (PIN --- КНОПКА --- GND)
      // LOW_PULL  - кнопка подключена к VCC, пин подтянут к GND
      // NORM_OPEN - нормально-разомкнутая кнопка
      // NORM_CLOSE - нормально-замкнутая кнопка
  LO.setDebounce(90);        // настройка антидребезга (по умолчанию 80 мс)
  LO.setTimeout(300);        // настройка таймаута на удержание (по умолчанию 500 мс)
  LO.setType(HIGH_PULL);
  LO.setDirection(NORM_OPEN);
  
  D1.setDebounce(90);        // настройка антидребезга (по умолчанию 80 мс)
  D1.setTimeout(300);        // настройка таймаута на удержание (по умолчанию 500 мс)
  D1.setType(HIGH_PULL);
  D1.setDirection(NORM_OPEN);
  
  D2.setDebounce(90);        // настройка антидребезга (по умолчанию 80 мс)
  D2.setTimeout(300);        // настройка таймаута на удержание (по умолчанию 500 мс)
  D2.setType(HIGH_PULL);
  D2.setDirection(NORM_OPEN);

  //Watchdog.enable(RESET_MODE, WDT_PRESCALER_512); // Режим сторжевого сброса , таймаут ~4с
}
//======================================================    
void loop() {
//  printTime(); // проверка внутреннего времени по RTC
 int H = rtc.getHours(); 
// int M = rtc.getMinutes(); 
   LO.tick();
   D1.tick();
   D2.tick(); 
   run_oil(); 
if (H >= H_start && H < H_finish) { 
   run_rele1();
   run_rele2();
}
//Watchdog.reset(); // Переодический сброс watchdog, означающий, что устройство не зависло
}
//=======================================================

//--------Работа 1-го реле----------
void run_rele1() {
  if (count1) {
    if ( ( millis() - RS1 ) >= INTERVAL_RELE1 || millis() < RS1 ) {
      RS1 = millis();
      if (digitalRead(RELE1_PIN))
        count1--;
      digitalWrite(RELE1_PIN, !digitalRead(RELE1_PIN));
    } 
  } 
    else if (D1.isPress())
    count1 = MODE1;
}
 //-------Работа 2-го реле---------
void run_rele2() {
  if (count2) {
    if ( ( millis() - RS2 ) >= INTERVAL_RELE2 || millis() < RS2 ) {
      RS2 = millis();
      if (digitalRead(RELE2_PIN))
        count2--;
      digitalWrite(RELE2_PIN, !digitalRead(RELE2_PIN));
    } 
  } 
    else if (D2.isPress())
    count2 = MODE2;
}
//------Раздел работы индикаторв LED_LOW OIL-----------
void run_oil(){
  if (LO.isHold()) {led_oil();} 
  else  digitalWrite(LED_LOW_OIL_PIN, LOW);
}  
void led_oil(){
if( ( millis() - LS ) > INTERVAL_LED || millis() < LS ){
    LS = millis();
   digitalWrite(LED_LOW_OIL_PIN, led_stat); 
  led_stat = !led_stat;
   }
}
// --- Вывод внутреннего времени с RTC в COM-порт для контроля
void printTime() {
  Serial.print(rtc.getHours());
  Serial.print(":");
  Serial.print(rtc.getMinutes());
  Serial.print(":");
  Serial.print(rtc.getSeconds());
  Serial.print(" ");
  Serial.print(rtc.getDay());
  Serial.print(" ");
  Serial.print(rtc.getDate());
  Serial.print("/");
  Serial.print(rtc.getMonth());
  Serial.print("/");
  Serial.println(rtc.getYear());
}

 

kukrpavt
Offline
Зарегистрирован: 15.08.2020

Да, я и не против в принципе... Но в моей жизни вряд ли уже это понадобится, увы.... Время уходит, а мои последние "научные" достижения остались в 95-м... на защите диплома...

А сегодня - так, по мелочи, решаем повседневные надоедливые задачи.... Упрощаем себе жись...

 

Feofan
Онлайн
Зарегистрирован: 28.05.2017

Цитата:
с защитой от переполнения millis()
http://arduino.ru/forum/programmirovanie/velikoe-perepolnenie-millis

b707
Онлайн
Зарегистрирован: 26.05.2017

kukrpavt пишет:

Вот так вот будет с защитой от переполнения millis() во всех функциях. Думаю, что для профи эта инфа не актуальна, а студентам - в самый раз.   ;-)

студентам эта информация только навредит, потому что она неправильная. Ну а профи сами знают, что в правильно написанной программе "защита от переполнения" - лишняя.

 

Клапауций 9999
Offline
Зарегистрирован: 27.11.2020
писал всякие штуки для миллис.
прошло время - снова за рыбу деньги.
 
класс титановый велосипед для delay без delay()
 
*прикрути сюда счётчик - будет мигать нужное количество раз.
kukrpavt
Offline
Зарегистрирован: 15.08.2020

b707, всё великое начинается с малого... Я говорю про то, что  "занимаюсь тесанием камня" только последние полгода, а вы мне  - про то, как "не правильно построена пирамида Гизы"....

Не, конечно в итоге вы правы... Но студент сначала использует delay, затем преполнение millis и предохранение от него, ну а уж потом становится как вы: класс для delay без delay()... Всё это разные этапы и разные уровни...

Нельзя просто так сесть и не зная нот родить симфонию Бетховена...

Ну а за ссылку на титановый велосипед  Клапауций 9999  -  огромная благодарность!

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

kukrpavt пишет:

b707, всё великое начинается с малого... Я говорю про то, что  "занимаюсь тесанием камня" только последние полгода, а вы мне  - про то, как "не правильно построена пирамида Гизы"....

Не, конечно в итоге вы правы... Но студент сначала использует delay, затем преполнение millis и предохранение от него, ну а уж потом становится как вы: класс для delay без delay()... Всё это разные этапы и разные уровни...

Нельзя просто так сесть и не зная нот родить симфонию Бетховена...

Видите ли, на Вершину всегда более одного пути. И чем раньше Вы освободитесь от delay(), тем Ваш путь будет короче, разнообразнее и интереснее. И, хотя на первый взгляд кажется, что повторение уже освоенного проще, на самом деле, чем раньше будет произведен переход к новому, тем безболезненнее это произойдет.

kukrpavt
Offline
Зарегистрирован: 15.08.2020

Да... Философии тоже разными бывают. ))) Что единственно верно - так это то, что у каждого свой путь к вершине (да и понятие вершины тоже).

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

короче, хочешь моргать без delay(), разбирайся

https://github.com/DetSimen/Arduino_TimerList

А я пацкажу, когда трезвый.