Санкт-Петербургский государственный университет

информационных технологий, механики и оптики

(СПбГУ ИТМО)

Кафедра вычислительной техники

Лабораторная работа № 4

«Клавиатура»

Работу выполнили:

Студенты группы 4103

Голубцов Евгений

Рябинина Александра

Санкт-Петербург

2010

Лабораторная работа № 4 «Клавиатура»

Задание

Разработать и написать драйвер клавиатуры для учебно-лабораторного стенда SDK-1.1. Написать тестовую программу для разработанного драйвера, которая выполняет определенную вариантом задачу.

Описание работы

Данная лабораторная работа посвящена изучению клавиатуры в составе контроллера SDK-1.1.

В рамках лабораторной работы необходимо разработать программу для контроллера SDK-1.1 (ведомый), который обменивается данными с персональным компьютером (ведущий). В качестве канала связи используется последовательный канал RS-232. На стороне персонального компьютера имеется инструментальное средство для обеспечения взаимодействия с SDK-1.1 – это терминальная программа M3P. Вариант задания такой же, как и в лабораторной работе № 3, с некоторыми изменениями, главным из которых является замена устройства ввода данных: вместо клавиатуры персонального компьютера используется клавиатура SDK-1.1. Устройством вывода является терминал персонального компьютера и светодиодные индикаторы SDK-1.1.

Программа должна выполнять две задачи. Первая задача – это тестирование клавиатуры (написанного драйвера): символы всех нажимаемых на клавиатуре SDK-1.1 кнопок выводятся в последовательный канал и отображаются в терминале. Вторая задача – выполнение варианта задания. Переключение между двумя задачами в программе должно быть выполнено с использованием DIP-переключателей.

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

Драйвер клавиатуры должен содержать: функцию инициализации, функции сканирования клавиатуры и обработки нажатия кнопок (вызываются в обработчике прерывания от таймера), циклический буфер клавиатуры (нажатые кнопки), API-функцию чтения символа из буфера клавиатуры (см. [14], IOS2003_lab5.pdf). Взаимодействие обработчика и API-функции осуществляется только через буфер. Кроме того, работа с клавиатурой должна быть организована с переповторами, т.е. с отслеживанием длительного нажатия кнопки (как на клавиатуре персонального компьютера). Что это значит? Если после фиксирования нажатой кнопки (соответствующий символ занесен в буфер клавиатуры) проходит время, равное задержке перед повтором символа, а она все еще нажата, то в буфер клавиатуры повторно заносится этот же символ. После этого через промежутки времени, определяемые скоростью повтора символа, код кнопки заносится в буфер до тех пор, пока не будет зафиксировано отпускание кнопки. При инициализации необходимо указать задержку перед повтором символа (первый параметр) и скорость повтора символа (второй параметр). Рекомендуемая величина задержки перед повтором – 1 секунда (не меньше).

Алгоритм опроса матричной клавиатуры рекомендуется реализовать в виде конечного автомата (Finite State Machine) – функции, которая в зависимости от своего состояния (значения определенной переменной) и входного воздействия, выполняет разную работу [2, 25, 29].

Драйвер клавиатуры должен адекватно обрабатывать одновременное нажатие нескольких кнопок. Каждое нажатие кнопки на клавиатуре должно сопровождаться коротким звуковым сигналом, что требует использования драйвера звукового излучателя (генерация звука реализуется после попадания символа в буфер клавиатуры). Драйвер звукового излучателя должен работать по прерыванию от таймера. Длительность генерации звука – десятки миллисекунд. Таким образом, получается «музыкальная клавиатура».

Кроме того, должен быть предусмотрен контроль ввода корректных значений в рамках второй задачи программы (лабораторная работа № 3). Сигнализация в случае ввода некорректных значений – сообщение об ошибке в последовательный канал и зажигание светодиодов.

Задание

Скорость последовательного канала – 4800 бит/с.

Вычитатель десятичных чисел. Диапазон значений уменьшаемого и вычитаемого – от 010 до 9910 включительно. Разность может быть как положительной, так и отрицательной. При помощи клавиатуры SDK-1.1 вводятся уменьшаемое и вычитаемое (десятичные числа), причем разделителем введенных значений является символ «-» (кнопка «B» на клавиатуре), символом начала вычисления – «=» (кнопка «#»). Вводимые с клавиатуры числа и результат должны выводиться в последовательный канал и отображаться в терминале персонального компьютера. Каждое новое выражение начинается с новой строки. Должен быть предусмотрен контроль ввода корректных значений.

Драйвер клавиатуры

#define KEY_PRESSED 1

#define KEY_UNPRESSED 0

#define KEY_REPTIME 100

struct FIFOb dFIFO, uFIFO;

unsigned char key;

unsigned short itime;

unsigned char APIKeyUp()

{

return PopF(&uFIFO);

}

unsigned char APIKeyDown()

{

return PopF(&dFIFO);

}

unsigned char Scan(unsigned char col_row)

{

WriteMax(0, 0x01);

if( ( ReadMax(0) & 0xF0 ) == 0xF0 ){

switch (col_row){

case 0xE0: return 0x31;

case 0xD0: return 0x34;

case 0xB0: return 0x37;

case 0x70: return 0x2A;

}

}

WriteMax(0, 0x02);

if( ( ReadMax(0) & 0xF0 ) == 0xF0 ){

switch (col_row){

case 0xE0: return 0x32;

case 0xD0: return 0x35;

case 0xB0: return 0x38;

case 0x70: return 0x30;

}

}

WriteMax(0, 0x04);

if( ( ReadMax(0) & 0xF0 ) == 0xF0 ){

switch (col_row){

case 0xE0: return 0x33;

case 0xD0: return 0x36;

case 0xB0: return 0x39;

case 0x70: return 0x23;

}

}

WriteMax(0, 0x08);

if( ( ReadMax(0) & 0xF0 ) == 0xF0 ){

switch (col_row){

case 0xE0: return 0x41;

case 0xD0: return 0x42;

case 0xB0: return 0x43;

case 0x70: return 0x44;

}

}

return 0;

}

void T0_ISR(void) __interrupt ( 1 ) //действия, выполняемы обработчиком прерываний таймера 0

{

static unsigned char regENA = ReadMax(4);

static unsigned char key=0, KeyPressed=0;

static unsigned char ktime=0;

unsigned char col_row;

TH0 = 0xDC; //100 per sec

TL0 = 0x01;

itime++;

WriteMax(0,0x00);

col_row = ReadMax(0);//читаем регистр клавы

ktime++;

if (col_row == 0xF0){

if(ktime<3) return;

if (key != 0){

PushF(&uFIFO, key);

key=0;

}

KeyPressed=0;

ktime=0;

return;

}

regENA ^= 0x1c;

WriteMax(4, regENA);

if( ktime > KEY_REPTIME){

ktime=0;

KeyPressed=0;

}

if (!KeyPressed){

key = Scan(col_row); //сканируем столбцы соответствующей строки клавы

PushF(&dFIFO, key); //записываем в буфер введенный символ

KeyPressed=1;

return;

}

}

Код программы

#include "aduc812.h"

#define MAXBASE 8

#define FBUFSize 16

#define FBUFSS FBUFSize-1

struct FIFOb{ //описываем структуру буфера последовательного канала

unsigned char buf[FBUFSize];

char start;//начало буфера

char end; //конечная позиция

};

struct FIFOb wFIFO, rFIFO, dFIFO, uFIFO;

unsigned char key;

unsigned short itime;

void WriteMax (unsigned char xdata *regnum, unsigned char val) //функция записи в регистр ПЛИС

{

unsigned char oldDPP = DPP;

DPP = MAXBASE;

*regnum = val;

DPP = oldDPP;

}

void WriteLED(unsigned char value) //функция "зажигания" сетодиодов

{

WriteMax(7, value);

}

unsigned char ReadMax (unsigned char xdata *regnum) //функция чтения из ПЛИС

{

unsigned char oldDPP = DPP;

unsigned char val1;

DPP = MAXBASE;

val1 = *regnum;

DPP = oldDPP;

return val1;

}

unsigned char GetDIP() //читает положение Dip-переключателей

{

unsigned char val = ReadMax(2);

return ~val;

}

void SetVector(unsigned char xdata * Address, void * Vector)// Функция, устанавливающая вектор прерывания Vector по адресу Address

{

unsigned char xdata * TmpVector;

*Address = 0x02;

TmpVector = (unsigned char xdata *)(Address+1);

*TmpVector = (unsigned char) ((unsigned short)Vector >> 8);

++TmpVector;

*TmpVector = (unsigned char) Vector;

}

void SendChar(char sch)//функция отправки символа

{

TI = 0; // Обнуление флага завершения посылки

SBUF = sch; // Инициация посылки символа sch

while( !TI ); // Ожидание завершения посылки

}

void SendString(const char* str) //функция отпраки строки

{

unsigned char i=0;

while(i<80){

if (str[i]=='\0') break;

SendChar(str[i++]);

}

}

unsigned char RcvChar(void) //получение символа

{

char res;

while(!RI);//когда RI=1 - вызов обработчика прерываний

res = SBUF; //начинаем процесс передачи

RI = 0;

return res;

}

void PushF(struct FIFOb* a, unsigned char c) //запись в буфер FIFO

{

#define Ae (a->end)

#define As (a->start)

a->buf[Ae] = c; //записываем символ в конец буфера

if (++Ae > FBUFSS) Ae=0; //контроль переполнения буфера, если буфер заполнен - начинать сначала

if (Ae == As){

if(++As > FBUFSS) Ae = 0;

}

}

unsigned char PopF(struct FIFOb* a) //извлечение из буфера FIFO

{

#define Ae (a->end)

#define As (a->start)

unsigned char c;

if (Ae == As) return 0;

c = a->buf[As];

if(++As > FBUFSS) As=0;

return c;

}

void APIWrite(unsigned char s)

{

EA = 0; // Запрещение прерываний

ES = 0;

PushF(&wFIFO, s); //запись в буфер символа

EA = 1; // Разрешение прерываний

ES = 1;

}

void APIUChar(unsigned char res)

{

unsigned char rr[3], c;

char i;

for(i=0; i<3; i++){

c = res % 10;

c |= 0x30;

rr[i]=c;

res /= 10;

if(res == 0) break;

}

for(;i>=0; i--) APIWrite(rr[i]);

}

void APIString(const unsigned char* str) //функция записи строки в буфер

{

unsigned char i=0;

while(i<80){

if (str[i]=='\0') break;

APIWrite(str[i++]);

}

}

unsigned char APIKeyUp()

{

return PopF(&uFIFO);

}

unsigned char APIKeyDown()

{

return PopF(&dFIFO);

}

unsigned char APIRead(void) //чтение из последовательного канала

{

unsigned char c;

EA = 0; // Запрещение прерываний

ES = 0;

c = PopF(&rFIFO);

EA = 1; // Разрешение прерываний

ES = 1;

return c;

}

void Echo(unsigned char c)

{

unsigned char i;

if((c<0x80) || (c>0xEF)) return;

if((c>0xAF) && (c<0xE0)) return;

if(c>0x9F) c -= 0x20;

if(c>0x9f) c -= 0x30;

SendChar(c);

if(c>0x8F)

{ c+=0x50; }

else

{ c+=0x20; }

for(i=0; i<5; i++){

if(++c>0xEF) break;

if((c>0xAF) && (c<0xE0)) c+=0x30;

SendChar(c);

}

SendChar('\n');

SendChar('\r');

return;

}

void DoMath(void)

{

#define ERR_MSG "\n\rIOError\n\r\0"

unsigned char c=0, s1=0, s2=0, i, f=0;

for(i=0; i<2; i++){

c = 0;

while(!c) c = APIKeyDown(); //ожидаем нажатия клавиши

if(c == 'B'){

APIWrite('-');

f=10;

break;

}

APIWrite(c);

if((c<0x30) || (c>0x39)){

f=1;

break;

}

s1 *= 10;

s1 += c & 0x0F;

}

if(f==1) {APIString(ERR_MSG); return;}

if(f!=10){

c=0;

while(!c) c = APIKeyDown();

if(c != 'B') {APIWrite(c); APIString(ERR_MSG); return;}

APIWrite('-');

}

f=0;

for(i=0; i<2; i++){

c = 0;

while(!c) c = APIKeyDown();

if(c == '#'){

APIWrite('=');

f=10;

break;

}

APIWrite(c);

if((c<0x30) || (c>0x39)){

f=1;

break;

}

s2 *= 10;

s2 += c & 0x0F;

}

if(f==1) {APIString(ERR_MSG); return;}

if(f!=10){

c=0;

while(!c) c = APIKeyDown();

if(c != '#') {APIWrite(c); APIString(ERR_MSG); return;}

APIWrite('=');

}

APIUChar(s1 - s2);

//ERROR MESSAGE

APIWrite('!');

APIWrite('\n');

APIWrite('\r');

}

void SIO_ISR( void ) __interrupt ( 4 ) //прерывание от последовательного канала

{

unsigned char c;

if(TI){

c = PopF(&wFIFO);

if(c){

SBUF = c;

TI=0;

}

}

if(RI){

PushF(&rFIFO, SBUF);

RI=0;

}

}

void InitUART(void)

{

TH1 = 0xFD; // Скорость 9600

TMOD = 0x21; // Таймер 1 в режиме autoreload, а 0 16bit

TCON = 0x50; // Запуск таймера 1 & 0

SCON = 0x50; // 8 bit UART, разрешение приема

PCON &= 0x7F; // Отключение дублирования скорости, установленной в TH1

TI = 1;

EA = 0; // Запрещение прерываний

wFIFO.start = wFIFO.end = rFIFO.start = rFIFO.end = 0;

SetVector(0x2023, (void *) SIO_ISR); //устанавливаем вектор прерывайний в пользовательской таблице от UART

}

unsigned char Scan(unsigned char col_row)

{

WriteMax(0, 0x01);

if( ( ReadMax(0) & 0xF0 ) == 0xF0 ){

switch (col_row){

case 0xE0: return 0x31;

case 0xD0: return 0x34;

case 0xB0: return 0x37;

case 0x70: return 0x2A;

}

}

WriteMax(0, 0x02);

if( ( ReadMax(0) & 0xF0 ) == 0xF0 ){

switch (col_row){

case 0xE0: return 0x32;

case 0xD0: return 0x35;

case 0xB0: return 0x38;

case 0x70: return 0x30;

}

}

WriteMax(0, 0x04);

if( ( ReadMax(0) & 0xF0 ) == 0xF0 ){

switch (col_row){

case 0xE0: return 0x33;

case 0xD0: return 0x36;

case 0xB0: return 0x39;

case 0x70: return 0x23;

}

}

WriteMax(0, 0x08);

if( ( ReadMax(0) & 0xF0 ) == 0xF0 ){

switch (col_row){

case 0xE0: return 0x41;

case 0xD0: return 0x42;

case 0xB0: return 0x43;

case 0x70: return 0x44;

}

}

return 0;

}

void T0_ISR(void) __interrupt ( 1 ) //действия, выполняемы обработчиком прерываний таймера 0

{

static unsigned char regENA = ReadMax(4);

static unsigned char key=0, KeyPressed=0;

static unsigned char ktime=0;

unsigned char col_row;

TH0 = 0xDC; //100 per sec

TL0 = 0x01;

itime++;

#define KEY_PRESSED 1

#define KEY_UNPRESSED 0

#define KEY_REPTIME 100

WriteMax(0,0x00);

col_row = ReadMax(0);//читаем регистр клавы

ktime++;

if (col_row == 0xF0){

if(ktime<3) return;

if (key != 0){

PushF(&uFIFO, key);

key=0;

}

KeyPressed=0;

ktime=0;

return;

}

regENA ^= 0x1c;

WriteMax(4, regENA);

if( ktime > KEY_REPTIME){

ktime=0;

KeyPressed=0;

}

if (!KeyPressed){

key = Scan(col_row); //сканируем столбцы соответствующей строки клавы

PushF(&dFIFO, key); //записываем в буфер введенный символ

KeyPressed=1;

return;

}

}

void main(void)

{

unsigned char c;

SetVector( 0x200B, (void *)T0_ISR ); // Установка вектора прерываний от таймер 0 в пользовательской таблице

InitUART(); //инициализацию UART

ET0=1; EA=1; // Разрешение прерываний от таймера 0

key=0;

while(1)

{

if(GetDIP() == 128){ //если установлен соответствующий ДИП-переключатель

while(1){ //выполняем вывод на экран соответствующего введенного символа

c = APIKeyDown(); //читаем кнопку

if(c){

SendChar(c);

}

if(GetDIP() != 128) break;

}

}

if(GetDIP() == 1){

while(1){

DoMath();

if(GetDIP() != 1) break;

}

}

}

}