Учебный курс. Опрос кнопок

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

Схема

   Схема подключения кнопок традиционная – один контакт кнопки соединен с выводом микроконтроллера, другой с нулем питания. Подтягивающие резисторы внутренние. Кнопки подключены к обычным выводам микроконтроллера, не обладающим функциями внешних прерываний.

Заголовочный файл

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

void BUT_Init(void); 
  Настраивает выводы к которым подключены кнопки на вход и включает подтягивающие резисторы. Вызывается обычно в начале main`a.

void BUT_Debrief(void);
  Выполняет однократный опрос кнопок. В функции реализована антидребезговая защита – чтобы номер кнопки был занесен в буфер, она должна быть нажата в течении заданного числа циклов опроса. Номер кнопки заносится в буфер однократно, даже если кнопка долго удерживается. Эта функция вызывается в прерывании таймера.

unsigned char BUT_GetKey(void);

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

void BUT_SetKey(unsigned char key);

  Записывает в буфер значение. Используется для имитации нажатия кнопки.

Сишный файл

   Си файл содержит реализацию всех функций. Из них интерес, пожалуй, представляет только функция опроса.

   Обычная тактовая кнопка может находиться в двух состояния – нажатом и отпущенном (третье состояние – сломанное, нас сейчас не интересует). В идеальном случае кнопка переходит из одного состояния  в другое мгновенно, однако в реальности при нажатии ее контакты многократно замыкаются и размыкаются, прежде чем окажутся в установившемся замкнутом состоянии. Это явление называется дребезгом контактов, и оно присуще всем механическим переключателям.
   Микроконтроллер работает на частоте в несколько мегагерц, и пока происходит дребезг, может опросить кнопку несколько раз. Если не принять каких-то мер, то дребезг контактов будет “воспринят” микроконтроллером как многократное нажатие. Для некоторых приложений это не критично, ну, например, если кнопка управляет включением лампы фонаря. Но в большинстве случаев это явление нежелательное.
   Дребезг контактов можно устранить аппаратно, с помощью пассивного НЧ фильтра. Но  гораздо проще побороть дребезг, сделав программу микроконтроллера невосприимчивой к нему. Вариант подобного алгоритма как раз и реализован в функции BUT_Debrief()

#define THRESHOLD 20

volatile unsigned char pressedKey = 0;
unsigned char comp = 0;

void BUT_Debrief(void)
{
unsigned char key;

  //последовательный опрос выводов мк

  if (BitIsClear(PIN_BUTTON, ENTER))    
    key = KEY_ENTER;
  else if (BitIsClear(PIN_BUTTON, CANCEL))   
    key = KEY_CANCEL;
  else if (BitIsClear(PIN_BUTTON, UP))       
    key = KEY_UP;       
  else if (BitIsClear(PIN_BUTTON, DOWN))     
    key = KEY_DOWN;
  else {
    key = KEY_NULL;
  }

  //если во временной переменной что-то есть
  if (key) {
 
    //и если кнопка удерживается долго
    //записать ее номер в буфер

    if (comp == THRESHOLD) {
      comp = THRESHOLD + 10;
      pressedKey = key;
      return;
    }
    else if (comp <  (THRESHOLD+5)) comp++;
   
  }
  else comp=0;
}

   В начале функции выполняется последовательный опрос выводов. Если какая-нибудь из  кнопка нажата, во временную переменную key заносится соответствующий код. Затем проверяется значение переменной comp, чтобы выяснить как долго кнопка находится в нажатом состоянии. Пока comp не достигнет некоего порогового значения THRESHOLD, она будет инкрементироваться. Как только значение comp станет равным THRESHOLD, временная переменная key будет скопирована в кнопочный буфер pressedKey. При этом comp получит новое значение, чтобы при следующих опросах кнопок микроконтроллер не инкрементировал эту переменную, и не производил запись в буфер. Таким образом, как бы долго пользователь не удерживал кнопку, микроконтроллер зафиксирует факт ее нажатия только один раз. 
 
   Довольно часто кнопки используются для установки каких-нибудь линейно меняющихся величин. В этом случае удобно использовать такой алгоритм - короткое нажатие на кнопку медленно изменяет величину, длительное нажатие быстро. Подправим часть функции опроса и получим простой вариант реализации этого алгоритма.
 

#define THRESHOLD2 300
...
 if (key)
  {
    if (comp > THRESHOLD2)
    {
      comp = THRESHOLD2 - 40;
      pressedKey = key;
      return;
    }
    else comp++;
   
    if (comp == THRESHOLD)
    {
     pressedKey = key;
      return;
    }
  }
  else comp=0;
...

   Теперь, если кнопка удерживается больше 300 циклов опроса, микроконтроллер снова и снова будет записывать в буфер ее код. 
   На этом пока все, но тема кнопок не закрыта. Продолжение следует...

У вас недостаточно прав для комментирования.