Simple Keyboard over I²C

Introduction

I wanted a keyboard for an SRAM project I have been working on. The keyboard was an interesting enough design I felt it warranted its own post. My needs for the keyboard were a minimum of 16 keys - each representing a hex character. I ultimately went with an 18-key keyboard as I had 18 6mm switches handy and it fit nicely on the protoboard.

Design

Disclaimer: I won't go into detail on how a keyboard matrix works or how I²C works, as that is beyond the scope of this post. If you're interested or need an introduction/refresher, Dave Driblin has a classic (and in-depth) reference on keyboard matrices. (Archived)

I decided to go with 3 rows of 6 columns, which meant I'd need 9 pins of the MCU I was using. In this case that MCU was an off-branded version of the SparkFun Pro Mini. For each key I also added a 1N4148 signal diode to handle any ghosting.

bf8ae254.png

BOM

For the keyboard alone (without the Pro Mini) the bill of materials is as follows:

Label Component Quantity
SW1-S18 6mm Push Button 18
DW1-D18 1N4148 18

Code

The code is fairly simple. On the high-level it transmits a 3-byte array via I²C. Each of the most significant 6 bits of the 3 bytes represents a key. See the various comments in the code for more specific explanations. For this code I'm using digital pins 2-4 on the Pro Micro for the rows of the matrix and digital pins 5-10 for the columns. The analog pins A4 and A5 on the Pro Micro are used for I²C SDA and SCL, respectively.

#include <Wire.h>

byte keys[3] = { 0 };

void setup() {
  Wire.begin(0x5C);  // Set the I²C address
  Wire.onRequest(sendData);  // Set the handler function
}

void loop() {
}

void sendData() {
  readKeys();

  // Transmit each byte one at a time
  for (int i = 0; i < 3; i++) {
    Wire.write(keys[i]);
  }
}

void readKeys() {
  for (int i = 0; i < 3; i++) {
    pinMode(i + 2, OUTPUT);  // Enable the current row
    digitalWrite(i + 2, LOW);
    keys[i] = 0;  // Clear the current row's byte
    for (int j = 0; j < 6; j++) {
      pinMode(j + 5, INPUT_PULLUP);  // Enable the current column
      keys[i] |= !digitalRead(j + 5) << (7 - j);  // Read the current column
      pinMode(j + 5, INPUT);  // Disable the current column
    }
    pinMode(i + 2, INPUT);  // Disable the current row
  }
}

Build

The first prototype build went surprisingly well. After soldering the first row I discovered a better technique for placement of the other two rows' diodes. I also added some screws for feet to make pressing the buttons a bit easier.

963ef6ff.jpg

Custom PCB

For this project I decided to learn KiCAD. I laid out the schematic above in KiKAD and also designed the PCB below. I added a header for programming the Pro Mini as well. All files are available under deliverables.

2379b5e9.png

260b635a.png

Deliverables

dustin
2021-05-20