PICSim.js - матричная клавиатура

Матричная клавиатура состоит и обычных кнопок, только соединённых в определённом порядке - сгруппированных в ряды и столбцы, образуя матрицу. Такое включение позволяет опрашивать большее количество кнопок, задействовав при этом меньшее количество ног микроконтроллера - например для считывания 16 простых кнопок понадобилось бы целых два 8-битных порта ввода, тогда как матрицирование позволяет обойтись всего одним т.к. 4*4=16.

Матричная клавиатура на рисунке подключена к порту PORTB, все ноги подтянуты резисторами к напряжению питания - т.е. читаются как 1 если ничего не нажато. Алгоритм опроса матричной клавиатуры на 16 кнопок сводится к следующим шагам:

  • инициализация - четыре сканирующие ноги RB0...RB3 на выход, четыре считывающие RB4...RB7 на вход. Ничто не запрещает поменять сканирующие/считывающие выводы, только после этого нужно поменять пару строчек в приведенной ниже функции _keyboard_scan()

  • опрос - на первой итерации выставляется ноль на RB0, на RB1..RB3 единицы, если это привело к посадке подтягивающего напряжения например на входе RB5, это указывает на нажатие кнопки 3. Если пересечение не найдено, итерация продолжается - теперь уже RB1 в ноль, RB0=RB2=RB3=1 и так вплоть до RB3

config.h | liquid-crystal.c | hex | picsim.js

screenshot

Работа с символьным дисплеем описывалась в предыдущем материале. Тут размер массива на этапе компиляции определяется с помощью расписанной ранее идиомы sizeof x / sizeof x[0]. Функция считывания клавиши keyboad_get_or_0() неблокирующая - если клавиша нажата (после чего отпущена), возвращается код символа, если же ничего не нажато, возвращается 0. Неблокирующую функцию всегда можно сделать при желании блокирующей, если поместить в цикл, а вот с блокирующими функциями не всё так просто.

keyboard.c

#include <xc.h>
#include <stdint.h>

static char _keyboard_scan() {

  const char key_code[4][4] = {
    {'1','2','3','+'},
    {'4','5','6','-'},
    {'7','8','9','/'},
    {'*','0','#','$'}};

  for(uint8_t r = 0; r < sizeof key_code / sizeof *key_code; r++) {
    // set PORTB and some delay for voltage to rise / fall
    PORTB = ~(1 << r);
    __delay_ms(1);

    for(uint8_t c = 0; c < sizeof *key_code / sizeof **key_code; c++) {
      if (!(PORTB & (0x80 >> c))) {
        return key_code[r][c];
      }
    }
  }

  return 0;
}

char keyboad_get_or_0() {
  static char _last_key;

  const char key = _keyboard_scan();
  if (key) {
    _last_key = key;
  } else if (_last_key) {
    const char key = _last_key;
    _last_key = 0;
    return key;
  }
  return 0;
}

main.c

/*
  xc8 --chip=18f4620 main.c
*/

#define _XTAL_FREQ 5e4

#include <xc.h>
#include "config-4620.h"

#include "liquid-crystal.c"
#include "keyboard.c"

int main() {
  // lcd
  PORTD = TRISD = 0;
  PORTE = TRISE = 0;

  // matrix keyboard: PORTB AN8..AN12 as digital
  ADCON1bits.PCFG0 = ADCON1bits.PCFG1 =
  ADCON1bits.PCFG2 = 1;
  PORTB = TRISB = 0xF0;

  lcd_init();
  lcd_no_cursor();
  lcd_no_blink();
  lcd_print("Keyboard:");
  lcd_set_cursor(0, 1); // row 2

  while(1) {
    const char key = keyboad_get_or_0();
    if (key) {
      lcd_dat(key);
    }
  }

  return 0;
}

Алгоритм, используемый в матричной клавиатуре, прекрасно масштабируется в сторону увеличения - например 8 бит для считывания и 8 для сканирования даст 8*8=64 комбинации, это один порт ввода-вывода для 16-битных pic24 или два порта для pic18...pic10 микроконтроллеров. В теории матричная клавиатура несколько уступает по скорости сканирования простым кнопкам и расходует какую-то незначительную чать процессорного времени, но всё это с лихвой компенсируется более рациональным использованием портов ввода вывода.

Далее часы реального времени.

links

social