picaxe with 3x4 matrix keypad

inglewoodpete

Senior Member
Is it at all possible to use this keypad with a picaxe like the 18x or 28x1?
I have seen Hippy's 20X2 thread but I'd like to use a chip I already have.
Yes, a simple keypad like the one you indicate can be used in a number of ways.

If you have enough I/O lines available on the PICAXE chip, then just strobe a column/row and then read the inverse (Ie row or column).

Another approach would be to use a number of different valued resistors and a single ADC input. A comprehensive discussion initiated by member BCJKiwi developed a solution some time ago.

Peter
 

fred_b

Member
If you have enough I/O lines available on the PICAXE chip, then just strobe a column/row and then read the inverse (Ie row or column).
Could you explain this further? Not sure what strobe means.

Thanks
 

hippy

Technical Support
Staff member
"Strobe" is another word for "pulsing an output".

In matrix keypad terms it usually means setting an output pin on one port and reading what comes back in on another port, what's read indicate which keys are pressed, and when done clearing that output pin, move on to the next output and repeat until the whole keypad has been scanned.

The matrix keypad will sit between one port driving rows ( or columns ) and another port reading columns ( or rows ). To save on ports, for 4 x 4 keypads, a single port can be used with some pins driving, some pins reading. Which is what my 20X2 code does and it also takes advantage of the PICAXE having internal pull-up resistors.
 

hippy

Technical Support
Staff member
@ mikie_121 : Following on from the above post - On the 28X1 you can use PORTC as a single port to handle a 4 x 4 keypad or use PORTB as outputs to the keypad, PORTC for the inputs. For PICAXE which don't have bi-directional I/O you will have to use one set of pins for output and another set of pins for input.

As long as you can get four inputs and four outputs you can handle a 4 x 4 keypad digitally. If you don't have those, it's possible to use analogue means to read the keypad but this will be slightly more complicated hardware.
 

mikie_121

Member
Here is my code. I somehow lost the file that only reads the keypad and I don't feel like stripping it back down again :)
I haven't tested this on my hardware yet, but it simulates well.

Code:
'Michael Eaton 2010
'Matrix keypad to 7 segment display.

'The program reads inputs from a 3x4 keypad and displays them on a 3 digit display.
'Pressing '*' clears the display, entering numbers makes them appear from right to
' left on the display. Entering more than 3 digits without clearing first will not
' change the display (ie subsequent numbers are ignored).

#Picaxe28X1

'Connections:
'Picaxe Pins	Keypad Pins		7 Segment pins
'C0			2 (column 2)
'C1			3 (row 1)
'C2			4 (column 1)
'C3			5 (row 4)
'C4			6 (column 3)
'C5			7 (row 3)
'C6			8 (row 2)
'0						Reset (14)
'1						Clock (16)
'2						Latch (15)
'3						Output Enable (13)
'4						Data (11)
'						GND (1,2,3,4)
'						12V (5,6,7,8)
'						Vcc (9,10)


symbol columnOutputs = %00010101		'Define keypad columns
symbol rowOutputs = %01101010			'Define keypad rows
symbol outNumber = b0				'variable for output number
symbol shiftCounter = b1			'output counter to send display data serially
symbol columnInput = b2				'column storage for keypad input
symbol digit1 = b3				'Rightmost digit
symbol digit2 = b4				'Middle digit
symbol digit3 = b5				'Leftmost digit
symbol digitCount = b6				'used to keep track of which digit is active
symbol number = b7				'variable used to read in bits and also to store the number. Used for lots of things

let shiftCounter = 0				'Initialise all variables to zero
let columnInput = 0
let digit1 = 0					
let digit2 = 0
let digit3 = 0
let digitCount = 0
let number = 0

'pin assignments
symbol shiftRegReset = 0			'leg 21
symbol dataClock = 1				'leg 22
symbol latch = 2				'leg 23
symbol outputEnable = 3				'leg 24
symbol serialData = pin4			'leg 25

high shiftRegReset
low outputEnable

main:

gosub inputNumber

if number = "*" then				'clear display when * is pressed
	
	let digitCount = 0			'reset digit counter to allow a new number to display
else
	if digitCount = 0 then			'put number into Rightmost digit and clear middle and leftmost digits
		digit1 = number
		digit2 = 0
		digit3 = 0
	elseif digitCount = 1 then		'put new number into middle digit, leaving rightmost and clearing leftmost
		digit2 = number
		digit3 = 0
	elseif digitCount = 2 then		'leave middle and rightmost, put number into leftmost digit
		digit3 = number	
	else goto main				'and don't output any more numbers
	endif
endif
high outputEnable				'blank display
gosub sendData
low outputEnable				'turn on the display
pause 300
goto main
end
	
'*****************************************************
' Subroutines
'*****************************************************

inputNumber:					'first turn columns to outputs and monitor the rows
let dirsc = columnOutputs
let pinsc = columnOutputs
inputLoop:					'then check for a button press
readportc number				'store the pins so we can work out which row was activated
let number = number & rowOutputs		'mask to only see the row input pins
if number <> 0 then				'if the port changes (does not equal what we set it to)
	let dirsc = rowOutputs			'transpose the inputs/outputs on portc
	let pinsc = rowOutputs
	readportc columnInput			'store the pins again so we can work out which column was activated
	let columnInput = columnInput & columnOutputs		'mask to only see the column input pins
else goto inputLoop
endif
let number = number or columnInput		'combine both variables to allow easier lookup
select case number				'lookup table
case %00000110					'row 1 column 1
	let number = %00001100 	'1
case %00000011					'row 1 column 2
	let number = %10110110 	'2
case %00010010					'row 1 column 3
	let number = %10011110 	'3
case %01000100					'row 2 column 1
	let number = %11001100 	'4
case %01000001					'row 2 column 2
	let number = %11011010 	'5
case %01010000					'row 2 column 3
	let number = %11111010 	'6
case %00100100					'row 3 column 1
	let number = %00001110 	'7
case %00100001					'row 3 column 2
	let number = %11111110 	'8
case %00110000					'row 3 column 3
	let number = %11011110 	'9
case %00001100					'row 4 column 1
	let number = "*"	'star
case %00001001					'row 4 column 2
	let number = %01111110 	'0
case %00011000					'row 4 column 3
	let number = %10000000 	'-
else
	let number = %00000000 	'blank
end select
return
end

'*****************************************************

sendData:
inc digitCount
let outNumber = digit1				'output all of the digits in sequence, starting with rightmost
gosub sendBits
let outNumber = digit2					
gosub sendBits
let outNumber = digit3
gosub sendBits
pulsout latch,1					'latch data
return
end

'*****************************************************

sendBits:
'This section of code sets the serial data pin to bit0 and then sends
' a clock pulse. Bit1 is then shifted to bit0, another clock pulse follows
' and this continues until the whole byte has been sent. A latch pulse
' is then sent and the number will be displayed.
for shiftCounter = 0 to 7
	serialData = bit0			'serial data
	pulsout dataClock,1			'serial clock pulse for 1 millisecond
	outNumber=outNumber/2			'shift bits along to output the next bit
	next shiftCounter			'loop 8 times to output the whole byte
return
end

'*****************************************************
 

cactusface

Senior Member
KeyPads...

Hi,
As somneone fairly new to PcAxe, I find the ADC keypad much simpler to use, less heavier on software and coding... It's so simple, and only uses one input on any? port. there's a few threads on the forum. I used a ready made 3x4 keypad, but I hope to build a 4x4 with tac switches and 10k 1% resistors. My current pad as resistors across the rows and columns, but next time I'll try the other way with a string of resistors (10k, 1%) and the switches tapped between them, then connected to ADC input.

Go for it, good luck.
Regards
Mel
 

ljg

New Member
how does the ADC method as drawn work in decoding multiple key presses? It seems that having rsitors with the same value would give like resistances for different key combinations.

It seems that if each resistor had twice the value of the previous, you'd get a unigue resistance for any keystroke combination.
 

mikie_121

Member
Well, I wired it all up and it works a treat!
You're right Larry & BCJKiwi, multiple simultaneous button presses results in the highest button being registered (closest button to the ADC pin) but that doesn't worry me for this application.
Attached is my latest version of the code working on a 20M, rather than the 28X1.
Code:
'Michael Eaton 2010
'Resistor matrix keypad to 7 segment display.

'The program reads analogue voltage from a 3x4 keypad and displays the numbers on a 3 digit serial display.
'Pressing '*' clears the display, entering numbers makes them appear from right to
' left on the display. Entering more than 3 digits without first clearing will not
' change the display (ie subsequent numbers are ignored).

#Picaxe20M

'Connections:
'Picaxe Pins	Keypad Pins		7 Segment pins
'ADC1			5
'0						Data (11)
'1						Output Enable (13)
'2						Latch (15)
'3						Clock (16)
'4						Reset (14)
'						GND (1,2,3,4)
'						12V (5,6,7,8)
'						Vcc (9,10)

'Input Pin Assignments
symbol InputPin = 1				'ADC pin for keypad input leg 9
'Output Pin Assignments
symbol serialData = pin0			'leg 18
symbol outputEnable = 1				'leg 17
symbol latch = 2					'leg 16
symbol dataClock = 3				'leg 15
symbol shiftRegReset = 4			'leg 14

'Initialise Output Pins
high shiftRegReset
high outputEnable

symbol outNumber = b0				'variable for output number
symbol shiftCounter = b1			'output counter to send display data serially
symbol digit1 = b3				'Rightmost digit
symbol digit2 = b4				'Middle digit
symbol digit3 = b5				'Leftmost digit
symbol digitCount = b6				'used to keep track of which digit is active
symbol number = b7				'variable used to read in bits and also to store the number. Used for lots of things

let shiftCounter = 0				'Initialise all variables to zero
let digit1 = 8					
let digit2 = 8
let digit3 = 8
let digitCount = 2
let number = 8

'Main Program Loop
main:
gosub inputLoop
if number = "*" then				'clear display when * is pressed
	digitCount = 0				'reset digit counter to allow a new number to display
	digit1 = 0
	digit2 = 0					'clear all 3 digits
	digit3 = 0
else
	if digitCount = 0 then			'put number into Rightmost digit and clear middle and leftmost digits
		digit3 = 0
		digit2 = 0
		digit1 = number
	elseif digitCount = 1 then		'put new number into rightmost digit, move rightmost to middle and clear leftmost
		digit3 = 0
		digit2 = digit1
		digit1 = number
	elseif digitCount = 2 then		'shift middle and rightmost left one digit, put number into rightmost digit
		digit3 = digit2
		digit2 = digit1
		digit1 = number	
	else goto main				'and don't store any more numbers
	endif
	inc digitCount
endif
high outputEnable					'blank display
gosub sendData					'send data to display
low outputEnable					'turn on the display
pause 300
goto main
end
	
'*****************************************************
' Subroutines
'*****************************************************

inputLoop:						'check for a button press
pause 1
readadc InputPin, number
if number < 15 then 
	goto inputLoop
elseif number < 58 then let number = %00001100	'1
elseif number < 63 then let number = %10110110	'2
elseif number < 67 then let number = %10011110	'3
elseif number < 74 then let number = %11001100	'4
elseif number < 80 then let number = %11011010	'5
elseif number < 88 then let number = %11111010	'6
elseif number < 100 then let number = %00001110	'7
elseif number < 115 then let number = %11111110	'8
elseif number < 135 then let number = %11011110	'9
elseif number < 175 then let number = "*"		'star
elseif number < 210 then let number = %01111110	'0
elseif number > 209 then let number = %10000000	'-
endif
return
end

'*****************************************************

sendData:
let outNumber = digit1				'output all of the digits in sequence, starting with rightmost
gosub sendBits
let outNumber = digit2					
gosub sendBits
let outNumber = digit3
gosub sendBits
pulsout latch,1					'latch data
return
end

'*****************************************************

sendBits:
'This section of code sets the serial data pin to bit0 and then sends
' a clock pulse. Bit1 is then shifted to bit0, another clock pulse follows
' and this continues until the whole byte has been sent. A latch pulse
' is then sent and the number will be displayed.
for shiftCounter = 0 to 7
	serialData = bit0				'serial data
	pulsout dataClock,1			'serial clock pulse for 1 millisecond
	outNumber=outNumber/2			'shift bits along to output the next bit
	next shiftCounter				'loop 8 times to output the whole byte
return
end

'*****************************************************

end
 
Last edited:

BCJKiwi

Senior Member
Depending on the switch type and wiring scheme it is possible to detect multiple switches pressed together but it requires a more complex arrangement of resistors and values than the simple resistor string. Even with readadc10 some combinations are very close to others so it becomes difficult to determine the combination with certainty/repeatability.
 

mikie_121

Member
One problem I have noticed: If you don't press a keypad button down hard then the program may read incorrectly. The buttons have about 200 Ohms resistance on a firm press but they can have much higher resistance on a weak press resulting in a lower number being registered.
Just something to be aware of.
 

premelec

Senior Member
1 Wire ADC 3x5 resistors etc

I made up a spreadsheet for resistors to output and found that using 3K3 3K6 10K 10K 10k 33K gave better spread [also I modled input R for key press resistance]. Anyhow I did empirical test with 20x2 and put in actual values in program - also .22uF cap across 33K output resistor to smooth things out for key contact R variation. I haven't fully implemeted the program yet so don't know if further deglitching will be needed - on first tests the outputs were very stable with an old TT keypad... the values are all above 100 and I plan to use values below 100 on the ADC input for other control purposes [such as what variable to load the input into etc...]. Anyhow thanks for this thread to get me going on it again!

@BCJKiwi thanks for that link to the past - I just reviewed it!
 
Last edited:

premelec

Senior Member
spread improvement

Well.. the 33K tail end instead of 10K help a lot and 3.6K for second from top also helped a bit - the minimum difference spread now 6 out of 255 - from my code

Symbol key1 =123 '121'ADC value for key 1 pressed
Symbol key2 =130 '127'ADC value for key 2 pressed
Symbol key3 =137 '133'ADC value for key 3 pressed
Symbol key4 =145' 141'ADC value for key 4 pressed
Symbol key5 =154 '149'ADC value for key 5 pressed
Symbol key6 =163 '158'ADC value for key 6 pressed
Symbol key7 =175 '169'ADC value for key 7 pressed
Symbol key8 =187 '181'ADC value for key 8 pressed
Symbol key9 =203 '194'ADC value for key 9 pressed
Symbol key10=220 '213'ADC value for key * pressed
Symbol key0 =241 '227'ADC value for key zero pressed
Symbol key11=248 '255'ADC value for key # pressed use greater than

To be selected by a "less than" function except for # key. As I said I'll use lower values for special functions - initially selecting by > 10 and <110 ... Seems to work so far!
 

BCJKiwi

Senior Member
@ Mikie121

Don't know which switches are being used but 200R variable seems like a poor switch.

When testing I used a number of different switches including an really old well used phone keypad and did not find resistances anything like that.

Would suggest that you may be fighting an uphill battle to get reliable sensing with switches of that type.
 

cactusface

Senior Member
ADC KeyPads

Hi,
Larry I think someone as already said it but pressing two keys as once only registers one key input. My keypads are very cheap ones, 80p for 2 on ebay. Their the usual squiggy rubber overlays, with a finger pattern on the PCB. But I get a consistant value returned and it works well. 200R seems a lot for a switch contact, perhaps I should test them. The software is compact and easy, no big lookup tables, etc.

keys:
readadc 0,b1 ;read ADC Value in to b1

if b1 = 0 then : return : end if : No key press return
If b1 = 85 Then : key = 1 : End If : These are the values my keypad returns.
If b1 = 67 Then : key = 2 ::End If :3x4 matrix ketpad... 10K between rows and
If b1 = 75 Then : key = 3 : End If :3K3 between columns, 10K in 5v line..
If b1 = 63 Then : key = 4 : End If
If b1 = 53 Then : key = 5 : End If
If b1 = 58 Then : key = 6 : End If
If b1 = 51 Then : key = 7 : End If
If b1 = 44 Then : key = 8 : End If
If b1 = 47 Then : key = 9 : End If
If b1 = 127 Then : key = 10 : End If
If b1 = 91 Then : key = 11 : End If
If b1 = 106 Then : key = 12 : End If


on key gosub nokey,job1,job2,job3,job4,job5,job6,job7,job8,job9,job10,job11,job12
let key =$00

Hope this helps, talking of HELP anyone good with HPWM see my new thread.
Regards
Mel.
 
Last edited:

MartinM57

Moderator
Haven't really followed the thread - I'd put a +/-1 tolerance on all your IF tests but I would still be a bit dubious about differentiating numbers like 44 and 47 as being different keys - temperature effects, switch contact degradation over time (they must be reject switches already if you're seeing 200R when 'on' ;)), pressing a key slightly harder etc
 

mikie_121

Member
Yeah, I think my keypad is pretty old. It had been used for a few projects before. I'll pick up a new one and see how that goes.
 
Top