Схема
Резисторы R2 – R4, R8 – R11 предназначены для ограничения входного/выходного тока в случае неправильной настройки портов или одновременного нажатия нескольких кнопок. Выводы PD0(RXD), PD1(TXD) подключены к преобразователю UART-RS232, который на схеме не отображен. Обмен по USART`у используется для отладки программы.
Алгоритм опроса матричной клавиатуры
Строки клавиатуры подключены к выводам PD4, PD5, PD6, PD7. Они настроены на выход и в начальном состоянии на этих выводах напряжение логического нуля. Столбцы подключены к выводам PC0, PC1, PC2. Они настроены на вход, внутренние подтягивающие резисторы отключены и эти линии “придавлены к нулю питания” с помощью внешних резисторов номиналом в 10 КОм.
Процедура сканирования клавиатуры выглядит следующим образом. Выставляем 1 на выводе PD4 и проверяем состояние выводов PC0, PC1, PC2 (то есть считываем содержимое регистра PINC). Если на каком-то из выводов установлена 1, значит, на клавиатуре в данный момент нажата кнопка, подключенная к первой строке. Сохраняем биты PD4, PD5, PD6, PD7 и PC0, PC1, PC2 в одной переменной – по этому коды мы будем определять номер нажатой кнопки. Если ни одна из кнопок не нажата, продолжаем процедуру сканирования.
Сбрасываем 1 на выводе PD4 и устанавливаем 1 на выводе PD5. Снова проверяем состояние выводов PC0, PC1, PC2, и в случае нажатия кнопки сохраняем биты PD4, PD5, PD6, PD7 и PC0, PC1, PC2 в переменной.
Повторяем описанную последовательность для двух оставшихся строк. Переключения кнопок клавиатуры сопровождаются дребезгом контактов, который микроконтроллер может “воспринимать” как многократные нажатия. В приложениях, использующих клавиатуры, это нежелательное явление, поэтому в программе опроса клавиатуры обязательно должна быть какая-нибудь защита. Обычно делают так - факт нажатия кнопки регистрируется, если она удерживается в течение нескольких циклов опроса.
Код, получаемый в процессе сканирования клавиатуры, часто требуется преобразовать в символьное значение номера/буквы кнопки (например, для передачи по USART`у). Для этого можно создать таблицу перекодировки - двумерный массив. В первом столбце таблицы будут храниться коды кнопок, а во втором соответствующие символьные значения. Методом последовательного перебора в таблице можно находить нужное значение.
Алгоритм опроса матричной клавиатуры можно реализовать в виде конечного автомата (State Machine) – функции, которая в зависимости от своего состояния (значения определенной переменной) и входного воздействия, выполняет разную работу. На рисунке ниже представлена диаграмма подобного автомат.
Код, получаемый в процессе сканирования клавиатуры, часто требуется преобразовать в символьное значение номера/буквы кнопки (например, для передачи по USART`у). Для этого можно создать таблицу перекодировки - двумерный массив. В первом столбце таблицы будут храниться коды кнопок, а во втором соответствующие символьные значения. Методом последовательного перебора в таблице можно находить нужное значение.
Алгоритм опроса матричной клавиатуры можно реализовать в виде конечного автомата (State Machine) – функции, которая в зависимости от своего состояния (значения определенной переменной) и входного воздействия, выполняет разную работу. На рисунке ниже представлена диаграмма подобного автомат.
Начальное состояние автомата - 0. В этом состоянии автомат находится, пока не будет нажата какая-нибудь кнопка. Когда зафиксировано нажатие на кнопку, запускается функция сканирования клавиатуры ScanKey(), запоминается код нажатой кнопки и автомат переходит в состояние 1.
В состоянии 1 автомат проверяет, нажата ли в данный момент та же кнопка, что и в состоянии 0 или нет. Если коды кнопок не совпадают, автомат возвращается в состояние 0, если совпадают, запускается функция FindKey(), которая находит символьное значение номера кнопки и устанавливает флаги, сигнализирующие системе о нажатой кнопке. По завершению функции автомат переходит в состояние 2.
Пока удерживается одна и та же кнопка, автомат находится в состоянии 2. Если произошли какие-то изменения, он переходит в состояние 3.
Если изменения было случайным, автомат возвращается в состояние 2, а если нет, переходит в начальное состояние, чтобы снова запустить функцию сканирования клавиатуры.
Программная реализация автомата
Представленную диаграмму конечного автомата легко преобразовать в программный код.//хранит текущее состояние автомата
unsigned char keyState;
//прототипы функций используемых автоматом
unsigned char AnyKey(void);
unsigned char SameKey(void);
void ScanKey(void);
unsigned char FindKey(void);
void ClearKey(void);
void ScanKeyboard(void)
{
switch (keyState){
case 0:
if (AnyKey()) {
ScanKey();
keyState = 1;
}
break;
case 1:
if (SameKey()) {
FindKey();
keyState = 2;
}
else keyState = 0;
break;
case 2:
if (SameKey()){}
else keyState = 3;
break;
case 3:
if (SameKey()) {
keyState = 2;
}
else {
ClearKey();
keyState = 0;
}
break;
default:
break;
}
}
Довольно наглядная запись. Теперь все, что нам остается, это написать/разобрать реализации недостающих функций. Чтобы код программы был максимально понятным, давайте обойдемся без макроопределений. Итак, по порядку.
unsigned char AnyKey(void)
{
PORTD |= 0xf0;
return (PINC & 0x07);
}
Устанавливаем на выводах PD7 – PD4 единицы, возвращаем состояния выводов PC2 – PC0. Если какая-нибудь из кнопок клавиатуры в этот момент нажата, функция вернет значение отличное от нуля, то есть true.
//хранит код нажатой кнопкиunsigned char keyCode;
void ScanKey(void)
{
unsigned char activeRow = (1<<4);
while (activeRow) {
PORTD = (PORTD & 0x0f)|activeRow;
if (PINC & 0x07) {
keyCode = (PINC & 0x07);
keyCode |= (PORTD & 0xf0);
}
activeRow <<= 1;
}
}
Устанавливаем в 1 четвертый бит переменной activeRow (активизируем первую строку). Обнулив биты PD7 – PD4, записываем переменную в PORTD. Если какой-нибудь из трех младших разрядов регистра PINC установлен в единицу, значит в данный момент нажата кнопка, относящаяся к первой строке. Сохраняем биты PD7 – PD4 и PC2 - PC0 в переменную keyCode. Сдвигаем влево значение переменной activeRow на один разряд и повторяем цикл еще три раза.
unsigned char SameKey(void)
{
PORTD = (PORTD & 0x0f) | ( keyCode & 0xf0);
return ((PINC & keyCode) & 0x07);
}
Функция проверяет, совпадает ли код нажатой в данный момент кнопки с кодом, полученным в предыдущем цикле опроса. Для этого на нужной строке устанавливается 1 – в PORTD записываются старшие 4 бита keyCode. Затем считывается регистр PINC, на него накладывается маска в виде переменной keyCode и выделяются 3 младших разряда. Полученное значение возвращается. Если коды кнопок совпадут, значение будет отлично от нуля, то есть true.
//хранит символьное значение нажатой кнопки
unsigned char keyValue;
//флаговая переменная - устанавливается, если кнопка удерживается
unsigned char keyDown;
//флаговая переменная - устанавливается, когда нажата новая кнопка
unsigned char keyNew;
//таблица перекодировки
__flash unsigned char keyTable[][2] = {
{ 0x11, '1'},
{ 0x12, '2'},
{ 0x14, '3'},
{ 0x21, '4'},
{ 0x22, '5'},
{ 0x24, '6'},
{ 0x41, '7'},
{ 0x42, '8'},
{ 0x44, '9'},
{ 0x81, '*'},
{ 0x82, '0'},
{ 0x84, '#'}
};
unsigned char FindKey(void)
{
unsigned char index;
for (index = 0; index < 12; index++) {
if (keyTable [index][0] == keyCode) {
keyValue = keyTable [index][1];
keyDown = 1;
keyNew = 1;
return 1;
}
}
return 0;
}
Эта функция ищет по таблице перекодировки номер кнопки, соответствующий коду, полученному на этапе сканирования клавиатуры. Если номер найден, он записывается в переменную keyValue и устанавливаются флаги keyDown и keyNew. Первый флаг сигнализирует о том, что кнопка удерживается нажатой, второй, что автомат зафиксировал нажатие новой кнопки.
void ClearKey(void)
{
keyDown = 0;
}
Эта функция сбрасывает флаговую переменную keyDown.
Для полного счастья нам не хватает функции инициализации и функции возвращающей содержимое переменной keyValue. Здесь мы их уже не будем разбирать, потому что ничего интересного там нет.
Полный текст программного модуля
keyboard.h
keyboard.c
Файлы
Тестовые проекты – микроконтроллер atmega8 опрашивает клавиатуру и посылает номер нажатой кнопки по USART`у в терминал.
IAR. Опрос матричной клавиатуры
WINAVR. Опрос клавиатуры
CodeVision. Опрос клавиатуры
Proteus – проект для отладки
Драйвер для матричной клавиатуры
Comments
Насущный вопрос, а то моих девайсов вечно управление какое-то не человеческое :oops:
давно ждал!
Очень интересно!
Не планируете сделать описание модной сейчас QTouch?
В чем недостатки использования резисторов на 330 Ом вместо диодов?
а если хочется комбинации использовать...
но статья полезная, 10х
может протеус у меня кривой какой-то.. не могу никак с кнопками научится в нем что-то делать, думал - тут же готовый работающий проект :(
шахматы из тестовых программ работают, мои тесты работают с выводом, с uart-ом..
грешу на кривость протеуса (мож с взломом както связанную).. отладка в avr studio показывает, что все нормально - но хотелось же упростить жизнь себе :)
то же самое в свою схему - не зажигает
попробовал на другой порт - зажигает
может где-то в инициализации порт используется как-то по другому..
Я только дополняю изложеное Вами.
У меня 4х4 клавиатура - все работает, но надо убрать эти резисторы.
Расскажи про теорию конечных автоматов(StateMachine).
Что это такое?
Как использовать в целом и приминительно
к контроллерам и написанию программ для МК?
Как строятся диаграммы?
Возможен ли вариант без применения подтягивающих резисторов?
Code:
(*(&PORTX_ROW-2))
В хедер файле задается название порта, например PORTB, а адреса остальных регистров PINB и DDRB вычисляются. Сделано для удобства.
Если ставить диоды, то анодом к МК, а катодом к клавиатуре?
Как добавить срабатывание буззера по нажатию клавиши?
Quote: Реализую в материале по клавиатуре 4 на 4. Думаю в этом месяце сгенерю.
может кто-нибудь такое в ассемблере изобразить??
задание: сделать домофон. вместо * и # сброс и вызов. должна быть светодиодная индикация нажатых цифр.
1)таймер TA0 работает в режиме NORMAL и считает в диапазоне от 0x83 до 0х00 (125 тактов). Если его переключить в режим CTC, то тогда в прерывании не надо будет перезаписывать счетчик TCNT0. 125 тактов по 256/16 = 16 мкс равно 2 мс, в комментарии в программе написано 4 мс. Я бы поставил минимум 20 мс.
2)Алгоритм цифрового автомата расчитан на "убирание дребезга" по одной кнопке. Именно по той, которую Вы затем будете удерживать. Реально есть такие вещи, как "слегка промазал". В этом случае появляется дребезг не только на той кнопке, которую Вы жмете, но и на той, которую Вы зацепили. Действие ScanKey стоит в цикле устранения дребезга. И оно первый раз может сработать по кнопке "которую промазали". Если через 2 мс "лишний" сигнал не снялся, нажатие воспринимается как правильное.
3)в комментарии к строке
Code:
return ((PINC & KeyCode) & 0x07)
я бы написал: "Если нажата еще та кнопка, по которой сформирован код". Потому, что тут никакого сравнения не производится. Это место в программе "перл", и я ставлю ему много плюсов!
4)Ну уж очень много глобальных переменных хранят внутреннее состояние программы - keyState, keyCode, keyDown, keyNew, KeyValue.
keyDown не используется, об ней не будем.
keyNew равна единице, когда keyValue не равна нулю. Т.е. keyValue прекрасно заменяет первую переменную, если в функции GetKey ее обнулять.
Я бы три оставшиеся переменные объявил как статические локальные для функции ScanKeyboard. Тогда они, как и прежде, будут доступны для "всех заинтересованны х", но при этом будут защищены от внешних воздействий.
RSS feed for comments to this post