Предлагается простая схема и программное обеспечение для универсального безвейтового контроллера PS/2 клавиатуры, подходящего для большинства "древних" бытовых компьютеров типа Спектрум, Орион, Корвет и других, в конструкции которых использовалась матричная клавиатура размером до 8*16. Разведены два варианта печатных плат.
Schematic_KbCtrl_2021-04-17.pdf
Gerber_PCB_KbCtrl_2021-03-06 (1).zip
Gerber_PCB_2020-10-21_18-11-37_2020-10-21_20-02-12 (1).zip
Код написан в среде Ардуино для ядра MiniCore, которое позволяет работать с Атмега8, 48, 168, 328 без кварца , но будет работать и на стандартном Ардуино Нано или Уно. Я использовал Атмегу168. Второй компонент системы - матрица аналоговых ключей MT8816. На Али их продают сотнями по бросовым ценам. Программа проста и написана в индийском стиле. Она содержит массив, который устанавливает соответствие между сканкодом и кодом активации соответствующего ключа матрицы.
Рекомендации по сборке.
Подключение к Спектруму.
Как модифицировать прошивку.
Как прошить:
Исходник программы для клавиатуры Спектрума
Скрытый текст
Код:#include <PS2KeyRaw.h> #define AX0 4 #define AX1 5 #define AX2 6 #define AX3 7 #define AY0 8 #define AY1 9 #define AY2 10 #define RSTMT 14 #define CSMT 15 #define DATMT 16 #define STBMT 17 #define DATAPIN 2 #define IRQPIN 3 volatile boolean d = HIGH; uint8_t table[128] = {127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,0,127,127,32,48,127,127,127 , 1,17,16,33,49,127,127,3,2,18,34,51,50,127,127,112,4,19,36,35,52,127,127,115,116,100,20,84,68,127,127,127,114,99,83,67,66, 127,127,127,98,82,81,64,65,127,127,127,127,97,127,80,127,127,127,127,127,127,127,127,127,127,127,113,96,127,127,127,127,127,127,127,127,127,127,127, 127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127}; PS2KeyRaw keyboard; void setup() { //Инициализация портов pinMode(AX0, OUTPUT); //AX0 pinMode(AX1, OUTPUT); //AX1 pinMode(AX2, OUTPUT); //AX2 pinMode(AX3, OUTPUT); //AX3 pinMode(AY0, OUTPUT); //AY0 pinMode(AY1, OUTPUT); //AY1 pinMode(AY2, OUTPUT); //AY2 pinMode(RSTMT, OUTPUT); //RES pinMode(CSMT, OUTPUT); //CS pinMode(DATMT, OUTPUT); //DAT pinMode(STBMT, OUTPUT); //STB //Инициализация клавиатуры keyboard.begin( DATAPIN, IRQPIN ); //Инициализация MT8816 SetAddr(0); digitalWrite(RSTMT, LOW); digitalWrite(CSMT, LOW); digitalWrite(DATMT, LOW); digitalWrite(STBMT, LOW); //инициализация digitalWrite(CSMT, HIGH); //выбор чипа digitalWrite(RSTMT, HIGH); digitalWrite(RSTMT, LOW); //сброс digitalWrite(CSMT, LOW); } void SetAddr(uint8_t addr) { digitalWrite(AX0,bitRead(addr,0)); digitalWrite(AX1,bitRead(addr,1)); digitalWrite(AX2,bitRead(addr,2)); digitalWrite(AX3,bitRead(addr,3)); digitalWrite(AY0,bitRead(addr,4)); digitalWrite(AY1,bitRead(addr,5)); digitalWrite(AY2,bitRead(addr,6)); } void SetKey(boolean data){ digitalWrite(CSMT, HIGH); //выбор чипа digitalWrite(STBMT, HIGH); //строб on digitalWrite(DATMT, data); //данные digitalWrite(STBMT, LOW); //строб off digitalWrite(CSMT, LOW); } void loop() { if( keyboard.available() ){ int c = keyboard.read(); //чтение кода switch (c) { case 0xE0: //если считался префикс 0xE0 break; case 0xF0: //если считался префикс 0xF0 (отпускание клавиши) d = LOW; break; default: SetAddr(table[c]); SetKey(d); d = HIGH; } } }[свернуть]
Исходник для клавиатуры Спектрум 128
Скрытый текст
Код:#include <PS2KeyRaw.h> #define AX0 4 #define AX1 5 #define AX2 6 #define AX3 7 #define AY0 8 #define AY1 9 #define AY2 10 #define RSTMT 14 #define CSMT 15 #define DATMT 16 #define STBMT 17 #define DATAPIN 2 #define IRQPIN 3 volatile boolean d = HIGH; uint8_t table[128] = {127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,0,127,127,32,48,127,127,127,1,17,16,33,49,127, 127,3,2,18,34,51,50,127,127,112,4,19,36,35,52,127,127,115,116,100,20,84,68,127,127,127,114,99,83,67,66,127, 127,127,98,82,81,64,65,127,127,127,127,97,127,80,127,127,127,127,127,127,127,127,127,127,127,113,96,127,127,127,127,127, 127,127,127,127,127,127,64,127,127,127,127,52,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127}; PS2KeyRaw keyboard; void setup() { //Инициализация портов pinMode(AX0, OUTPUT); //AX0 pinMode(AX1, OUTPUT); //AX1 pinMode(AX2, OUTPUT); //AX2 pinMode(AX3, OUTPUT); //AX3 pinMode(AY0, OUTPUT); //AY0 pinMode(AY1, OUTPUT); //AY1 pinMode(AY2, OUTPUT); //AY2 pinMode(RSTMT, OUTPUT); //RES pinMode(CSMT, OUTPUT); //CS pinMode(DATMT, OUTPUT); //DAT pinMode(STBMT, OUTPUT); //STB //Инициализация клавиатуры keyboard.begin( DATAPIN, IRQPIN ); //Инициализация MT8816 SetAddr(0); digitalWrite(RSTMT, LOW); digitalWrite(CSMT, LOW); digitalWrite(DATMT, LOW); digitalWrite(STBMT, LOW); //инициализация digitalWrite(CSMT, HIGH); //выбор чипа digitalWrite(RSTMT, HIGH); digitalWrite(RSTMT, LOW); //сброс digitalWrite(CSMT, LOW); SetAddr(table[0x12]); SetKey(LOW); } void SetAddr(uint8_t addr) { digitalWrite(AX0,bitRead(addr,0)); digitalWrite(AX1,bitRead(addr,1)); digitalWrite(AX2,bitRead(addr,2)); digitalWrite(AX3,bitRead(addr,3)); digitalWrite(AY0,bitRead(addr,4)); digitalWrite(AY1,bitRead(addr,5)); digitalWrite(AY2,bitRead(addr,6)); } void SetKey(boolean data){ digitalWrite(CSMT, HIGH); //выбор чипа digitalWrite(STBMT, HIGH); //строб on digitalWrite(DATMT, data); //данные digitalWrite(STBMT, LOW); //строб off digitalWrite(CSMT, LOW); } void loop() { if( keyboard.available() ){ int c = keyboard.read(); //чтение кода switch (c) { case 0xE0: //если считался префикс 0xE0 break; case 0xF0: //если считался префикс 0xF0 (отпускание клавиши) d = LOW; break; case 0x66: //если считался код 0x66 [BS] SetAddr(table[0x12]); SetKey(d); SetAddr(table[0x45]); SetKey(d); d = HIGH; break; case 0x6B: //если считался код 0x6B [Left] SetAddr(table[0x12]); SetKey(d); SetAddr(table[0x2E]); SetKey(d); d = HIGH; break; case 0x72: //если считался код 0x72 [Down] SetAddr(table[0x12]); SetKey(d); SetAddr(table[0x36]); SetKey(d); d = HIGH; break; case 0x75: //если считался код 0x75 [Up] SetAddr(table[0x12]); SetKey(d); SetAddr(table[0x3D]); SetKey(d); d = HIGH; break; case 0x74: //если считался код 0x74 [Right] SetAddr(table[0x12]); SetKey(d); SetAddr(table[0x3E]); SetKey(d); d = HIGH; break; case 0x76: //если считался код 0x76 [Edit] SetAddr(table[0x12]); SetKey(d); SetAddr(table[0x16]); SetKey(d); d = HIGH; break; case 0x58: //если считался код 0x58 [Caps lock] SetAddr(table[0x12]); SetKey(d); SetAddr(table[0x1E]); SetKey(d); d = HIGH; break; case 0x0D: //если считался код 0x0D [Ext mode] SetAddr(table[0x12]); SetKey(d); SetAddr(table[0x59]); SetKey(d); d = HIGH; break; case 0x41: //если считался код 0x41 [,] SetAddr(table[0x59]); SetKey(d); SetAddr(table[0x31]); SetKey(d); d = HIGH; break; case 0x49: //если считался код 0x49 [.] SetAddr(table[0x59]); SetKey(d); SetAddr(table[0x3A]); SetKey(d); d = HIGH; break; case 0x4C: //если считался код 0x4C [;] SetAddr(table[0x59]); SetKey(d); SetAddr(table[0x44]); SetKey(d); d = HIGH; break; case 0x52: //если считался код 0x52 ["] SetAddr(table[0x59]); SetKey(d); SetAddr(table[0x4D]); SetKey(d); d = HIGH; break; default: SetAddr(table[c]); SetKey(d); d = HIGH; } } }[свернуть]
Новая версия прошивки для Спектрум 128
Скрытый текст
Код:#include <PS2KeyRaw.h> #define AX0 4 #define AX1 5 #define AX2 6 #define AX3 7 #define AY0 8 #define AY1 9 #define AY2 10 #define RSTMT 14 #define CSMT 15 #define DATMT 16 #define STBMT 17 #define DATAPIN 2 #define IRQPIN 3 volatile boolean d = HIGH; uint8_t table[128] = {127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,0,127,127,32,48,127,127,127,1,17,16,33,49,127, 127,3,2,18,34,51,50,127,127,112,4,19,36,35,52,127,127,115,116,100,20,84,68,127,127,127,114,99,83,67,66,127, 127,127,98,82,81,64,65,127,127,127,127,97,127,80,127,127,127,127,127,127,127,127,127,127,127,113,96,127,127,127,127,127, 127,127,127,127,127,127,64,127,127,127,127,52,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127}; PS2KeyRaw keyboard; void setup() { //Инициализация портов pinMode(AX0, OUTPUT); //AX0 pinMode(AX1, OUTPUT); //AX1 pinMode(AX2, OUTPUT); //AX2 pinMode(AX3, OUTPUT); //AX3 pinMode(AY0, OUTPUT); //AY0 pinMode(AY1, OUTPUT); //AY1 pinMode(AY2, OUTPUT); //AY2 pinMode(RSTMT, OUTPUT); //RES pinMode(CSMT, OUTPUT); //CS pinMode(DATMT, OUTPUT); //DAT pinMode(STBMT, OUTPUT); //STB //Инициализация клавиатуры keyboard.begin( DATAPIN, IRQPIN ); //Инициализация MT8816 SetAddr(0); digitalWrite(RSTMT, LOW); digitalWrite(CSMT, LOW); digitalWrite(DATMT, LOW); digitalWrite(STBMT, LOW); InitMt(); } //Сброс MT void InitMt() { digitalWrite(STBMT, HIGH); //инициализация digitalWrite(CSMT, HIGH); //выбор чипа digitalWrite(RSTMT, HIGH); digitalWrite(RSTMT, LOW); //сброс digitalWrite(CSMT, LOW); digitalWrite(STBMT, LOW); } void SetAddr(uint8_t addr) { digitalWrite(AX0,bitRead(addr,0)); digitalWrite(AX1,bitRead(addr,1)); digitalWrite(AX2,bitRead(addr,2)); digitalWrite(AX3,bitRead(addr,3)); digitalWrite(AY0,bitRead(addr,4)); digitalWrite(AY1,bitRead(addr,5)); digitalWrite(AY2,bitRead(addr,6)); } void SetKey(boolean data){ digitalWrite(CSMT, HIGH); //выбор чипа digitalWrite(STBMT, HIGH); //строб on digitalWrite(DATMT, data); //данные digitalWrite(STBMT, LOW); //строб off digitalWrite(CSMT, LOW); } void loop() { if( keyboard.available() ){ int c = keyboard.read(); //чтение кода switch (c) { case 0xE1: //Если считался префикс 0xE1 сброс MK InitMt(); break; case 0xE0: //если считался префикс 0xE0 break; case 0xF0: //если считался префикс 0xF0 (отпускание клавиши) d = LOW; break; case 0x66: //если считался код 0x66 [BS] SetAddr(table[0x12]); SetKey(d); SetAddr(table[0x45]); SetKey(d); d = HIGH; break; case 0x6B: //если считался код 0x6B [Left] SetAddr(table[0x12]); SetKey(d); SetAddr(table[0x2E]); SetKey(d); d = HIGH; break; case 0x72: //если считался код 0x72 [Down] SetAddr(table[0x12]); SetKey(d); SetAddr(table[0x36]); SetKey(d); d = HIGH; break; case 0x75: //если считался код 0x75 [Up] SetAddr(table[0x12]); SetKey(d); SetAddr(table[0x3D]); SetKey(d); d = HIGH; break; case 0x74: //если считался код 0x74 [Right] SetAddr(table[0x12]); SetKey(d); SetAddr(table[0x3E]); SetKey(d); d = HIGH; break; case 0x76: //если считался код 0x76 [Edit] SetAddr(table[0x12]); SetKey(d); SetAddr(table[0x16]); SetKey(d); d = HIGH; break; case 0x58: //если считался код 0x58 [Caps lock] SetAddr(table[0x12]); SetKey(d); SetAddr(table[0x1E]); SetKey(d); d = HIGH; break; case 0x0D: //если считался код 0x0D [Ext mode] SetAddr(table[0x12]); SetKey(d); SetAddr(table[0x59]); SetKey(d); d = HIGH; break; case 0x41: //если считался код 0x41 [,] SetAddr(table[0x59]); SetKey(d); SetAddr(table[0x31]); SetKey(d); d = HIGH; break; case 0x49: //если считался код 0x49 [.] SetAddr(table[0x59]); SetKey(d); SetAddr(table[0x3A]); SetKey(d); d = HIGH; break; case 0x4C: //если считался код 0x4C [;] SetAddr(table[0x59]); SetKey(d); SetAddr(table[0x44]); SetKey(d); d = HIGH; break; case 0x52: //если считался код 0x52 ["] SetAddr(table[0x59]); SetKey(d); SetAddr(table[0x4D]); SetKey(d); d = HIGH; break; default: SetAddr(table[c]); SetKey(d); d = HIGH; } } }[свернуть]
Исходник программы для клавиатуры Корвета
Скрытый текст
Код:#include <PS2KeyRaw.h> #define AX0 4 #define AX1 5 #define AX2 6 #define AX3 7 #define AY0 8 #define AY1 9 #define AY2 10 #define RSTMT 14 #define CSMT 15 #define DATMT 16 #define STBMT 17 #define DATAPIN 2 #define IRQPIN 3 volatile boolean d = HIGH; uint8_t table[128] = {127,127,127,44,42,40,41,127,127,127,127,127,43,102,127,127,127,127,112,127,117,33,65,127,127,127,50,35,1,39,66,127, 127,3,48,4,5,68,67,127,127,103,38,6,36,34,69,127,127,22,2,16,7,49,70,127,127,127,21,18,37,71,80,127,127,127,19,17,23,64,81,127,127,127,127,20,127,32, 127,127,127,127,127,127,127,127,127,127,127,119,96,127,127,127,127,127,127,127,127,127,127,127,101,127,127,9,127,12,15,127,127,127,8,22,10,13,14,24, 115,127,127,127,11,127,127,25,127,127}; PS2KeyRaw keyboard; void setup() { //Инициализация портов pinMode(19, OUTPUT); pinMode(AX0, OUTPUT); //AX0 pinMode(AX1, OUTPUT); //AX1 pinMode(AX2, OUTPUT); //AX2 pinMode(AX3, OUTPUT); //AX3 pinMode(AY0, OUTPUT); //AY0 pinMode(AY1, OUTPUT); //AY1 pinMode(AY2, OUTPUT); //AY2 pinMode(RSTMT, OUTPUT); //RES pinMode(CSMT, OUTPUT); //CS pinMode(DATMT, OUTPUT); //DAT pinMode(STBMT, OUTPUT); //STB //Инициализация клавиатуры keyboard.begin( DATAPIN, IRQPIN ); //Инициализация MT8816 SetAddr(0); digitalWrite(RSTMT, LOW); digitalWrite(CSMT, LOW); digitalWrite(DATMT, LOW); digitalWrite(STBMT, LOW); //инициализация digitalWrite(CSMT, HIGH); //выбор чипа digitalWrite(RSTMT, HIGH); digitalWrite(RSTMT, LOW); //сброс digitalWrite(CSMT, LOW); } void SetAddr(uint8_t addr) { digitalWrite(AX0,bitRead(addr,0)); digitalWrite(AX1,bitRead(addr,1)); digitalWrite(AX2,bitRead(addr,2)); digitalWrite(AX3,bitRead(addr,3)); digitalWrite(AY0,bitRead(addr,4)); digitalWrite(AY1,bitRead(addr,5)); digitalWrite(AY2,bitRead(addr,6)); } void SetKey(boolean data){ digitalWrite(CSMT, HIGH); //выбор чипа digitalWrite(STBMT, HIGH); //строб on digitalWrite(DATMT, data); //данные digitalWrite(STBMT, LOW); //строб off digitalWrite(CSMT, LOW); } void loop() { if( keyboard.available() ){ int c = keyboard.read(); //чтение кода switch (c) { case 0xE0: //если считался префикс 0xE0 break; case 0xF0: //если считался префикс 0xF0 (отпускание клавиши) d = LOW; break; default: SetAddr(table[c]); SetKey(d); d = HIGH; } } }[свернуть]
Исходник программы для УКНЦ Электроника МС 0511 (клавиатура МС 7007)
Скрытый текст
Код://UKNZ #include <PS2KeyRaw.h> #define AX0 4 #define AX1 5 #define AX2 6 #define AX3 7 #define AY0 8 #define AY1 9 #define AY2 10 #define RSTMT 14 #define CSMT 15 #define DATMT 16 #define STBMT 17 #define DATAPIN 2 #define IRQPIN 3 #define LAT 0 #define EXT 1 #define RUS 2 volatile boolean d = HIGH; volatile uint8_t lang = LAT; volatile uint8_t e = lang; uint8_t table[3][132] = {{127,116,127,8,5,3,4,127,127,11,100,117,7,17,0,127, 127,65,64,127,33,50,19,127,127,127,106,68,53,52,20,127, 127,35,72,42,22,6,21,127,127,70,90,34,71,55,23,127, 127,39,57,105,40,51,24,127,127,127,69,18,36,9,10,127, 127,74,37,54,56,121,122,127,127,88,118,41,2,38,67,127, 127,127,58,127,25,120,127,127,66,49,102,26,127,89,127,127, 127,127,127,127,127,127,85,127,127,82,127,83,80,127,127,127, 81,97,98,99,115,96,1,127,127,84,114,16,84,112,127,127, 127,127,127,101}, {127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127, 127,49,127,127,33,127,127,127,127,127,127,127,127,127,127,127, 127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127, 127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127, 127,127,127,127,127,127,127,127,127,127,119,127,127,127,127,127, 127,127,127,127,127,127,127,127,127,127,113,127,127,127,127,127, 127,127,127,127,127,127,127,127,127,127,127,73,127,127,127,127, 127,127,87,127,86,103,127,127,127,127,127,127,127,127,127,127, 127,127,127,127}, {127,116,127,8,5,3,4,127,127,127,100,117,7,17,127,127, 127,65,64,127,33,18,19,127,127,127,50,51,34,35,20,127, 127,68,67,52,36,6,21,127,127,70,69,53,22,37,23,127, 127,71,54,55,38,39,24,127,127,127,72,56,40,9,10,127, 127,57,41,25,26,121,122,127,127,58,119,42,90,106,67,127, 127,127,89,127,105,120,127,127,66,49,102,104,127,127,127,127, 127,127,127,127,127,127,85,127,127,82,127,83,80,127,127,127, 81,97,98,99,115,96,1,127,127,127,114,16,84,112,127,127, 127,127,127,101}}; PS2KeyRaw keyboard; void setup() { //Инициализация портов pinMode(AX0, OUTPUT); //AX0 pinMode(AX1, OUTPUT); //AX1 pinMode(AX2, OUTPUT); //AX2 pinMode(AX3, OUTPUT); //AX3 pinMode(AY0, OUTPUT); //AY0 pinMode(AY1, OUTPUT); //AY1 pinMode(AY2, OUTPUT); //AY2 pinMode(RSTMT, OUTPUT); //RES pinMode(CSMT, OUTPUT); //CS pinMode(DATMT, OUTPUT); //DAT pinMode(STBMT, OUTPUT); //STB //Инициализация клавиатуры keyboard.begin( DATAPIN, IRQPIN ); //Инициализация MT8816 SetAddr(0); digitalWrite(RSTMT, LOW); digitalWrite(CSMT, LOW); digitalWrite(DATMT, LOW); digitalWrite(STBMT, LOW); InitMt(); } //Сброс MT void InitMt() { digitalWrite(STBMT, HIGH); //инициализация digitalWrite(CSMT, HIGH); //выбор чипа digitalWrite(RSTMT, HIGH); digitalWrite(RSTMT, LOW); //сброс digitalWrite(CSMT, LOW); digitalWrite(STBMT, LOW); } //Установка адреса MT void SetAddr(uint8_t addr) { digitalWrite(AX0,bitRead(addr,0)); digitalWrite(AX1,bitRead(addr,1)); digitalWrite(AX2,bitRead(addr,2)); digitalWrite(AX3,bitRead(addr,3)); digitalWrite(AY0,bitRead(addr,4)); digitalWrite(AY1,bitRead(addr,5)); digitalWrite(AY2,bitRead(addr,6)); } //Установка ключа MT void SetKey(boolean data){ digitalWrite(CSMT, HIGH); //выбор чипа digitalWrite(STBMT, HIGH); //строб on digitalWrite(DATMT, data); //данные digitalWrite(STBMT, LOW); //строб off digitalWrite(CSMT, LOW); } void loop() { if( keyboard.available() ){ int c = keyboard.read(); //чтение кода switch (c) { case 0xE1: //Если считался префикс 0xE1 сброс MK InitMt(); break; case 0xE0: //Если считался префикс 0xE0 e = EXT; //тогда код будет из table[1] break; case 0xF0: //Если считался префикс 0xF0 (отпускание клавиши) d = LOW; //тогда готовимся записывать "0" в MT break; case 0x78: //Если считался код 0x78 [F11] lang = RUS; //переключаемся на русскую раскладку e = lang; d = HIGH; //помним о 0xF0 при отпускании [F11], break; //фактически раскладка переключается дважды. case 0x07: //Если считался код 0x07 [F12] lang = LAT; //переключаемся на латинскую раскладку e = lang; d = HIGH; //помним о 0xF0 при отпускании [F12] break; default: SetAddr(table[e][c]); //получаем адрес ключа из текущей таблицы SetKey(d); //переключаем ключ в состояние d d = HIGH; //если было отжатие ключа, принудительно переводим в нажатие e = lang; //если был код из table[1], возвращаем на текущую раскладку } } }[свернуть]
Схема подключения:
J2(8..1) контроллера >> XS3(8..1) УКНЦ, J3(12..1) контроллера >> XS4(12..1) УКНЦ, J3(13) контроллера >> XS3(9) УКНЦ.
Прошивка для Орион-128 от камрада Helbr
Доработанная прошивка для Корвета от камрада surinm.
Прошивка для Вектор-06ц от камрада Improver.
Вполне можно объединить поддержку нескольких клавиатур в одной прошивке и переключать их горячими клавишами.
Если сия поделка кого-то заинтересует, могу рассказать о ней подробнее и ответить на вопросы. У меня осталось несколько пустых плат и даже компоненты для сборки, могу создать тему на барахолке.
Продажа здесь.
Идею данного контроллера подсказал andykarpov