Now that I had something I could use for debugging purposes, it was time to get the input section working. The “keypad” was pretty simple: a 3×3 grid of switches wired up to PORTB on the PIC. Nothing you haven’t seen before.
So I add a section to my test code to drive the thing. The theory is simple: sit in a loop, and each time through, enable each of the “row” pins in sequence, driving an output logic low. Test the column pins, and if any of them are reading low, the switch is closed. The PIC’s internal PORTB pull-up resistors are enabled, of course, so everything should effectively default to a logic high if the switches are open.
This worked great, at least at first.
As it turns out, however, when Microchip says “weak pull-ups,” they mean weak. It was just on the border of not having enough drive to keep the pins pulled up, which meant occasional ghosting or other bizarre behavior that was quite difficult to track down. On top of that, some unrelated trickery I was attempting was causing the port pins to latch up every so often, which was no fun at all.
If you’ve never encountered latch-up before, it’s really nifty. Something drives the input buffers out of whatever their nominal range is, and suddenly the pin starts driving an output high, no matter what you do. The firmware can change the output bit all it wants, or even tristate the pin – it’ll still drive a logic high. And, near as I can tell, it’ll do this until you power-cycle the hardware.
So between the occasional latch-up and the pull-ups not being strong enough, I end up ripping the whole thing out and going to a more traditional active-high setup with external pull-down resistors in place of the internal pull-ups. Problem solved; the buttons work perfectly.
Well, except for switch bounce.
Everyone knows that switches are fun for this reason: when you flip a switch, it results in multiple make/break cycles, some on the order of milliseconds or less, because the contacts quite literally bounce. The end result is that a single switch press can result in multiple actions being taken when that was never the intention. The solution to this, of course, is called “debouncing.” [editor’s note: my spell checker wants to call it “debunking,” which is somehow appropriate in my mind.]
One common hardware solution is to hang a capacitor on the switch output, but that’s not a real good idea in a keyscan scenario. Instead, I ended up solving it in software – with a sort of software-implemented “capacitor.” Very simple, and works perfectly.
Basically, each switch has a variable associated with it. When the processor records that the switch is pressed, it sets the variable to the maximum allowed value (0xFF in the case of my little array of bytes). If the variable was zero at the time, a switch make event is generated. Then, each time through the keyscan loop, it goes through and decrements any positive values by one; any values that reach zero result in switch break events.
Like I said, very simple, and very effective.