My 4x3 keypad coding needs help?

OLDmarty

Senior Member
Hi All,

As the title says, i need some input for my 4x3 keypad routine.

I started putting together a code routine to read a 4x3 (12 button) keypad, but i'm having trouble working out how i can store up to 3 key-presses and how to keep track of how many key-presses were detected....

Overall, i want to to enter a single digit, or up to 3 digits, where each keypress (data) is then stored into 3 locations, such as b10, b11, b12 variables for 1's, 10's and 100's key press values.
So, pressing "123" would write to those 3 variables etc.

Based on 3 stored NIBBLES, i would then assign the Nibble data to output pins to light up (upto 12) LEDs etc.

I'm not strict about any changes to my simple code, i know many people out there could reduce my code to 10 or less lines lol.


Code:
#PICAXE 28X2        
#NO_DATA            
#NO_TABLE            
#Terminal 9600

;### 4x3 KEYPAD scanning trial

; Define Keypad I/O pins
symbol row1 = C.0            ;INPUT row, Keypad Pin #2.
symbol row2 = C.1            ;INPUT row, Keypad Pin #7.
symbol row3 = C.2            ;INPUT row, Keypad Pin #6.
symbol row4 = C.5            ;INPUT row, Keypad Pin #4.

symbol col1 = A.1            ;OUTPUT column, Keypad Pin #3.
symbol col2 = A.2            ;OUTPUT column, Keypad Pin #1.
symbol col3 = A.3            ;OUTPUT column, Keypad Pin #5.


; Define variables
symbol key_pressed = b9
symbol _1s        = b10
symbol _10s      = b11
symbol _100s    = b12

dirsA = %11111111            ;Set A.0-A.7 to OUTPUTS(1)
dirsB = %11111111            ;Set B.0-B.7 to OUTPUTS(1)
dirsC = %00011000            ;Set C.0-C.7 to  INPUTS(0), EXCEPT C.3 & C.4 reserved for i2c for later use.



;---------------------------------------------------------------------
; Initialize
    low col1            ;Turn ALL 3 COLUMNS "OFF" (0 value).
    low col2
    low col3

main:


; Start Scanning the keypad for a button-press.
    high col1            ;Ready to read Keypress on 1,4,7 or "*".
    low col2            ;Turn OFF Column 2.
    low col3            ;Turn OFF Column 3.

    if pinC.0 = 1 then
      key_pressed = "1"
      goto key_pressed_detected
    endif
    if pinC.1 = 1 then
        key_pressed = "4"
        goto key_pressed_detected
    endif
    if pinC.2 = 1 then
        key_pressed = "7"
        goto key_pressed_detected
    endif
     if pinC.5 = 1 then
        key_pressed = "*"
        goto key_pressed_detected
    endif

;---------------------------------------------------------------------

    low col1            ;Turn OFF Column 1.
         high col2            ;Ready to read Keypress on 2,5,8 or 0.
      low col3            ;Turn OFF Column 3.

    if pinC.0 = 1 then
        key_pressed = "2"
        goto key_pressed_detected
    endif
    if pinC.1 = 1 then
        key_pressed = "5"
        goto key_pressed_detected
    endif
    if pinC.2 = 1 then
        key_pressed = "8"
        goto key_pressed_detected
    endif
    if pinC.5 = 1 then
        key_pressed = "0"
        goto key_pressed_detected
    endif


;---------------------------------------------------------------------

    low col1           ;Turn OFF Column 1.
    low col2            ;Turn OFF Column 2.
        high col3            ;Ready to read Keypress on 3,6,9 or "#".

    if pinC.0 = 1 then
        key_pressed = "3"
        goto key_pressed_detected
    endif
    if pinC.1 = 1 then
        key_pressed = "6"
        goto key_pressed_detected
    endif
    if pinC.2 = 1 then
        key_pressed = "9"
        goto key_pressed_detected
    endif
    if pinC.5 = 1 then
        key_pressed = "#"
        goto key_pressed_detected
    endif


;---------------------------------------------------------------------


; Key press detected
key_pressed_detected:
    pause 100            ;Debouncing delay
    SerTxd( key_pressed, CR, LF )    ;/send Key_pressed data to serial window to confirm values are correct.

key_pressed = ""            ;Set Key_pressed value to blank, in between key presses that are detected.

goto main                ;goto beginning to look for next key-press.
 
Last edited:
Hi,

When you have a "sequential" process, the bptr (byte pointer) can be a very useful tool. In principle you can use the @bptr variable in exactly the same way as any other variable such as b9. Thus a sequence of button-presses can be handled with very minor changes and additions (Below, untested). I am assuming that you want to (always) enter 3 digits with the 100s first, but this could be changed with minor alterations to how the bptr is initialised and decremented or incremented.
Code:
; ........
symbol key_pressed = @bptr     ; Was b9 [ which is identical if bptr were initialised to 9 ]
symbol _1s        = b10
symbol _10s      = b11
symbol _100s    = b12

Initialize:
    bptr = 12        ; Assuming first byte to be received is the hundreds (b12)    [ or =10 if first digit is units ]
    low col1          ; Turn ALL 3 COLUMNS "OFF" (0 value)       [I doubt if this is needed].
;    etc.
main:
;  This section unchanged    [ But could be much more compact ! ]

key_pressed_detected:
   pause 100                        ; Debouncing delay       [Not sure what this achieves]
   SerTxd( key_pressed, " " )       ; Send Key_pressed data to serial window to confirm values are correct.
   dec bptr                            ; Move the pointer to the next digit (e.g. b11 then b10) [ or INC if units first ]
   key_pressed = ""                  ; Set Key_pressed value to blank, in between key presses that are detected.
   if bptr > 9 then goto main    ; Until 3 digits have been received, goto the beginning to look for next key-press. [ Or <13 if hundreds last ]
   sertxd(CR,LF,"The number is ",b12,b11,b10)   ; Variables contain ASCII values (i.e. were defined within quotes " ")
   goto Initialize                      ; Try again or do whatever else is needed

Cheers, Alan.
 
Hi,

When you have a "sequential" process, the bptr (byte pointer) can be a very useful tool. In principle you can use the @bptr variable in exactly the same way as any other variable such as b9. Thus a sequence of button-presses can be handled with very minor changes and additions (Below, untested). I am assuming that you want to (always) enter 3 digits with the 100s first, but this could be changed with minor alterations to how the bptr is initialised and decremented or incremented.
Code:
; ........
symbol key_pressed = @bptr     ; Was b9 [ which is identical if bptr were initialised to 9 ]
symbol _1s        = b10
symbol _10s      = b11
symbol _100s    = b12

Initialize:
    bptr = 12        ; Assuming first byte to be received is the hundreds (b12)    [ or =10 if first digit is units ]
    low col1          ; Turn ALL 3 COLUMNS "OFF" (0 value)       [I doubt if this is needed].
;    etc.
main:
;  This section unchanged    [ But could be much more compact ! ]

key_pressed_detected:
   pause 100                        ; Debouncing delay       [Not sure what this achieves]
   SerTxd( key_pressed, " " )       ; Send Key_pressed data to serial window to confirm values are correct.
   dec bptr                            ; Move the pointer to the next digit (e.g. b11 then b10) [ or INC if units first ]
   key_pressed = ""                  ; Set Key_pressed value to blank, in between key presses that are detected.
   if bptr > 9 then goto main    ; Until 3 digits have been received, goto the beginning to look for next key-press. [ Or <13 if hundreds last ]
   sertxd(CR,LF,"The number is ",b12,b11,b10)   ; Variables contain ASCII values (i.e. were defined within quotes " ")
   goto Initialize                      ; Try again or do whatever else is needed

Cheers, Alan.
Thanks for your help Alan,

I've never used the @bptr option before, so that's another learning curve for me :-)


In general, The entered number would not always be 3 digits, so a 1 or 2 digit number would also need to be processed.
I'd prefer the first keypress to be shifted along into the next most significent position *when* other key presses are received.

For example:
Pressing "1" would become "0,0,1" loaded into the 3 variables, b12 (100's), b11 (10's), b10 (1's)
Pressing "1,2" would become "0,1,2" loaded into the 3 variables, b12 (100's), b11 (10's), b10 (1's)
Pressing "1,2,3" would become "1,2,3" loaded into the 3 variables, b12 (100's), b11 (10's), b10 (1's)

Additionally:
Pressing "1,2,3,4" would become "2,3,4" as the "1" would be pushed out of the MSB variable (b12) and the remaining 3 values are loaded into b12 (100's), b11 (10's), b10 (1's)


Another task is to add a keypress timeout, so after a key press is detected, it will only load into the variable(s) after say 200mSecs (duration to be confirmed with testing), which allows some time to press a 2nd or 3rd button if needed.
After the final keypress times out, THEN it would load the number(s) into the variable(s).
This is basically like a TV remote control channel selection, where the remote will only change the channel after it detects no more key presses.
 
Hi,

The use of bptr and @bptr is not essential, the same function could be based on, for example, the following. But the bptr can be more efficient, particularly when using the (optional) @bptrdec "post-decrement" (of the pointer address) feature.
Code:
symbol digit_pointer = b8     ; Equivalent to bptr
symbol key_pressed = b9      ; Equivalent to @bptr
digit_pointer = 12
....... 
key_pressed_detected:
   poke digit_pointer , key_pressed
   dec digit_pointer
;   etc.

I think the timeout needs to be at least one or two seconds. In practice, the "main" section probably needs to loop "continuously" (e.g. by ending with a GOTO main) until the first digit has been detected and then set a GOTO timeout after a specified number of additional loops after any digit has been received.

The variable length field is probably most easily handled by initially setting the BPTR to elsewhere in memory, perhaps above the normal variables at say address 64. Then when the timeout occurs you can simply work backwards copying @BPTRINC to b10, b11 and b12 depending on how far the pointer has been offset from its initial value. Or strictly, you don't need b10 - b12 at all, a "final" value could be calculated directly from the bptr with something like (untested):
Code:
init:
   bptr = 64
; Insert most of the code as listed above
timeout:
   number_of_digits = 64 - bptr MAX 3    ; Assuming bptr (pointer) has been decrementing
   bptr = bptr + number_of_digits
   final_value = 0
do
   dec bptr                                         ;  Work backwards through the digits
   final_value  = final_value * 10                 ; Increase the "weight" of previous digits
   final_value  = @bptr AND 15 + final_value    ;  Convert the ASCII byte to a binary number (0 - 9) and add in
   dec number_of_digits
loop until number_of_digits = 0
Cheers, Alan.
 
Hi,

" 1 , 2 , 3 , * " is potentially easier to code and maybe faster and more reliable than " 1 , 2 , 3 , [wait] " :) . BUT, "The User" needs to "know" that an [Enter] button operation (i.e. "*" or "#" ?) is required, and/or whether (or not) the other button gives a "Backspace" or a "Delete" (all digits, which again may be easier to code).

Much depends on whether The User is to receive any direct "feedback" or confirmation of the digits that they have (or haven't) entered. ;)

Cheers, Alan.
 
Hi,

" 1 , 2 , 3 , * " is potentially easier to code and maybe faster and more reliable than " 1 , 2 , 3 , [wait] " :) . BUT, "The User" needs to "know" that an [Enter] button operation (i.e. "*" or "#" ?) is required, and/or whether (or not) the other button gives a "Backspace" or a "Delete" (all digits, which again may be easier to code).

Much depends on whether The User is to receive any direct "feedback" or confirmation of the digits that they have (or haven't) entered. ;)

Cheers, Alan.

Hi Alan,
It would be preferable for me to have the [wait] option at the end of the entered number(s), but i see it may be more complex compared to using a manual [Enter] button.
I'm simply trying to avoid the [Enter} button, to make the data entry more like a TV remote control.

The picaxe uses the i2c port to drive an external LED matrix chip (Holtek HT16K33), to display the 3x 7-seg numbers as instant feedback while the number(s) are being entered.
 
Hi,
I've never used a keypad, but I noticed what looks like a "cut-and-paste" error:

low col3 ;Turn OFF Column 1.
low col3 ;Turn OFF Column 2.
high col3 ;Ready to read Keypress on 3,6,9 or "#".

Anyway, I think you should be writing to the rows and reading the columns. I may be wrong...
I'd get the keypad working properly using the terminal before connecting anything else up.
 
Hi,
I've never used a keypad, but I noticed what looks like a "cut-and-paste" error:

low col3 ;Turn OFF Column 1.
low col3 ;Turn OFF Column 2.
high col3 ;Ready to read Keypress on 3,6,9 or "#".

Anyway, I think you should be writing to the rows and reading the columns. I may be wrong...
I'd get the keypad working properly using the terminal before connecting anything else up.

Wow! Thanks for noticing that typo, I have now updated my OP above to show the correct column info, it also fixed a slight bug in my code here on the workbench....with thanks ;-)
 
Last edited:
Back
Top