Обычно клавиатуру для микроконтроллеров организуют в виде матрицы с опросом. Однако в ряде случаев такое решение может быть нецелесообразно, т.к. занимает значительное число портов ввода/вывода и, кроме того, работы "по опросу" (как обычно программно реализуется подобное решение) вообще желательно избегать, максимально используя аппаратные прерывания. Поэтому может быть полезно организовать интерфейс со стандартной клавиатурой, тем более, что при всего двух занятых линиях (а по сути — одной, т.к. линия прерываний как правило идет помимо портов ввода/вывода контроллера) можно получить практически любое потребное число клавиш. А поскольку к интерфейсу PS/2 без проблем подключается и клавиатура USB, то выбор подходящей клавиатуры существенно упрощается. Естественно, для полноценного обмена (выдачи контроллером команд на клавиатуру) следует задействовать еще пару линий, но для начала вполне достаточно только входных.
Принцип подключения не зависит от типа контроллера, главное, чтобы у него была свободная линия аппаратных прерываний. От типа контроллера (и, естественно, его системы команд) зависит программная реализация прерывания. Моя конструкция описана для контроллера Am188ES.
Схема соединений для подключения клавиатуры приведена на рис. 1.
Для понимания принципа обработки прерываний, рассмотрим временную диаграмму передачи данных от клавиатуры (рис. 2).
Передача данных отличается от обычной передачи по интерфейсу RS232 наличием сигнала синхронизации. Отрицательный фронт каждого синхроимпульса соответствует готовности соответствующего бита на линии данных. Задача функции обработки прерываний "уложить" биты данных в соответствующем порядке.
Функция обработки прерываний для приема скан-кода клавиши приведена ниже. Следует отметить, что необходимость настройки прерывания по положительному фронту (как указано в тексте функции, п. 3) связана с аппаратной особенностью реализации прерываний этого типа контроллеров. Срабатывание по отрицательному фронту синхроимпульса клавиатуры обеспечивается инвертированием сигнала синхронизации перед подачей на вход прерываний.
int PS2count;
int PS2buf[256];
BYTE PS2code; // байт обработки приема с клавиатуры
BYTE PS2out; // код клавиши для вывода в программу
int PS2byte;
int PS2end;
int PS2click; // флаг символа отпускания клавиши
int PS2double; // флаг двойного скан-кода клавиатуры
...
//============================================================
// INTERRUPT SUBROUTINES
//============================================================
//
// IsrPS2. Прерывание клавиатуры
// -----------------------------
// Перед обращением в функции InitSoftware настроить используемые
// линии ввода/вывода!!!
// INT0 - J2.10 - (I6) - вход прерывания клавиатуры
// P18 - CTS1/PTS2 - J2.24 - (IO9) - ввод данных с клавиатуры
//
// прием данных с клавиатуры PS/2 осуществляется по следующему
// алгоритму:
//
// 1. отсекается повтор клавиш (проходит только первое нажатие
// 2. отсекается отпускание клавиш (проходит только нажатие)
// 3. прерывание настраивается на срабатывание по переднему
// фронту синхроимпульса клавиатуры.
// 4. отмечено (на одном типе клавиатуры), что необходимо
// перед началом работы обнулять линию данных, иначе
// клавиатура не передает коды клавиш.
// 5. необходимо следить, чтобы не было наложения прерывания
// с более высоким приоритетом, иначе (напр., при выводе
// кодов нажатых клавиш в COM-порт, происходила потеря
// отдельных битов в посылке клавиатуры.
//
void
interrupt far IsrPS2(void)
{
int c;
c = (inport(0xff7a) & 0x4); // прием и выделение бита данных
if(PS2count == 0)
{
if(PS2end == 1) // проверка и поиск стопового бита
{
if(c == 0) PS2end = 0;
}
else if(c != 0) PS2count++; // поиск стартового бита
}
else // обработка байта данных
{
if(PS2count < 9)
{
PS2code = PS2code >> 1;
if(c == 0) PS2code |= 0x80;
PS2count++;
if(PS2count >= 9)
{
if(PS2out != PS2code) // проверка на повтор символа
{
switch (PS2code)
{
case 0xf0: // проверка на отпускание клавиши
{
PS2click = 1;
PS2out = 0; // сброс символа
PS2double = 0;
} break;
case 0xe0: // проверка двойного скан-кода
{
PS2double = 1;
} break;
default:
{
if(PS2click != 0) PS2click = 0;
else
{
PS2out = PS2code;
PS2byte = 1;
}
} break;
}
}
PS2code = 0;
PS2end = 1;
PS2count = 0;
}
}
}
outport(0xff22, 0x800c); // команда сброса прерывания
}
На функцию прерываний возложены некоторые дополнительные задачи по обработке принятых данных.
При удержании нажатой клавиши клавиатура начинает повторять передачу скан-кода вплоть до момента ее отпускания. В большинстве случаев автоматический повтор не нужен, поэтому обработчик прерываний выдает скан-код только один раз — в момент нажатия. Повторы клавиш отсекаются.
При отпускании клавиши клавиатура передает скан-код, предваряя его посылкой кода F0. Например, Если нажата клавиша "А" (скан-код 1С, см. табл. 1), то при отпускании клавиши "А" будет передана двухбайтовая последовательность F0 1C. В большинстве случаев фиксация отпускания клавиши также не требуется, поэтому обработчик просто отсекает передачу этих последовательностей.
Отсутствует обработка бита четности. Конечно, это сильное упрощение, т.к. всегда есть возможность пропуска бита и возникновения ошибок при приеме. Возможно, что в будущем (если потребуется) будет введен как контроль четности, так и контроль за пропуском бит (что возможно, при одновременной работе нескольких программ аппаратных прерываний).
Конечно, разработчик может написать обработчик прерываний под свои потребности, с передачей в программу всех передаваемых клавиатурой кодов.