ARDUINO Помогите победить цикл for, для создания не замысловатого ночника.

buf121

✩✩✩✩✩✩✩
10 Янв 2021
2
0
Есть код,который будет представлен ниже, смысл в том чтобы происходила смена режима по нажатию кнопки в любой момент. Когда программа переключается на task_1(); то переключение на следующий режим становится возможным после прохода цикла и на смену есть несколько секунд( прерывания пробовал вставлять, эффекта не замечал). Замечал что цикл в принципе не любит быструю смену но так и не нашел как это решить.
Ещё раз (на всякий). Задача в том чтобы по нажатию на кнопку(она одна ) происходило переключение режима независимо где находится программа ( в середине цикла или в начале).
C++:
/*
   Люстра
*/

#define PIN 3        // кнопка подключена сюда (PIN --- КНОПКА --- GND)
#define MODE_AM 5    // количество режимов (от 0 до указанного)
#include "GyverButton.h"
byte diod_1 = 4;  //Диоды и их расположение на пинах
byte diod_2 = 5;  //Шим порт
byte diod_3 = 6;    //Шим порт
byte diod_4 = 7;
byte diod_5 = 9;
#define LEDS              diod_1,diod_2,diod_3,diod_4,diod_5 //массив диодов
#define STEP_TIME         500 // Время на рандоме
uint8_t leds[] = { LEDS };


GButton butt1(PIN);  // создаём нашу "кнопку"

int mode = 0;       // переменная режима

void setup() {
  Serial.begin(9600);
  pinMode(3, INPUT_PULLUP);
 
  for (uint8_t o = 0; o <  sizeof(leds); o++)
    pinMode(leds[o], OUTPUT);
}

void loop() {
  butt1.tick();             // обязательная функция отработки. Должна постоянно опрашиваться
  if (butt1.isPress()) {    // правильная отработка нажатия с защитой от дребезга

    // увеличиваем переменную номера режима. Если вышла за количество режимов - обнуляем
    if (++mode >= MODE_AM) mode = 0;



    switch (mode) {
      case 1:
      task_0();
      break; // Режим 1. включение всех диодов сразу
      
      case 2:
      task_1();
      break; // Режим 2. Включение диодов по очереди плавно в цикле

      case 3:
      task_2();   
      break;// Режим 3. Рандомное загорание диодов

      case 4:
      task_3();  break;// Режим 4. Включение диодов как во 2-ом режиме и + включается лишний диод

    }
  }
}

// наши задачи, внутри функций понятное дело может быть всё что угодно
void task_0() {
  Serial.println("task_0");
  digitalWrite(diod_1, HIGH);
  digitalWrite(diod_2, HIGH);
  digitalWrite(diod_3, HIGH);
  digitalWrite(diod_4, HIGH);
  digitalWrite(diod_5, HIGH);

}

void task_1() {
  Serial.println("task_1");
  // плавное включение светодиода
  // начальное значение на Pin6 i=0, если i<=255, то прибавляем к i единицу
  digitalWrite(diod_1, HIGH);
  digitalWrite(diod_2, HIGH);
  digitalWrite(diod_3, HIGH);
  digitalWrite(diod_4, HIGH);
  for (int i = 255; i >= 0; i--) {  //i--
    analogWrite(diod_3, i);
    delay(30); // ставим задержку для эффекта 30
    if (i == 5) {
      digitalWrite(diod_1, LOW);
    }
  }
  if (digitalRead(diod_1) == LOW) {
    for (int i = 255; i >= 0; i--) {  // i++
      analogWrite(diod_2, i);
      delay(30); // ставим задержку для эффекта 30
      if (i == 5) {
        digitalWrite(diod_4, LOW);
      }
    }
  }
  delay(1000);  //Задержка между разгоранием и затуханием
  //плавное затухание светодиода
  // начальное значение на Pin6 i=255, если i>=255, то вычитаем от i единицу

  for (int i = 0; i <= 255; i++) {
    analogWrite(diod_3, i);
    delay(30); // ставим задержку для эффекта 30
    if (i == 250) { // Загорнется лишний диод и остается гореть
      digitalWrite(diod_1, HIGH);
    }
  }
  if (digitalRead(diod_1) == HIGH) {
    for (int i = 0; i <= 255; i++) { 
      analogWrite(diod_2, i);
      delay(30); // ставим задержку для эффекта 30
      if (i == 250) {
        digitalWrite(diod_4, HIGH);
      }
    }
  }
  delay(1000);
}
void task_2() {

  Serial.println("task_2");
    digitalWrite(diod_1, LOW);
  digitalWrite(diod_2, LOW);
  digitalWrite(diod_3, LOW);
  digitalWrite(diod_4, LOW);
  digitalWrite(diod_5, LOW);

  static bool back;
  for (uint8_t j = 0; j < sizeof(leds); j++) {
    uint8_t o;
    do
      o = random(0, sizeof(leds));
    while (digitalRead(leds[o]) != back);
    digitalWrite(leds[o], !back);

    delay(STEP_TIME);
  }
  back = !back;
}
void task_3() {
  Serial.println("task_3");
  digitalWrite(diod_1, HIGH);
  digitalWrite(diod_2, HIGH);
  digitalWrite(diod_3, HIGH);
  digitalWrite(diod_4, HIGH);
  digitalWrite(diod_5, HIGH);

  if (digitalRead(diod_5) == HIGH) {
    delay(1000);
    digitalWrite(diod_5, LOW);
    for (int i = 255; i >= 0; i--) {  //i--
      analogWrite(diod_3, i);
      delay(10); // ставим задержку для эффекта 30
      if (i == 1) {
        digitalWrite(diod_1, LOW);
      }
    }
    if (digitalRead(diod_1) == LOW) {
      for (int i = 255; i >= 0; i--) {  // i++
        analogWrite(diod_2, i);
        delay(10); // ставим задержку для эффекта 30
        if (i == 1) {
          digitalWrite(diod_4, LOW);
        }
      }
    }
    delay(1000);

  }

  if (digitalRead(diod_5) == LOW) {
    digitalWrite(diod_5, HIGH);
    delay(1000);
    for (int i = 0; i <= 255; i++) {  // i++
      analogWrite(diod_3, i);
      delay(10); // ставим задержку для эффекта 30
      if (i == 250) { // Загорнется лишний диод и остается гореть
        digitalWrite(diod_1, HIGH);
      }
    }
    if (digitalRead(diod_1) == HIGH) {
      for (int i = 0; i <= 255; i++) {  // i++
        analogWrite(diod_2, i);
        delay(10); // ставим задержку для эффекта 30
        if (i == 250) {
          digitalWrite(diod_4, HIGH);
        }
      }
    }
  }
  delay(2000);  //Задержка между разгоранием и затуханием


}
 

bort707

★★★★★★✩
21 Сен 2020
2,902
863
так и не нашел как это решить.
простого решения нет

Относительно простых (но костыльных) варианта два:
  • написать свою функцию delay() и вставить в нее обработку кнопки
  • сделать обработку кнопки в прерывании и в каждом проходе цикла проверять ее состояние

Правильное решение - переписать весь код без delay и без циклов
 

buf121

✩✩✩✩✩✩✩
10 Янв 2021
2
0
простого решения нет

Относительно простых (но костыльных) варианта два:
  • написать свою функцию delay() и вставить в нее обработку кнопки
  • сделать обработку кнопки в прерывании и в каждом проходе цикла проверять ее состояние

Правильное решение - переписать весь код без delay и без циклов
Спасибо за совет. Буду пробовать переписывать.
 

p-a-h-a

★✩✩✩✩✩✩
18 Фев 2019
35
28
Просто если, то можно задержку 30 мс заменить на:
if (mydelay(30)) return;

и написать функцию:
C++:
bool mydelay(uint32_t ms) {
  for (; ms > 1; ms--)
  {
    delay(1);
    if (interrupt_) {
      interrupt_ = false;
      return true;
    }
  }
  return false;
}
В прерывании присваиваем переменной bool interrupt_ = true;
 

poty

★★★★★★✩
19 Фев 2020
2,994
895
@p-a-h-a, не всё так просто. При применении библиотеки Гайвера такой финт не сработает, если включена защита от дребезга. Дело в том, что прерывание "сработает" при первом импульсе от кнопки, в нём, допустим, выставим interrupt_ в true и сделаем butt.tick(). Затем, за время <1мс мы вернёмся из mydelay в task, оттуда - в loop и вызовем ещё один butt.tick(). Защита от дребезга, как правило, устанавливается на время в десятки миллисекунд, поэтому butt1.isPress() не сработает и мы снова окажемся в долгих for и delay, но со сбоем в программе "эффекта". Скорее всего, к этому времени снова произойдёт прерывание, мы снова выскочим из цикла... Результат будет зависеть от случайного совпадения последнего прерывания и сработки isPress (и, скорее, не в пользу правильной сработки).
Единственный правильный вариант - переход с delay на таймеры и обработка всех событий в одном цикле loop.