Path: Home => AVR-Overview => Keyboard => resistor matrix

12- and 16-pin keypad on an AVR

This page shows
  1. connecting a 12- or 16-key keypad to an AD converter input,
  2. how to calculate the resistor values, using provided software,
  3. how to optimize the assembler software to read the keypad.

1 Connecting a keypad to an AD converter input

1.1 Connections, 12 keys

12 key keypad The scheme shows a 12 key wide keypad and its external resistor matrix. The four rows are connected with four stacked resistors that are tied to the positive operating voltage. The three columns are connected with three stacked resistors that are tied to the negative operating voltage.

If none of the keys is pressed, there is no connection between plus and minus, the output voltage at point Y is zero because R1, R2 and R3 pulls it down to the negative operating voltage. If Y is connected with an analog-to-digital converter (ADC), its state reads as zero.

If a key is pressed, e.g. key 5, it connects the row line (here: the horizontal connection between 4-5-6) with the column line (here the vertical connection between 2-5-8-0).

1.1.1 The resistors if key 1 is pressed

Key 1 pressed This is what happens if the key 1 is pressed: this key connects row 1 with column 1. Now R1, R4, R5, R6 and R7 operate as a voltage divider. The current through these five resistors in a row, I1 = U+ / (R1 + R4 + R5 + R6), causes a small voltage on R1, Y1 = I1 * R1. As the ADC input is of a very high resistance, the voltage on R1 appears on Y and the ADC sees this voltage.

The formula below combines the current calculation and the voltage calculation. The calculated Y1 is the fraction of the operating voltage (U+) that appears on the ADC, whenever key 1 is pressed.

To the top of the page

1.1.2 The resistors if key 9 is pressed

Key 9 pressed If the key 9 is pressed, the voltage divider works different. Now R1, R2 and R3 together are the lower resistor of the voltage divider. The upper resistor of the voltage divider are the resistors R6 and R7. The current now is I9 = U+ / (R1 + R2 + R3 + R5 + R6 + R7), The voltage on Y is then Y9 = I9 * (R1 + R2 + R3).

Every key combines specific resistors to a voltage divider, and so produces a specific voltage on output Y. With that voltage it can be determined if any one and which specific key is currently pressed. The task then is to select the resistor values in an optimal way (using the software provided below).

1.2 Keypad with 16 keys

16 key pad Four more keys require an additional resistor to work in a similar way. Looks very much like the 12-key version, but the calculation of the voltage divider is different and produces very different voltages. In that case the resistors have to be of a higher accuracy of 2 or 1% (see below).

1.3 Alternative schematic with parallel resistors

The four upper resistors do not have to be stacked necessarily, they can also be parallel. The following schemes show this alternative.

12 keys parallel 16 keys parallel

This changes the mode of calcuation slightly, but the main calculation scheme remains the same.

To the top of the page

2 Calculating resistor values

Here a tool for calculating resistors is provided and introduced.

2.1 Calculation basics and rules

This chapter shows the rules and how to calulate resistor values in a correct way. Calculating the resistors for those voltage dividers is not trivial: To the top of the page

2.2 Calculation software

resmatrix start As designing such matrices is a lengthy process, I designed this software tool. It allows to play around with values and parameters.

The software provided here (Win64 executable) or here for Linux i386-x64 is a simple command line application. Download it, unzip the executable and start it. You can download the pascal source code, too, compile it with the Free Pascal Compiler fpc and run this executable. If you are cautious with downloaded executables or if you need the executable file for other operating systems, you can go this way.

The version including upper serial as well as parallel resistors is provided as Win64 executable here and as Free Pascal Source Code here.

The software allows to The window displays
To the top of the page

2.3 Example calculation 12 keys

The following example is based on the following parameters: tolerance 5%, 8 bit resolution, resistor row E12.

Step 1 This is the result after a single step. The random step selected R4, decreased this resistor from 68k to 56k, resulting in reducing the overlaps and the differences between nominal and reference readings from 599 to 548.

Step 2 Another step increases R5 from 27k to 33k, reducing overlaps, but increasing the differences.

Step 1000 This is the result after 1,000 iteration steps. The number of overlaps is zero, the differences are minimized to 32. All voltages are within a well suitable bandwidth of ADC readings: not too small bandwidth, enough difference to the next key.

Note that the iteration step does not always lead to optimal results. As the steps are done randomly, different runs might lead to sub-optimal one-way-streets. Try another iteration (select 16 keys, then 12 keys again, then restart iteration) or change resistors manually.

To the top of the page

2.4 Example calculation 16 keys

16 keys, start By pressing k the calculation switches to 16 keys, 1% tolerance and 10 bits resolution of the ADC. These are necessary values to get the matrix working (5% is too large, 2% would also be sufficient, 8 bit is too small). Resistor row still is E12 to ease availability.

With standard resistors the number of overlaps is already at zero, but differences can be optimized.

16 keys, iterated The result after 1,000 iteration steps. Also looks fine. Problem solved.

To the top of the page

2.5 12 key result files

12 key result file A view on the result files documenting the results, with a simple editor. The name of the result file reflects the parameters used, the example file for 12 keys is here.

The upper portion of the files shows the program output. The lower portion provides a table to be copied to the assembler source file. The table can be used to identify the key pressed (see below). The first value on every line holds the 8-bit lower limit for the respective key, the second the value at the end of the bandwidth plus one. So a key is recognized if the ADC result is equal or above the lower limit (a CP instruction ends with the carry flag clear) AND is smaller than the given upper limit plus one (a CP instruction ends with the carry flag set).

The table ends with two zero bytes to signal that the end of table has been reached.

The last two lines in the file provide an ASCII table with the respective key values.

2.6 16 key result files

16 key result file The file name produced reflects the parameters used, the example file is here.The upper portion of the file again shows the program output.

The lower portion again provides a table to be copied to the assembler source file. Its content are now two 16-bit values per line, with the lower limit and the upper limit plus one.

The table ends with a zero word to signal that the end of table has been reached.

The last two lines in the file again provide an ASCII table with the respective key values.

3 Assembler software

The assembler software is a little bit different for the 8 and 10 bit ADC versions.

3.1 Assembler software for 8 bit ADC values

The following assumes The table as provided in the result file is included:

; Decimal table for assembler
Keytable: ; lower limit, upper limit +1), 'Key'
.DB 25, 31 ; '1'
.DB 34, 41 ; '2'
.DB 45, 54 ; '3'
.DB 66, 78 ; '4'
.DB 84, 96 ; '5'
.DB 104, 117 ; '6'
.DB 117, 130 ; '7'
.DB 138, 151 ; '8'
.DB 159, 171 ; '9'
.DB 178, 189 ; '*'
.DB 194, 204 ; '0'
.DB 209, 217 ; '#'
.DW 0 ; table end
Keyvalues:
.DB "123456789*0#" ; Key values ascii 
The following subroutine first reads in the lower limit from the table. If the lower limit is zero, the table is at the end and no key is recognized (routine returns with FF in R0). Then it checks if the ADC value is smaller then the lower limit. If yes, no key is pressed or the value is in between two valid key limits (routine returns with FF in R0). Then the upper limit plus one is read from the table. If the ADC value is higher than this upper limit, the routine continues from the start. If the ADC value is smaller, a valid key has been detected, the respective ASCII value is read from the table and returned in R0.

GetKey12: ; get the ascii value of the key pressed, return result in R0
    clr R1 ; clear counter                        +-------------------+
    dec R1 ; set counter to FF                    | Set counter R1=FF |
    ldi ZH,HIGH(2*Keytable) ; point Z to table    | Set Z to table    |
    ldi ZL,LOW(2*Keytable) ;                      +-------------------+
GetKey1:                                                    |
    lpm ; read first value (lower limit)             ->Read lower limit
    tst R0 ; table at the end?                      /      / \__________> yes
    breq GetKey3 ; table end reached               |       \0/no         |
    inc R1 ; count up                              |    Inc counter      v
    cp R2,R0 ; compare ADC result with lower limit |       / \__________>|
    brcs GetKey3 ; smaller than lower limit        |       \C/no         |
    adiw ZL,1 ; upper limit in table               |  Read upper limit   |
    lpm ; read to R0                               |    and compare      |
    adiw ZL,1 ; next byte                          |        |            |
    cp R2,R0 ; compare with upper limit             \______/ \           |
    brcc GetKey1 ; larger than upper limit               no\C/           |          
    ldi ZH,HIGH(2*KeyValues) ; point to result table  Z to value table   |
    ldi ZL,LOW(2*KeyValues) ;                               |            |
    add ZL,R1 ; add counter                           Add counter        |
    brcc GetKey2 ; no overflow                              |            |
    inc ZH ; overflow, add one to MSB                 Inc MSB table      |
GetKey2: ;                                                  |            |
    lpm ; read character to R0                         Read char to R0   |
    ret ; return with result                               RET           |
GetKey3: ; table at end or value in between, return FF                   v
    clr R0 ; clear result                                R0 to FF <------
    dec R0 ; return FF                                      |
    ret ;                                                  RET
That is it all.

3.2 Assembler software for 10 bit ADC values

10 bit ADC values require word-wise comparision. The following registers are used:

; Decimal table for assembler, 16 keys
Keytable: ; Lower lim, upper lim+1, 'Key'
.DW 96, 100 ; '1'
.DW 122, 127 ; '2'
.DW 158, 164 ; '3'
.DW 191, 198 ; 'A'
.DW 263, 272 ; '4'
.DW 319, 329 ; '5'
.DW 388, 399 ; '6'
.DW 444, 455 ; 'B'
.DW 497, 508 ; '7'
.DW 566, 578 ; '8'
.DW 640, 650 ; '9'
.DW 693, 703 ; 'C'
.DW 742, 751 ; '*'
.DW 794, 802 ; '0'
.DW 842, 849 ; '#'
.DW 874, 880 ; 'D'
.DW 0 ; table end
Keyvalues16:
.DB "123A456B789C*0#D" ; Key values ascii 
;
; Convert ADC value in R4:R3 to key ascii code
GetKey16:
	clr R5 ; clear counter
        dec R5 ; set to FF
        ldi ZH,HIGH(2*Keytable) ; Z to table
        ldi ZL,LOW(2*Keytable)
GetKey16a:
        inc R5 ; increase counter
	lpm ; read LSB from table
	adiw ZL,1 ; next byte
	mov R1,R0 ; copy to R1
	lpm ; read MSB from table
	adiw ZL,1 ; next byte
        mov R2,R0 ; copy to R2
	tst R1 ; LSB zero?
	brne GetKey16b ; no, go on
	tst R2 ; MSB zero
	breq GetKey16d ; table at end
GetKey16b:
	cp R3,R1 ; compare LSB
        cpc R4,R2 ; compare MSB plus overflow
	brcs GetKey16d ; carry indicates value smaller then lower limit
	lpm ; read LSB upper limit
	adiw ZL,1 ; next byte
	mov R1,R0 ; copy to R1
        lpm ; read MSB upper limit
	adiw ZL,1 ; next byte
	mov R2,R0 ; copy to R2
	cp R3,R1 ; compare LSB
	cpc R4,R2 ; compare MSB plus overflow
	brcc GetKey16a ; ADC value higher or equal upper limit
	ldi ZH,HIGH(2*KeyValues16) ; Z to table
	ldi ZL,LOW(2*KeyValues16)
	add ZL,R5 ; add counter
	brcc GetKey16c ; no overflow
	inc ZH ; overflow, add 1 to MSB
GetKey16c:
	lpm ; read ascii value from table to R0
	ret ; return
GetKey16d:
	clr R0 ; set R0 to FF
	dec R0
	ret ; return with R0=FF
Hint: The LPM instructions used here are AVR old-style. By use of LPM Z+,R a few lines can be saved.



To the top of the page

©2014-2017 by http://www.avr-asm-tutorial.net