10

Yes, I've searched the Arduino.cc forums and here.  Yes, I've found the articles regarding the ps2dev library.  Yes, I've read (okay, some I skimmed) the definitive PS/2 interface article at this website. Yes, I have this working, kinda.  I need some ideas for making the leap to fully working.  :)

No, I can't just emulate a USB HID Keyboard and leave it at that--it needs to be PS/2 Keyboard emulation.  Yes, I am sending proper make and break signals--it even handles very complicated keystroke combinations.  As it stands right now, I have code written for my Arduino as posted below (technically a Freeduino 1.22), and I've sent keystrokes via the Serial Monitor or PuTTY terminal, as well as with a handy Python wrapper/driver which sends actual PS/2 scancode information--and generally makes my life much easier--also taking some of the load off the Arduino.

Right now, I have a sketch running on the Arduino which emulates a PS/2 Keyboard.  Naturally, I have to boot my "target" machine (machine that PS/2 Plug goes into), and I see the "handshake" take place.  Boot to WinDoze, open notepad, and drive keystrokes to the screen (succesfully) using my Python "driver".  (The driver simply takes place of the Serial Monitor/PuTTY terminal and reads/writes to the serial port using a module called PySerial.)  This is all done on a AMD in ASUS motherboard "target".

Now, the goal is to get it working on my Intel in Intel motherboard based "target", I plug it in, boot, and no dice.  So, I modified the sketch a bit to try and give myself a heads-up of what's actually going on on my little Ardy friend.  The version after the mods is displayed below.  As I understand it (code was "borrowed" from another Arduino.cc forum post, here) It will try and establish a connection with the "target" over PS/2 first, blinking the onboard LED at a .5 second period until the connection is established.  The Intel target doesn't get past the .5 second period blinks and the Serial Connection is never established with the "host".

My question is this: is there a major difference in the way ps/2 keyboards establish communication with their target machine?  Is it really a design difference or should I be looking for something more basic that's the issue here?  I've heard something about needing pull-up resistors on the data/clock inputs, but that should be handled in the code, especially because it's WORKING on another target, just not the one I need it to work on.

Any ideas?  I'd love to get this working ASAP--I'm going to keep doing debug, any pointers or suggestions would be greatly appreciated.  They will all be given full consideration because I need some fresh eyes on this issue.  Perhaps better implementation in the ps2dev library is needed?

#include "ps2dev.h" // to emulate a PS/2 device

// 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;

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();
}

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();
      keyboard.write(incomingByte);      
      Serial.print("Host: 0x");
      Serial.print(incomingByte, HEX);
      Serial.print(" ");
      Serial.print(incomingByte);
      Serial.print(" ");
      Serial.println(incomingByte, BIN);
    }
  }
}
Kortuk
  • 13,362
  • 8
  • 60
  • 85
chisaipete
  • 201
  • 2
  • 5
  • A few questions: "Sketch" is Arduino-lingo for "program"? This python driver stuff is independent of the target machine, right? Your problem is that it works on the one target machine and not on the other, right? Did you try booting the non-working target with a PS/2 keyboard attached and then swap that with the Arduino? – AndreKR Jun 17 '11 at 01:59
  • Yes, Sketch == program in Ardu-lingo. I tried this and it didn't seem to work (but I need to modify the sketch so it doesn't wait for the ACKs from the target before sending characters.) I'll let you know when I get the chance to test it later today. – chisaipete Jun 21 '11 at 17:47
  • So, I tested the program as you suggested, and it works! In the end I'd like to be able to power cycle the target with the keyboard emulator installed and be able to change BIOS settings with it. So, I'm thinking the start-up handshake is off? – chisaipete Jun 21 '11 at 19:42
  • Yes, probably. Did you see the initialization sequence at the very bottom of http://www.computer-engineering.org/ps2keyboard/? I'd start by comparing my sequence to that. – AndreKR Jun 21 '11 at 23:21
  • Yes I saw that, but thanks for pointing it out again. I'll do a comparison later today. In order to make the comparison I need more debug information--I'm going to setup a second SoftSerial Connection which will be able to dump all the communication both directions while I'm interfacing with the target. – chisaipete Jun 22 '11 at 17:48
  • If you put a series resistor on the end where the pullup is *not*, you can tell by the voltage level who is talking. On a DSO you can see both, on a LA you have to record twice with different logic levels and compare the recordings. – AndreKR Jun 22 '11 at 18:07
  • @AndreKR, can you make an answer out of your help. It seems you solved it! I will throw in 50 rep for you to do so. – Kortuk Sep 23 '11 at 22:39
  • 1
    Sorry, I've let this thread go stale--I haven't had time to try out AndreKR's solution. Besides, I'm not using pullup resistors, so it's difficult to determine which end does not have pullup resistors :) – chisaipete Sep 23 '11 at 23:25
  • Sounds like you need a logic analyser. – Connor Wolf Sep 25 '11 at 11:19
  • I can't help thinking you'd be able to solve this in a few minutes with a logic analyzer. Considered getting one? The Logic by Saleae is cheap and awesome. – Nick Johnson Sep 26 '11 at 02:01
  • @Kortuk Done. :) – AndreKR Sep 28 '11 at 20:10
  • Quick question: how were you connecting the pins from the Arduino to the PS/2 input port? Are you connecting only the DATA and CLK pins? What about VCC and GND? – kolrie Nov 20 '18 at 13:08
  • @kolrie I believe I connected all the pins...although at 7 years ago, I've given up on this project. :) – chisaipete Nov 21 '18 at 19:51
  • @chisaipete wow, I didn't expect you to respond after all this time. I managed to only use DATA, GND and CLOCK and made it work. Thanks! – kolrie Nov 21 '18 at 23:58
  • @kolrie Awesome! I'm glad it worked! – chisaipete Nov 25 '18 at 01:11

3 Answers3

5

As I understand, you connect your Arduino to two different target machines and on one it works and on the other it doesn't.

So it seems there is a difference between the initialization requirements of the two machines. On this page at the very bottom there is a listing of a possible initialization sequence. Start by comparing your initialization to that one.

It will be a lot easier by using a logic analyzer. I am using the Intronix Logicport, but there are both cheaper and better ones, though not at the same time.

Tapping into an open-collector bus is a bit cumbersome because you don't see which device is talking. However, if you put in a series resistor at the end where the pullup is not, you can tell by the voltage level which device is holding down the bus. Every open-collector bus (like PS/2) needs pullup resistors, usually they are built in in the PC. You can see the different voltage levels easily on a DSO. With only a LA you have to record twice with different threshold voltages.

AndreKR
  • 2,999
  • 3
  • 22
  • 28
  • The decision of whom to give the bounty was harder then I expected but your answer garnered the most votes and I slightly prefer. I would have preferred reward everyone! – Kortuk Sep 30 '11 at 13:58
3

Given that your project works with one motherboard and not another, you seem to have a classic case of "partial spec compliance" - in your project, and maybe even in one of the motherboards. But most keyboards will work with any motherboard, so a robust implementation should be portable. The challenge is you are going to have to figure out why yours isn't.

You may be able to do this by just staring at the problem and thinking about how it is supposed to work (perhaps after a break - or one day the answer hits you in the shower) but you'll be more effective if you can monitor what is going on. For electrical issues that means a scope, for protocol ones a logic analyzer. There are some cheap options available in that area, for example the "bus pirate" board which has some specific capability for keyboard protocol or something FPGA-based which could have a longer capture buffer (see sump.org).

Another thing you could try would be using another device, either a microcontrollor or an FPGA, to build a keyboard host and use that to test your project towards the limits of the specification.

Chris Stratton
  • 33,282
  • 3
  • 43
  • 89
2

I haven't looked at the ps2dev library to see exactly how it works, but one thing does jump out at me.

At the moment a single attempt is made to connect to the "host" computer. When that fails, an entire second is waited (LED on 0.5s, LED off 0.5s) before another attempt is made.

If the Intel motherboard is not waiting long enough for keyboard detection then it may never be getting the connection attempt before it continues its boot sequence.

If you decrease the waiting time to say 0.1s (change the delay(500) lines to delay(50)) you may have some luck.

If not, try even faster. Hell, even try it with no delay at all and see how that goes.

Majenko
  • 55,955
  • 9
  • 105
  • 187