PS2 Клавиатура

Снова хочу вернуться к подключению PS2 клавиатуры. Недавно заметил у стандартной библиотеки большой изъян — при чтении(kbd.read()) программа встает намертво, пока не получит символ. Вскрыв код библиотеки, я увидел кучу пустых while-ов:

1
2
3
4
5
6
7
8
9
10
while (digitalRead(_ps2clk) == HIGH)
	;
if (digitalRead(_ps2data) == HIGH)
{
data = data | bit;
}
while (digitalRead(_ps2clk) == LOW)
 
;
//и еще много таких

После двух мучительных часов, мой мозг выдал следующее:

Перед передачей данных, PS2-устройство проверяет линию Clock. Она должна быть в высоком состоянии >50 мкс, перед передачей. Если она в низком состоянии, то хост запрещает передачу данных и нужно ждать, пока линия не освободится. Т.е. PS2-устройство отправляет бит данных на линию Data когда Clock в высоком состоянии, а хост читает когда Clock в низком состоянии.

Получается, чтобы избежать таких задержек, нужно использовать прерывания и т.п. Я попытался — увы, ничего не получилось.  😥

Чуть позже я нашел отличную замену PS2 библиотеки — PS2KeyboardExt2.

Стандартно клавиатуру нужно подключать так: CLOCK — 3pin(см. дальше), DATA — 2pin. Причем CLOCK пин нельзя изменить! Так как pin-3, у всех «видов» ардуин, — аппаратное прерывание. Давайте рассмотрим стандартный пример библиотеки:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <PS2Keyboard.h>  
 
#define KBD_CLK_PIN  3
#define KBD_DATA_PIN 2
 
PS2Keyboard keyboard;
 
void setup() {
  keyboard.begin(KBD_DATA_PIN, true);
 
  Serial.begin(9600);
  delay(1000);
}
 
#define is_printable(c) (!(c&0x80))   // don't print if top bit is set
 
void loop() {
 
  if(keyboard.available()) {
    byte       c = keyboard.read();
 
    if      (c==PS2_KC_UP)      Serial.print("up\n");
    else if (c==PS2_KC_DOWN)    Serial.print("down\n");
    else if (c==PS2_KC_BKSP)    Serial.print("backspace\n");
    else if (c==PS2_KC_ESC)   { Serial.print("escape and reset\n"); keyboard.reset(); }
    else if ( is_printable(c) ) Serial.print(c);   // Если это не спец.символ, то текст
  } 
}

Из этого кода получается почти готовый текстовый редактор. Правда, русских символов там нет. 🙂 Shift, Capslock — работают. Здесь, функция read() не останавливает программу, так как все работает по прерыванию. Меня огорчило только то, что нельзя читать «чистый» код клавиш и посылать напрямую команды клавиатуре. Я это исправил  :mrgreen: *Ссылка* Все функции библиотеки:

  • .begin(DATA_PIN, lights) = DATA_PIN — пин DATA, lights — разрешить/запретить управление светодиодами клавиатуры
  • .read() = чтение символов(с учетом кл.SHIFT и т.д.) и некоторых других клавиш по таблице Scan Key 2.
  • .write(DATA) = отправить PS2 команду клавиатуре. Все команды здесь.
  • .read_extra() = читать дополнительные байты информации.
  • .readCode() = читать «чистый» код клавиши. Стандартно в клавиатуре установлен Scan Key 2, но может быть еще и Scan Key 1, Scan Key 3.
  • .readCodeBuf() = Тоже самое, что и .readCode(), но с доп. буфером на 45 код-ов. Для медленных/тормозящих программ.
  • .available() = Сигнализирует о нажатии клавиши или о имеющемся коде в буфере. Возвращает true/false
  • .availableCode() = Аналогично предыдущему, но реагирует на любую поступающую информацию от клавиатуры.
  • .reset() = Сброс/Перезагрузка контроллера клавиатуры.

Заметьте, когда мы нажимаем клавишу и удерживаем, то вначале выводится 1 символ, затем они повторяются, но с небольшой задержкой. Это все хорошо для редактирования текста, для каких-либо действий, нуждающихся в точности. Но как быть с играми или задачами, которым нужна скорость, а не точность? В старом добром QBasic была функция типа: ScanKey, Readkey, KeyCode…

  *Некоторые команды от хоста к клавиатуре*

Код команды Название Описание (все команды см.там)
0xFF Reset Клавиатура отвечает “ACK” – 0xFA, и затем переходит в режим сброса
0xF6 Set Default Устанавливает временные задержки клавиш по-умолчанию и включает набор скан-кодов Scan Code Set 2
0xF5 Disable Клавиатура перестаёт сканировать клавиши, устанавливает временные задержки клавиш по-умолчанию, включает набор скан-кодов Scan Code Set 2 и ждёт дальнейших команд
0xF4 Enable Отменяет команду Disable
0xF3 Set Typematic Rate/Delay  Устанавливает частоту повтора клавиши при длительном нажатии (typematic rate) и задержку перед началом повторов (delay). За этим байтом следует байт аргументов.
0xF0 Set Scan Code set Клавиатура отвечает “ACK”, потом ждет байт аргумента от компьютера. Если байт аргумента 0×01, 0×02 или 0×03, то устанавливается соответствующий набор скан-кодов Scan Code Set. Если байт 0х00, то клавиатура возвращает номер текущего набора. Затем клавиатура отвечает “ACK.
0xEE Echo Клавиатура отвечает “Echo” (0xEE)
0xF8 Set all keys to make/release Отключает typematic, отправляет только информацию о нажатии и отпускании клавиши.

Последняя команда нам и нужна! Но, есть условие, эта команда работает только со Scan Code 3, а стандартно устанавливается 2, для этого используем Scan Code set.

1
2
3
4
5
6
7
8
9
10
11
12
13
void setup() {
  Serial.begin(9600);
  kbd.begin(2); //Инициализация
  kbd.write(0xF0); //Установить Scan Code
  kbd.write(0x03); //...3
  delayMicroseconds(20); //Пропускаем ACK
  kbd.write(0xF8); // Режим make/release
  delayMicroseconds(20); //ACK
}
void loop() {
  byte c = kbd.readCode();
  Serial.println(c, HEX);
}

Теперь при нажатии и отпускании клавиши мы будем получать следующую картину.

 

Для удобства можно создать несколько переменных типа boolean, для каждой участвующей клавиши. Нам нужно при нажатии приводить переменную(клавиши) в значение true, и следить за появлением кода 0xF0, при его наличии ждать код клавиши, а затем обнулить соответствующую переменную. Вот реализация: для ENTER, SPACE и стрелок:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <PS2Keyboard.h>
byte code;
const int DataPin = 2;
PS2Keyboard kbd;
 
boolean K_UP    = false;
boolean K_DOWN  = false;
boolean K_LEFT  = false;
boolean K_RIGHT = false;
boolean K_ENTER = false;
boolean K_SPACE = false;
 
void kbd_init() {
 
  kbd.write(0xF0);
  kbd.write(0x03);
  delayMicroseconds(20);
  kbd.write(0xF8);
  delayMicroseconds(20);
}
 
void setup() {
  kbd.begin(DataPin, true);
  kbd.write(0xF0);
  kbd.write(0x03);
  delayMicroseconds(20);
  kbd.write(0xF8);
  delayMicroseconds(20);
  Serial.begin(9600);
}
 
void readButton() {
 code = kbd.readCodeBuf();
  switch(code) {
     case 0x63: K_UP=!K_UP; break;
     case 0x60: K_DOWN=!K_DOWN; break;
     case 0x61: K_LEFT=!K_LEFT; break;
     case 0x6A: K_RIGHT=!K_RIGHT; break;
     case 0x5A: K_ENTER=!K_ENTER; break;
     case 0x29: K_SPACE=!K_SPACE; break;
     case 0xF0:  break; //Игнорируем F0
 
   }  
}
 
void loop() {
  readButton();
  Serial.println(" ");
  Serial.print("UP ");
  Serial.print(K_UP, DEC);
  Serial.println(" ");
  Serial.print("ENTER ");
  Serial.print(K_ENTER, DEC);
  Serial.println(" ");
  Serial.print("LEFT ");
  Serial.print(K_LEFT, DEC);
}

Собственно говоря, это все. enJoy!  😎

Ссылки:

Вы можите оставить комментарий, или поставить трэкбек со своего сайта.

5 комментариев к “PS2 Клавиатура”

  1. Сергей:

    😕 а почему сразу не прописать в библиотеке символы ❓

    • Символы уже есть в библиотеке. (Только.англ.) Они считываются функцией .read. Если вы об этом ➡

  2. Андрей:

    Хай, я записываю в масив числа а потом хочу вывести их через серийку тик вон ненашол как ❓ вот так нейдет Serial.print(flicker[]);

Написать комментарий

XHTML: Вы можете использовать эти теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Bug Report
Локализовано: шаблоны Wordpress