Path: AVR_EN => Applications => Key matrix => Stacked keys
Stacked resistors Applications of
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

Single resistor scheme 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

Keys and resistors This is a possible variation that differs only in the mode of calculation.

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
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 ranges from 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.

Because of all that it is not recommendable to solve this by up to ten equations. It is more appropriate to
  1. vary randomly selected resistors and to determine
  2. whether a smaller value or a larger resistor value reduces the sum of differences between all target and effective values.
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.

3 Software for optimizing resistor values

Nkey at startup 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

Nkey table values 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:
  1. If the ADC result is below the first entry, no key has been pressed.
  2. If the ADC result is larger or equal the first entry and smaller than the second entry, the respective key is pressed.
  3. 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.
  4. If the first table entry is zero, the last key is valid and the conversion is complete.

10 keys stacked 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 voltage distribution.

4 Assembler source code for identifying pressed keys

The following code
  1. sets the AD multiplexer to channel 0 and selects ADLAR (8 bit ADC),
  2. switches the ADC on and starts a conversion,
  3. waits for the ADC to complete,
  4. reads in the ADC result and
  5. converts the result to find the key pressed.
The assembler source code (here in asm format) is as follows:

; ***********************************
; * Decode keys                     *
; * (C)2018 by *
; ***********************************
.include "" ; 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 ADMUX,rmp
  ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
  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
  ldi ZL,Low(2*Keytab)
  clr rTab ; No key attached?
  lpm rmp,Z+ ; Read first byte from table
  tst rmp ; Last byte in table?
  breq KeyNIdentified
  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
  rjmp KeyIdentified
; No key pressed or illegal voltage
  tst rTab ; Key = 0?
  breq KeyIdentified ;  No key pressed
  clr rmp
  dec rmp ; Illegal voltage, rmp at 0xFF
  ; The key number is in rTab
  rjmp KeyIdentified ; Loop forever
; Include the value table
.include "" ; Include the key table

The included table, as generated with the software, is as follows (here is the source code file):

; 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

When simulating program execution with avr_sim yields the following results.

ADC conversion started The conversion has been started with an analog voltage of 2.5 V. Conversion takes some time because the selected ADC clock is slow.

ADC result read The result of the conversion, 0x7F or 127 decimal, has been read to the register rAdcH in R14.

Key found After executing the conversion routine the assembler code found that the fifth key has been pressed (in rTab or R15).

©2018 by