Tuesday, August 11, 2009

Allen organ project - reading the keypad.

The first thing that I tried with the organ project was some code to read the keypad. I looked at http://arduino.cc/, and while I saw a few suggestions, nothing did quite what I wanted. No matter, reading a keypad isn't that hard.

It turns out that the Atmel chip that the Arduino uses is great for this function, because it's got the capability of putting a pull-up resistor on its inputs. So there's no need for any external components - all the rows and columns of the keypad can just be connected with wires. Here's the plan for discovering what keys are pressed at any given moment:

  • Initially, configure all the rows and columns as inputs, but put pull-up resistors on the rows by writing HIGH to them as inputs. In this quiescent state, reading any row pin will see a logic HIGH.
  • When polling the keyboard, switch column 0 to be an output. This will put it into a low-impedance state, and overwhelm the pull-up resistor for any pin where there's a key pressed in column 0. Read each row pin in turn, and record what key, if any is pressed.
  • Set column 0 back to be an input and column 1 to be an output. Then do the same thing with column 2.
  • If no key is pressed, return a constant KP_NOKEY. If multiple keys are pressed, return a constant KP_ROLLOVER. Otherwise, return the number of the key that was pressed (0-9, or one of the constants KP_ASTERISK or KP_OCTOTHORP).
The relevant source code is the kpInit() and kpState() procedures in the file 'terminal/terminal.pde' from ZIP archive of auxiliary files.

Now, it turns out that to read a keypad you need to do a little bit more than just poll the state. You also have to deal with key bounce, which is the fact that the key switch will often open and close many times when being pressed or released. You also have to deal with key rollover, where the operator presses a second key before releasing the first one. (The code I'm presenting here implements "2-key rollover", which handles only the case of two keys pressed at once. Full "n-key rollover" requires diodes in series with the key switches, which this cheap keypad doesn't have. Fortunately, n-key rollover is mostly needed for typewriter-style keyboards and touch typists.)

The kpPoll() function in the source file handles both of these. It is intended to be called often from the scan loop. It determines the current time, and reads the keyboard state. It ignores what it has read, unless the state has not changed at any time during the last 20 milliseconds (KP_DEBOUNCE_MILLIS). This check provides the "debouncing" function that makes sure that key bounce doesn't cause redundant inputs. If the stable state is now different from the last stable state, it's returned - unless it's KP_ROLLOVER, which indicates that multiple keys are pressed at once, or KP_NONE, which indicates that no key is pressed at all. So the return of 0-9 or of KP_ASTERISK or KP_OCTOTHORP indicates that a key was just pressed, and demands response to a user action.

And that's pretty much all there is to reading the keypad. Since the pins are in a scrambled sequence, I keep a little table in PROGMEM of where the rows and columns are, and what order the keys are in. There are also three RAM variables: the last state of the keyboard, the last state that was stable for 20 ms, and the time at which the state last was observed to change.

Next up: writing the display. That bit was a little trickier.

No comments: