Урок 22. Работа со временем в Ардуино. Проект спортивного секундомера.

Секундомер Ардуино

Рассмотрим функции работы со временем. Разработаем спортивный секундомер на базе платы Ардуино.

Предыдущий урок     Список уроков     Следующий урок

Для работы со временем в системе Ардуино существуют 4 стандартные функции:

  • delay(time);
  • delayMicroseconds(time);
  • millis();
  • micros().

 

Рассмотрим эти функции подробно.

void delay(unsigned long time)

Функция приостанавливает работу программы на время time, заданное в мс.

delay(500);  // пауза на 0,5 сек

Мы уже использовали эту функцию в предыдущих уроках. Использовали в отладочных программах. В практических программах функция применяется редко.

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

Кроме того, функция не позволяет создавать циклы с заданным временем. Например, время следующего цикла не будет точно равно 100 мс.

while(true) {
  // код программного блока
  delay(100);
}

Время цикла равно сумме: времени 100 мс, времени выполнения программного блока и времени перехода на начало цикла while(). Время программного блока может меняться в зависимости от алгоритма выполнения программы. Время перехода на начало цикла тоже не определено точно. В результате время цикла можно определить только приблизительно.

Для организации циклов с заданным временем лучше использовать прерывание по таймеру (урок 10). Надо только понимать, что такой способ обеспечивает стабильное время цикла, но оно может несколько отличаться от заданного. Например, библиотека MsTimer2 задает время в мс. Реальное время может отличаться на 1-2 мкс. В некоторых приложениях, например в часах, ошибка будет накапливаться и приведет к недопустимой погрешности.

 

void delayMicroseconds(int time)

Функция приостанавливает работу программы на время time, заданное в мкс.

delayMicroseconds(50);  // пауза на 50 мкс

Аналог функции delay(), только обеспечивает более короткие остановки программы. Вполне допустима для использования в практических программах по двум причинам.

  • Короткие отрезки времени сложно реализовать другими методами.
  • Остановка программы даже на несколько десятков микросекунд не вызовет катастрофических последствий.

 

unsigned long millis(void)

Функция возвращает время в мс, с момента запуска текущей программы. Значение времени переполняется через 1193 часов, приблизительно 50 суток.

tm = millis();  // чтение времени в tm

По сути это функция чтения системного времени Ардуино. Время считается в параллельном процессе и не зависит от алгоритмов выполнения программы, остановок, в том числе и функцией delay(), и т.п. Для измерения интервалов времени необходимо считать системное время в начале и конце интервала и выполнить разность этих значений. Ниже будет пример программы для работы с временными интервалами.

Точность работы функции millis().

Точность отсчета времени функцией millis() определяется точностью и стабильностью частоты кварцевого резонатора платы Ардуино. Даже для дешевых резонаторов погрешность частоты не превышает 30 ppm. Вместе с температурной нестабильностью, в нормальных условиях, это 50 ppm, что соответствует ± 0,00005 %. Таким образом, суммарная абсолютная ошибка системного времени Ардуино составит:

  • 0,18 сек для 1 часа;
  • 4,32 сек для суток;
  • 129,6 сек для месяца;
  • 26 минут для года.

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

unsigned long micros(void)

Функция возвращает время в мкс, с момента запуска текущей программы. Значение переполняется приблизительно через 70 минут.

tm = micros();  // чтение времени в tm

На платах Ардуино с частотой тактирования 16 мГц разрешение значения функции micros() составляет 4 мкс. Для плат с частотой 8 мГц – 8 мкс.

 

Спортивный секундомер на базе платы Ардуино.

Реализуем достаточно простой проект – секундомер. Наверное, практическое применение такого устройства ограничено. Секундомер с питанием от сети имеет право на существование  только в стационарных устройствах. В табло для спортивных соревнований, в системах для интеллектуальных игр, и т.п. Но разобравшись в программе, Вы легко сможете подкорректировать секундомер под свои задачи или создать совершенно другое устройство.

Управление секундомером осуществляется двумя кнопками:

  • ПУСК/СТОП;
  • СБРОС.

После нажатия на кнопку ПУСК/СТОП секундомер начинает отсчитывать время. Повторное нажатие на эту кнопку останавливает счет. Следующее нажатие продолжает отсчет времени с остановленного значения. Таким образом, секундомер может быть использован для отсчета ”чистого ” времени спортивных соревнований.

Для сброса значения времени необходимо нажать кнопку СБРОС.

Время отображается на четырех разрядном семисегментном индикаторе в следующих форматах.

Значение времени Разряд 3 Разряд 2 Разряд 1 Разряд 0
0 … 59 сек секунды сотые доли секунды
1 … 10 минут минуты секунды десятые доли секунды
10 … 99 минут минуты секунды
Больше 99 минут - - - -

Формат вывода данных меняется автоматически в зависимости от значения времени. Минуты, секунды и дробная часть отделяются на индикаторе децимальными точками. Нажатия на кнопки сопровождаются звуковыми сигналами.

 

Схема спортивного секундомера на базе платы Arduino UNO R3.

По уже хорошо известным нам схемам подключаем к плате Ардуино:

  • 4х разрядный семисегментный светодиодный индикатор GNQ-3641BUE;
  • две кнопки;
  • звуковой пьезоизлучатель.

Схема спортивного секундомера на Ардуино

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

Если кнопки управления секундомером физически расположены на значительном расстоянии от платы, то лучше их подключить по схеме из охранной сигнализации, урок 17. Или хотя бы соединить входы кнопок с питанием + 5 В через резисторы 1 кОм.

Но для отладки и проверки программы достаточно приведенной схемы. Я собрал ее на макетной плате.

Спортивный секундомер на Ардуино

Программа для спортивного секундомера на Ардуино.

Скетч программы можно загрузить по этой ссылке загрузить . Не забудьте установить библиотеки из предыдущих уроков: MsTimer2.h (урок 10), Led4Digits.h (урок 20) и Button.h (урок 9).

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

// спортивный секундомер
#include <MsTimer2.h>
#include <Led4Digits.h>
#include <Button.h>

#define SOUND_PIN 18     // звуковой излучатель, вывод 18 (A4)

// тип индикатора 1; выводы разрядов 5,4,3,2; выводы сегментов 6,7,8,9,10,11,12,13
Led4Digits disp(1, 5,4,3,2, 6,7,8,9,10,11,12,13);

Button buttonReset(16, 10);  // кнопка СБРОС, вывод 16 (A2)
Button buttonStartStop(17, 10);  // кнопка ПУСК/СТОП, вывод 17 (A3)

byte mode= 0; // режим, 0 - СТОП, 1 - ПУСК
unsigned long msTime=0; // время интервала, милисекунды
byte  minTime=0;  // время интервала, минуты
byte  decMinTime=0;  // время интервала, десятки минуты
unsigned long prevTime;  // предыдущее значение времени
unsigned long curentTime;  // текущее значение времени
byte soundCount=0;  // счетчик времени звука

void setup() {
  MsTimer2::set(2, timerInterrupt); // прерывания по таймеру 2 мс
  MsTimer2::start();               // разрешение прерывания
  pinMode(SOUND_PIN, OUTPUT); // вывод звукового излучателя 
}

void loop() {

//----------------------- переключение режима СТАРТ/СТОП
  if ( buttonStartStop.flagClick == true ) {
    buttonStartStop.flagClick= false; // сброс признака
    // инверсия режима
    if ( mode == 0) {
      mode= 1;                  // СТАРТ
      soundCount= 250; // звук на старт 250*2 мс
    }
    else {  // СТОП     
      mode= 0;    
      soundCount= 50; // звук на стоп 50*2 мс
    }
  }

//----------------------- кнопка СБРОС
  if ( buttonReset.flagClick == true ) {
    buttonReset.flagClick= false; // сброс признака
    msTime=0;
    minTime=0;
    decMinTime=0;
    soundCount= 50; // звук на сброс 50*2 мс
  }

//----------------------- отсчет времени
  if ( mode == 0 ) {
    // СТОП
    prevTime= millis();
  }
  else {
    // ПУСК   
    curentTime= millis(); // чтение текущего времени
    msTime += curentTime - prevTime;  // прибавление времени к милисекундам
    if ( msTime > 59999 ) { 
      // милисекунды переполнились, больше минуты
      msTime -= 60000;
      minTime ++; // +1 к единицам минут
      if ( minTime > 9 ) {
        // единицы минут переполнились
        minTime -= 10;
        decMinTime ++;  // +1 к десяткам минут
      }     
    }               
    prevTime= curentTime; // перегрузка предыдущего времени
  }

//--------------------- отображение времени 
  if ( (minTime == 0) && (decMinTime == 0)) {
    // меньше минуты
    disp.print(msTime / 10, 4, 0);  // вывод четырех разрядов милисекунд
    // точки
    disp.digit[0] &= 0x7f;  // погасить
    disp.digit[1] &= 0x7f;  // погасить
    disp.digit[2] |= 0x80;  // зажечь
    disp.digit[3] &= 0x7f;  // погасить
  }
  else if ( decMinTime == 0 ) {
    // меньше 10 минут
    disp.print(msTime / 100, 3, 0);  // вывод трех разрядов милисекунд   
    disp.tetradToSegCod(3, minTime);  // в старший разряд вывод единиц минут
    // точки
    disp.digit[0] &= 0x7f;  // погасить
    disp.digit[1] |= 0x80;  // зажечь
    disp.digit[2] &= 0x7f;  // погасить
    disp.digit[3] |= 0x80;  // зажечь    
  }
  else if ( decMinTime < 10 ) {
    // меньше 100 минут
    disp.print(msTime / 1000, 2, 0);  // вывод двух разрядов милисекунд
    disp.tetradToSegCod(3, decMinTime);  // в старший разряд вывод десятков минут
    disp.tetradToSegCod(2, minTime);  // в 3 разряд вывод единиц минут
    // точки
    disp.digit[0] &= 0x7f;  // погасить
    disp.digit[1] &= 0x7f;  // погасить
    disp.digit[2] |= 0x80;  // зажечь
    disp.digit[3] &= 0x7f;  // погасить    
  }
  else {
    // больше 100 минут
    // ----
    disp.digit[0]= 0x40;
    disp.digit[1]= 0x40;
    disp.digit[2]= 0x40;
    disp.digit[3]= 0x40;   
  }
}

//----------- обработчик прерывания 2 мс
void  timerInterrupt() {
  disp.regen(); // регенерация индикатора
  buttonReset.filterAvarage();  // сканирование кнопки, метода фильтрации по среднему
  buttonStartStop.filterAvarage();  // сканирование кнопки, метода фильтрации по среднему

  // звук
  if (soundCount != 0) {
    digitalWrite(SOUND_PIN, ! digitalRead(SOUND_PIN));
    soundCount--;
  }
}

Большая часть программы разработана по принципам, подробно описанным в предыдущих уроках.

  • Реализовано прерывание по таймеру с периодом 2 мс.
  • В обработчике прерывания вызываются:
    • метод регенерации индикатора;
    • методы сканирования сигналов кнопок;
    • блок формирования звукового сигнала.
  • По признакам нажатия кнопок происходит:
    • переключение режима (СТАРТ/СТОП);
    • сброс показаний времени.
  • В зависимости от значения времени в блоке отображения меняется формат вывода данных на индикатор.

Подробно хочется остановиться на программном блоке “отсчет времени”. Счет времени можно реализовать разными способами. Например, можно считать время в миллисекундах, а в блоке отображения делать перевод в нужный формат. Я решил, что намного проще считать время в формате:

  • миллисекунды;
  • минуты;
  • десятки минут.

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

 

Думаю, Вы без труда разберетесь в этой программе и сможете создать устройство под свои задачи, будь это спортивный секундомер, система для интеллектуальных игр ”Что? Где? Когда?” или ”Брейн ринг” и многое другое. Мы вернемся к секундомеру и работе со временем при разработке спортивных табло на базе светодиодных модулей. Надеюсь, уже этим летом.

Предыдущий урок     Список уроков     Следующий урок

0

Автор публикации

не в сети 3 дня

Эдуард

8
Комментарии: 1128Публикации: 124Регистрация: 13-12-2015

58 комментариев на «Урок 22. Работа со временем в Ардуино. Проект спортивного секундомера.»

  1. Доброго времени, подскажите пожалуйста, Как изменить программу что бы секундомер работал при удержании кнопки? А при отпускании останавливался?

    0
    • Здравствуйте. Я могу сходу ошибиться, но попробуйте изменить блок «переключение режима СТАРТ/СТОП» на простой алгоритм: если кнопка нажата, то режим ПУСК (mode=1), если кнопка отжата — СТОП (mode=0).

      if ( buttonStartStop.flagPress == true ) mode=1;
      else mode=0;

      0
  2. я не разбираюсь но помоему так как кнопка при работе всегда нажата ее же опрашивает программа — вот и вылазит погрешность — точнее не то что опрашивает а то что при каждом цикле обрабатывает условие

    0
    • Нет. Вы все правильно сделали? Там независимый программный блок, который вырабатывает единственный признак mode и счетчик звука. Все должно работать. Я подумаю. Можете проверить поставить просто mode=1; И посмотреть как идет время.

      0
  3. //———————— переключение режима СТАРТ/СТОП
    if ( buttonStartStop.flagPress == true ) {
    buttonStartStop.flagClick= false; // сброс признака
    // инверсия режима
    if ( mode == 0) {
    mode= 1; // СТАРТ

    }
    else { // СТОП
    mode= 0;
    }
    }

    //———————— кнопка СБРОС

    вот эта часть

    0
  4. ну уже измененный=)
    и вот при постоянном удерживании кнопки в два раза медленнее работает секундомер=)
    мне просто нужно управлять им с помощью реле — то есть при появлении 24вольт на определенном разъеме

    0
  5. по всякому пробывал…..не откомпилировать — ошибки, я в синтаксисе не особо понимаю…..но все равно спасибо что потратили на меня время=) буду учить матчасть

    0
  6. спасибо Вам огромное, все работает — я нарушил синтаксис программы, вот и ругалось!!!!!!!

    0
  7. Здравствуйте ещё раз) Эдуард.
    У меня просто нет слов, как Вы это делаете?
    Чтобы мне было интересней изучать МК товарищ подкинул идею сделать ему таймер для игры в шахматы (убывающее время). Я там такого нагородил чтобы вычленить разряды минут секунд прочего, а тут просто переполнение и вывод на индикацию…
    Восхищаюсь!
    Не знаю читать дальше нет.
    Чтобы совсем интерес не потерять на фоне того что всё без моего участия так хорошо придумано)

    0
  8. ДОБРЫЙ ДЕНЬ!
    КОПИРУЮ СКЕТЧ, ПРОВЕРЯЮ….
    Arduino: 1.8.2 (Windows 7), Плата:»Arduino Nano, ATmega328″

    fatal error: MsTimer2.h: No such file or directory

    compilation terminated.

    exit status 1
    Ошибка компиляции для платы Arduino Nano.

    КАК ЖИТЬ ДАЛЬШЕ?

    0
    • Здравствуйте!
      Проверьте, что установлена библиотека MsTimer2.h.
      Убедитесь, что проект не сохранен в папке названной русскими буквами.

      0
  9. Здравствуйте, собрал секундомер по вашему уроку, спасибо вам огромное за ваши уроки. Тестировал на arduinoUNO все работало, а после решил перенести на arduinoMEGA 2560. И перестали работать кнопки. Борюсь, но никак не могу решить проблему. Плата рабочая, скетчи заливаются, кнопки через digitalRead функционируют. Но через библиотеку Button.h действий не выполняют. Подключал к ним светодиод кнопку замыкается никаких проблем. перепробовал все пины и ничего. Может вы сможете мне помочь понять суть проблемы и найти решение?

    0
    • Здравствуйте!
      На этой плате не работает библиотека MsTimer2. Надо использовать другую. Посмотрите в комментариях к уроку 10.

      0
  10. На платах Ардуино с частотой тактирования 16 мГц разрешение значения функции millis() составляет 4 мкс. Для плат с частотой 8 мГц – 8 мкс.
    Может это о micros()?

    0
  11. Здравствуйте. А возможно ли применение 6 секционного индикатора? 00.00.00 (минуты, секунды, сотые).
    Еще несколько нюансов — После нажатия кнопки «старт» загорается красная лампочка и начинается обратный отсчет 5,4,3,2,1 и с нолем тухнет красная, загорается зеленая лампочка и стартует секундомер. Окончание работы секундомера по пересечению ик-створа. Время на индикаторе висит до нажатия на кнопку «сброс».
    Такое возможно? Спасибо

    0
    • Здравствуйте!
      Все возможно. Но в данном уроке используется библиотека, которая поддерживает только 4 разряда LED индикатора.

      0
  12. Скетч работает, но если быстро тыкать кнопку СТАРТ/СТОП, зуммер начинает пищать и не останавливается пока несколько раз не потыкаешь все кнопки.

    0
  13. Если не трудно, скажите пожалуйста, что нужно изменить с скетче для индикаторов с общим катодом?

    0
    • Здравствуйте!
      При описании объекта Led4Digits первым аргументом указать тип индикатора 2.
      Led4Digits disp(2, 5,4,3,2, 6,7,8,9,10,11,12,13);
      В уроке 20 описаны варианты индикаторов.

      0
  14. Спасибо! Из всего, что я нашел в инете, Ваш прибор единственный, который нормально работает, а особенно удобно то, что всего при четырех разрядах обеспечивается такой широкий диапазон измерения за счет масштабирования младших разрядов.

    0
  15. Эдуард! Скажите пожалуйста, точность счета зависит только от кварца? Наблюдается не сильное, но заметное отставание секундомера от реального.

    0
    • В принципе да. А погрешность укладывается в пределы, которые я указываю в статье?
      Можете попробовать заменить библиотеку прерывания по таймеру MsTimer2 на библиотеку TimerOne.h. О ней написано в уроке 29. В этой библиотеке период прерывания задается с дискретностью 1 мкс и вы можете подобрать время под ваш кварц с высокой точностью.
      Можете открыть тему на форуме сайта, я помогу с программой.

      0
  16. Отставание 5 с. за 15 мин.
    Спасибо за направление действий. Буду разбираться.

    0
  17. Огромное Спасибо, но есть одно НО. Хочу деткам для бассейна сделать большой секундомер, но там нужны ещё и минуты… Должно быть 6 цифр (сегментов). Подскажите пожалуйста, что для этого нужно.

    0
    • Здравствуйте!
      Устройство переключается на индикацию минут при времени более минуты. При этом показывает десятые доли секунд. Неужели такой точности недостаточно?
      Если нет, то надо разрабатывать новое устройство. Библиотека Led4Digits поддерживает только 4 индикатора. Вам, наверное, надо использовать индикаторы больших размеров. Прежде всего решайте, какие индикаторы вы будете использовать, как их подключать к Ардуино.

      0
      • Да, индикаторы должны быть больших размеров и их должно быть 6 штук. Первые два — минуты, вторые два — секунды, последние два — сотые… Как такое реализовать???
        P.S. Индикаторы сам спаяю как надо.

        0
  18. Здравствуйте!
    Спасибо за полезный урок.
    А как изменить скетч, чтобы использовать светодиодный индикатор на на TM1637?

    0
  19. Здравствуйте. У меня проблема:когда нажимаю стоп, то на экране появляются все одинаковые цифры и на каждом сегменте горит точка. Подскажите пожалуйста в чем может быть проблема.

    0
    • Здравствуйте!
      Ошибка только когда удерживаете кнопку нажатой?
      Может, тогда аппаратное замыкание вывода для кнопки. Проверьте вывод кнопки. Попробуйте подключите кнопку к другому выводу. Только в программе не забудьте изменить.

      0
      • Нет, не только. После того как отпустил кнопку всё происходит также: цифры одинаковые, все точки горят

        0
          • Здравствуйте. На табло, после остановки секундомера отображаются неправильно цифры (тоесть во всех 4х сегментах грамотно не отображается число (не хватает одной или несколько рисок)). Пересмотрел схему и нашел несколько недочетов: GND подсоединена была не на том выводе; AREF не был подключен. Поменял в программе входы и на схеме, но все оказалось без изменений. Скажите, пожалуйста от чего вы запитывали AREF?

            0
          • AREF здесь совсем не причем. Программа не работает с АЦП. Я почти уверен, что у вас аппаратная ошибка — замыкание выводов управления индикаторами. Просмотрите и прозвоните все связи. Запустите мою программу из урока 20, которая отображают увеличивающееся число. Таким образом проверьте работу индикатора.

            0
  20. Здравствуйте, нашел ошибку: нужно было ИЗМЕНИТЬ ИНДИКАТОР в скетче и выбрать значения С ОБЩИМ КАТОДОМ, после этого все грамотно заработало. Благодарю Вас за помощь и отзывчивость

    0
  21. ДОБРЫЙ ДЕНЬ!!! Очень полезный Ваш урок, спасибо огромное за полезную информацию и грамотный урок. Подскажите пожалуйста, как сделать в вашем проекте, чтобы одна кнопка работала на Старт, а вторая на стоп?

    0
    • Здравствуйте!
      Откройте тему на форуме сайта. Например, в разделе «Проекты Ардуино». Напишите, что нужен аналог секундомера из урока 22, только c отдельными кнопками. Если можно, напишите, с какой целью будете использовать. Я подкорректирую скетч. В комментариях неудобно общаться, неудобно приводить скетчи.

      0
  22. Огромное спасибо за урок.
    Простым языком, наглядно, по существу и доступно!

    0
  23. Доброго времени суток!
    Не получается подогнать этот скетч под дисплей ТМ1637

    Как правильно написать программу в моём случае?

    Общая ситуёвина такова — необходимо сделать секундомер так, чтобы он работал только лишь при зажатой кнопке, а при разжимании отчет времени становился на паузу.

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

    0
    • Здравствуйте!
      В комментариях к уроку я рассказывал, как сделать такой алгоритм.
      Кстати, интересно, а для чего такой способ управления?

      0
      • Такой способ управления необходим для создания игрового устройства для ролевых игр.

        0
      • Такой способ управления необходим для создания игрового устройства для ролевых игр.
        Как ни крути , не могу понять как вывести на этот дисплей счёт(

        0
        • Есть библиотека для этого дисплея TM1637.h.

          disp.displayInt(value); // вывести 1-4 значное число
          disp.displayIntZero(value); // вывести 1-4 значное число с нулями СЛЕВА

          0
  24. Здравствуйте.
    В принципе, интересный вариант, но довольно ресурсоемкий, в смысле цифровых портов ардуинки.
    А чем плох вариант, когда программы-обработчики нажатия на кнопки вешать на irq (вх 2 и 3), а в качестве экрана использовать I2C ЖК дисплей 16×2 ?
    Ведь важно точно засечь время, то есть моменты старта и финиша, а выводить на табло можно «лениво», особенно не парясь по поводу пауз между регенерациями дисплея.

    0
  25. Здравствуйте. Подскажите включение b dsrk.xtybt нагрузки по времени на часах ds1302.

    0

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *