EDITED 28 Jan 2010: I got a Grayhill keypad from work and replaced the problematic Ebay keypad. This is a 3x4 keypad, however, so there are no A, B, C or D key. Problems immediately came up showing that the code needed more work.
- I had to be VERY careful about the ranges that defined each key. I couldn't just bang keys, guess at a "center" value and then define the range as plus/minus five from the center value.
- I now look for the difference in values between keypresses in the 10-loop. If the difference between the new value and previous ("old") value are greater than 10 the entire 10-loop is redone.
As always, the new code is well commented. The result is a much more reliable program.
I can enter keys at will. Every successful key press is signalled with the terminal bell. When done entering a key sequence, I press the reset switch on the development board to clear the screen and restart the process.
Enjoy!
**********************************************************************
This app note applies to all Atom parts.
Kindly do not hold me liable and/or responsible for any problems or misapplication in any of your commercial projects for yourself or your employer.This is largely given here for education of the hobbyist. Enjoy.
There is a "Tips and Tricks" reference at Microchip that includes a note about using a keypad in a voltage-divider situation, allowing the user to use only a single ADC pin to determine which key was pressed. The typical beginners' notes tell you that eight pins are needed for a 4x4 (16 keys) keypad, four for column pins, four for row pins. This tip means you could install six keypads like this on the lowly Nano18!
That reference is here:
http://ww1.microchip.com/downloads/en/devicedoc/40040b.pdfThis is tip #7. Check out the rest while you're at it.
Problem is, they don't give any resistor values. I cranked up an Excel sheet and started experimenting with resistor values. The sheet showed what the resistance and expected ADC values for a key press were, and how far apart the ADC readings were from one key to the next. I called the last value the "space between keys". Because of anticipated "bobble" in the ADC readings, I had to allow for a range for each key. I wanted to create a range for each key that was five below and five above (not anymore, read edit!) the ADC reading I got for each key. This "space" value told me when the ADC values were getting too close together. And they did, usually around the keys with the highest resistances. This forced me to try new resistor values and rerun the Excel sheet. Another goal I set myself was to use only two resistor values for the circuit. I eventually discovered that precision 1% resistors of 1.0K and 200 Ohm did the job nicely. The upper resistance in the divider bridge wanted to be 2 KOhm, so I used two 1.0K resistors in series. See the circuit drawing for the layout. The measured resistances and ADC values are in the drawing.
Attachment:
KeyPad Using ADC.JPG [ 75.37 KiB | Viewed 1674 times ]
I purchased a one dollar 4x4 keypad off Ebay to use in this app note. What a pain. Never again. It didn't take long for the contacts within to degrade with use to the point where internal switch resistance swung wildly and threw off my ADC readings. But it worked long enough to prove the concept adequately, so I have no hesitation in presenting this here for your education and use. Just be sure to use a good keypad, like Grayhill.
And now we come to the code. This is well commented, probably well enough to skip a detailed explanation. In short:
1. We initialize variables
2. Two loops are used to collect ADC readings and keep track if switch bounce is messing up readings
3. The ADC ranges mentioned above are used in conditional IF-THEN statements to determine which key was pressed
4. Once a key has been satisfactorily identified, it is reported to the terminal screen and a "bell" sounded
5. This process runs constantly
We check constantly throughout to detect a keypress and key release.
Code:
; One keypad (3x4) to one ADC pin, all Atoms
; 28 January 2010 By Ken Jennejohn
; Wiring: check wiring diagram supplied with post (this is now a 3x4 keypad!)
;****** Variable Assignments *******
kpad_in var word
old_kpad_in var word
old_kpad_in = 0
key var byte
old_key var byte
result var word
result = 0
looper var byte ; for loops less than 256
bigloop var byte
pause 1000 ; Needed for PIC to initialize internally
serout s_out, i19200,[0, 7] ; clear the screen, sound terminal "bell"
MAIN
; Initialize main variables
key = "X"
result = 0
kpad_in = 0
old_kpad_in = 0
;Is a key pressed (ADC reading less than 900)? If not, go back to MAIN.
ADIN P6, kpad_in
IF kpad_in > 900 then MAIN
; Falls thru when key pressed...
; These two loops look for a legit key press, and determine which one.
pause 50 ; This allows keypad switch to finish "bounce"
FOR bigloop = 1 to 3 ; has to ID same key three times to pass
FOR looper = 1 to 10 ; read keypad input 10 times
ADIN P6, kpad_in
; keypad switch "bounce" handled next
; Uncomment the next line for troubleshooting
;serout s_out, i19200, [dec (ABS(old_kpad_in - kpad_in)), 13]
; The difference between this key press and the last must be
; less than ten. We use the ABS(olute) modifier to keep results
; positive and avoid negative results. Greater than 10, do it over.
IF ABS (old_kpad_in - kpad_in) > 10 then
old_kpad_in = 0
looper = 0
result = 0
ENDIF
result = result + kpad_in
old_kpad_in = kpad_in
next ; looper
; get average of readings
result = result / 10
; This next output routine is available for troubleshooting, just uncomment it.
;serout s_out, i19200, ["result: ", dec result,13]
; We next do a gosub to routine that determines which key has been pressed
gosub Key_Is_
; Upon returning, we determine if the key was properly determined
IF key = "X" then MAIN ; If key still "X" we failed
; If passed, we save it to check later. Must pass this three times to accept key press value.
IF key <> old_key then
bigloop = 0 ; If comparison fails, restart bigloop
ENDIF
old_key = key ; it's OK, save latest key value
next ; bigloop
; If key press looks good after three tries we report it here and sound terminal "bell".
Report_
serout s_out, i19200, | ; We use pipe symbol (the "|") to continue to the next line
[key, 7] ; clear screen, home cursor first
; We wait here for key release
stay_here_
ADIN P6, kpad_in
IF kpad_in < 900 then stay_here_
; And then we do it all over again.
GOTO MAIN
;****** SubRoutine(s) ******
Key_Is_
; Uncomment the next line for troubleshooting
;serout s_out, i19200, ["key_is_ result val: ", dec result, 13]
IF result > 592 AND result < 602 then
key = "1"
ENDIF
IF result > 615 AND result < 625 then
key = "2"
ENDIF
IF result > 640 AND result < 650 then
key = "3"
ENDIF
IF result > 425 AND result < 435 then
key = "4"
ENDIF
IF result > 465 AND result < 475 then
key = "5"
ENDIF
IF result > 502 AND result < 512 then
key = "6"
ENDIF
IF result > 695 AND result < 705 then
key = "7"
ENDIF
IF result > 710 AND result < 720 then
key = "8"
ENDIF
IF result > 725 AND result < 735 then
key = "9"
ENDIF
IF result > 778 AND result < 782 then
key = "0"
ENDIF
IF result > 0 AND result < 0 then
key = "A"
ENDIF
IF result > 0 AND result < 0 then
key = "B"
ENDIF
IF result > 0 AND result < 0 then
key = "C"
ENDIF
IF result > 0 AND result < 0 then
key = "D"
ENDIF
IF result > 768 AND result < 772 then
key = "*"
ENDIF
IF result > 790 AND result < 801 then
key = "#"
ENDIF
return