Path: AVR_EN =>
Key matrix => Stacked keys
AVR Single chip controllers AT90S, ATtiny, ATmega and ATxmega
Stacked keys and resistors on an AD input
Keys and switches via an AD converter channel
1 Keys and resistors
If your program needs to read in two or more keys and has to detect
which one of the several keys is pressed you can attach each key
to the switch and to one input pin, set the internal pull-up
resistor and wait for a low to occur at any of those pins. But
you can solve this task in a very much simpler way: by switching
together all your keys and by generating a specific voltage for
each of the pressed keys. Only one single pin, an AD converter
input pin, is necessary for that.
1.1 Single voltage dividers
In this scheme, each key pressed switches a voltage divider on.
If no key is pressed, the voltage is zero.
The voltages produced by pressed keys are
Un = Uop * R0 / (R0 + Rn)
The resistors R1 to RN have to be different
to produce different voltages. And they should be selected to
provide as-far-as-possible different voltages depending from the
number N of total keys.
1.2 Stacked keys and resistors
This is a possible variation that differs only in the mode of
Here is how you can stack your keys, associated with several resistors.
In case that no key is pressed, the ADC senses zero or one on this input.
If one of the attached keys is pressed the resistors form a voltage divider:
The resulting voltage is
- The resistor R0 is the lower part of the divider.
- Depending from the key pressed, the resisitors above the pressed
key add up. If key 1 is pressed R1, R2, ...,
RN add up and the resistor series is attached to
the positive voltage.
U = Uop * R0 / (R0 + R1 + R2 + ... + RN)
Depending from the operation mode of the ADC is 8 or 10 bits wide (8 bits =
ADLAR set and read high byte of the AD result), the resulting count is
either between 0..255 or between 0..1023.
You can either use a wild bunch of resistors and measure the AD result
to identify the key-specific readings. Simpler is it to calculate those
in advance and independent.
2 Algorithm for selecting resistors
In order to have as-high-as-possible differences between the voltages
of the keys those should be equally distributed over the whole available
range of voltages. When 2 keys are attached, the voltage of the first
key should be the operating voltage divided by two. If three keys are
attached the first two keys shall be at the 0.33-fold resp. 0.66-fold
of the operating voltage. If ten keys are attached, the first voltage
shall be the 0.111-fold of the operating voltage.
Unfortunately, real resistors are not exact but have a tolerance. This
can be +/-5% for standard carbon resistors or +/-1% for metall film
resistors. Therefore the calculated voltages all have a range. This
Further, resistors are only available in standardized rows. For each
decade either 12 different values are available (E12 row) or 24 (E24
row). A little more exotic are 48 values per decade (E48), but those
are not generally available.
- the lowest voltage, when R0 is on its lowest tolerance
(-5%) and all other resistors are on their upper tolerance (+5%), to
- the highest voltage, when R0 is on its upper range and
all other resistors are on their lower tolerance (-5%).
Because of all that it is not recommendable to solve this by up to
ten equations. It is more appropriate to
By minimizing the differences the number of overlaps between the
upper bound voltage of one key and the lower bound voltage of the
next key also reduces. If the number of such overlaps does not
reduce to zero, 2% or 1% resistor values or resistors of the E24
row can be used.
- vary randomly selected resistors and to determine
- whether a smaller value or a larger resistor value reduces the
sum of differences between all target and effective values.
3 Software for optimizing resistor values
The software is, for Windows operating systems or for Linux's Wine,
the the executable code is here (906 kB
zipped, 2.82 MB unzipped).
At startup, two keys are selected. Use the drop-down field to have
more. Check the other dro-down-fields for more options. Note that
all selections reset the resistor values to 1k, so that iteration
has to be restarted.
Iteration can be done steps by step or with 100 steps at once. If
the differences do not reduce any more, no succesful steps have
been made any more. If you are not lucky with the result, restart
and repeat the iteration process.
The results of the iteration can be viewd in the resistor window
and in the values window. The resistor window shows the currently
selected resistors. If those do not change after a single steps
has been performed, the iteration step did not result in a smaller
difference sum. Because the resistor is selected randomly, further
iterations might lead to a different result. The table on the
bottom shows the targetted voltages, the nominal voltages and
the lower and upper bounds
If you click on the button "Source code" an assembler
table is created that holds the minima and (maxima+1) of the
selected keys. You can save this as an include file or copy it
to the clipboard and paste this into your source code.
The entries in that table are as follows:
- If the ADC result is below the first entry, no key has
- If the ADC result is larger or equal the first entry
and smaller than the second entry, the respective key
- If the result is equal or above the second entry and
smaller than the next first entry, the voltage is in
between two valid voltage ranges and no valid key is
pressed, an error results.
- If the first table entry is zero, the last key is
valid and the conversion is complete.
If the button "Schematic" is pressed a window opens
displaying a graphics file with the schematic. The file can be saved
in PNG or in BMP format by clicking on it.
The resistor values in the stacked format show that anything else
than equal values are appropriate to achieve an appropriate
4 Assembler source code for identifying pressed keys
The following code
The assembler source code (here in asm format)
is as follows:
- sets the AD multiplexer to channel 0 and selects ADLAR
(8 bit ADC),
- switches the ADC on and starts a conversion,
- waits for the ADC to complete,
- reads in the ADC result and
- converts the result to find the key pressed.
The included table, as generated with the software, is as
follows (here is the source code file):
; * Decode keys *
; * (C)2018 by avr-asm-tutorial.net *
.include "tn13def.inc" ; ATtiny13
; R E G I S T E R S
.def rAdcH = R14 ; High byte ADC result
.def rTab = R15 ; Key table value
.def rmp = R16 ; Multipurpose register
; Used: Z = R31:R30 for LPM
; Get an ADC value from channel 0
ldi rmp,1<<ADLAR ; Left-adjust, channel = 0
out ADCSRA,rmp ; Start ADC, start conversion
sbic ADCSRA,ADSC ; Wait for ADC complete
rjmp WaitAdc ; Wait on
in rAdcH,ADCH ; Read MSB
; Convert ADC value to key pressed
ldi ZH,High(2*Keytab) ; Point Z to key table
clr rTab ; No key attached?
lpm rmp,Z+ ; Read first byte from table
tst rmp ; Last byte in table?
cp rAdcH,rmp ; Compare ADC value with table byte
brcs KeyUnderflow ; ADC value below
inc rTab ; Next key
lpm rmp,Z+ ; Read upper bound of key value
cp rAdcH,rmp ; Compare voltage
brcs KeyIdentified ; Key found
rjmp IdentifyKey ; Go on table check
; Last key identified
inc rTab ; Key = N
; No key pressed or illegal voltage
tst rTab ; Key = 0?
breq KeyIdentified ; No key pressed
dec rmp ; Illegal voltage, rmp at 0xFF
; The key number is in rTab
rjmp KeyIdentified ; Loop forever
; Include the value table
.include "Key10_single.inc" ; Include the key table
When simulating program execution with
yields the following results.
; Table for recognizing pressed keys
; 10 keys, ADC resolution = 256
; R0 = 1k
.db 25,31 ; Key=1, R1=8k2
.db 48,57 ; Key=2, R2=3k9
.db 74,86 ; Key=3, R3=2k2
.db 96,109 ; Key=4, R4=1k5
.db 121,135 ; Key=5, R5=1k
.db 146,159 ; Key=6, R6=680
.db 178,189 ; Key=7, R7=390
.db 197,206 ; Key=8, R8=270
.db 225,231 ; Key=9, R9=120
.db 0,0 ; No more keys
The conversion has been started with an analog voltage of 2.5 V.
Conversion takes some time because the selected ADC clock is slow.
The result of the conversion, 0x7F or 127 decimal, has been read to
the register rAdcH in R14.
After executing the conversion routine the assembler code found that
the fifth key has been pressed (in rTab or R15).
©2018 by http://www.avr-asm-tutorial.net