Таймер реакции с использованием миллис

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

TCCR1A=0; 
TCCR1B=0; 
timer1_counter=65536; 
TCNT1=timer1_counter; 
TCCR1B |=(1<<CS12); 

TIMSK1 |=(1<<TOIE1); 
interrupts();  
}

ISR(TIMER1_OVF_vect) 
{
  TCNT1=timer1_counter; 
  int_flag=1;
 }



  digitalWrite(LED,LOW); 
  randomSeed(analogRead(A1)); 
  NumSecs=random(1000,7000);  
  delay(NumSecs);  
  digitalWrite(LED,HIGH);
  while(digitalRead(PUSHBUTTON) == HIGH)  
  {
  }
  if (int_flag==1){
  millisecs++;
  int_flag=0;
  }

, 👍1


2 ответа


0

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

while(digitalRead(PUSHBUTTON) == HIGH)
{
}

вы ждете, пока пользователь нажмет кнопку. Программа делает больше ничего во время ожидания. Это событие не учитывает время. Здесь:

if (int_flag==1){
    millisecs++;
    int_flag=0;
}

вы увеличиваете один раз счетчик миллисекунд после ожидания.

Очевидное решение — считать миллисекунды во время ожидания нажатие кнопки:

millisecs = 0;  // не забудьте сбросить секундомер!
while (digitalRead(PUSHBUTTON) == HIGH) {  // кнопка не нажата
    if (int_flag) {
        millisecs++;
        int_flag = false;
    }
}

(примечание: int_flag должен быть bool).

С этой программой по-прежнему существует множество проблем. Прежде всего, следует убедитесь, что пользователь не нажал кнопку до того, как загорелся светодиод.

Тогда период прерывания не составляет одну миллисекунду, потому что прерывание требуется некоторое время для обработки процессором и прохождения его пролог. К тому времени, как вы это сделаете TCNT1=timer1_counter;, вы как минимум опоздание на несколько десятков циклов. Вместо этого вам следует настроить таймер на сохраните правильный период самостоятельно, обычно используя режим CTC.

Кроме того, нет смысла позволять основной программе подсчитывать миллисекунды, когда ISR мог это сделать. Например

ISR(TIMER1_OVF_vect)
{
    millisecs++;
}

// Избегаем состояния гонки при чтении миллисекунд.
static inline unsigned int getMillisecs()
{
    noInterrupts();
    int millisecsCopy = millisecs;
    interrupts();
    return millisecsCopy;
}

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

Наконец, это может быть хорошим опытом работы с прерывания, но в конечном итоге просто использование millis() или micros() будет самый простой способ добиться желаемого.

,

Спасибо. Я попробую millis()., @MochaJ


1

Вы слишком много думаете об этом:

  digitalWrite(LED,LOW); 
  randomSeed(analogRead(A1)); 
  NumSecs=random(1000,7000);  
  delay(NumSecs);  
  digitalWrite(LED,HIGH);
  unsigned int startTime = millis();
  while(digitalRead(PUSHBUTTON) == HIGH)  
  {
  }
  unsigned int elapsed = millis() - startTime;

После этого кода elapsed будет содержать количество миллисекунд, в течение которых пользователь нажал кнопку. Однако обратите внимание, что пользователь может схитрить и просто постоянно нажимать кнопку, ожидая, пока светодиод изменит состояние. Вы можете добавить логику, чтобы гарантировать, что кнопка не будет нажата непосредственно перед изменением состояния светодиода, но я оставлю это на ваше усмотрение.

,