| Menu | JAQForum Ver 19.10.27 |
Forum Index : Microcontroller and PC projects : RP2040 program for Keypad with more than 4x4 keys.
| Page 1 of 2 |
||||||
The RP2350 MMBasic can utilize large keypads. This provides that for a RP2040. There is bound to be much room for improvement in how it goes about it so any suggestions are welcome. However it seems to get the job done as it is. The initial version was much simpler but the PORT Command and Function GP numbers had to be set one at a time throughout the program. This sets them all at the start. ' KeyPad2040.bas - Program for more than 4x4 Key Pad 'rows = number of rows 'cols = number of columns 'RowPinGP = first of the row GP pin numbers 'ColPinGP = first of the column GP pin numbers 'Key = number of the key that has been pressed. eg. 0 to 19 for 4 columns x 5 rows. 1st row 0 to 3, last 16 to 19. Option base 0 Dim integer n, Key=-1, RowPinGP=0, rows=5, ColPinGP=5, cols=4 For n=RowPinGP To RowPinGP+rows-1 SetPin MM.Info(pinno "GP"+Str$(n)),DOUT 'set the Port out pins Next For n=ColPinGP To ColPinGP+Cols-1 SetPin MM.Info(pinno "GP"+Str$(n)), INTH, Key.Pad, PULLDOWN 'set the Port input pins and ISR Next Execute "Port(GP"+Str$(RowPinGP)+",rows) = 2^rows-1" 'set all row pins high Do ' main processing loop If Key+1 Then Print Key, Hex$(Key) : key=-1 'to retain the value of Key set a flag in the sub and reset it here Loop Sub Key.Pad Local integer y, x For y = 0 To rows-1 'read the rows Execute "Port(GP"+Str$(RowPinGP)+",rows) = 1 << y" Pause 9 'contact bounce delay Execute "x = Cint(Log(Port(GP"+Str$(ColPinGP)+",cols)+.001) / Log(2))" 'read the columns, +.001 prevents divide by zero error Key = y*4+x 'get key no. from row no and col. no. Execute "If Port(GP"+Str$(ColPinGP)+",cols) Then Exit For" Next Execute "Port(GP"+Str$(RowPinGP)+",rows) = 0" 'block further input to prevent double press ' Print y, x,, Key, Hex$(Key) Pause 200 Execute "Port(GP"+Str$(RowPinGP)+",rows) = 2^rows-1" 'prepare for next key input 'Set a flag here if main prog. needs it End Sub End Footnote added 2025-12-31 17:12 by phil99 First error. In the Sub For-Next 2nd last line replace:- Key = y*4+x 'get key no. from row no and col. no. With:- Key = y * cols + x 'get key no. from row no and col. no. |
||||||
|
||||||
Thanks Geoff, if no problems or improvements are found I will post it on FoTS. |
||||||
First error. In the Sub For-Next 2nd last line replace:- Key = y*4+x 'get key no. from row no and col. no. With:- Key = y * cols + x 'get key no. from row no and col. no. |
||||||
Does it need to use Execute? John |
||||||
The initial version didn't use Execute but the GP numbers had to be set individually throughout the program as the Port command and function won't accept a string for the GP number. They do accept Pico pin numbers but then you have to account for the ground pin numbers breaking the sequence. That appeared even more complicated. |
||||||
@john, Unless you hard code the GP Numbers, the port command needs it. @phill, So LOG(0) causes a dividend by zero error. Didn’t know that. Thanks for educating. Happy new year, must be now in your location.., Volhout Edited 2025-12-31 19:49 by Volhout |
||||||
It would be fantastic if this command could be used as an OPTION. Then you could create a portable device with an LCD and keyboard using ONLY ONE CHIP. Frank |
||||||
I didn't think it would happen at all, but occasionally that line gave a 'Divide by zero' error. I don't know why, perhaps contact bounce. Execute Isn't causing it as it was happening in the first version without Execute. My workaround is an ugly kludge but the error hasn't happened since adding it. |
||||||
Peter did that for the RP2350. Footnote added 2026-01-01 14:30 by phil99 It is the Palm Pico in this thread. |
||||||
Just for fun, how close to log(0) can you get? This was one step too close! > ? log(0.00000000000000000000000000001) Error : Expression syntax Error : Not enough Heap memory Error : Not enough Heap memory Error : Not enough Heap memory Error : Not enough Heap memory Error : Not enough Heap memory Error : Not enough Heap memory Error : Not enough Heap memory Error : Not enough Heap memory Error : Not enough Heap memory Error : Not enough Heap memory Error : Not enough Heap memory Error : Not enough Heap memory Error : Not enough Heap memory Error : Not enough Heap memory Error : Not enough Heap memory *** PANIC *** Out of memory |
||||||
Instead of Execute "Port(GP"+Str$(RowPinGP)+",rows) = 2^rows-1" I was hoping (well, expecting) such as this would work Port("GP"+Str$(RowPinGP), rows) = 2^rows-1 Does it fail? John |
||||||
There is an mm.info that translates a pin number to a GPxx. Maybe that can be used for the starting point in the PORT command. Not at home atm, so cannot test. Volhout |
||||||
> RowPinGP = 0 rows = 5 > Port("GP"+Str$(RowPinGP), rows) = 2^rows-1 Error : Expected a number > Yes, used that for the SETPIN commands but for PORT the numbers must be contiguous. You have to start again after each ground pin. Eg for 8 pins starting with 1 :- PORT(1,2, 4,4, 9,2) as pins 3 and 8 are ground pins. Edited 2025-12-31 20:51 by phil99 |
||||||
Odd. The PORT command uses getargs which is a macro for makeargs and that reads as if it would work. @matherp - is it a bug / can it be made to work (without too much hassle)? edit: Oh, it's not expecting a string; it wants such as GP0 rather than any variant of "GP0" Does feel worth fixing... those Executes are not lovely. John Edited 2025-12-31 20:49 by JohnS |
||||||
If you want nxm keypads you can use the RP2350 where they are fully supported. Do you want me to include this functionality on the RP2040? Worst case it increases flash usage by 16Kbytes and reduces A: drive by the same amount. Best case no change in flash usage as this always goes up in 16K chunks |
||||||
Yes, and the extra pins of the RP2350B makes it better still. The RP2040 can already do 4x4 and most of the time that is enough. |
||||||
An updated version. This handles dodgy button contacts a little better by resetting for another attempt. Replaced the the Log() function with a x=2^n loop as it tolerates bad data without exiting with an error. Plus some general tidying up. ' KeyPad2040v2.bas - Program for more than 4x4 Key Pad 'rows = number of rows 'cols = number of columns 'RowPinGP = first of the row GP pin numbers 'ColPinGP = first of the column GP pin numbers 'Key = number of the key that has been pressed. eg. 0 to 29 for 6 columns x 5 rows. 1st row 0 to 4, last 25 to 29. ' X x Y Keypad Option base 0 Dim integer n, Key=-1, RowPinGP=0, rows=5, ColPinGP=5, cols=6 For n=RowPinGP To RowPinGP+rows-1 SetPin MM.Info(pinno "GP"+Str$(n)),DOUT 'set the Port out pins Next For n=ColPinGP To ColPinGP+Cols-1 SetPin MM.Info(pinno "GP"+Str$(n)), INTH, Key.Pad, PULLDOWN 'set the Port input pins and ISR Next Execute "Port(GP"+Str$(RowPinGP)+",rows) = 2^rows-1" 'set all row pins high Do ' Your main processing loop If Key+1 Then Print Key, Hex$(Key,2) : key=-1 'to retain the value of Key set a flag in the sub and reset it here Loop Sub Key.Pad Local integer y, x, n Pause 5 'contact bounce delay For y = 0 To rows 'scan the rows Execute "Port(GP"+Str$(RowPinGP)+",rows) = 1 << y" 'set the row to read Execute "x=Port(GP"+Str$(ColPinGP)+",cols)" 'read the columns For n = 0 To cols-1 'get the column number from the Port value If x=2^n Then : x=n+1 : Exit For : EndIf Next If x>cols Or y>rows-1 Then 'check for values out of range and abort this read x=0 :y=0 : Key=-1 Execute "Port(GP"+Str$(RowPinGP)+",rows) = 2^rows-1" 'prepare for next key input Exit Sub EndIf Key = y * cols + x - 1 'get key no. from row no. and col. no., starting from 0 If x Then Exit For Next Execute "Port(GP"+Str$(RowPinGP)+",rows) = 0" 'block further input to prevent double press ' Print y, x,, Key Pause 150 Execute "Port(GP"+Str$(RowPinGP)+",rows) = 2^rows-1" 'prepare for next key input 'Set a flag here if main prog. needs it End Sub End Tested up to 8x8 with all keys tested. Edited 2026-01-01 14:25 by phil99 |
||||||
Or use a Zero to scan a *big* keypad and feed it back over I2C plus an interrupt line or a COM port. I like to make things complicated. :) The point is, very few pins are needed and I2C can be shared with a RTC. The interrupt says "I have a character, read me" as I2C isn't buffered. |
||||||
Yet another version. The previous ones hold the row output pins high until a key is pressed. If there is a short to ground the chip may be damaged. This holds the output port low while idle and uses inverted logic to read the keys. ' KeyPad2040v3.bas - Program for more than 4x4 Key Pad ' With inverted logic to minimize risk of damage if an ' output is shorted to ground - the Port is normally at 0V. ' ' rows = number of rows ' cols = number of columns ' RowPinGP = first of the row GP pin numbers ' ColPinGP = first of the column GP pin numbers ' Key = number of the key that has been pressed. ' eg. 0 to 29 for 6 columns x 5 rows. 1st row 0 to 4, last 25 to 29. ' X x Y Keypad with buttons bridging column x to row y Option base 0 Dim integer n, Key=-1, RowPinGP=0, rows=5, ColPinGP=5, cols=6 ' Dim KeyMap$(rows * cols - 1)=("Q","W","E","R","T","Y", etc) 'map keys to characters if req. For n=RowPinGP To RowPinGP+rows-1 SetPin MM.Info(pinno "GP"+Str$(n)),DOUT 'set the output Port pins Next For n=ColPinGP To ColPinGP+Cols-1 SetPin MM.Info(pinno "GP"+Str$(n)), INTL, Key.Pad, PULLUP 'set the input Port pins and ISR Next Do ' Your main processing loop If Key+1 Then Print Key, Hex$(Key,2) : key=-1 'to retain the value of Key set a flag in the sub and reset it here Loop Sub Key.Pad Local integer y, x, n Pause 5 'contact bounce delay For y = 0 To rows 'scan the rows Execute "Port(GP"+Str$(RowPinGP)+",rows) = INV (1 << y)" 'set the row to read Execute "x=2^cols-1-Port(GP"+Str$(ColPinGP)+",cols)" 'read the columns For n = 0 To cols-1 'get the column number from the Port value If x=2^n Then : x=n+1 : Exit For : EndIf Next If x>cols Or y>rows-1 Then 'check for values out of range and abort this read x=0 :y=0 : Key=-1 Execute "Port(GP"+Str$(RowPinGP)+",rows) = 0" 'prepare for next key input Exit Sub EndIf Key = y * cols + x - 1 'get key no. from row no. and col. no., starting from 0 If x Then Exit For Next ' Execute "Port(GP"+Str$(RowPinGP)+",rows) = 2^rows-1" 'block further input to prevent double press ' Print y, x,, Key Pause 100 'set to minimum value that is reliable, or remove it and/or the Execute line above Execute "Port(GP"+Str$(RowPinGP)+",rows) = 0" 'prepare for next key input 'Set a flag here if main prog. needs it End Sub End One way to use KeyMap$() to enter text is assign a Key to be <Enter> and append characters to a string until that character (eg Chr$(10), Line Feed) is entered. In this partial example Shift Lock is the second last Key and Enter is the last. It uses 4 rows x 5 columns or 2 rows x 10 columns so adjust accordingly. Add the setup lines to the program above and replace its Main Loop with the one below. Eg. Dim KeyMap$(rows * cols - 1)=("q","w","e","r","t","y","u","i","o","p","a","s","d","f","g","h","j","k",Chr$(8),Chr$(10)) Dim S$ 'temp string to hold text till you process it Dim ShiftLock% 'Shift lock flag SetPin GP22, DOUT 'Shift lock LED ... Do ' Your main processing loop If Key+1 Then If Key = Rows*Cols-2 Then ShiftLock% = Not ShiftLock% 'toggle ShiftLock, next to last Key is the <ShiftLock> key Pin(GP22) = ShiftLock% 'set ShiftLock LED Else If ShiftLock% Then Inc S$, UCase$(KeyMap$(Key)) 'append upper case characters to S$ Print UCase$(KeyMap$(Key)); Else Inc S$, KeyMap$(Key) 'append lower case characters to S$ Print KeyMap$(Key); EndIf EndIf key=-1 EndIf If Right$(S$,1) = Chr$(10) Then 'do stuff with S$ Print " = S$ = ";S$ S$ = "" 'then reset S$ EndIf Loop Edit. Another slight refinement. ' KeyPad2040v4.bas - Program for more than 4x4 Key Pad ' ' rows = number of rows ' cols = number of columns ' RowPinGP = first of the row GP pin numbers ' ColPinGP = first of the column GP pin numbers ' Key = number of the key that has been pressed. ' eg. 0 to 29 for 6 columns x 5 rows. 1st row 0 to 4, last 25 to 29. ' X x Y Keypad with buttons bridging column x to row y Option base 0 Dim integer n, Key=-1, RowPinGP=0, rows=5, ColPinGP=5, cols=6 ' Dim KeyMap$(rows * cols - 1)=("Q","W","E","R","T","Y", etc) 'map keys to characters with KeyMap$(Key) if req. For n=RowPinGP To RowPinGP+rows-1 SetPin MM.Info(pinno "GP"+Str$(n)),DOUT 'set the output Port pins Next For n=ColPinGP To ColPinGP+Cols-1 SetPin MM.Info(pinno "GP"+Str$(n)), INTL, Key.Pad, PULLUP 'set the input Port pins and ISR Next Do ' Your main processing loop If Key+1 Then Print "Key";Key, "&H";Hex$(Key,2) : key=-1 'to retain the value of Key set a flag in the sub and reset it here Loop Sub Key.Pad Local integer y, x, x1, n Pause 10 'contact bounce delay, set to minimum value that is reliable For y = 0 To rows 'scan the rows Execute "Port(GP"+Str$(RowPinGP)+",rows) = INV (1 << y)" 'set the row to read Execute "x1 = (2^cols-1) XOR Port(GP"+Str$(ColPinGP)+",cols)" 'read the columns If x1 Then x=Log(x1)/Log(2)+1 'get the column number from the Port value If x>cols Or y>rows-1 Then 'check for values out of range and abort this read x=0 :y=0 : Key=-1 Execute "Port(GP"+Str$(RowPinGP)+",rows) = 0" 'prepare for next key input Exit Sub EndIf Key = y * cols + x - 1 'get key no. from row no. and col. no., starting from 0 If x1 Then Exit For Next ' Execute "Port(GP"+Str$(RowPinGP)+",rows) = 2^rows-1" 'block further input to prevent double press ' Print y, x,, Key Pause 150 'set to minimum value that is reliable, or remove it and/or the Execute line above Execute "Port(GP"+Str$(RowPinGP)+",rows) = 0" 'prepare for next key input 'Set a flag here if main prog. needs it End Sub End Edited 2026-01-06 21:30 by phil99 |
||||||
| Page 1 of 2 |
||||||
| The Back Shed's forum code is written, and hosted, in Australia. |