Матричная клавиатура 4х4 алгоритм. AVR. Учебный курс. Процедура сканирования клавиатуры. А что, если выходов с открытым стоком у нас нет

При необходимости использования в устройстве клавиатуры с большим количеством кнопок, например в кодовом замке, очень часто применяют матричную клавиатуру. Если подключить 12 кнопок обычным способом потребуется 12 выводов микроконтроллера плюс общий провод, в матрице же используется всего один порт контроллера, что способствует экономии выводов контроллера. Кнопки в такой клавиатуре подключаются к общим столбцам и к общим строкам, линии порта микроконтроллера разделяются на ввод PB7-PB4 и вывод PB3-PB0. В каждый момент времени сигнал низкого уровня (логический ноль) подается только на одну строку кнопок, на остальные должна подаваться логическая единица. Это исключит неоднозначность определения номера нажатой кнопки. Двоичные сигналы, присутствующие при этом на столбцах клавиатуры, считываются через порт ввода микроконтроллера.

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

Ниже показан пример программы в которой при нажатии определенной клавиши ее значение высвечивается на семисегментном индикаторе. Микроконтроллер Atmega8 работает от внутреннего генератора частотой 8MHz.

/*** Подключение матричной клавиатуры к микроконтроллерам AVR ***/ #include #include // Массив значений для порта вывода unsigned char key_tab = {0b11111110, 0b11111101, 0b11111011, 0b11110111}; // Функция опроса клавиатуры unsigned char scan_key(void) { unsigned char key_value = 0; unsigned char i; for(i = 0;i < 4;i++) { PORTB = key_tab[i]; // выводим лог. 0 в порт вывода _delay_us(10); switch (PINB & 0xF0) { case 0b11100000: key_value = 1 + i * 3; return (key_value); case 0b11010000: key_value = 2 + i * 3; return (key_value); case 0b10110000: key_value = 3 + i * 3; return (key_value); default: break; } } return (key_value); } int main(void) { // массив цифр для индикатора unsigned char num = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F}; DDRB |= (1 << PB3)|(1 << PB2)|(1 << PB1)|(1 << PB0); // Порт вывода DDRB &= ~(1 << PB7)|(1 << PB6)|(1 << PB5)|(1 << PB4); // Порт ввода PORTB = 0xF0; // Устанавливаем лог. 1 в порт ввода DDRD = 0xFF; // Выход на индикатор PORTD = 0x00; _delay_ms(10); while(1) { // Выводим значение нажатой кнопки на индикатор if(scan_key()==1) PORTD = num; if(scan_key()==2) PORTD = num; if(scan_key()==3) PORTD = num; if(scan_key()==4) PORTD = num; if(scan_key()==5) PORTD = num; if(scan_key()==6) PORTD = num; if(scan_key()==7) PORTD = num; if(scan_key()==8) PORTD = num; if(scan_key()==9) PORTD = num; if(scan_key()==11) PORTD = num; } }

Архив для статьи "Подключение матричной клавиатуры к микроконтроллерам AVR"
Описание: Проект AVRStudio и Proteus
Размер файла: 37.33 KB Количество загрузок: 1 905

В этой статье мне бы хотелось познакомить читателей с подключением к микроконтроллерам клавиатуры. Дело в том, что обычно большинству схем на микроконтроллерах для ввода данных требуется одна или несколько кнопок. Но, когда проекты становятся сложнее, может возникнуть потребность в применении небольшой клавиатуры. Встречаются варианты клавиатур 3х4 или 4х4, и почти всегда клавиши в них соединены по схеме матрицы. Применение матрицы необходимо из -за того, что для её подключения нужно минимальное число линий ввода - вывода. Например, для клавиатуры 4х4 необходимо, состоящей из 16 кнопок необходимо 16 линий ввода, поэтому рациональнее организовать её в виде матрицы, т.е. расположить 4 кнопки в 4 строки, использовав 8 линий ввода - вывода (один порт микроконтроллера). Самое распространённое решение подключения матрицы на один порт - это подключить строки к старшим разрядам, а столбцы к младшим. Однако, здесь возникает проблема - считывание состояния клавиатуры происходит при возникновении прерывания, но в микроконтроллере ATtiny2313 мы можем использовать два внешних прерывания (остальные выводы заняты). Эта проблема решается подключением четырёх диодов, образующих с подтягивающим резистором на входе INT0 элемент «ИЛИ».

Схема подключения клавиатуры 4х4 матричного типа к микроконтроллеру приведена на Рис.1. Она имеет разъём DB-9F, преобразователь уровней MAX3232, которые необходимы для взаимодействия UART микроконтроллера с RS-232, и работа которых была описана в предыдущей статье. Резисторы R3 - R6 защищают микроконтроллер от короткого замыкания питания на землю. На входе INT0 имеется подтягивающий резистор R7 . Четыре диода VD1 - VD4 подключены к линиям ввода- вывода клавиатуры (катодами) и к выводу INT0 (анодами). Теперь при нажатии любой кнопки, если на столбцы подать нули, на входе INT0 появится низкий уровень.

Как показано на Рис.1 клавиатура подключена к порту В микроконтроллера. Её схема представлена на Рис.2.

Предположим, что весь порт В настроен на вход, и все входа имеют подтяжку:

DDRB = &B00000000

PORTB = &B11111111

Пусть кнопки (Рис.2) каким -то образом соединены ещё и «землёй» (GND), тогда при нажатии, например, кнопки «1» на контактах порта В РВ3 и РВ4 будет низкий уровень, т.е. порт примет значение PORTB = &B11100111, которое и является кодом кнопки «1». Аналогично и в отношении остальных кнопок (код кнопки «В» - 01111011, «5» - 11011011 и т.д.). Но, поскольку кнопки не соединены с GND, необходимо ввести раздельное определение строк и столбцов с последующим суммированием и идентификацией результата с названием кнопки.

Сделаем строки РВ0 - РВ3 входами и включим их подтяжку, а столбцы РВ3 - РВ7 - выходами:

DDRB = &B11110000

PORTB = &B00001111

При нажатии кнопки определённая строка будет иметь низкий уровень. Например, при нажатии кнопки «1» порт В примет значение 00000111, которое является кодом строки. Одновременно с этим включится прерывание, при обработке которого необходимо считать данный код в переменную Stro:

Инвертируем настройки порта - сделаем столбцы РВ3 - РВ7 входами и включим их подтяжку, а строки РВ0 - РВ3 - выходами:

DDRB = &B00001111

PORTB = &B11110000

Теперь при нажатой кнопке «1» порт будет иметь значение 11100000, которое является кодом столбца. Считаем этот код в переменную Col.

Иногда мы сталкиваемся с проблемой нехватки портов на Arduino. Чаще всего это относится к моделям с небольшим количеством выводов. Для этого была придумана матричная клавиатура. Такая система работает в компьютерных клавиатурах, калькуляторах, телефонах и других устройств, в которых используется большое количество кнопок.

Для Arduino чаще всего используются такие клавиатуры:

Самыми распространенными являются 16 кнопочные клавиатуры 4x4. Принцип их работы достаточно прост, Arduino поочередно подает логическую единицу на каждый 4 столбцов, в этот момент 4 входа Arduino считывают значения, и только на один вход подается высокий уровень. Это довольно просто, если знать возможности управления портами вывода в Arduino , а так же портами входа/ввода.

Для программирования можно использовать специализированную библиотеку Keypad, но в этой статье мы не будем её использовать для большего понимания работы с матричной клавиатуры.

Подключаем клавиатуру в любые порты ввода/вывода.

На красные порты будем подавать сигналы, а с синих будем их принимать. Зачастую на синие провода подводят подтягивающие резисторы, но мы их подключим внутри микроконтроллера Arduino .

В программе будем вычислять нажатую кнопку и записывать её в Serial порт.
В данном методе есть один значительный недостаток: контроллер уже не может выполнять других задач стандартными методами. Эта проблем решается подключением матричной клавиатуры с использованием прерываний .

Int PinOut {5, 4, 3, 2}; // пины выходы int PinIn {9, 8, 7, 6}; // пины входа int val = 0; const char value { {"1", "4", "7", "*"}, {"2", "5", "8", "0" }, {"3", "6", "9", "#"}, {"A", "B", "C", "D"} }; // двойной массив, обозначающий кнопку int b = 0; // переменная, куда кладется число из массива(номер кнопки) void setup() { pinMode (2, OUTPUT); // инициализируем порты на выход (подают нули на столбцы) pinMode (3, OUTPUT); pinMode (4, OUTPUT); pinMode (5, OUTPUT); pinMode (6, INPUT); // инициализируем порты на вход с подтяжкой к плюсу (принимают нули на строках) digitalWrite(6, HIGH); pinMode (7, INPUT); digitalWrite(7, HIGH); pinMode (8, INPUT); digitalWrite(8, HIGH); pinMode (9, INPUT); digitalWrite(9, HIGH); Serial.begin(9600); // открываем Serial порт } void matrix () // создаем функцию для чтения кнопок { for (int i = 1; i <= 4; i++) // цикл, передающий 0 по всем столбцам { digitalWrite(PinOut, LOW); // если i меньше 4 , то отправляем 0 на ножку for (int j = 1; j <= 4; j++) // цикл, принимающих 0 по строкам { if (digitalRead(PinIn) == LOW) // если один из указанных портов входа равен 0, то.. { Serial.println(value); // то b равно значению из двойного массива delay(175); } } digitalWrite(PinOut, HIGH); // подаём обратно высокий уровень } } void loop() { matrix(); // используем функцию опроса матричной клавиатуры }

С использованием библиотеки считывание данных с цифровой клавиатуры упрощается.

#include const byte ROWS = 4; const byte COLS = 3; char keys = { {"1","2","3"}, {"4","5","6"}, {"7","8","9"}, {"#","0","*"} }; byte rowPins = {5, 4, 3, 2}; byte colPins = {8, 7, 6}; Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS); void setup(){ Serial.begin(9600); } void loop(){ char key = keypad.getKey(); if (key != NO_KEY){ Serial.println(key); } }

Допустим нам надо подавать команды нашему девайсу. Проще всего это делать посредством обычных кнопок, повешенных на порт. Но одно дело когда кнопок две три, и другое когда их штук двадцать. Не убивать же ради этого двадцать выводов контроллера. Решение проблемы есть — матрицирование . То есть кнопки группируются в ряды и столбцы, а полученная матрица последовательно опрашивается микроконтроллером, что позволяет резко снизить количество нужных выводов ценой усложнения алгоритма опроса.

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

Считывающий порт включается в режиме Pull-up входа, то есть вход с подтягивающими резисторами. Если контроллер это не поддерживает, то эти резисторы надо повесить снаружи.

Сканирующий порт работает в режиме выхода, он подключен к столбцам. Столбцы должны быть подтянуты резисторами к питанию. Впрочем, если используется полноценный Push-Pull то подтяжка не нужна — выход сам поднимет ногу на нужный уровень.

Работает следующим образом.

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

Теперь считываем сразу все значение из читающего порта. Если на столбце А не нажата ни одна кнопка, то в порту будут все единички. Но стоит нажать любую кнопку из столбца А, так она сразу же замкнет линию А, на этот вывод порта. В линии А у нас в данный момент 0, это обеспечивает ноль на сканирующем выводе контроллера. Поэтому и на соответствующем выводе порта будет 0
Так что, если будет нажата кнопка, например, 6, то на линии Р1 будет 0.

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

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

Увеличение разрядности
Но что делать если у нас кнопок не просто много, а очень много. Что даже матрицирование не спасает от огромного расхода линий порта. Тут приходится либо жертвовать несколько портов, либо вводить дополнительную логику. Например дешифратор с инверсным выходом .

Дешифратор , это такая микросхема, принимающая на вход двоичный код, а на выходе выдает единицу в выбранный разряд. Т.е. подали число «101» — получили «1» на выводе номер 5. Ну, а у инверсного дешифратора будет 0.

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

Если сканируется обычная клавиатура, нажимаемая человеком, то можно не заморачиваться на скорость опроса и сделать его в качестве побочного продукта, повесив на какое-нибудь левое прерывание. Достаточно чтобы клава опрашивалась хотя бы 10-20 раз в секунду. Этого уже достаточно, для комфортной работы.

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

Но микроконтроллер работает с такой скоростью, что успевает посчитать эти всплески как устойчивые состояния. Решить эту проблему можно аппаратно, с помощью RS триггера, так и программно — внеся небольшую задержку перед следующим опросом кнопки . Задержка подбирается такой, чтобы дребезг успел прекратиться к ее окончанию.

Зачастую свободных выводов микроконтроллера не хватает для подключения необходимого количества кнопок. При непосредственном соединении для n кнопок надо выделить столько же линий ввода-вывода, что не всегда возможно. Для более рационального использования линий портов можно воспользоваться матричной схемой подключения на рис.1. В данном случае матрица, подключённая к порту D, имеет размер 4*4=16 кнопок (4 строки r0…r3 и 4 столбца с0…с3). Линии PD0…PD3, являющиеся строками r0…r3, всегда настроены на ввод, и подтянуты к шине питания резисторами R (типичный номинал 4,7…10 кОм). С них и производится считывание состояния кнопок SB1-SB16. На линиях PD4…PD7 (столбцы с0…с3) поочерёдно формируется сигнал логического нуля. Первоначально низкий уровень устанавливается на столбце с0, а на всех остальных столбцах при этом Z-состояние. Теперь только при нажатии кнопок этого столбца (SB1…SB4) на линиях строк r0…r3 может возникнуть низкий логический уровень. Далее логический 0 выставляется на столбце с1 и сканируются группа кнопок SB5…SB8 и т.д. Точно такой же алгоритм опроса кнопок применяется и для любой другой матричной клавиатуры не зависимо от числа строк и столбцов. В общем случае количество кнопок n является произведением числа строк и столбцов. Так, например, матрица 2*2 (4 линии) будет содержать 4 кнопки. Но с другой стороны столько же линий ввода-вывода понадобится и для непосредственного подключения того же числа кнопок. Таким образом, экономия выводов начинает проявляться при числе кнопок, превышающем 4…6, и становится ещё более существенной с ростом их количества.

Рис.1 Матричная схема подключения кнопок

Элементы VD1…VD4, и R1…R4 не являются обязательными на схеме. Диоды служат для защиты от короткого замыкания между линиями строк и столбцов. Если, например, при нажатии на кнопку SB1 (в момент сканирования столбца c0) линия строки r0 вдруг окажется настроенной на вывод и на ней будет высокий логический уровень, то по цепи c0r0 начнет протекать недопустимо большой ток. Хотя логика программы не допускает такого режима работы, по разным причинам это все-таки может произойти. Поэтому, по крайней мере, при отладке программы диоды не будут лишними. Если емкость монтажа, приведенная к выводам PD3…PD0, не слишком велика, то сопротивления подтяжки к шине питания, вполне можно заменить внутренними “pull-up” резисторами. Для этого необходимо установить соответствующие разряды в регистре PORTD, когда линии настроены на ввод.

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

Def button = R16 ;регистр с кодом нажатой кнопки.def temp = R17 ;регистр для промежуточных операций ldi temp,high(RAMEND) ;инициализация стека out SPH,temp ldi temp,low(RAMEND) out SPL,temp . clr temp ;настраиваем линии порта D на ввод out DDRD,temp ldi temp, (1 << PD0)|(1 << PD1)|(1 << PD2)|(1 << PD3) out PORTD,temp . rcall btn_pol . ; Подпрограмма опроса матричной клавиатуры; R16 – номер нажатой кнопки на выходе из подпрограммы, ; если ни одна кнопка не нажата, то R16=0; ; если нажаты две и более кнопок, то R16=0xFF ; R17 – регистр для определения номера строки; R18 – регистр для задания номера столбца; R19 – счётчик столбцов; R20 – регистр для промежуточных операций btn_pol: clr R16 ;обнуляем регистры с кодом нажатой clr R19 ;кнопки и номером столбца ldi R18,0x0F ;очищаем регистр данных PORTD порта D out PORTD,R18 ldi R18,0x00010000 bp1: out DDRD,R18 ;настраиваем на вывод линию соответствующего nop ;столбца через регистр DDRD порта D in R17,PIND ;считываем состояние строк из регистра PIND com R17 andi R17,0x0F ;выделяем значение 4-х строк ldi R20,0 ;если ни одна кнопка в столбце не нажата, breq bp5 ;перемещаемся на следующий столбец cpi R17,0x01 ;если нажата кнопка в строке c0, ldi R20,1 ;то вычисляем её номер breq bp2 cpi R17,0x02 ;если нажата кнопка в строке c1, ldi R20,2 ;то вычисляем её номер breq bp2 cpi R17,0x04 ;если нажата кнопка в строке c2, ldi R20,3 ;то вычисляем её номер breq bp2 cpi R17,0x08 ;если нажата кнопка в строке c3, ldi R20,4 ;то вычисляем её номер brne bp3 ;если нажато более одной кнопки, завершаем опрос bp2: tst R16 ;если в предыдущих столбцах были нажаты breq bp4 bp3: ldi R16,0xFF ;кнопки, то завершаем опрос с кодом 0xFF ret bp4: push R19 ;иначе вычисляем номер кнопки, как lsl R19 ;N = 4*column + row + 1 = 4*R19 + R20 + 1 lsl R19 add R20,R19 mov R16,R20 pop R19 bp5: inc R19 lsl R18 cpi R19,4 ;повторяем цикл опроса пока не будут brne bp1 ;опрошены все 4 столбца ret

При всех преимуществах матричная схема подключения обладает и одним недостатком. С её помощью тяжело реализовать чтение комбинаций кнопок. В случае, когда на такой клавиатуре будут нажаты любые три кнопки, образующие между собой прямой угол (например, SB1,SB2,SB5), то программой опроса будет зафиксировано ложное нажатие кнопки, лежащей в свободном углу прямоугольника (в данном случае SB6). При определённом раскладе такой “фантомной” кнопкой может оказаться любая кнопка на клавиатуре.

Список радиоэлементов

Обозначение Тип Номинал Количество Примечание Магазин Мой блокнот
DD1 МК AVR 8-бит

ATmega8

1 В блокнот
VD1-VD4 Диод 4 В блокнот
R, R, R, R Резистор 4
  • Сергей Савенков

    какой то “куцый” обзор… как будто спешили куда то