AVR-Single-Chip-Processors AT90S, ATtiny, ATmegaof ATMEL in practical examples. |

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

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).

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

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).

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

To the top of the page

- Resistors are available with an accuracy of plus and (!) minus 0.1, 1, 2 or 5% only. Resistor values with 5% inaccuracy so are within a band of 10% of the nominal value. For a 1k resistor that means that its value can be anywhere between 950 and 1,050 Ohms. The resulting voltages produced so are within a certain bandwidth. The minimum voltage produced appears if all resistors in the lower resistor chain are on their lowest value and all those in the upper resistor chain are in their upper bandwidth value. The maximum voltage is gained if the lower resistor chain is on their highest value and the resistors of the upper chain on their highest. Those inaccuracies have to be taken into account, the bandwidth requires that a lower and upper limit has to be checked if the key is to be identified. With that we do not have to select accurate values manually and we do not need to keep the temperature of our matrix constant. Fortunately we do not need resistors with accuracies of 0.1%, so we can forget this.
- Resistors are only available with certain discrete values, known as E rows. The most common one is the E12 row: 1.0 - 1.2 - 1.5 - 1.8 - 2.2 - 2.7 - 3.3 - 3.9 - 4.7 - 5.6 - 6.8 - 8.2, and their decadic manifolds, such as 10 - 12 - etc. or 100 - 120 - etc.. The E48 row has double, the E96 row four times more discrete values. So we can only select resistor values from these rows. Solving the problem with discrete formulas (with 7 resp. 8 unknowns) would in almost every case lead to resistor values that are not commercially available, so forget this theoretical calculation option.
- Even having those extended E48 and E96 rows, most of these resistors are not available commercially in practice. Most sellers fear having hundreds of boxes with exotic values only rarely saled and sitting around in their boxes for years. So we have to stick with the E12 and E24 row, and E48 or E96 values are only theoretical (or only for those with excellent connections to certain dealers). You can play around with those extended rows, but a visit in your local electronics store or a look into the catalogue of your preferred parts dealer gets you down to earth and to E12 values. Finally the good message: the problem is resolvable with only E12 values.
- Whenever we change one resistor value to a different one in the E row, nearly the whole set of voltages (and their voltage bandwidth) changes. So selecting values has to be iterated step by step, keeping an ideal state in mind (the voltages produced are uniformly deployed over the whole range from 0 to the operating voltage) and no overlaps occur (every key produces its unique voltage range).
- Another criterion is that pressing keys does not lead to elevated currents, e.g. of 100 mA. The sum of the resistors R1 and R7 (resp. R8) so should not be smaller than 1,000 Ohms, causing a maximum current of less than 5 mA.
- The other case would be too large resistor values. If the currents through the matrix would be less than 10µA the potential influence of noise would be too large. So all the resistor values should be divided then by ten reducing noise on the voltages.
- If the AD converter works with the operating voltage of the keypad as reference voltage, the design is especially easy. If 8 bit resolution is selected (the left adjust result bit ADLAR is set to one, only the MSB of the ADC is read), the results are between 0 and 255. In the 10 bit case the results are between 0 and 1023. In both cases no conversion to voltages is necessary.

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

- select between 12 and 16 key keypads (switch by pressing the key k),
- single step through the iteration process (press s for a single step, selects one of the resistors to be processed randomly),
- perform 1,000 iteration steps at once (press i),
- select 8 or 10 bit resolution of the ADC (press r, input 8 or 10),
- set resistors R1 to R7 resp. R8 to desired values manually (press n, input resistor value in Ohms),
- select resistor accuracy and E row (press t, input 1, 2 or 5% tolerance, input resistor row E24, E48 or E96),
- write the current results to a text file (press w),
- exit the application (press x).

- the current resistor values R1 to R7 resp. R8,
- the ADC results for the 12 resp. 16 keys, according to the current ADC resolution, as nominal, minimum and maximum values as well as the ideal reference value,
- the current key row by produced nominal voltages (can change),
- the selected resistor row (E12, E24, E48, E96),
- the number of current overlaps (total number of overlapping bandwidths, should be zero),
- the sum of differences between the nominal values and their ideal reference values (should be as small as possible),
- the tolerance of the resistors and the ADC resolution.

To the top of the page

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.

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

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

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

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

To the top of the page

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.

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.

- that the 8-bit ADC value is in register R2,
- registers R0 and R1 are available and can be temporarily used,
- register pair Z can be used.

```
; 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.
- R0 returns the pressed key character, if invalid the FF,
- R2:R1 is used for the table value,
- R4:R3 stores the input value,
- R5 counts,
- Z is used for pointer purposes.

```
; 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