AVR313
Интерфейс с AT клавиатурой персонального компьютера
Характеристики:
- Интерфейс со стандартной АТ клавиатурой персонального компьютера
- Использует только две линии ввода-вывода. Одна линия должна быть подключена к выводу, имеющем функцию внешнего прерывания
- Не требуется внешних компонентов
- Законченная программа на C, осуществляющая преобразование интерфейса клавиатуры в последовательный интерфейс
Введение
Большинство микроконтроллеров требуют обеспечения интерфейса ч пользователем. В данном примере применения описывается способ обеспечения такого интерфейса при помощи стандартной АТ клавиатуры персонального компьютера (ПК).
Физический интерфейс
Физический интерфейс между клавиатурой и микроконтроллером показан на рисунке 1. Под синхроимпульсы и данные задействованы две сигнальные линии. В клавиатуре сигнальные выходы представляют собой каскады с открытыми коллекторами и подтягивающими резисторами. Это позволяет одновременно и клавиатуре и микроконтроллеру устанавливать низкий уровень на линии. В качестве разъемов можно использовать 5- выводной DIN разъем или миниатюрный 6- выводной mini-DIN разъем. Назначение контактов этих разъемов приведено в таблице 1.
Рисунок 1. Физический интерфейс между клавиатурой и микроконтроллером.
Таблица 1. Назначение контактов АТ клавиатуры
АТ компьютер |
|
|
Сигналы |
5- выводной DIN41524 |
6- выводной mini-DIN (PS2) |
Синхроимпульсы |
1 |
5 |
Данные |
2 |
1 |
Не используется |
3 |
2,6 |
GND |
4 |
3 |
+5 В |
5 |
4 |
Экран |
Экран |
Экран |
Синхронизация
Временная диаграмма передачи данных от клавиатуры показана на рисунке 2. При передаче используется следующий протокол: сначала передается старт-бит (всегда "0"), затем восемь бит данных, один бит проверки на нечетность и один стоп-бит (всегда "1"). Данные должны считываться в тот момент, когда синхросигнал имеет низкое значение. Формирование синхросигнала осуществляет клавиатура. Длительность как высокого, так и низкого импульсов синхросигнала обычно равняются 30-50 мкс.
Ведущая система может послать клавиатуре команды путем удерживания линии синхроимпульсов в низком состоянии. После этого на линии данных устанавливается низкий сигнал (старт-бит). Затем линия синхронизации должна быть освобождена. После этого клавиатура сформирует 10 синхроимпульсов. Данные должны быть установлены до спадающего (заднего) фронта синхроимпульса. После приема десятого бита клавиатура проверяет наличие на линии данных высокого сигнала (стоп-бит). При обнаружении высокого уровня на линии данных клавиатура выставляет на ней низкий уровень, который сигнализирует ведущему устройству что данные получены. Программное обеспечение данного примера применения не будет посылать клавиатуре никаких команд.
Скан-код
АТ клавиатура сопоставляет каждой нажатой клавише свой персональный скан-код. При нажатии клавиши посылается ее скан-код. Если кнопка удерживается нажатой в течение некоторого времени, то посылка скан-кода повторяется. Обычно скорость повторения посылки скан-кода равняется 10 раз в секунду. При отпускании кнопки передается код отпускания ($F0). У большинства кнопок скан-код имеет размер 1 байт. Некоторые клавиши, например "Home", "Ins", "Del" и т.д. имеют размер скан-кода от двух до пяти байт. Первый байт у них всегда равняется $E0. Это также относится и к коду отпускания кнопок, т.е. он будет иметь вид E0 F0 хх …
АТ клавиатуры имеют три набора скан-кодов, причем по умолчанию используется второй комплект кодов. Данный пример применения использует только второй комплект скан-кодов.
Программное обеспечение
Программное обеспечение данного примера применения просто преобразует интерфейс АТ клавиатуры в RS-232 интерфейс. Скан-коды, полученные от клавиатуры преобразуются в соответствующие ASCII коды и передаются при помощи UART. Исходный текст программы написан на С и может быть легко преобразован для использования любым AVR микроконтроллером, имеющим СОЗУ.
Замечание: линк-файл (AVR313.xcl), включенный в данный пример применения, должен использоваться вместо стандартного линк-файла. Это делается при помощи меню XLINK - Options. Линк-файл применяется только для микроконтроллера AT90S8515.
Алгоритм
Прием данных от клавиатуры осуществляется при помощи функции обработки прерываний INT0_interrupt. Эта функция работает независимо от остальной части программы.
Алгоритм весьма прост: По спадающему фронту синхроимпульса сохраняется значение, присутствующее на линии данных. Это легко реализовать, если подать синхросигнал на вывод INT1 или INT0. Функция обработки прерывания будет выполняться по каждому спадающему фронту синхросигнала. После приема всех битов производится декодирование данных. Выполняет декодирование данных функция decode. Для символьных кнопок функция сохраняет в буфере соответствующий ей ASCII код. При этом учитывается, была ли нажата ли клавиша Shift при нажатии символьной кнопки. Другие специальные кнопки типа функциональных кнопок, навигационных кнопок (стрелочки, прокрутка страниц и т.д.), Ctrl и Alt игнорируются.
Преобразование принятых скан-кодов в ASCII коды производится при помощи двух таблиц, одна из которых содержит символы, соответствующие просто нажатой кнопке, а другая - нажатию кнопки при нажатой кнопке Shift.
Изменения и уточнения
Если ведущее устройство выйдет из синхронизма с клавиатурой, то все принятые данные будут неверными. Один из способов решения этой проблемы - использование времени ожидания. Если за 1.5 мс не было получено 11 битов, то принимается решение о возникновении ошибки. Счетчик битов должен быть сброшен, а неправильные данные - отвергнуты.
Для установки параметров клавиатуры, например типовой скорости обмена данными и типового времени задержки, необходимо передать клавиатуре специальные команды. Выше было описано, как это реализовать. Конкретный формат команды надо смотреть в техническом описании клавиатуры.
Рисунок 2. Временные диаграммы передачи данных от клавиатуры
Ниже приведен листинг управляющей программы для AVR микроконтроллера.
Main.c
#include
#include
#include
#include "io8515.h"
#include "serial.h"
#include "gpr.h"
#include "kb.h"
void main(void)
{
unsigned char key;
init_uart(); // Инициализация передающего буфера UART
init_kb(); // Инициализация приемника клавиатуры
while(1)
{
key=getchar();
putchar(key);
delay(100);
}
}
Low_level_init.c
#include
#include
int __low_level_init(void)
{
UBRR = 12; // Скорость передачи 19200 бод при частоте 4 МГц
UCR = 0x08; // активизация передатчика
GIMSK= 0x40; // Разрешение прерывания INT0
_SEI();
return 1;
}
Serial.c
#include
#include
#include /* SFR declarations */
#include "serial.h"
#define ESC 0x1b
#define BUFF_SIZE 64
flash char CLR[] = {ESC, '[','H', ESC, '[', '2', 'J',0};
unsigned char UART_buffer[BUFF_SIZE];
unsigned char *inptr, *outptr;
unsigned char buff_cnt;
void init_uart(void)
{
inptr = UART_buffer;
outptr = UART_buffer;
buff_cnt = 0;
}
void clr(void)
{
puts_P(CLR); // Передача команды "очистки экрана" ("clear screen") терминалу VT100
}
int putchar(int c)
{
if (buff_cnt= UART_buffer + BUFF_SIZE) // Просмотр указателя
inptr = UART_buffer;
UCR = 0x28; // Активизация прерывания при очистке
// регистра данных UART
return 1;
} else {
return 0; // Буфер заполнен
}
}
// Управление прерыванием передатчика
interrupt [UART_UDRE_vect] void UART_UDRE_interrupt(void)
{
UDR = *outptr; // Посылка следующего байта
outptr++; // Увеличение указателя
if (outptr >= UART_buffer + BUFF_SIZE) // Просмотр указателя
outptr = UART_buffer;
if(--buff_cnt == 0) // Если буфер пуст
UCR = UCR && (1<
#include "kb.h"
#include "serial.h"
#include "gpr.h"
#include "scancodes.h"
#define BUFF_SIZE 64
unsigned char edge, bitcount; // 0 = отриц. 1 = положит.
unsigned char kb_buffer[BUFF_SIZE];
unsigned char *inpt, *outpt;
unsigned char buffcnt;
void init_kb(void)
{
inpt = kb_buffer; // Инициализация буфера
outpt = kb_buffer;
buffcnt = 0;
MCUCR = 2; // Установка прерывания INT0 по спадающему фронту
edge = 0; // 0 = спадающий фронт 1 = нарастающий фронт
bitcount = 11;
}
interrupt [INT0_vect] void INT0_interrupt(void)
{
static unsigned char data // Захват принятого скан-кода
if (!edge) // Вход в подпрограмму по спадающему фронту
{
if(bitcount < 11 && bitcount > 2) // Биты с 3 по 10 – биты данных
{ // старт- и стоп- биты игнорируются
data = (data >> 1);
if(PIND & 8)
data = data | 0x80; // Сохранение "1"
}
MCUCR = 3; // Установка прерывания по нарастающему фронту
edge = 1;
} else { // Вход в подпрограмму по нарастающему фронту
MCUCR = 2; // Установка прерывания по спадающему фронту
edge = 0;
if(--bitcount == 0) // Все биты получены
{
decode(data);
bitcount = 11;
}
}
}
void decode(unsigned char sc)
{
static unsigned char is_up=0, shift = 0, mode = 0;
unsigned char i;
if (!is_up) // Последний принятый идентификатор был идентификатором отпускания кнопки
{
switch (sc)
{
case 0xF0 :// Идентификатор отпускания кнопки
is_up = 1;
break;
case 0x12 :// Левый SHIFT
shift = 1;
break;
case 0x59 :// Правый SHIFT
shift = 1;
break;
case 0x05 :// F1
if(mode == 0)
mode = 1;// Вход в режим скан-кода
if(mode == 2)
mode = 3;// выход из режима скан-кода
break;
default:
if(mode == 0 || mode == 3) // Если режим ASCII
{
if(!shift) // Если SHIFT не нажата
{ // тогда надо просмотреть таблицу
for(i = 0; unshifted[i][0]!=sc && unshifted[i][0]; i++);
if (unshifted[i][0] == sc)
{
put_kbbuff(unshifted[i][1]);
}
} else { // если SHIFT нажата
for(i = 0; shifted[i][0]!=sc && shifted[i][0]; i++);
if (shifted[i][0] == sc)
{
put_kbbuff(shifted[i][1]);
}
}
} else{ // режим скан-кода
print_hexbyte(sc);// Вывод скан-кода
put_kbbuff(' ');
put_kbbuff(' ');
}
break;
}
} else {
is_up = 0; // Два кода 0xF0 в строке невозможно
switch (sc)
{
case 0x12 :// Левый SHIFT
shift = 0;
break;
case 0x59 :// Правый SHIFT
shift = 0;
break;
case 0x05 :// F1
if(mode == 1)
mode = 2;
if(mode == 3)
mode = 0;
break;
case 0x06 :// F2
clr();
break;
}
}
}
void put_kbbuff(unsigned char c)
{
if (buffcnt= kb_buffer + BUFF_SIZE) // Просмотр указателя
inpt = kb_buffer;
}
}
int getchar(void)
{
int byte;
while(buffcnt == 0); // Ожидание данных
byte = *outpt; // Получение байта
outpt++; // Увеличение указателя
if (outpt >= kb_buffer + BUFF_SIZE) // Просмотр указателя
outpt = kb_buffer;
buffcnt--; // Уменьшение счетчика буфера
return byte;
}
Gpr.c
#include "gpr.h"
void print_hexbyte(unsigned char i)
{
unsigned char h, l;
h = i & 0xF0; // Старшая тетрада байта
h = h>>4;
h = h + '0';
if (h > '9')
h = h + 7;
l = (i & 0x0F)+'0'; // Младшая тетрада байта
if (l > '9')
l = l + 7;
putchar(h);
putchar(l);
}
void delay(char d)
{
char i,j,k;
for(i=0; i<d; i++)
for(j=0; j<40; j++)
for(k=0; k<176; k++);
}
Pindefs.h
//*************************
// Файл определения выводов
// подключения клавиатуры
//*************************
#define PIN_KB PIND
#define PORT_KB PORTD
#define CLOCK 2
#define DATAPIN 3
Scancodes.h
// Нижние символы
flash unsigned char unshifted[][2] = {
0x0d,9,
0x0e,'|',
0x15,'q',
0x16,'1',
0x1a,'z',
0x1b,'s',
0x1c,'a',
0x1d,'w',
0x1e,'2',
0x21,'c',
0x22,'x',
0x23,'d',
0x24,'e',
0x25,'4',
0x26,'3',
0x29,' ',
0x2a,'v',
0x2b,'f',
0x2c,'t',
0x2d,'r',
0x2e,'5',
0x31,'n',
0x32,'b',
0x33,'h',
0x34,'g',
0x35,'y',
0x36,'6',
0x39,',',
0x3a,'m',
0x3b,'j',
0x3c,'u',
0x3d,'7',
0x3e,'8',
0x41,',',
0x42,'k',
0x43,'i',
0x44,'o',
0x45,'0',
0x46,'9',
0x49,'.',
0x4a,'-',
0x4b,'l',
0x4c,'ш',
0x4d,'p',
0x4e,'+',
0x52,'ж',
0x54,'е',
0x55,'\\',
0x5a,13,
0x5b,'Ё',
0x5d,'\'',
0x61,'<',
0x66,8,
0x69,'1',
0x6b,'4',
0x6c,'7',
0x70,'0',
0x71,',',
0x72,'2',
0x73,'5',
0x74,'6',
0x75,'8',
0x79,'+',
0x7a,'3',
0x7b,'-',
0x7c,'*',
0x7d,'9',
0,0
};
// Верхние символы
flash unsigned char shifted[][2] = {
0x0d,9,
0x0e,'§',
0x15,'Q',
0x16,'!',
0x1a,'Z',
0x1b,'S',
0x1c,'A',
0x1d,'W',
0x1e,'"',
0x21,'C',
0x22,'X',
0x23,'D',
0x24,'E',
0x25,'¤',
0x26,'#',
0x29,' ',
0x2a,'V',
0x2b,'F',
0x2c,'T',
0x2d,'R',
0x2e,'%',
0x31,'N',
0x32,'B',
0x33,'H',
0x34,'G',
0x35,'Y',
0x36,'&',
0x39,'L',
0x3a,'M',
0x3b,'J',
0x3c,'U',
0x3d,'/',
0x3e,'(',
0x41,';',
0x42,'K',
0x43,'I',
0x44,'O',
0x45,'=',
0x46,')',
0x49,':',
0x4a,'_',
0x4b,'L',
0x4c,'Ш',
0x4d,'P',
0x4e,'?',
0x52,'Ж',
0x54,'Е',
0x55,'`',
0x5a,13,
0x5b,'^',
0x5d,'*',
0x61,'>',
0x66,8,
0x69,'1',
0x6b,'4',
0x6c,'7',
0x70,'0',
0x71,',',
0x72,'2',
0x73,'5',
0x74,'6',
0x75,'8',
0x79,'+',
0x7a,'3',
0x7b,'-',
0x7c,'*',
0x7d,'9',
0,0
};
Документация:
|
|
Engl 92Kb Исходный фаил |
|
|
6Kb Программа |
окна пвх