Uno interfacing to system with scanned keypad

Project: To automate the programming of a retro computer system into which the only way of entering code is via a hex keypad.

The keypad uses the well established row/column scanning principle - on the system, the four row lines of the keypad matrix are connected to outputs which are normally high, driven momentarily low one after the other while the system watches the state of the eight column inputs, which are pulled high by 4K7 resistors. When a key is pressed it joins a row output line to a column input line. When the system takes the row line low, the column line it is connected to by the switch is also taken low.

Each time the system takes one of the row output lines low it looks at the eight column inputs to see if any of them have gone low. If one has, the combination of the row output which was driven low along with the column input which was detected low tells the system which key was pressed.

Now, there are various ways to get an external device to mimic the actions of someone pressing keypad keys. One is to connect relay outputs across all of the keyswitches and have the external device operate the relays in the correct sequence. Reasons not to do this are: Relays are expensive (there are twenty keys on the keypad), relays take up a lot of space, relays are physically slow, so limit the maximum speed that the system can be programmed at, relays take a lot of power, relays are noisy, and finally, they can not be directly driven by microprocessor / Arduino pins.

Another way (which I have already successfully done, with a PIC chip) is to build an optocoupler matrix with the optocoupler outputs connected across the keypad switches of the target system, and have the programming device operate the optocoupler LEDs in the correct sequence to programme the target system. That actually works beautifully, but wiring up the complex optocoupler matrix and connecting it to the microcontroller ports on one side and the keypad scan lines on the other side requires the patience of a saint. (I did it once, never again).

The opto method (and the relay method) have the advantage that they are fully isolated: There is no direct wired connection between the target system's keypad scan lines and the external programming device, so the programming device can be connected to / disconnected from the target system at will without any real danger of damaging the target system even while it is powered.

However, there is a dirtier, more direct method which can be used if the programming interface can be left connected all the time, and that involves watching the row scanning output lines and providing a pulldown on the appropriate column line input at the right moment. For example, suppose the '0' key on the keypad joins row output line '0' and column input line '0' when it is pressed. Imagine key '0' -is- pressed. When the system, taking each row line low in turn as it scans the keyboard, takes row line '0' low, that causes column input line '0' to be taken low for as long as row line '0' is low. The system sees this and registers this as a '0' keypress.

Therefore, to imitate this action, the programming device needs the four row lines connected to four inputs and it needs eight open-collector or open-drain outputs connected to the column input lines. Let's say the programming device knows that the next key which needs to be pressed is '0'. It watches the row output lines (which are connected to four inputs on the programming device) and waits for row output line '0' to go low, whereupon it immediately drives column input line '0' low and holds it in that state for as long as the row output line remains low, then releases it. Since this is exactly what happens when key '0' is pressed, the system thinks that key '0' has been pressed.

And now finally to my question: An Arduino could provide the necessary 8 * open-collector or drain pulldown outputs by connecting the 8 column drive output pins to the keypad column lines using a ULN transistor driver IC, but I would like to make it even simpler by making the Arduino outputs themselves behave like open collector outputs. On a PIC chip I can do this by presetting the output latches on the port to '0' and leaving them that way. Control of the output is then done by switching the mode of the port pin from input to output. When the pin is an input, the pullup resistor on the corresponding keypad column line keeps that line high. When the port pin is switched to an output it assumes the state determined by the content of the output latch (low) and drives the keypad column line low. The question is will this 'simulation' of open-collector output work as well on an Arduino Uno (ie, on Atmega 328P) as it does on a PIC chip - can the outputs sink current as well as source it and is the relationship between the port output latch and the port pin direction mode the same as I have described it to be on the PICs?

For a key matrix, the Keypad library can be used.
http://playground.arduino.cc/code/Keypad

It is possible to switch between input and output and the Arduino has also an internal pull-up resistors which can be activated. You can use the Arduino functions, or in some specific situations the registers can be set directly. See the datasheet or these pages:

http://playground.arduino.cc/Learning/PortManipulation

Could you add a schematic or drawing or photo ?
I do not exactly understand how the keypad is and how the pullup resistors are connected.

Thanks for the reply- however I'm not sure I explained very well what I am trying to do - I am not trying to use the Arduino to READ the keypad, but instead to interact with the row and column scan lines on an existing independent microprocessor system in such a way that it is able to generate keypresses. Therefore the 'Keypad' library will not be useful in this actual instance.

The idea is that the Arduino monitors the keypad row output scan lines of the existing system and manipulates the existing system keypad input column lines with correct timing in such a way that the existing system thinks that a physical key has been pressed. In order to make the existing system think a key has been pressed, the Arduino must take the corresponding column input line on the existing system low when the appropriate row output line goes low.

The psuedocode to generate one character keypress would be something like:

-Establish which keypad key needs to be pressed next.
-Obtain the row line number (r) and and column line number (c) corresponding to that key from a look up table
-monitor keypad row line (r) until it goes low
-pull column (c) line low
-wait until the row line (r) goes high
-release the column line (c), allowing it to be pulled high by its pullup resistor

The output from the arduino to the keypad column lines has to be (or to seem like it is) an open collector driver so that it does not prevent normal operation of the keypad. I don't have a diagram handy - I'll try to knock up a little sketch to illustrate the principle.

OK, the attached image illustrates what I'm talking about. I know it's crude, I just threw it together in MSPaint.

For simplicity, I've shown the existing retro system's key row / column hardware and keypad (here drawn in black) as just a 2 * 2 matrix. The original is actually a 4 * 8 matrix, but the hardware is otherwise the same.

The hypothetical added on Arduino which will give an electronic / automated means of pressing the keys is shown in red. The issue is that the outputs to the column lines can't have solid high / low drive like a normal digital output because that would interfere with the normal operation of the keypad. The outputs to the column lines need to be either hard pulldown, like an open-collector output turned on, or high impedance like an open-collector output turned off. The technique I have used on other microprocessors (ie, PIC) is to preload the port bit's output latch bit with zero and leave it that way, and then control the output by switching it between input mode and output mode.

This has "FPGA" written all over it. With an FPGA you could automatically make a certain column low when a certain row gets low.

The Arduino is able to switch between output_low and input. That is some kind of open collector.
It is possible to set the output low, and after that setting the pin to output.
digitalWrite(pin,LOW); // this is already the default at power on
pinMode(pin,OUTPUT);
To disable the output, just make it input.
pinMode(pin,INPUT);

You can do this also with direct register access. You can check the links in my previous post and read the datasheet about PORTB, DDRB and PINB.

The row outputs (of the original hardware) to the input pins of the Arduino need series resistors of 1k to 10k for protection.
And even with that, if the Arduino has no power, the current via the resistors might be enough to make the Arduino start working.

I think with an dedicated Arduino you could respond in about 1us. But is will be not easy.
With "dedicated" I mean no interrupts (disabling the Arduino timing interrupt) and optimized with precalculated data and tables and receiving the keystrokes from a second Arduino. The second Arduino interfaces between the dedicated Arduino and the rest of the world.

An other option is to have interrupts enabled for all the rows. That way the respons time is perhaps 2 or 3us.

You need the PCINT interrupts for that.

How reliable it will be depends greatly on the original hardware and software. I assume that very little is known about that, so it will be very hard to guarantee a 100% solid solution.

The easy way is to use optocouplers or reed relays for every button. Some reed relays can be directly driven by an Arduino pin (you still need a flyback diode).

Hi, thanks for the informative reply - FPGAs are something I know absolutely nothing about, unfortunately. Although I have been involved with microprocessors for a long time (the retro system I have been talking about is the first 'computer' I ever bought) things like PLDs and FPGAs came in later under my radar - the main problem with them is that they tend not to be available in human-usable form, ie, not in DIP packages, so unless I have access to printed circuit design / manufacturing facilities (I do not) they are normally impossible for me to use.

I actually do know a great deal about the original system, which is running a 4Mhz National Semiconductor INS8060, more commonly referred to as the 'SC/MP'. I even have the operating system / monitor code listing for it. I know exactly how the machine scans the keyboard, which is roughly as outlined in the drawing above. I believe you may be right, in that I might have to go to pure assembly language or at least direct register access via 'C' in order for the Uno to react quickly enough, but I think that the 16Mhz speed of the Uno may make native Arduino code worth trying at least.

As you may already have read, I did do this already with a PIC and optocouplers across all the keyswitches. It works beautifully, but it was a nightmare to wire up and so is not easy to reproduce. Even if I made the full schematic diagram and the code for the microcontroller available, I doubt that many other people would have the heart to build it.

There are a few other people around who have one of these systems in working order but have the same problem, which is that the only way to get code into it initially is to type it in. Programs are held in RAM, so when the machine is switched off, all code which has been typed into it vanishes instantly. Worse still, one uncorrected error in the code will almost certainly lead to the program running wild and trashing its own code.

That very much discourages anyone from trying to do anything with them.

The original PIC / Optocoupler interface version of the project also includes an RS232 input and Intel Hex code receiving code, so basically, I write code for the system with an assembler on any system which has an SC/MP assembler plus a serial interface, then send the assembled code to the interface, which then types the code into the target machine at very high speed - about thirty times faster than the optional cassette tape interface which was originally available for the system. If I do this and the program code crashes and erases itself because I made a mistake, not a problem. I correct the error, assemble it and it takes about ten seconds for the interface to type the whole program in again.

My aim now is to make something similar with ready-built hardware (so now you see why I am considering the Arduino) and using a simpler direct wired interface to the target system - four row lines, eight column lines, plus 5V power, so that other people can more easily build it and use it. I envisage that the Arduino interface will be powered by the target system's 5V supply and will not be connected to or disconnected from it while the target system is on - so the potential problem of powering the Arduino via its I/O ports would not arise.

This is a situation where you need to use interrupts to good effect. Whilst the FPGA approach would be in a sense, optimal, I agree that I have yet to see an inexpensive development system for these, the programming software is virtually always encumbered and expensive, even if the hardware appears to be relatively cheap.

The advantage you have is that the ATMega is so much faster than the SC/MP, so that you can - with an interrupt - set up the appropriate response to the keyboard scan while the SC/MP is getting around to reading it.

As you have the source code, you would know how it performed key debouncing - presumably the key is read many times before it is accepted as valid. It is on this account important that the emulation code responds accurately, though I would not be surprised if the initial keypress actually stops the keyboard scanning until the debounce (and release) is completed.

Paul__B:
... you would know how it performed key debouncing ...

That was the first thing that came to my mind. If there is key debouncing, it might not be a problem if the Arduino missed one and you would know how long a simulated key should be active. A debounce will increase the reliability a lot.

The most important question: When you take a look at the source code, can you estimate how long it takes before a column is read after a row is set ? If it is 1us, it might be possible with a dedicated Arduino. If it is 5us, the Arduino has lots of time and can use interrupts for the rows.

An other way to do this, is with extra hardware. For example with two chips. One register/memory and one ULN transistor open collector driver. The register/memory chip will be written by the arduino. I don't have a register/memory chip in mind, but I'm sure there is one. Perhaps even a mux (or an encoder or decoder), the rows are the mux input and the Arduino selects which output will be connected to which input. I might have to give this another thought.

I'm away for the weekend and so will not be able to follow up with more detailed information for a few days: But remember that my primary aim here is simplicity, something that is easy for other people to build. For that reason, I am aiming to use no hardware other than the Arduino itself if possible.

There is one headache that I've just thought of - the aim was to use the Arduino's serial USB port for communication with the host PC (so I could send an intel hex file to it with, eg, Hyperterminal). However, the USB connection also provides a hardwired 5V supply to the Arduino, which is awkward as I want it to be powered from the target system, for the reason previously discussed. The target system certainly can not draw its power from the USB connection via the Arduino - it takes well in excess of half an amp.

I actually can't remember if the act of pressing a key stops the scanning action or not - I'll look into it. Certainly on the prototype (using optocouplers) the target system's debounce delay is what ultimately limits the maximum typing speed of the interface - it has to press each key for just long enough for the target system to always accept it as a valid keypress. Nonetheless, it is still surprisingly fast. If you want to see the optocoupler prototype in action, here's a youtube video of it working. Scroll through to about 1:58 to get to the interesting bit.

If you are wondering where the optocouplers are, look carefully at the programming interface which is built on stripboard. It's actually a sandwich of two identical-sized pieces of stripboard mounted one above the other. The PIC processor and MAX RS232 level shifter and an 8K serial SRAM are mounted on the top board: The optocoupler matrix occupies the entire lower board.

Caltoa:
Perhaps even a mux (or an encoder or decoder), the rows are the mux input and the Arduino selects which output will be connected to which input.

Now we might be gettign somewhere. Two (analog or digital) multiplexers, back-to-back, one selects a scanning input and the other connects it to a scanning output. Two chips only.

That's lateral thinking!

Two mux'es back-to-back ? That's it !

Two 74HC4051 with common out/in connected.

Both have ABC to select the channel. So that would require 6 Arduino pins.

SiriusHardware didn't like extra hardware, but this is such a good solution. I think nothing can beat this.

Perhaps an Arduino Uno with a prototype shield and two 74HC4051.
Or a small Arduino Micro or Nano on top of a prototype pcb board.

Just about to leave for the weekend, but I checked in to see if there have been any further developments - I have to admit the back to back 4051 idea is pretty good, although I would use the INH lines as well (so four Arduino port pins per 4051) to make them disappear from the scan lines when they were not actually generating a key press.There's still the USB power problem to untangle, but I admit it's a good compromise.

SiriusHardware:
I would use the INH lines as well (so four Arduino port pins per 4051) to make them disappear from the scan lines when they were not actually generating a key press.

Of course, that goes without saying, but the two inhibit/ enable lines are common; connected together, so seven lines overall.

You can always use a shift register if the number of lines becomes a concern.

Addenda: No, you don't of course really need the inhibit/ enable lines because you are not using all of the selected options on the multiplexers, the one controlling four row lines will have four open "ports" which can be selected. You can either use the inhibit, or the third select line to effectively disable the selection, total remains six control lines in all. As we are talking about (old) digital logic, it makes no real difference whether the common line between the two multiplexers is entirely floating or not when disabled.

What may be a concern, and the reason that I mentioned digital multiplexers/ demultiplexers may be more appropriate, is the nominal 250 ohm "on" resistance of each 4051 when operated at 5V.

Paul__B:

SiriusHardware:
I would use the INH lines as well (so four Arduino port pins per 4051) to make them disappear from the scan lines when they were not actually generating a key press.

Of course, that goes without saying, but the two inhibit/ enable lines are common; connected together, so seven lines overall.

If it's a 4x4 keypad, then you don't need all 8 ways of the 4051, you only need 4 ways. So you only need to drive 2 of the select lines of each 4051. Plus one of the inhibit pins (or both connected together) so that you also have the state of no key pressed. So you only need 5 Arduino pins in total.

Paul__B:
What may be a concern, and the reason that I mentioned digital multiplexers/ demultiplexers may be more appropriate, is the nominal 250 ohm "on" resistance of each 4051 when operated at 5V.

The 74HC4051 (as distinct from the earlier CD4051) has an on-resistance of only 80 ohms typical @ 4.5V. Keypads also have some resistance because of the carbon contacts they typically use. So I don't think the resistance will be a problem.

dc42:
If it's a 4x4 keypad, then you don't need all 8 ways of the 4051, you only need 4 ways. So you only need to drive 2 of the select lines of each 4051. Plus one of the inhibit pins (or both connected together) so that you also have the state of no key pressed. So you only need 5 Arduino pins in total.

He did spec a 4 by 8 matrix - not the earlier system described in the video above.

dc42:
The 74HC4051 (as distinct from the earlier CD4051) has an on-resistance of only 80 ohms typical @ 4.5V. Keypads also have some resistance because of the carbon contacts they typically use. So I don't think the resistance will be a problem.

Ah! I looked at the wrong datasheet. The resistance did seem a bit high compared with what I recalled building my Doppler scan system some (nearly? or more than?) thirty years ago.

Paul__B:

dc42:
If it's a 4x4 keypad, then you don't need all 8 ways of the 4051, you only need 4 ways. So you only need to drive 2 of the select lines of each 4051. Plus one of the inhibit pins (or both connected together) so that you also have the state of no key pressed. So you only need 5 Arduino pins in total.

He did spec a 4 by 8 matrix - not the earlier system described in the video above.

OK, so 6 pins needed then (3 for one 4051, 2 for the other, and one common inhibit) - assuming he needs to simulate pressing all 32 keys.

dc42:

Paul__B:

dc42:
If it's a 4x4 keypad, then you don't need all 8 ways of the 4051, you only need 4 ways. So you only need to drive 2 of the select lines of each 4051. Plus one of the inhibit pins (or both connected together) so that you also have the state of no key pressed. So you only need 5 Arduino pins in total.

He did spec a 4 by 8 matrix - not the earlier system described in the video above.

OK, so 6 pins needed then (3 for one 4051, 2 for the other, and one common inhibit) - assuming he needs to simulate pressing all 32 keys.

Actually, the system in the video above DOES use an eight line by four line keypad matrix, but with many of the intersections not occupied by keys. The reason for this odd arrangement is that the eight lines are also used as drive-low lines for the common cathode terminals of the eight seven segment displays. And look again - there are actually 20 keys on the keypad, not 16. They are more widely spaced across than they are deep, in order to put them in the same positions as the original membrane keypad keys were.

Paul__B:

SiriusHardware:
I would use the INH lines as well (so four Arduino port pins per 4051) to make them disappear from the scan lines when they were not actually generating a key press.

Of course, that goes without saying, but the two inhibit/ enable lines are common; connected together, so seven lines overall.

You can always use a shift register if the number of lines becomes a concern.

Addenda: No, you don't of course really need the inhibit/ enable lines because you are not using all of the selected options on the multiplexers, the one controlling four row lines will have four open "ports" which can be selected. You can either use the inhibit, or the third select line to effectively disable the selection, total remains six control lines in all. As we are talking about (old) digital logic, it makes no real difference whether the common line between the two multiplexers is entirely floating or not when disabled.

What may be a concern, and the reason that I mentioned digital multiplexers/ demultiplexers may be more appropriate, is the nominal 250 ohm "on" resistance of each 4051 when operated at 5V.

The series resistance of the two muxes combined will not be a problem. The target system's outputs to the keypad matrix are strong current sink outputs and the target system's inputs from the keypad matrix are pulled up by 4K7, so 500R (approx) series resistance for the pulldown will comfortably give a logic 0 state when required - and I gather the series resistance is actually somewhat lower.

I agree that the 4-line MUX could be disconnected by just selecting a channel higher than 03, but I have to think about what will happen to the various lines as the Arduino goes through its reset phase - ideally, everything should definitely be held inactive during that time so it remains totally hands-off from the target system until I actually want it to start pressing keys. I had in mind to have a fairly strong pullup on the _INH / _EN pins so that only an intentional low drive out from an Arduino pin would activate the 4051s.

I've decided to give both methods (my original arduino-only setup) and the multiplexers idea a try. The latter would be a great system to use for general repetitive setup/programming of all sorts of systems which can only be programmed via a keypad - just temporarily disconnect the keypad (or leave it connected), connect the arduino / mux interface to the keypad connector, upload a keypress file to the arduino and watch it programme the system / device.