четверг, 5 мая 2016 г.

USB клавиатура из обычной PS2 (ps/2 to usb converter).

Порт PS/2, до широкого распространения USB, устанавливался в большинстве системных боков компьютеров для взаимодействия этого блока с клавиатурой. После широкого распространения USB, порт PS/2 стал очень редко использоваться и в современных ноутбуках и компьютерах таких портов почти не осталось поэтому, в большинстве случаев, использовать клавиатуру со старым разъёмом PS/2 можно только применяя специальный переходник PS/2 - USB. Протокол PS/2 сильно отличается от протокола USB поэтому нельзя так просто взять и перепаять с разъёма PS/2 на USB и надеяться что это сработает. PS/2 - это простой синхронный последовательный интерфейс не имеющий больших вступительных и заключительных данных, можно даже сказать (не сильно ошибившись) что передача по PS/2 - это просто поток битов (примерно как по SPI). Частота импульсов синхронизации невелика с составляет несколько десятков килогерц. Уровень логической единицы в PS/2 равен 5В, логического нуля 0В. В общем интерфейс PS/2 достаточно прост для того чтобы его можно было самостоятельно реализовать программно на микроконтроллере. Интерфейс USB гораздо сложнее и его программная реализация - это задача очень нетривиальная и требующая большого объёма работы но к счастью для нас одна Австрийская фирма (Objective Development) выпустила библиотеку v-usb с лицензией GNU GPL т.е. бесплатной для коммерческого и некоммерческого использования, а это значит что библиотекой v-usb можно пользоваться бесплатно. Также существует вариант данной библиотеки для Arduino а это значит что написать код для взаимодействия с компьютером по USB можно буквально в несколько строк что сильно упрощает жизнь. Для клавиатуры PS/2 также существует библиотека и при том не одна и использовать их можно на Arduino. Одной из таких библиотек является библиотека PS2KeyAdvanced которую написал Paul Carpenter. По идее всё должно быть просто но использовать две библиотеке в одном скетче для Arduino не получается т.к. они обе используют внешние прерывания. Переделать библиотеку PS2KeyAdvanced для работы без прерываний можно но при этом коды нажатых клавиш будут определяться не всегда поэтому самым простым выходом в данном случае является использование 2х плат Arduno каждая из которых будет "заниматься своим делом" т.е. одна Arduino будет принимать данные с клавиатуры, перерабатывать их (т.к. PS/2 гораздо проще и меньше грузит микроконтроллер) и отсылать на другую которая будет отсылать коды нажатых клавиш по USB на компьютер. Теперь давайте рассмотрим схему:
Рисунок 1 - Переходник PS/2 - USB

С подключением PS/2 клавиатуры нет никаких проблем, она подключается напрямую к Arduino по стандартной схеме. Подключить USB разъём к Arduino можно двумя способами:
1) через стабилитроны,
2) с понижением питания.
Я выбрал вариант 2 т.к. при подключении через стабилитроны могут возникать и возникают часто (судя по переписках в форумах) проблемы. Стабилитроны д.б. маломощными и быстрыми и когда я ранее пытался реализовать данный способ связи то у меня ничего не вышло, пришлось использовать понижение питания диодами и всё заработало (см. статью включение светодиода через usb)! Если Arduino 3х вольтовое то никаких дополнительных мер по снижению напряжения его питания применять не надо, в противном же случае придётся "влезть" в плату паяльником и внеси необходимые изменения. В моём случае изменений вносить не пришлось т.к. я использовал самодельную Arduino на микроконтроллере ATmega8 и питание мог делать любое которое возможно с таким микроконтроллером. Для того чтобы связать две Arduino с разными логическими уровнями по последовательному интерфейсу UART пришлось использовать резисторы с большим сопротивлением т.к. это не не позволит портам микроконтроллеров перегореть.
Теперь давайте рассмотрим скетч для Ардуино работающего с клавиатурой:
#include "PS2KeyAdvanced.h"

#define DATAPIN 7
#define IRQPIN  3

uint16_t c;

PS2KeyAdvanced keyboard;

byte keys[1000];//convert ps2 to usb hid keyboard

void setup()
{
  for(byte i = 0;i<255 i="" p="">  {
    keys[i] = 0;
  }
keyboard.begin( DATAPIN, IRQPIN );
Serial.begin( 9600 );

//2ps to usb hid
keys[65]=0x4;//A
keys[66]=0x5;//B
keys[67]=0x6;//C
keys[68]=0x7;//D
keys[69]=0x8;//E
keys[70]=0x9;//F
keys[71]=0xa;//G
keys[72]=0xb;//H
keys[73]=0xc;//I
keys[74]=0xd;//J
keys[75]=0xe;//K
keys[76]=0xf;//L
keys[77]=0x10;//M
keys[78]=0x11;//N
keys[79]=0x12;//O
keys[80]=0x13;//P
keys[81]=0x14;//Q
keys[82]=0x15;//R
keys[83]=0x16;//S
keys[84]=0x17;//T
keys[85]=0x18;//U
keys[86]=0x19;//V
keys[87]=0x1a;//W
keys[88]=0x1b;//X
keys[89]=0x1c;//Y
keys[90]=0x1d;//Z
keys[49]=0x1e;//1 !
keys[50]=0x1f;//2 @
keys[51]=0x20;//3 #
keys[52]=0x21;//4 $
keys[53]=0x22;//5 %
keys[54]=0x23;//6 ^
keys[55]=0x24;//7 &
keys[56]=0x25;//8 *
keys[57]=0x26;//9 (
keys[48]=0x27;//0 )
keys[0x1e]=0x28;//Enter
keys[0x1b]=0x29;//Escape
keys[0x1c]=0x2a;//BackSpace
keys[0x1d]=0x2b;//Tab
keys[0x1f]=0x2c;//Space
keys[0x16]=0x4f;//Right Arrow
keys[0x15]=0x50;//Left Arrow
keys[0x18]=0x51;//Down Arrow
keys[0x17]=0x52;//Up Arrow
keys[0x9]=0xe4;//Right ctrl
keys[0x8]=0xe0;//Left ctrl
keys[0x7]=0xe5;//Right shift
keys[0x6]=0xe1;//Left shift
keys[0xb]=0xe6;//Right alt
keys[0xa]=0xe2;//Left alt
keys[0x61]=0x3a;//f1
keys[0x62]=0x3b;//f2
keys[0x63]=0x3c;//f3
keys[0x64]=0x3d;//f4
keys[0x65]=0x3e;//f5
keys[0x66]=0x3f;//f6
keys[0x67]=0x40;//f7
keys[0x68]=0x41;//f8
keys[0x69]=0x42;//f9
keys[0x6a]=0x43;//f10
keys[0x6b]=0x44;//f11
keys[0x6c]=0x45;//f12
keys[0x3c]=0x2d;//-_
keys[0x5f]=0x2e;//+=
keys[0x5d]=0x2f;//{[
keys[0x5e]=0x30;//}]
keys[0x5c]=0x31;//|\
keys[0x5b]=0x33;//:;
keys[0x3a]=0x34;//"'
keys[0x3b]=0x36;//<,
keys[0x3d]=0x37;//>.
keys[0x3e]=0x38;//?/
keys[0x4]=0x46;//Print Screen
keys[0x40]=0x35;//`ё
keys[0x19]=0x49;//insert
keys[0x11]=0x4a;//Home
keys[0x13]=0x4b;//Page Up
keys[0x1a]=0x4c;//Delete
keys[0x12]=0x4d;//End
keys[0x14]=0x4e;//Page Down
keys[0x3]=0x39;//Caps Lock
keys[0x2]=0x41;//Scroll Lock
keys[0x1]=0x41;//Num Lock
}


void loop()
{
  if( keyboard.available() )
  {
    c = keyboard.read();
    if( ((c >> 8) != 0x81) && ((c >> 8) != 0x80) && ((c >> 8) != 0xc0) && ((c >> 8) != 0x90) && ((c >> 8) != 0x91))
    {
      Serial.write(keys[c & 0xFF]);
    }
  }
}

Скетч получился довольно длинный т.к. для преобразования кодов клавиш с клавиатуры в коды для отправки по USB используется массив "keys". Это обычный массив который используется необычным способом а точнее как ассоциативный массив и из за этого он избыточен и занимает много места в и без того забитой памяти микроконтроллера но зато дальше можно написать только одну строку не длинного текста для преобразования кодов что очень удобно. В условии в основном цикле проверяется "не отпущена ли кнопка". В данный массив записаны не все коды клавиш, в скетче много "магических чисел" и вообще он далеко не идеален и клавиатура в конце концов не сможет делать всё что могла бы обычная usb клавиатура но данный скетч в открытом доступе и его может исправить и дополнить любой желающий который также может выложить свой скетч в открытый доступ поэтому давайте перейдём с следующему скетчу:

#include "UsbKeyboard.h"

void setup() {
  Serial.begin(9600);

  TIMSK &= !(1  cli();
  usbDeviceDisconnect();
  delayMs(250);
  usbDeviceConnect();
  sei();
}

void loop()
{
  UsbKeyboard.update();

  if (Serial.available() > 0)
  {
    UsbKeyboard.sendKeyStroke(Serial.read());
  }
}

// helper method for V-USB library
void delayMs(unsigned int ms)
{
  for( int i=0; i  {
    delayMicroseconds(1000);
  }
}

Этот скетч короче но совсем не проще. В главной функции настройка прерываний и всего остального, в основном цикле обязательная функция UsbKeyboard.update() которая должна вызываться не реже чем 20 раз в секунду (иначе всё работать не будет), поэтому микроконтроллер с v-usb сильно грузить не рекомендуется но, к счастью для нас (и для этого микроконтроллера), другую серьёзную задачу выполняет другой микроконтроллер который находится в другой Ардуине. После того как по UARTу принимается байт он сразу же отправляется по USB в компьютер и всё это происходит быстрее чем за 50мс судя по тому что всё работает (см. видео ниже). Т.к. данная библиотека реализует класс HID то на компьютер к которому будет подключён данный адаптер с клавиатурой не надо устанавливать никаких драйверов всё должно заработать и на Windows и на Linux. Я же делал данный переходник (адаптер) для Raspberry pi 3 с операционной системой Raspbain и клавиатура с ними работала замечательно (см. видео ниже), с Windows 7 были некоторые проблемы т.к. она решила что нужен драйвер к неизвестному устройству, решила его найти и установить но после того как не получилось вывелось сообщение об этом и дальше клавиатурой можно было спокойно пользоваться. Теперь можно посмотреть видео с испытаниями и некоторой другой полезной информацией:




Первый скетч (ps2) можно скачать по ссылке https://yadi.sk/d/7RgFtn9ErWkXm
Второй (usb hid) по ссылке https://yadi.sk/d/b5mNkEIarWkXn

КАРТА БЛОГА (содержание)

Arduino UNO можно заказать по ссылке http://ali.pub/qtjyr, Atmega8-pu которую (можно превратить в Arduino) по ссылке http://ali.pub/qgw75, Raspberry Pi 3 (на котором испытывалась клавиатура в видео) по ссылке http://ali.pub/91xb2.

4 комментария:

  1. hi, i need to use a USB keyboard into an arduino, so could you please send me the UsbKeyboard.h file?

    im trying to doit, but it does not work :/

    thank you all, luis Ctes

    luisctesf@gmail.com

    ОтветитьУдалить
  2. Вопрос только в том зачем вторая ардуина работает на 3,3В ведь для меги8 и меги328 на порты как раз можно подавать 5В и ничего не сгорит?

    ОтветитьУдалить
    Ответы
    1. Это нужно для согласования уровней с usb. На выводы данных usb нельзя 5В подавать.

      Удалить