Мой способ обработки нажатия кнопок (2 вывода контроллера, сколько угодно кнопок)

Andrew-6676
Offline
Зарегистрирован: 05.01.2015

Покритикуйте мой способ обработки нажатия кнопок по прерыванию. Может прерывание тут вообще лишнее? Или как-то можно оптимизировать? Не реализована обработка одновременного нажатия двух и более кнопок.

Задача вообще стоит такая: выполнить действие по быстрому нажатию кнопки (менее 500 ms) или по длительному нажатию кнопки (более 500 ms)

Подключение: каждая кнопка через свой резистор к любому аналоговому входу и одновременно к прерыванию INT0 или INT1 (см. рисунок). В итоге занято всего два выхода. В программе по прерыванию считываем состояние аналогового входа, тем самым проверяем, какая кнопка нажата.

Алгоритм.

Прерывание настраивается по переднему фронту. При нажатии на кнопку срабатывает пррерывание, в обработчике запоминаем время нажатия, переключаем прерывание на срабатывание по заднему фронту (отпускание кнопки).  При отпускании кнопки считаем время, которое она была зажата, переключаем прерывание в срабатывание по переднему фронту.  Устанавливаем нужные переменные, которые проверяются в основном цикле и в зависимости от результата запускается нужная функция. Ниже приведён пример кода. 

Время нажатия я считал в основном цикле, чтобы при достижении нужной длительности сразу же выполнить действие (500мс). Но можно время считать и в прерывании и, проверив, как долго была нажата кнопка при отпускании, выполнить нужное действие (в этом случае время может быть какое угодно, всё зависит от того, столько я буду держать кнопку нажатой). Так же исключает дребезг контактов.

1. Обработка прерывания

     // настройка  INT1
  EICRA |= (1 << ISC11) | (1 << ISC10);     // настраиваем на срабатывание INT1 по переднему фронту
  EIMSK |= (1 << INT1);                     // разрешаем внешнее прерывание INT1
interrupts();                                         // глобально разрешаем прерывания

/*-------  I N T  1 - нажатие кнопок. -----------------------------------*/
ISR(INT1_vect) {
  if (btn_down_mills == 0) {              //  если 0 - кнопка до этого не была нажата, а это момент нажатия кнопки
    btn_down_mills = millis();            // запоминаем время, когда её нажали
    EICRA &= ~(1 << ISC10);               // переключаем прерывание по заднему фронту
    btn_code = analogRead(19);            // запоминаем какую кнопку нажали
    digitalWrite(Led_Pin, HIGH);          // включаем светодиод
  } else {  // если не 0 - значит это момент отпускания кнопки
    int btn_pressed_time = millis() - btn_down_mills;   // вычисляем как долго была нажата кнопка
    if (50<btn_pressed_time && btn_pressed_time<490) {  // если кнопка была зажата более 50ms, но менее 500ms, то обрабатываем нажатие
       button_short_press = true;
    } 
    btn_down_mills = 0;      // обнуляем длительность нажатия кнопки
    EICRA |= (1 << ISC10);   // переключаем прерывание по переднему фронту

  }
}

2. Основной цикл (loop)

     // если было короткое нажатие на кнопку
  if (button_short_press) {
    button_short_press = false;
    press_button(btn_code, SHORT_PRESS);        // запускаем обработчик нажатой кнопки
  }
      // проверяем долгое нажатие кнопки
  if (btn_down_mills > 1) {   // если кнопка нажата
    int btn_pressed_time = millis()-btn_down_mills;     // считаем время удержания кнопки нажатой
    if (btn_pressed_time > 500) {               // Если кнопка зажата уже более 500ms
         btn_down_mills = 0;                             // обнуляем переменную - бльше кнопка не нажата
        digitalWrite(Led_Pin, LOW);                   // гасим светодиод
         EICRA |= (1 << ISC10);                              // переключаем прерывание по переднему фронту
        button_short_press = false;
        press_button(btn_code, LONG_PRESS);             // запускаем обработчик нажатой кнопки
    }
  }

 

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Почему вы считаете , что прерывание будет четко фиксировать нажатие кнопки ? У вас же получается не логический уровень, а непонятное состояние входа. 

Gippopotam
Gippopotam аватар
Offline
Зарегистрирован: 12.09.2014

brokly пишет:

Почему вы считаете , что прерывание будет четко фиксировать нажатие кнопки ? У вас же получается не логический уровень, а непонятное состояние входа. 

Поясните - почему нет?

 

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Вроде пояснил уже. Ладно давайте так. По даташиту прерывания осуществляется изменением логического уровня. Логический уровень это 0 или 1 которым соответствуют определенные диапазоны вольтажа, в даташите написано конкретнее, мне леньтуда лезть. Все что не попадает в диапазон ноля или единицы является неопределенным состоянием, на которое процессор не обязан реагировать. 

Например, по схеме нажатие средней кнопки сформирует на ноге int1 уровень 3.3333 вольта. Как вы считаете это 0 или 1 ? 

Если уж очень хочется работать такой схемой, то оторвите землю с r7 .  На оторванную ногу переключите int1. Оторвите питание с ног резисторов, как раз туда воткните землю. При нажатии любой кнопки на int 1 сформируется 0. Сработает прерывание. Переключите ногу int1 на выход и выдайте на нее 1. Считайте аналоговое значение с adc5. Полученное значение будет соответствовать нажатой кнопке. Верните все в прежнее положение. Ожидайте отпускания кнопки . Итд

Andrew-6676
Offline
Зарегистрирован: 05.01.2015

Свой косяк понял. Буду дорабатывать. brokly, спасибо!

Andrew-6676
Offline
Зарегистрирован: 05.01.2015

Добавил транзистор. Вольтметр в протеусе показывает 5 вольт при отпущенной кнопке на входе прерывания. При нажатой кнопке 0 вольт. Поменялся фронт прерывания: при нажатии кнопки - по заднему, при отпускании - по переднему.

p.s. в транзисторах я почти ноль.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

С моей точки зрения это лишнее усложнение. Не люблю дискретных элементов в  цифровых схемах. Думаю r4 следует уменьшить, мегаом тут явный перебор. Будет нестабильно работать, возбуждаться и прочее. Да и вот еще транзистор - аналоговый элемент. У вас в настройках шпрота наверняка эмуляция установлена цифровая. Если измените на аналоговую получите большее приближение к жизни.

R2 можно убрать и включить подтяжку на ноге int1