Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

RS232 keypad with autorepeat and 2 key rollover.

Status
Not open for further replies.

Pommie

Well-Known Member
Most Helpful Member
This project uses a 12F508 (or 12F509) to read a keypad and send the results over RS232. It is most useful as an add-on to other pic projects that require some way to input numbers. This keypad acts the same way as your PC keyboard in that when a key is held down, it has the short delay before it repeats the key. Both the delay and repeat speed are settable in software just like your PC keyboard.

Many people will wonder what "2 key rollover" means. The best answer is with a little demonstration. Open your favourite text editor, hold down the "A" key and when it starts repeating press the "B" key. Even though 2 keys are now held down the keyboard sends the last key pressed. This is useful because many people will press the keys so fast that they inadvertently press two keys at once.

How does it work.
The Schematic,
picture.php


The 12F508 only has 6 input/output (I/O) pins and we need 1 to send the serial data out which leaves only 5 left to read the keypad. The keypad has 4 rows and 3 columns which is too many for our little pic chip. The way we get around this problem is by using the same pins twice. You see the three resistors in the diagram, well they will make the three pic pins low by connecting them to ground. If we now make GP4 high the diode on row 1 will conduct and if any keys are pressed in row 1 then the corresponding pin GP0, GP1 or GP3 will also go high. We have successfully read row 1. We can repeat this with GP4 and read row 3.

Now comes the tricky part because the diodes on rows 0 and 2 are backward and so the only way to read those rows is by outputting a zero on GP4 and GP5 thus making the input pins low. But, wait a minute, they are already low due to those resistors and so we won't see any change. Luckily for us the pics have internal resistors that connect the (input) pins to the 5V line that we can turn on and off in software. These resistors have a value around 20k and so will overpower the 200k external ones and make the input pins 5V. So, with these internal resistors turned on we can make GP4 an output, set it low and any pins that are now low will indicate a key pressed on row 0. We can repeat this using GP5 to read row 2.

The board,
picture.php


Closer,
picture.php

The two sets of connectors are just for convenience, I added the second set to enable me to use it on a breadboard.

The finished article,
picture.php


The code,
Code:
;*******************************************************************
;		Title	12F509 RS232 KeyPad
;		Author	Mike Webb
;		Date 	12th January 2009
;		Version	1.0
;*******************************************************************
;
	processor	12F509

	include	"p12f509.inc"

	errorlevel	-302
	radix	dec

	__CONFIG   _MCLRE_OFF & _CP_OFF & _WDT_OFF & _IntRC_OSC

;uncomment the following line to invert the RS232 signal.
;when inverted it can connect straight to a PC without a MAX232

;#define		Inverted

#define		GPIO0	0
#define		GPIO1	1
#define		GPIO2	2
#define		GPIO3	3
#define		GPIO4	4
#define		GPIO5	5

#define		b_GPIO0	GPIO,0
#define		b_GPIO1	GPIO,1
#define		b_GPIO2	GPIO,2
#define		b_GPIO3	GPIO,3
#define		b_GPIO4	GPIO,4
#define		b_GPIO5	GPIO,5


#define		RS232	2
#define		b_RS232Out GPIO,RS232

		cblock	07h
OutByte
Temp
Count
Key0
Key1
Old0
Old1
Edge0
Edge1
KeyCount
		endc

KeyDelay	equ	35	;delay in 100th second until repeat kicks in
KeyRepeat	equ	10	;delay between repeated keys


		org     0h

;********************************************************************
;	4 meg clock = 1.0 meg instructions


		org     0h
		movwf	OSCCAL		;write calibration value

start		movlw	0
		OPTION
	#ifdef	Inverted
		bcf	b_RS232Out	;ensure RS232 is high
	#else
		bsf	b_RS232Out	;ensure RS232 is high
	#endif
		movlw	b'11111111'-(1<<RS232)
		TRIS	GPIO		;all input except RS232 output


		call	Wait100th
		call	Wait100th

Loop		Call	Wait100th	;needed for timing and debounce
		movfw	Key0
		movwf	Old0		;keep copy of previous keys
		movfw	Key1
		movwf	Old1
		call	ReadKeys	;read the key matrix into Key0 and Key1
		movfw	Key0
		iorwf	Key1,W		;any key pressed
		btfsc	STATUS,Z
		goto	Loop
		movfw	Key0		;get key state
		xorwf	Old0,W		;keep changed bits (pressed and released)
		andwf	Key0,W		;keep only new presses
		btfss	STATUS,Z
		goto	NewKeyDown
		movfw	Key1
		xorwf	Old1,W
		andwf	Key1,W
		btfsc	STATUS,Z
		goto	KeySame	
NewKeyDown	movlw	KeyDelay	;start key delay
		movwf	KeyCount	
		movfw	Key0		;edge = (key^old)&key
		xorwf	Old0,W
		andwf	Key0,W
		movwf	Edge0		;edges contains a 1 for each new key press
		movfw	Key1
		xorwf	Old1,W
		andwf	Key1,W
		movwf	Edge1
		goto	HaveKey
KeySame		decfsz	KeyCount,F	;key delay
		goto	Loop
		movlw	KeyDelay	;if KeyDelay is zero
		iorlw	0		;then no repeat allowed
		btfsc	STATUS,Z
		goto	Loop
		movlw	KeyRepeat
		movwf	KeyCount
HaveKey
;	having got to here then a key is pressed for the first time
;	or the repeat time is up and so it needs repeating.
;	Edge0 or Edge1 contain the key pressed.

		movfw	Edge0		;does edge0 have a bit set
		btfsc	STATUS,Z
		goto	IsEdge1		;no so must be edge1
		movwf	Temp
		movlw	255		;set count to -1
		goto	CountBits
IsEdge1		movfw	Edge1		;move edge1 
		movwf	Temp		;into temp and
		movlw	7		;count to 7
CountBits	movwf	Count
		bsf	STATUS,C	;ensure it can't get stuck in loop
CountBitsL	incf	Count,F		;increment counter
		rrf	Temp,F		;until we find a set bit
		btfss	STATUS,C
		goto	CountBitsL
		movfw	Count		;W=key number 0-15
		Call	Bit2Key		;convert to key character
		call	Send		;send key via RS232
		goto	Loop



ReadKeys	movlw	0<<NOT_GPPU		;   WPUs ON
		OPTION
		movlw	b'11111111'-(1<<GPIO5)-(1<<RS232)
		tris	GPIO			;make I/O 5 output
		bcf	b_GPIO5			;and make it low
		call	BitDelay		;avoid RMW
		clrf	Key0			;will hold key state
		btfss	b_GPIO0			;if I/O 0 low then key pressed
		bsf	Key0,0			;so set the bit
		btfss	b_GPIO1			;repeat
		bsf	Key0,1			;for
		btfss	b_GPIO3			;other
		bsf	Key0,2			;keys
		movlw	b'11111111'-(1<<GPIO4)-(1<<RS232)
		tris	GPIO			;make I/O 4 output
		bcf	b_GPIO4			;and make it low
		Call	BitDelay
		btfss	b_GPIO0			;read next row
		bsf	Key0,4
		btfss	b_GPIO1
		bsf	Key0,5
		btfss	b_GPIO3
		bsf	Key0,6
		movlw	1<<NOT_GPPU		;WPUs OFF
		OPTION
		movlw	b'11111111'-(1<<GPIO5)-(1<<RS232)
		tris	GPIO			;make I/O 5 output
		bsf	b_GPIO5			;and make it high
		call	BitDelay
		clrf	Key1
		btfsc	b_GPIO0			;I/O 0 will be pulled low by
		bsf	Key1,0			;220k resistors unless key pressed
		btfsc	b_GPIO1
		bsf	Key1,1			;same for rest of row
		btfsc	b_GPIO3
		bsf	Key1,2
		movlw	b'11111111'-(1<<GPIO4)-(1<<RS232)
		tris	GPIO			;make I/O 4 output
		bsf	b_GPIO4			;and high
		call	BitDelay
		btfsc	b_GPIO0
		bsf	Key1,4			;read next row
		btfsc	b_GPIO1
		bsf	Key1,5
		btfsc	b_GPIO3
		bsf	Key1,6
		retlw	0
		
Bit2Key		andlw	15
KeyTable	addwf	PCL,F
		retlw	'7'
		retlw	'8'
		retlw	'9'
		retlw	'x'
		retlw	'1'
		retlw	'2'
		retlw	'3'
		retlw	'x'
		retlw	'*'
		retlw	'0'
		retlw	'#'
		retlw	'x'
		retlw	'4'
		retlw	'5'
		retlw	'6'
		retlw	'x'

	#ifdef	Inverted

Send		movwf	OutByte
		bsf	b_RS232Out
		call	BitDelay
		bsf	STATUS,C
SendLoop	rrf	OutByte,F
		movfw	OutByte
		btfsc	STATUS,Z
		goto	EndTransmit
		bsf	b_RS232Out
		btfsc	STATUS,C
		bcf	b_RS232Out
		call	BitDelay
		bcf	STATUS,C
		goto	SendLoop
EndTransmit	bcf	b_RS232Out
		call	BitDelay
		call	BitDelay
		retlw	0

	#else

Send		movwf	OutByte
		bcf	b_RS232Out
		call	BitDelay
		bsf	STATUS,C
SendLoop	rrf	OutByte,F
		movfw	OutByte
		btfsc	STATUS,Z
		goto	EndTransmit
		bcf	b_RS232Out
		btfsc	STATUS,C
		bsf	b_RS232Out
		call	BitDelay
		bcf	STATUS,C
		goto	SendLoop
EndTransmit	bsf	b_RS232Out
		call	BitDelay
		call	BitDelay
		retlw	0

	#endif


; at 1 meg instructions and 2400 baud
; delay is 1,000,000/2400 = 416 cc
; the calling loop and the call/return = 13
; therefore delay needs to be 403 cycles

; to change to 9600 baud
; delay needs to be 104 - 13 = 91
; which would be (3*30)-1 = 89 + 2 = 91

BitDelay	movlw	30	;134	change back to 134 for 2400 baud
		movwf	Temp
DelayLoop	decfsz	Temp,F;		(3*134)-1 = 401 + 2 = 403
		goto	DelayLoop
		retlw	0

Wait100th	clrf	Count
		movlw	13
		movwf	Temp
Delay100L	decfsz	Count,F;	(256*3)-1 = 767
		goto	Delay100L
		decfsz	Temp,F;		(13*(767+3))-1 = 10009 + 3 = 10012
		goto	Delay100L
		retlw	0

		END

A video.
[embed]http://www.youtube.com/v/Q2xrVV9w-yE&hl=en&fs=1[/embed]

Note, this will not work with newer pic chips as they don't have the WPU on I/O 3.

<edit>Somethings I forgot,
The connections from left to right are GND, RS232 out and 5V.
The output is TTL and so can be connected straight to a pic RX pin without a MAX232 chip.
If you want an inverted signal to connect to a PC then uncomment the #define inverted line in the source.
You can change the repeat delay and repeat rate in the source.
</edit>

Mike.
 

Attachments

  • Board B&W.png
    Board B&W.png
    18.8 KB · Views: 895
Last edited:
Mike, you're posting these interesting projects too fast.

I'm still studying the rom search algorithm in your DS18x20 project code (grin).

Regards, Mike
 
Mike, you're posting these interesting projects too fast.

I actually wrote this code about 2 years ago and have just gotten around to posting it and so you'll probably have a few years to study these two before there is any more.

As far as I am aware I think this is the first charlieplexed(ish) keypad code/circuit around. It reads 12 keys with 5 I/O pins so not quite n*(n-1).

Mike.
 
I dismissed the idea of using a standard multiplexed keypad in a Charlieplexed matrix a long time ago because of the "float" pin wiring. I never thought of using an additional pin to get around the problem.

Be careful Mike, there are moments when you could be mistaken for a bloomin' genius (grin).

Nice job. I look forware to studying the code, as always.

Mike, K8LH
 
Note, this will not work with newer pic chips as they don't have the WPU on I/O 3.

I have tried all kind of external hardware tricks trying to provide a HIGH to the GPIO3 pin to mimic missing WPU so your code can also be used for newer 8-pin PICs like 12F629, 12F635 or 12F675 but so far all my efforts failed.

It is so d*** hard to do just that.:mad:
 
I have tried all kind of external hardware tricks trying to provide a HIGH to the GPIO3 pin to mimic missing WPU so your code can also be used for newer 8-pin PICs like 12F629, 12F635 or 12F675 but so far all my efforts failed.

It is so d*** hard to do just that.:mad:

Believe me I've tried. I wanted to make a combination lock with one of the newer 8 pin chips and store the code in EEPROM. Alas, it's not doable because of that missing WPU.

Doing a combination lock with a fixed code would be doable with the above code but that wouldn't be much use, or would it?

Mike.
 
What about using 1 wire keypad & measure the AD Value to detect which key has pressed like (Kifo) did his keypad thing?Never tried auto repeat feature with that method.May be a good project :)
 
Last edited:
Believe me I've tried. I wanted to make a combination lock with one of the newer 8 pin chips and store the code in EEPROM. Alas, it's not doable because of that missing WPU.

Don't give up so soon! There's more ways to skin this cat. For the cost of an extra resistor you can remove all pullups.

Or use 4 I/O lines, 3 resistors and 3 capacitors for the keypad.

Or use 1 I/O line, 5 resistors & 3 caps...

See attached schems for examples. I'm sure there's more ways that I haven't thought of..

Doing a combination lock with a fixed code would be doable with the above code but that wouldn't be much use, or would it?
Of course it would. The single fixed code could be the 'master reset' code, which could be entered to set the other code(s). So long as power is never interrupted (e.g. it's on batteries), then the code(s) remains. If the pic's in sleep most of the time, battery changes may not be for years.
 

Attachments

  • MATRIX_DEC.PDF
    33.4 KB · Views: 758
I don't like any scheme that involves analogue voltages or timed RC solutions as it really messes up the simple debounce and 2 key rollover code. However, the third diagram is interesting. Can you explain how you read this matrix?

Mike.
 
I don't like any scheme that involves analogue voltages or timed RC solutions as it really messes up the simple debounce and 2 key rollover code.
Only the 3rd cct fits into that category.

However, the third diagram is interesting. Can you explain how you read this matrix?
pressing a switch connect 2 unique resistors in series to a cap. The IO pin can time the charge/discharge rate of the cap by switching between output (to provide current) & input (to monitor voltage). There's a function in Proton+ picbasic called 'pot' (I think) that will do this for you.

If you wanted to make the code easier, in addition to allowing a simple wake up on key press, an IO pin would also be connected directly to the capacitor.
 
I was looking at the page in portrait layout and so I was counting the first (left) as the third (bottom). Now I've turned it around the one that's interesting is the first one. That diagram will work without using the internal pullups and so solves the lack of pullups on the newer pic chips. I may have a play with this tomorrow just to make sure it works as I envisage. Assuming it does a programmable combination lock with one of the 8 pin pic chips should be doable.

Will you be watching "State of Origin" tonight?

Mike.
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top