Итак я нашел кучу описаний как можно проэмулировать и клавиатуру и мышь на arduino благодаря библиотеке ps2dev найденную на сайте ардуинки http://arduino.cc/playground/componentLib/Ps2mouse. Там описание почти никакое, поэтому необходимо обратиться за помощью к бывалым:
http://robocraft.ru/blog/communication/100.html - наилучшие примеры по работе с ардуино, но в данном случае не совсем то, но по теме.
http://www.computer-engineering.org/ps2protocol/ - описание протокола на английском языке, там же много полезного по теме.
http://marsohod.org/index.php/ourblog/11-blog/56-ps2 - описание протокола переведенное на русский.
На этом описание заканчивается, а проблемы нет!
1. Проблема аккуратности подключения провода с разъемом ps/2. Можно перепутать провода - это понятно. Можно получить плохой контакт, т.е. вроде он есть, но... Когда я просто взял провод от старой мышки и подключил его согласно схеме к ардуинке у меня от компа приходили постоянно reset и resend. Выходит, что arduino получает команды хоста, а отправить ответ правильно не может или не всегда отправляет. Проблема оказалась в плохом контакте, видать искрило. Кстати, я так и не понял нужно ли от порта ps/2 подключать к контролеру "Vcc +5", если контроллер питаетс от usb другого компа? Земля, разумеется общая, поэтому подключение обязательно, а vcc я оставил в итоге в воздухе, походу от нее скорее проблему будут, если конечно устройство не должно питаться от ps/2.
2. Данные вроде пошли верно, но устройство зависает, хотя инициализацию проходит. Стал разбираться. В описании протокола нашел следующее: "Контроллер материнской платы может сигнализировать устройству о невозможности приема опустив сигнал Clock в логический ноль. На практике этого по моему тоже никто не делает". Проверка путем подключения ардуинки к другому компу доказала мое предположение - тут все завелось сразу. Так в чем же дело? Демонстрационный скетч, который я использовал, выводил информацию чрез com-порт на ноутбук, которым я заодно и перепрошивал ардуинку. Одним из выводимых параметров был текст на каком Pin`е был опущен сигнал, когда программа начинает прием данных от хоста, а затем только принятый байт команды от хоста. Собственно, во время инициализации все шло хорошо, т.к. хост постоянно что-то запрашивал у устройства, а затем хост "Должен" поднимать clock и data, как бы говоря, что просто слушает порт. Но в поднятом состоянии оба pin`а находились всего пару секунд. Затем опускался clock и программа переходила в режим приема данных от хоста и все... Выходит это и есть тот самый случай, когда контроллер хоста запрещает передавать даные. Т.е. один мой комп постоянно слушает порт, а другой переиодически хочет отдохнуть - непорядок!
Смотрим код библиотеки ps2dev (C:\arduino-1.0.1\libraries\ps2dev\ps2dev.cpp), а главное команду read():
Код: Выделить всё
//wait for data line to go low
while (digitalRead(_ps2data) == HIGH) {
}
//wait for clock line to go high
while (digitalRead(_ps2clk) == LOW) {
}
Первая команда ожидает когда data будет опущен на землю, но поскольку хост и не собирался передавать данные, то это не происходит до тех пор, когда хосту действительно не понадобиться передать данные (например, при перезагрузке компа). Получается, что это и есть точка, где подвисает программа. Согласно протоколу, должен быть сперва опущен clock, затем data, затем, они оба поднимаются и ожидается подтверждение приема. Данный код подразумевает, что передача данных должна обязательно начаться, а вот нет. Нам надо, если data так и не опущен, а clock все-равно подняли - отменить прием данных.
Патчим бибилиотеку до:
Код: Выделить всё
//wait for data line to go low
while (digitalRead(_ps2data) == HIGH) {
if(digitalRead(_ps2clk) == HIGH){
*value=0;
return 0;
}
}
//wait for clock line to go high
while (digitalRead(_ps2clk) == LOW) {
}
Теперь, если Хост решит отдохнуть (а зачем?), процедура чтения вернет "0х00". А может это хост и хотел передать? Интересно, что если в общем теле loop(), в конце, поставить delay(1), то таких пустых команд от хоста будет несколько, а вот если поставить delay(10), то будет отработана только одна пустая команда, а остальные пропущены. Собственно, все-равно, если хост опустил clock, то устройство не должно передавать данные согласно протоколу. Далее у нас будет несколько секунд, когда хост будет покорно слушать устройство. Если не нужна повышенная скорость реакции, то можно тут более не беспокоиться.
Собственно сам скетч для проверки/отладки эмуляции:
Код: Выделить всё
#include "ps2dev.h"
// Orange = 2
// Blue = 3
// Red = 5V (3 in)
// Black = GND (4 in)
// EXT Power, USB for COM only
PS2dev keyboard(3,2); // PS2dev object (2:data, 3:clock)
int enabled = 0; // pseudo variable for state of “keyboard”
boolean serialConnected = false;
int incomingByte = 0;
int tempint=0;
void ack() {
//acknowledge commands
while(keyboard.write(0xFA));
}
int kbdCmd(int command) {
unsigned char val;
switch (command) {
case 0xFF: //reset
ack();
//the while loop lets us wait for the host to be ready
while(keyboard.write(0xAA)!=0);
break;
case 0xFE: //resend
ack();
break;
case 0xF6: //set defaults
//enter stream mode
ack();
break;
case 0xF5: //disable data reporting
//FM
enabled = 0;
ack();
break;
case 0xF4: //enable data reporting
//FM
enabled = 1;
ack();
break;
case 0xF3: //set typematic rate
ack();
keyboard.read(&val); //do nothing with the rate
ack();
break;
case 0xF2: //get device id
ack();
keyboard.write(0xAB);
keyboard.write(0x83);
break;
case 0xF0: //set scan code set
ack();
keyboard.read(&val); //do nothing with the rate
ack();
break;
case 0xEE: //echo
//ack();
keyboard.write(0xEE);
break;
case 0xED: //set/reset LEDs
ack();
keyboard.read(&val); //do nothing with the rate
ack();
break;
}
}
void connectHost() {
while (Serial.available() <= 0) {
Serial.print("A"); // send a capital A
delay(300);
}
}
void setup() {
pinMode(13, OUTPUT);
//establish serial connection with host
Serial.begin(9600);
// establish ps/2 connection with target
while(keyboard.write(0xAA)!=0){
digitalWrite(13, HIGH);
delay(500);
digitalWrite(13, LOW);
delay(500);
}
delay(100);
connectHost();
Serial.println("\nSerial Host Connected");
Serial.flush();
}
int SerialreadHEX(){
byte c = Serial.read();
if(c>-'0' && c<='9'){
return c - '0';
}
else if (c>='a' && c<='f'){
return c - 'a'+10;
}
else if (c>='a' && c<='f'){
return c - 'a'+10;
}
else if (c>='A' && c<='F'){
return c - 'A'+10;
}
else {
return -1;
}
}
void loop() {
unsigned char c;
if( (digitalRead(3)==LOW) || (digitalRead(2) == LOW)) {
if(digitalRead(3)==LOW){
Serial.println("pin 3 is LOW");
}
else {
Serial.println("pin 2 is LOW");
}
while(keyboard.read(&c));
kbdCmd(c);
Serial.print("Target: 0x");
Serial.println(c, HEX);
}
else {//if host device wants to send a command:
//echo ASCII code from terminal and write to ps/2
if(Serial.available() > 0) {
//incomingByte = Serial.read();
incomingByte=SerialreadHEX();
delay(5);
if(incomingByte>-1 && Serial.available() > 0) {
tempint=SerialreadHEX();
if (tempint>-1){
incomingByte=(incomingByte*16)+tempint;
}
}
keyboard.write(incomingByte);
Serial.print("ost: 0x");
Serial.print(incomingByte, HEX);
Serial.print(" ");
Serial.print(incomingByte);
Serial.print(" ");
Serial.println(incomingByte, BIN);
}
}
delay(10);
}
Изначально код был написан неким автором (прошу прощения, потерял где взял) и доработан немного мной для более удобной отладки. Сперва устройство будет ждать инициализации com-порта, т.е. будет на него слать "а", до тех пор пока небудт получен ответ от порта в виде любого байта. Как-только в буфере контроллера окажется хоть один байт, наш эмулятор заработает. Включем испытуемый комп. Далее в "мониторе порта" мы видим как комп инициализирует клаву. Винда тоже будет ее инициализировать когда загрузится. Теперь мы можем тоже слать данные.
http://www.computer-engineering.org/ps2keyboard/scancodes2.html - тут мы видим коды клавишь. Разумеется они никак не соответсвуют кодировкам. Поэтому я сделал прием по два байта с com-порта, представляющих собой HEX код клавиши, которая будет отправлена хосту. Например вводим "1СА01С", будет проинтерпритировано как нажатие клавиши "a" (0х1C), и отпускание ее (0xF0, 0x1C). В открытом блокноте (если конечно его открыли), можно увидеть появившийся символ "a".
Вариантов использования море! Главное наконец заработало.
Далее добавлю скетч для тестирования мышки.