Таймер реакции с использованием миллис
Я написал этот код для игры с таймером реакции. Время реакции должно отображаться на последовательном мониторе в миллисекундах как можно точнее. Время реакции отображается неправильно. Я включаю свою процедуру настройки таймера и увеличиваю миллисекунды.
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;
}
@MochaJ, 👍1
2 ответа
Ваш 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()
будет
самый простой способ добиться желаемого.
Вы слишком много думаете об этом:
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
будет содержать количество миллисекунд, в течение которых пользователь нажал кнопку. Однако обратите внимание, что пользователь может схитрить и просто постоянно нажимать кнопку, ожидая, пока светодиод изменит состояние. Вы можете добавить логику, чтобы гарантировать, что кнопка не будет нажата непосредственно перед изменением состояния светодиода, но я оставлю это на ваше усмотрение.
- Как справиться с rollover millis()?
- Использование millis() и micros() внутри процедуры прерывания
- ардуино - миллисекунды ()
- Кнопка с таймером переключения и функцией сброса времени + светодиод обратной связи
- Использовать timer0, не влияя на millis() и micros().
- Торговый автомат Arduino для мониторинга ввода монет в слот во время ожидания ввода пользователя
- Влияет ли `millis()` на длинные ISR?
- nodeMCU — Millis() — Простой счетчик — Как долго горит светодиод?
Спасибо. Я попробую millis()., @MochaJ