Выполнение цикла до наступления новых условий на входах.

Acris

✩✩✩✩✩✩✩
20 Апр 2025
13
0
@Николай Ск, Спасибо большое. Неудобно как-то вышло, Ваш вариант не получилось проверить. Он почему-то не вталкивается в мою плату.компелируется, грузит и под конец 90% выдает кучу красного текста. Не разбирался, чего хочет.
Код интересный , поизучал флаги и логику исполнения. Спасибо
 

Николай Ск

✩✩✩✩✩✩✩
28 Мар 2023
11
2
@BOT_Zilla, добавлюсь к поблпгодарившим за скетч, это прекрасный наглядный пример подхода к реализации машины состояний, а также хороший пример (для меня открытие) использования enum. В примере хорошо видно что это ТИП ДАННЫХ, а не просто перечесление (в 39 сточке функция а 89 добавленая переменная). Поэтому большое СПАСИБО @BOT_Zilla , за то что запарился и написал скетч а не просто посоветовал читать литературу.

@Николай Ск, Спасибо большое. Неудобно как-то вышло,
непарься у BOT_Zilla, однозначно скетч лучше, просто я не люблю использовать циклы, и посоветовал обратить внимание на void yield().
А по поводу аварийного выхода из цыкла может добавишь в циклы опрос обратной кнопки и break;
от ркпп не попадает ни в одну известную.
BOT_Zilla предусматрел нужно добавить в switch (state) еще условие case UNKNOWN {код дествий} break;
“Длительное зажатие кнопки” Я может буду кеп очевидность, но изучи <EncButton.h> (GyverButton)там (как описано) предусмотрены дребезги, а в примере есть описание режимов нажатия кнопки
 
Изменено:
  • Лойс +1
Реакции: Acris

Acris

✩✩✩✩✩✩✩
20 Апр 2025
13
0
Всем доброго дня и хорошего настроения.
Как и говорил, отчитываюсь. На сколько позволил мой уровень понимания, доработал.
Прошу строго не судить, возможно корявенько вышло.
С UNKNOWN у меня почему-то не получилось.
C++:
// Обзовем все используемые пины через константы, чтобы легче читались
const int PIN_LOW_BTN = 13;
const int PIN_4WD_BTN = 14;
const int PIN_IN2 = 2;
const int PIN_IN3 = 3;
const int PIN_IN4 = 4;
const int PIN_IN5 = 5;

const int PIN_MOTOR_FWD = 7;
const int PIN_MOTOR_REV = 8;
const int PIN_LAMP_4WD = 10;
const int PIN_LAMP_LOW = 11;
const int PIN_BUZZER = 25;



/*
  / Логика таблицы отслеживает только три состояния коробки, в которых двигатель всегда СТОП.
  / Состояния (по таблице):
  /Входы (2–3–4–5)    Состояние
  /       1 1 0 1        2WD
  /       0 1 1 0        4WD
  /       0 0 1 1        4WD LOW
  / Из состояния 2WD двигатель можно включить только вперед, из состояния 4WD LOW - только назад.
  / Из состояния 4WD можно включить в обе стороны.
  / Контрольные состояния соберем в перечисление, а разбирать будем через switch-case
*/
enum GearState
{
  UNKNOWN,
  STATE_2WD,
  STATE_4WD,
  STATE_4WD_LOW,
  STATE_ERROR
};

/* Функция для опроса состояния входов, вернет текущее состояние
  / или UNKNOWN, если процесс переключения не завершен или завершен не успешо,
  / например, сгорел двигатель в коробке, или что-то заклинило.
  / Здесь этот вариант не обрабатывается.
*/
GearState currentState()
{
  bool in2 = digitalRead(PIN_IN2);
  bool in3 = digitalRead(PIN_IN3);
  bool in4 = digitalRead(PIN_IN4);
  bool in5 = digitalRead(PIN_IN5);

  if (in2 && in3 && !in4 && in5)
    return STATE_2WD;
  if (!in2 && in3 && in4 && !in5)
    return STATE_4WD;
  if (!in2 && !in3 && in4 && in5)
    return STATE_4WD_LOW;
  else
    return STATE_ERROR;

  return UNKNOWN;
}

bool isPressed(int pin) // опрос кнопки
{
  return digitalRead(pin) == LOW; // Если кнопки с PULLUP, без PULLUP "==LOW" убрать
}

void setup()
{ // настройка пинов
  pinMode(PIN_MOTOR_FWD, OUTPUT);
  pinMode(PIN_MOTOR_REV, OUTPUT);
  pinMode(PIN_LAMP_4WD, OUTPUT);
  pinMode(PIN_LAMP_LOW, OUTPUT);
  pinMode(PIN_BUZZER, OUTPUT);

  pinMode(PIN_LOW_BTN, INPUT_PULLUP); // это если вход подтянут к питанию,
  pinMode(PIN_4WD_BTN, INPUT_PULLUP); // если к земле через внешний резистор, то просто INPUT
  pinMode(PIN_IN2, INPUT);
  pinMode(PIN_IN3, INPUT);
  pinMode(PIN_IN4, INPUT);
  pinMode(PIN_IN5, INPUT);

  digitalWrite(PIN_MOTOR_FWD, LOW);
  digitalWrite(PIN_MOTOR_REV, LOW);
}

void loop()
{
  bool lowBtn = isPressed(PIN_LOW_BTN); // опрашиваем состояние кнопок
  bool wdBtn = isPressed(PIN_4WD_BTN);

  if (lowBtn && wdBtn)
    return; // защита от дурака: одновременное нажатие — ничего не делаем

  GearState state = currentState(); // получаем текущее состояние входов

  switch (state)
  {
    case STATE_2WD:
      /*
        / Из этого состояния может двигаться только вперед,
        / к положению 4WD, поэтому проверяем только одну кнопку
      */
      digitalWrite(PIN_LAMP_4WD, LOW);//  вывод на табло состояния РКПП
      digitalWrite(PIN_LAMP_LOW, LOW);
      if (wdBtn)
      {
        // Если нажата кнопка 4WD, переключаемся 2WD -> 4WD (вперёд)
        digitalWrite(PIN_MOTOR_FWD, HIGH);// включаем мотор вперед
        tone(PIN_BUZZER, 600); // звук процесса переключения
        unsigned long lastMillis = millis();
        while (state != STATE_4WD && !(millis() - lastMillis > 7000))// условия прекращения цикла -  изменение статуса или истечение времени
        {
          state = currentState(); // получаем текущее состояние входов
          delay(10);
        }
        digitalWrite(PIN_MOTOR_FWD, LOW); //останавливаем мотор
        noTone(PIN_BUZZER); //выключаем звук
      }
      break;

    case STATE_4WD:

      digitalWrite(PIN_LAMP_4WD, HIGH);// опрос состояния РКПП, вывод на табло
      digitalWrite(PIN_LAMP_LOW, LOW);
      /*
        / Из этого положения можем двигаться как вперед к 4WD LOW,
        / так и назад к 2WD, поэтому контролируем обе кнопки
      */
      if (wdBtn)
      {
        // Переход 4WD -> 2WD (назад)
        digitalWrite(PIN_MOTOR_REV, HIGH);
        tone(PIN_BUZZER, 600);
        unsigned long lastMillis = millis();
        while (state != STATE_2WD && !(millis() - lastMillis > 5000))
        {
          state = currentState();
          delay(10);
        }
        digitalWrite(PIN_MOTOR_REV, LOW);
        digitalWrite(PIN_LAMP_4WD, LOW);
        digitalWrite(PIN_LAMP_LOW, LOW);
        noTone(PIN_BUZZER);
      }
      else if (lowBtn)
      {
        // Переход 4WD -> 4WD LOW (вперёд)
        digitalWrite(PIN_MOTOR_FWD, HIGH);
        tone(PIN_BUZZER, 600);
        unsigned long lastMillis = millis();
        while (state != STATE_4WD_LOW && !(millis() - lastMillis > 5000))
        {
          state = currentState();
          delay(10);
        }
        digitalWrite(PIN_MOTOR_FWD, LOW);
        digitalWrite(PIN_LAMP_4WD, HIGH);
        digitalWrite(PIN_LAMP_LOW, HIGH);
        noTone(PIN_BUZZER);
      }
      break;

    case STATE_4WD_LOW:

      digitalWrite(PIN_LAMP_4WD, HIGH);  // опрос состояния РКПП, вывод на табло
      digitalWrite(PIN_LAMP_LOW, HIGH);
      /*
        / Из этого положения можем двигаться только назад, к положению 4WD,
        / контролируем только одну кнопку
      */
      if (lowBtn)
      {
        // Переход 4WD LOW -> 4WD (назад)
        digitalWrite(PIN_MOTOR_REV, HIGH);
        tone(PIN_BUZZER, 600);
        unsigned long lastMillis = millis();
        while (state != STATE_4WD && !(millis() - lastMillis > 5000))
        {
          state = currentState();
          delay(10);
        }
        digitalWrite(PIN_MOTOR_REV, LOW);
        digitalWrite(PIN_LAMP_4WD, HIGH);
        digitalWrite(PIN_LAMP_LOW, LOW);
        noTone(PIN_BUZZER);
      }
      break;

    case STATE_ERROR:  //состояние ошибки

      digitalWrite(PIN_LAMP_4WD, HIGH);   // в режиме неполного переключения моргают обе лампы, звучит буззер
      digitalWrite(PIN_LAMP_LOW, HIGH);
      tone(PIN_BUZZER, 600, 500);
      delay (500);
      digitalWrite(PIN_LAMP_4WD, LOW);
      digitalWrite(PIN_LAMP_LOW, LOW);
      tone(PIN_BUZZER, 1000, 500);
      delay (500);

      if (lowBtn)         //нажимаем кнопку LOW
      {
        digitalWrite(PIN_MOTOR_REV, HIGH); //включаем двигатель назад на короткий промежуток времени, с целю "качнуть" вилку переключения
        tone(PIN_BUZZER, 2000);            // звук звучит в другой тональности
        delay(3000);                        // время "качания " вилки
        digitalWrite(PIN_MOTOR_REV, LOW);
        noTone(PIN_BUZZER);
        state = currentState();
        delay(10);
      }

      if (wdBtn)          // кнопка 4WD  чтобы "качнуть" вилку в другую сторону
      {
        digitalWrite(PIN_MOTOR_FWD, HIGH);
        tone(PIN_BUZZER, 2000);
        delay(3000);
        digitalWrite(PIN_MOTOR_FWD, LOW);
        noTone(PIN_BUZZER);
        state = currentState();
        delay(10);
      }
      break;

       default:
      // В переходном или ошибочном состоянии ничего не делаем
      break;
  }
}
 

BOT_Zilla

★✩✩✩✩✩✩
1 Апр 2022
21
13
Есть вопрос к логике. Например, в этой части:
StateError:
 if (lowBtn)         //нажимаем кнопку LOW
      {
        digitalWrite(PIN_MOTOR_REV, HIGH); //включаем двигатель назад на короткий промежуток времени, с целю "качнуть" вилку переключения
        tone(PIN_BUZZER, 2000);            // звук звучит в другой тональности
        delay(3000);                        // время "качания " вилки
        digitalWrite(PIN_MOTOR_REV, LOW);
        noTone(PIN_BUZZER);
        state = currentState();
        delay(10);
      }
пока работает delay(3000), мотор работает бесконтрольно, так как состояние входов не опрашивается в этот период. По окончании этого делэя механизм коробки оказывается в неизвестном положении, и опять может быть StateError.
Вариант алгоритма обработки ошибки: если процесс переключения завершился с ошибкой, то НЕМНОГО откручиваем мотор в обратном направлении (это можно сделать без контроля входов), а затем пытаемся довести механизм до нужного положения уже с контролем входов. И таких пару попыток. Если после этих попыток процесс до конца не завершился, тогда уже выдавать ошибку на сигнальные лампочки. Все это делается автоматически.

Второе. Откуда Вы знаете, какая кнопка в какую сторону крутит двигатель? Сейчас, может быть, Вы это и помните, но через полгода-год, 100% это забудется. Доверьте это дело контроллеру, пусть он помнит куда он крутил мотор и куда его окручивать обратно, чтобы "качнуть" вилку.
 

Acris

✩✩✩✩✩✩✩
20 Апр 2025
13
0
Согласен
Торопился не приложил пояснений. Время везде установлено завышенное, для того, чтобы на макетной плате успевать перекидывать контакты неспеша. На самом деле "качать" вилку будет по 0.5 сек.
Время на переключение будет выделено не более 2.5 сек, т.к. переключение обычно происходит примерно за 2 секунды.
А вот , что сейчас помню а потом забуду , где вперёд, где назад, это согласен.