Keyboard/mouse controller for ThinkPad X41 keyboard

Below some code that you can try. Notes:

  • The code is totally untested, though it does compile on my system. Don't hesitate to ask if something doesn't work.
  • The compiler doesn't seem to like the typedef statement. Therefore, code is a little wordy with all those struct statements.
  • I removed the check for the input detect pin. Feel free to add it back and compare scan speed.
  • I now let the Arduino read from port D in one go, hoping that this improves speed. At the moment, only six columns are supported, but that can be extended by reading from port B, possibly in every other call to loop(). In addition, perhaps you can free pins 0 and 1 from port D and use them for columns.
  • I am currently investigating how to analyze the generated machine code, in order to estimate performance.
const char decadeCounterResetPin = 12; //4017 reset pin
const char decadeCounterClockPin = 13; //4017 clock pin

struct key_s {
  char code;
  bool isPressed; // may later contain a brief time stamp for debouncing
};

// Keys for the inputs 2 to 7 on port D, six columns, organized row by row.
struct key_s keys[] = {
  (struct key_s) { 194, false },
  (struct key_s) { 195, false },
  (struct key_s) { 196, false },
  (struct key_s) { 197, false },
  (struct key_s) { 198, false },
  (struct key_s) { 199, false },

  (struct key_s) { '1', false },
  (struct key_s) { '2', false },
  (struct key_s) { '3', false },
  (struct key_s) { '4', false },
  (struct key_s) { '5', false },
  (struct key_s) { '6', false },

  (struct key_s) { 'q', false },
  (struct key_s) { 'w', false },
  (struct key_s) { 'e', false },
  (struct key_s) { 'r', false },
  (struct key_s) { 't', false },
  (struct key_s) { 'y', false },

  (struct key_s) { 'a', false },
  (struct key_s) { 's', false },
  (struct key_s) { 'd', false },
  (struct key_s) { 'f', false },
  (struct key_s) { 'g', false },
  (struct key_s) { 'h', false },

  (struct key_s) { 'y', false },
  (struct key_s) { 'x', false },
  (struct key_s) { 'c', false },
  (struct key_s) { 'v', false },
  (struct key_s) { 'b', false },
  (struct key_s) { 'n', false },

  (struct key_s) { 204, false },
  (struct key_s) { 177, false },
  (struct key_s) { ' ', false },
  (struct key_s) { ' ', false },
  (struct key_s) {  45, false },
  (struct key_s) { ' ', false },

  (struct key_s) { 205, false },
  (struct key_s) {  96, false },
  (struct key_s) { 167, false }, // §
  (struct key_s) { 193, false },
  (struct key_s) {  91, false },
  (struct key_s) { ' ', false },

  (struct key_s) { 178, false },
  (struct key_s) { 179, false },
  (struct key_s) { ' ', false },
  (struct key_s) { ' ', false },
  (struct key_s) {  39, false },
  (struct key_s) {  32, false }
};

inline void resetDecadeCounter() {
  digitalWrite(decadeCounterResetPin, HIGH);
  digitalWrite(decadeCounterResetPin, LOW);
}

inline void incrementDecadeCounter() {
  digitalWrite(decadeCounterClockPin, HIGH);
  digitalWrite(decadeCounterClockPin, LOW);
}

inline void processKey(bool keyIsDown, struct key_s &key) {
  if (keyIsDown) {
    if (!key.isPressed) {
      Keyboard.press(key.code);
      key.isPressed = true;
    } // else: no change
  } else { // key is up
    if (key.isPressed) {
      Keyboard.release(key.code);
      key.isPressed = false;
    } // else: no change
  }
}

inline void processRow(char pinStates, struct key_s *key_p) {
  pinStates >>= 2; // first two pins not used for columns

  while (pinStates) {
    processKey(pinStates & 1, *key_p);
    pinStates >>= 1;
    key_p++;
  }
}

void setup() {
  pinMode(decadeCounterClockPin, OUTPUT);
  pinMode(decadeCounterResetPin, OUTPUT);
  Keyboard.begin();
}

void loop(){
  resetDecadeCounter();

  for (struct key_s *key_p = keys; key_p < keys + 8 * 6; key_p += 6) {
    char pinStates = PIND;
    processRow(pinStates, key_p);
    incrementDecadeCounter();
  }
}

If you can make it work, what's the scan rate? I am worried about speed because I want the Arduino to also interface with the PS/2 mouse. There is a PS/2 library for Arduino, and it looks quite complete. It has several delay functions in it, and I hope that I can replace the delays with some keyboard processing. The PS/2 protocol is sensitive to timing, though.

Initially when I tested the 595's loop rate (without any code to do anything that wasn't cycle a bit through the 595) I got 793Hz, with the 4017, with just the code need to count from 0 to 7, I got 6KHz.

Concerning loop rate:

  • How did you measure it? With an oscilloscope?
  • 6KHz is very low, compared to the Arduino's 16MHz. Why do you think that is? Did you try counting in a simple loop while (true) incrementDecadeCounter();?

Attached, a circuit diagram for connecting modifier keys in a non-blocking way.