BasicMicroUK - Forums

www.basicmicro.co.uk
It is currently Thu Apr 26, 2018 2:53 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 2 posts ] 
Author Message
 Post subject: App Note: Using a Keypad with a single ADC pin, all Atoms
PostPosted: Sun Jan 24, 2010 2:26 am 
Offline
Master

Joined: Mon Aug 18, 2008 1:26 am
Posts: 799
Location: CA bay Area
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! :D
That reference is here:
http://ww1.microchip.com/downloads/en/devicedoc/40040b.pdf
This 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
KeyPad Using ADC.JPG [ 75.37 KiB | Viewed 7038 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

_________________
kenjj
http://blog.basicmicro.com/
http://kjennejohn.wordpress.com/


Top
 Profile  
 
 Post subject: UPDATE: App Note: A One-Pin Keypad with Series Resistors
PostPosted: Fri Apr 23, 2010 10:35 am 
Offline
Master

Joined: Mon Aug 18, 2008 1:26 am
Posts: 799
Location: CA bay Area
This is merely a different way to wire the keypad. In fact, it is a "Roll-Your-Own" (RYO) keypad. Instead of the usual matrix keypad you find, designed as a 3x4 or 4x4 matrix, it uses a switches/resistors chain to form one long voltage divider.
This looks like:
Attachment:
Keypad_Series Resistors Method.jpg
Keypad_Series Resistors Method.jpg [ 40.22 KiB | Viewed 6985 times ]


Of course, it is a bear to wire up on a breadboard, but not impossible. It would be simpler to buy 16 switches, seventeen 1% resistors, a 3-pin header, and a prototyping board of some kind, then just wire it up according to the schematic above. Or wait for me to make a few boards at Expres PCB and buy a blank board from me and assemble it yourself. This looks like:
Attachment:
One-Pin Keypad PCB_Inverted.JPG
One-Pin Keypad PCB_Inverted.JPG [ 96.07 KiB | Viewed 6985 times ]

I use three 10-pin/5-resistor esistor networks to simplify assembly. They're easy to locate and purchase.

The theory of operation immediately shows this is a slam dunk, compared to the resistors-in-a-matrix method I did first. I went that route because the keypads are readily available in the matrix format, and I wanted to find a way to use them with a single ADC pin.

This method is just a series of resistors in a typical voltage divider. Each resistor in the lower chain is connected to a switch. All switches go to ground on the other side.
If no switch is pressed, this is just a pullup resistor. The ADC value is 1023, typical for a 10-bit result as produced by an Atom(Pro) part. Pressing any momentary switch places ground to a resistor in the chain. You now have a resistance in the lower leg of the divider, so the voltage at the common point, the input to the ADC pin, changes to a value based on the number of resistors between ground and the common point.
Let's continue with the understanding that all resistors are the same value.
If you press SW1, you have two resistors of equal value dividing the Vcc by two. This half-Vcc input, 2.5V, becomes an ADC value of 512/513 according to The Atom(Pro).
If you press SW16, you have one resistor in the upper part of the chain and 16 resistors in the lower part. This gives you an input value of (Vcc*(16/17)). That's 5V * 0.941, or 4.705V to the ADC pin. So the possible input range, outside of 5V with no switch pressed, is 2.5V to 4.7 Volts. That is a difference of 2.2V. There are 16 possible equal values for switch closures, so each step up the range is 2.2V/16, or approx. 135 mV each. If you go back and read about the struggle to keep a "space" between one key to the next, this suddenly looks reeeeal good! In fact, this means 20 and 24 key systems are easy to do.

Just thought you'd like to know about the possibility.
Have fun with your project(s).

_________________
kenjj
http://blog.basicmicro.com/
http://kjennejohn.wordpress.com/


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 2 posts ] 

All times are UTC [ DST ]


Who is online

Users browsing this forum: No registered users and 0 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Jump to:  
cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group

phpBB SEO