Пинбол — бонусный счет «Все дорожки освещены» работает не так, как ожидалось.

Я пытаюсь построить (очень простой) автомат для игры в пинбол с помощью Arduino Uno. Эта часть кода определяет, когда мяч проходит 3 дорожки. Когда полоса пройдена, полоса активируется (laneState), начисляется балл (+10) и загорается светодиод.

Ожидаемый результат:

  • Когда все 3 полосы освещены, присвойте оценку проезду по полосе (+10), назначьте бонусную оценку (+100), сбросьте все состояния полосы на 0 и переведите все светодиоды в положение НИЗКИЙ.
  • Всего добавляется 110 баллов.

Фактический результат:

  • Огни последней пройденной полосы снова становятся активными (хотя я отключил их все)
  • Всего добавляется 120 баллов (вместо 110?)

Любые намеки на то, почему я вижу такое поведение, приветствуются!


Скетч и плату можно найти здесь: https://circuits.io/circuits/2739062 -poc-lane-passage-001/ Включите «Редактор кода» и включите последовательный монитор, чтобы увидеть результаты.


Дополнительная информация: Я рассматривал возможность использования прерываний, но здесь это не вариант (у Uno только 2 контакта для прерываний, мне нужно 3)

Спасибо!

РЕДАКТИРОВАТЬ: сюда также добавлен код:

// Результаты
int totalScore = 0;
const int lanePassageScore = 10; // 10 очков за прохождение полосы
const int allLaneActiveBonus = 100; // 100 баллов за активацию ВСЕХ полос

// Цифровые контакты для определения состояния кнопок
const int laneButtonPin[] = {2,3,4};
const int laneStateLedPin[] = {10,11,12};

// все три линии «неактивны» в начале игры
int laneButtonState[] = {0,0,0};
int lastLaneButtonState[] = {0,0,0};
int laneState[] = {0,0,0};


void setup() {
  Serial.begin(9600); // для отладки
}

void loop() {
  // определяем, были ли активированы полосы с 1 по 3
  detectLaneButtonPassage(0); // Полоса 1
  checkAllLanesLit();
  detectLaneButtonPassage(1); // Полоса 2
  checkAllLanesLit();
  detectLaneButtonPassage(2); // Полоса 3
  checkAllLanesLit();

  Serial.println(totalScore); // для отладки

  delay(5);
}

void detectLaneButtonPassage(int laneId){
  // Проверяем состояние кнопки
  laneButtonState[laneId] = digitalRead(laneButtonPin[laneId]);
  if(laneButtonState[laneId] != lastLaneButtonState[laneId]){
    if(laneButtonState[laneId] == HIGH){
      // Кнопка полосы переводится в положение ВКЛ, переключает состояние полосы
      if(laneState[laneId] == 0){
        // полоса еще не была активной, теперь она становится активной
        laneState[laneId] = 1;
        digitalWrite(laneStateLedPin[laneId], HIGH);
        rewardScore(lanePassageScore);
      }else{
        // полоса уже была активна, теперь она становится неактивной
        laneState[laneId] = 0;
        digitalWrite(laneStateLedPin[laneId], LOW);
        rewardScore(lanePassageScore/2);
      }
    }
  }
  lastLaneButtonState[laneId] = laneButtonState[laneId];
}

void rewardScore(int score){
  totalScore += score;
}

void checkAllLanesLit(){
  // Проверяем, активированы ли ВСЕ полосы
  if(laneState[0]==1 && laneState[1]==1 && laneState[2]==1){
    // сбрасываем все кнопки и состояния
    for(int laneId=0; laneId<=2; laneId++){
      laneButtonState[laneId] = 0;
      lastLaneButtonState[laneId] = 0;
      laneState[laneId] = 0;
      digitalWrite(laneStateLedPin[laneId], LOW); //Выключаем светодиод
    }
    rewardScore(allLaneActiveBonus);
  }
}

, 👍3


2 ответа


Лучший ответ:

1

Из функции checkAllLanesLit() необходимо удалить две строки кода:

  laneButtonState[laneId] = 0;
  lastLaneButtonState[laneId] = 0;

Объяснение:
Происходит следующее: detectLaneButtonPassage() срабатывает как обычно. Затем запускается checkAllLanesLit() и очищает флаг lastLaneButtonState.

Программа снова повторяется, но кнопка полосы все еще нажата. Однако detectLaneButtonPassage() срабатывает снова, поскольку lastLaneButtonState был сброшен и отображается в программе как нарастающий фронт. Отсюда дополнительные 10 баллов.


Кстати, следующие две строки можно сократить до одной:

if(laneButtonState[laneId] != lastLaneButtonState[laneId]){
    if(laneButtonState[laneId] == HIGH){

Приведенные выше строки кода обнаруживают нарастающий фронт. Они могут быть выражены как:

if(laneButtonState[laneId] && !lastLaneButtonState[laneId]){

Чтобы оператор if сработал, laneButtonState должен иметь высокий уровень, а lastLaneButtonState должен быть низким.

,

Я попробую это как можно скорее. Спасибо, что уже уделили время., @Cagy79

Работает идеально! Теперь имеет смысл не иметь этих строк. Я также еще не настолько опытен и эффективен в объединении двух строк кода в одну. Но я обязательно учту это на будущее! Еще раз спасибо!, @Cagy79


1

Похоже, проблема со временем.

В каком бы порядке вы ни нажимали кнопки в последнем, остается горящим (и вы получаете дополнительные 10 баллов).

Я установил точку останова:

rewardScore(allLaneActiveBonus);

...и просто подождал секунду, а затем продолжил, и ваш код работал как положено.

Поэтому я думаю, что вы устанавливаете для всех строк НИЗКИЙ уровень в checkAllLanesLit(), а затем имеете лишь небольшую задержку (5 мс) перед повторным циклом и проверкой (в этот момент кнопка все еще ВЫСОКАЯ) поскольку вы недостаточно быстро убрали с него палец) - в результате вы получаете дополнительные 10 очков и эта полоса снова освещается.

,