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

baton
Offline
Зарегистрирован: 04.08.2014

приветствую сообщество.

я только делаю первые шаги в программировании, потому прошу сразу ногами не пинать за вопросы, если они покажутся глупыми.

есть потребность в устройстве, которое будет определять частоту нажатий на кнопку без фиксации. это не вся суть устройства, есть еще индикация, опрос аналогового порта с целью замера сопротивления, общение с другой микросхемой по SPI или I2C.
но пока я только начинаю осваивать МК и потому программа будет усложняться по мере обучения.

для начала нужно просто посчитать время между нажатиями на кнопку. алгоритм представляется такой: зажигаем зеленый светодиод, опрашиваем кнопку, если не была нажата, светодиод продолжает гореть, снова опрашиваем кнопку до тех пор, пока не обнаружится первое нажатие.
если нажатие зафиксировано, ждем следующего нажатия. если прошло 2 секунды, а повторного нажатия не было, зажигаем красный светодиод, если нажатие было, ждем следующего, определяем время, мигаем зеленым светодиодом с частотой нажатий (f=1/t).

собственно вопрос пока в том, как посчитать время между нажатиями? нужно задействовать таймер или функций millis/micros будет достаточно? подскажите, как лучше реализовать. быть может у кого-то есть примеры на данную тему. буду признателен за помощь.
 

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

опрос сделать по прерываниям

http://arduino.ru/Reference/AttachInterrupt

вот для примера

а время считать millis

Motto
Offline
Зарегистрирован: 05.06.2014

Борьбу с дребезгом контактов как организовали?

 

Нужно архитектуру программы изначально спроектировать.

С учетом масштабируемости проекта, чтобы ее больше не менять.

Если всё будет вертеться вокруг нажатия кнопок, тогда лучше опрос.

Ну т.е. если программа ждет нажатия кнопок, а потом отрабатывает какое либо действие.

 

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

Прерываниями вызывающимися по изменению сигнала.

Или если трудности любите преодолевать, то на прерывание от таймера и в нём кнопки опрашивать с необходимой частотой.

В прерывании по таймеру лучше опрашивать порты низкоуровнево, не используя функции digitalRead и analogRead.

Больно уж они медленные, много тактов процессора используют, сажая производительность.

 

Кстати есть стандартная библиотека для опроса кнопок по таймеру.

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

 

С таймерами вообще нужно аккуратнее.

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

baton
Offline
Зарегистрирован: 04.08.2014

jeka_tm, Motto, спасибо за ваши ответы.

для меня это пока сложно, но я попытаюсь разобраться.
Motto, если под архитектурой понимается блок-схема, то таковую я для себя накидал. но сначала хотел разобраться с блоками: мигания диодами, счет времени, работа по SPI, а потом все соединить.
устройство не ждет нажатия на кнопку, оно функционирует самостоятельно, мк должен менять режим работы устройства, если/когда на кнопку стали нажимать.
принцип работы можно сравнить с метрономом, только метроном тикает с заданной частотой, а мне нужно частоту определить и уже по ней выставить метроном. как бы обратная операция. (ну, это образно и только для примера, в реальности нет никакого метронома)
 

achest
achest аватар
Offline
Зарегистрирован: 01.10.2012

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

long lastTime =0;
long time = 0;

void Setup() {
  lastTime= millis();
   ....
}
void loop ()
{
    if ( readKey())  {
         time = millis()-lasttime;
         lasttime = millis();
    }
    if (time > 0 ) { DoSomething(time); }
}

Сорри за Код, на коленке, Он подразумевает опрос кнопки, что стоит процессорного времени. Но предпочтительнее, потому, что пока ты разберешься с прерываниями пройдет время.

Важно, что бы твоя фунция    DoSomething(time); не содержит Delay   и не задерживет  выполнение процессора.

baton
Offline
Зарегистрирован: 04.08.2014

кратко о функционировании:
1. инициализация: устройство включается (подается питание), считывается состояние перемычек (чтобы задать конфигурацию измерения времени: по 2 нажатиям, по 3 или 4), считывается значение сопротивления на аналоговом порту, по  SPI передаются команды в другую ИМС о значении сопротивления, мигаем светодиодом с рассчитанной частотой (выражение заранее известно и зависит от величины сопротивления на аналоговом порту). конец инициализации. этот кусок программы больше выполнять нет смысла.

2. Проверяем, была ли нажата кнопка.

2.1. Если кнопка не была нажата,

3. проверяем, не изменилось ли значение сопротивления:
3.1. если не изменилось, снова проверяем кнопку и сопротивление;
3.2. если изменилось, передает данные о значении сопротивления по SPI, пересчитываем частоту, мигаем светодиодом с новой частотой.

2.2. Если кнопка была нажата, запускаем счет, ждем еще 1 (2 или 3 в зависимости от конфигурации перемычек) нажатие на кнопку, считаем время между нажатиями, (усредняем, если нажатий больше чем 2), мигаем светодиодом с частотой нажатий.

да, фукцию delay хотелось бы обойти, чтобы не было проблем с обработкой прерываний.
наверное, было бы резонно сделать 2 внешних прерывания: одно по нажатию на кнопку, второе по изменению сопротивления на аналоговом порту (только как я пока не знаю), а все остальное время пусть МК спит, чтобы не было потребления.

achest
achest аватар
Offline
Зарегистрирован: 01.10.2012

1- прерывание по изменению аналого входа... Не такого не бывает. т.е. проще поставить еще один микропроцессор под это. Иначе придется городить сложную схему из внешнего АЦП и долго долго мучаться с помехами/дребезгом..

В твоем случае Я не вижу необходимости в прерываниях!  Тебе нужно избавиться от   Delay в коде, путем перевода кода на Even ориентированное прогаммирование.

Идея: У тебя есть состояние, которое помнит контроллер. В лупе в зависимости от состояния вызываются разные фунции: опрос клавиатуры, включение/выключение светодиода и тп. При этом нигде нельзя использовать Delay.  для включения/выкл светодиода используешь функцию  millis(), mircros().  расчитываешь время когда надо его выключить, и просто проверяешь,насто время или нет.

Да, это плохо с точки зрения потребления электричества, но мне кажеся важнее сделать так, что бы схема вообще заработала. А если ты в конце лупа добавишь delay(20), то контроллер 80% времени будет спать, а ты задержки в 20 миллисикунд может быть и не заметишь.

int state =0 ;
boolean keyStay = false;
void loop ()
     analog = AnalogRead(....) // вызов стоит 50..200 микросекунд..
     keyStaty = readKey(); //Вызов стоит 3..5 микросекунд.
     switch (state) {
             case 0:  // Init
             case 1: // Кнопка нажата , реагируем, засекаем время
             case2:  // мигаем 
                          doLight()
}

void doLight()
{  if ( millis()> nextTurn)
    {   DigitalWrite( On/ofF)
         nextTurn = millis()+timeDelay;
     }

}



 

afrikazil
Offline
Зарегистрирован: 21.01.2016

Получилось сделать TapTempo?