4x4 matrix keypad code help
Don M
Posts: 1,653
I need some help understanding some code posted originally by Peter Jakacki. I have a 4x4 keypad with the following layout:
and here is his driver code:
My first question is how would you make a "dat" table for the keypad shown in above picture? In his code he refers to this:
Second question- in my main method I want to just display what keys are pressed on PST but don't understand what would be returned from Peter's code to display. Here is a start to my Main code with ? where I don't understand:
The keypad is connected to the prop as follows (with appropriate pull-up resistors as mentioned in his code):
Col 1 -> P0
Col 2 -> P1
Col 3 -> P2
Col 4 -> P3
Row 1 -> P4
Row 2 -> P5
Row 3 -> P6
Row 4 -> P7
So in kp.start I set up 4 rows, 4 columns, Columns start on P0, Rows start on P4, and then 0 because I don't know what to put there for lookup table.
Thanks.
Don
and here is his driver code:
{{ KEYPAD ENCODER - Peter Jakacki Starts up a cog that scans a keypad matrix and translates and buffers the codes. This code was originally written as a hardcoded 4X4 (tested) but was adapted for larger matrices (not tested) This is a simple but complete Spin implementation Keypad actions don't have to be fast but should be reliable so debounce periods are several ms The keypad matrix is made up of rows (horizontal) and columns. (vertical) All column lines are driven low while the row lines are inputs pulled high (10K). When a key is pressed one of the row lines will become low. The column lines are now scanned one by one to find out which column it belongs to. When the column is found the column index is combined with the row index to form a keycode. The keycode is translated in a user specified table and pushed into the keypad buffer. }} CON bufsz = 16 ' keypad buffer size (keep in multiples of 2) VAR long stack[32] long rows,cols,colpins,rowpins,akey,lastkey,keyrd,keywr,keycodes,ms1 byte keybuf[bufsz] ' Start the keypad scanner - column and row pins do not have to be adjacent ' If keypad code translation is required then set keycodeptr to address of ' translation table or else set to zero for no translation ' pub start(keyrows,keycols,firstcolpin,firstrowpin,keycodeptr) : okay | i rows := keyrows cols := keycols colpins := firstcolpin rowpins := firstrowpin keyrd := 0 keywr := 0 keycodes := keycodeptr ' remember pointer to user keycodes ms1 := clkfreq / 1000 - 4296 okay := cognew(scankeys,@stack) +1 ' run keypad in it's own cog ' Returns a non-zero value if there is a keypad code in the buffer ' pub getkey : keycode keycode := 0 if keywr <> keyrd keycode := keybuf[keyrd++&(bufsz-1)] return ' ******************* background methods run in their own cogs ******************** pri scankeys 'main scanning loop dira[rowpins..rowpins+rows-1]~ 'ensure row lines are inputs repeat outa[colpins..colpins+cols-1]~ 'drive all columns low (all active) dira[colpins..colpins+cols-1]~~ lastkey := 0 repeat until row 'wait until any key is pressed ms(5) 'oh hum, this is so boring ms(5) 'there was a signal, debounce if row 'double-check row again (noise?) keypressed 'seems solid, let's try to encode it pri keypressed | i outa[colpins..colpins+cols-1]~ 'columns are always active low repeat i from 0 to cols-1 'find out whch column is active dira[colpins..colpins+cols-1]~ 'all columns inactive except... dira[colpins+i]~~ 'only one column active ms(1) 'debounce period if row 'ignore false press (glitch) akey := i + (( >|row)-1 )*cols 'convert row and column if keycodes 'translate keycodes? akey := @keycodes.byte[akey] 'lookup new code (usually ASCII) if lastkey <> akey 'great! but is it a new key? lastkey := keybuf[keywr++&(bufsz-1)] := akey 'yes, let's buffer it release 'wait for button release return return pri release repeat until row == 0 'row lines are released repeat while row 'wait if still glitching (shouldn't) ms(5) 'debounce a further 5ms pri row return !ina[rowpins..rowpins+rows-1] & (rows*cols-1) pri ms(period) waitcnt(ms1*period + cnt)
My first question is how would you make a "dat" table for the keypad shown in above picture? In his code he refers to this:
' Start the keypad scanner - column and row pins do not have to be adjacent ' If keypad code translation is required then set keycodeptr to address of ' translation table or else set to zero for no translation
Second question- in my main method I want to just display what keys are pressed on PST but don't understand what would be returned from Peter's code to display. Here is a start to my Main code with ? where I don't understand:
pub main | ? term.start(31, 30, %0000, 115_200) ' start terminal (use PST) kp.start(4, 4, 0, 4, 0) ' start keypad driver term.str(string("Keypad demo testing... press a key", 13)) kp.getkey( ? ) term.str( ? )
The keypad is connected to the prop as follows (with appropriate pull-up resistors as mentioned in his code):
Col 1 -> P0
Col 2 -> P1
Col 3 -> P2
Col 4 -> P3
Row 1 -> P4
Row 2 -> P5
Row 3 -> P6
Row 4 -> P7
So in kp.start I set up 4 rows, 4 columns, Columns start on P0, Rows start on P4, and then 0 because I don't know what to put there for lookup table.
Thanks.
Don
Comments
the method getkey returns a 32bit integer-value.
I would display this value with the method ".dec" from the term-object.
I guess it will return the values 1-16. But I haven't looked into the scancode.
Anyway by analysing it this way you will know what values are coming.
Based on that you simplay code a case-statement
best regards
Stefan
Out of curiousity I tried changing kp.start(4, 4, 0, 4, 0) to kp.start(4, 4, 0, 4, 1) and with that change it will give me a different result for each key but instead of being 0 - 15 they are now 64 - 79 so I don't understand the keycode translation table mentioned in the code.
Here is my Main code:
another solution is to use a case-statement.
It doesn't really matter what the values are
if the returned value matches a certain number your code can surely conclude which key is pressed.
I haven't looked into the code so I have no idea how the code works.
But anyhow if no key is pressed the return-value should be different from any pressed key.
If you want more help attach your archived code to a posting by using the archive-project-function of the propellertool.
mainmenu File - archive ... project
best regards
Stefan
The keypad code itself seems very solid at recognizing keypresses. If you press 2 keys at once it only recognizes whichever key was pressed first.
You the user can define a translation-table. As far as I have seen it this translationtable is not included in the object.
You the user would have to define it in your top-object-file (in your case the file "Mach_control_Main.spin)
the last parameter of the start-method of the Keypad_encoder-object is the pointer to this translation-table
in the method keypressed there is an if-condition
if keycodes ... which means if variable keycodes contains a value different from zero
the code is translated through the codeline
What I don't understand - and want more pointer-experienced user to chime in - is
the last parameter of
is named keycodeptr which makes me think it is a pointer to a variable
now this value is copied to variable "keycodes" and later on this codeline
uses the "@"-operator which means use adress of variable "keycodes"
Now if I understand right keycodes already contains an adress to a variable
and this does not make any sense for me.
But I guess I'm overlooking something or haven't understand an important detail of the code.
So please take the tomatoes from my eyes: how the heck does this code work?
And how does a translation-table look like in the top-object-file?
it's not my code but now I'm very interested in how it works.
By the way: this is a "good" example of how too small code-documentation makes code hard to understand.
I appreciate each uploaded object but this is not what I would call a gold-standard-object. Not even silver-standard-object.
It's just an object. Not more not less.
best regards
Stefan
It is more likely a data sequence that whatever the position in the data file will return the value in that location.
When I was developing my 74C922/74C923 Keypad object I had to use an lookupz
lookupz(bufkey2: 1, 2, 3, 10, 4, 5, 6, 11, 7, 8, 9, 12, 15, 0, 14, 13, 16, 17, 18, 19) since pressing a certain key would give an value of 0, and as the Don M had it would fly past as 0's. I then had to use another method to make sure the it correctly debounced the key press.
And I sometimes over-document my code, so I can read my coding later because I can read some of my code and don't understand how I got it working and the logic behind it at a later date.
The keycode transaction table needs to be set up by you if you want one. By changing the kp.start(4, 4, 0, 4, 0) to kp.start(4, 4, 0, 4, 1) the method is accessing data at location 1 rather than not accessing any data when you have written an 0 in the method.
An example DAT section translation table could look like this: and would be communicated to the driver as @table.
So where do I place this table? In the Main object or the Keypad object?
it is placed in the main-object.
Then the fourth parameter of the keyboard-startmethod is "@table" which means tell the start-method the RAM-adress of the table that the keypad-object can access the table through the given pointer
Pointers are one possability to access variables across objects.
best regards
Stefan
if you want to proceed faster as with asking and waiting for answers in the forum.
look inside the FullDuplexSerial-object what methods this object provides. Think a couple minutes about how a method might work.
If you are familiar with programming in general I estimate in 70% of the cases you will understand what the code does.
If you don't understand it you have two options
1.) recode the method in a testprogram and add debug-output to whatever you are interested in
2.) come back to the forum with a specific question
the codeline
makes the compiler store the ASCII-codes of character "A" and "B" into HUB-RAM.
The ASCII-codes of "A" is decimal 65 and "B" decimal 66
If you want to display a "A" in the terminalprogram you use
You could have find out that yourself by looking into the FullDuplexSerial-object what methods it does provide and how they work
best regards
Stefan
Thanks again.
BTW- I don't mind posting and waiting as it helps me learn and hopefully others as well. During the downtime in between I do try other bits of code to see what the results are.
to
as you mentioned earlier in the thread. With your and others help here I made the simple demo program to test the driver.
Here's the demo program object: