Shop OBEX P1 Docs P2 Docs Learn Events
Keyboard Scanning - A trap for young and old. — Parallax Forums

Keyboard Scanning - A trap for young and old.

Cluso99Cluso99 Posts: 18,069
edited 2011-04-18 18:51 in Propeller 1
I just fell into a trap that is not prevalent on older and slower micros. It may have been dealt with on a past thread, but I will explain here just in case.

When scanning keys it is usual to use a row & column matrix, such that each key shorts between 1 row and 1 column. A pullup or pulldown resistor is usually found on either the row or column set, and these are the inputs to the micro. Lets assume the resistors are pulldowns (10K in my case) and on the rows. What is then usual is that you enable 1 column at a time (it is set high in the case of pulldowns) and read the corresponding rows. Only 1 row will be high, and all others will be pulled low by the resistors.

So this is how you determine the key that has been pressed. It assumes only 1 key can be pressed at a time.

In my case, I want a scan code and by using a 4x4 matrix, 16 keys can be decoded. I wanted a scancode, so I just scanned each column, and read the 4 bit row values and shifted them until I had 16 bits. I then pass this to the spin routine to use a case statement to decode the 16 posible combinations.

I have ignored debouncing, and repeating keys.

The Problem

Pasm is very fast and the prop has high impedance inputs. This permits the prop to do ADC inputs (see the SigmaDelta writeups for more information). This is what caused my problem because while scanning from one column to the next, the capacitive effect was holding my row pins high for longer than the column was an output, such that when I enabled and read the next column the older row that was high was still mostly high due to the slow decay because of lead length, capacitance in the prop and the 10K discharge resistors.

The Solution

Between each column scan, set all column and row pins =0 (if pulldowns), set them all as outputs, then disable them as outputs (this clears the residual capacitance. Then set them all as =1 (because you already have this as a mask for setting all outputs) and enable 1 column as an output, read the row inputs. Now repeat the process for the remaining 3 columns (i.e. set all =0 and enable all, etc).

Sample Code
dat
'' 10K pulldowns on rows 1-4(P0-3), columns 1-4(P4-7)
'' scan the keypad: return keycode
scan          mov       keycode, #0                     ' initialise keycode
              mov       scancol, #%0001_0000            ' preset col 1 = P4
              mov       count,#4                        ' 4 cols to scan
'' scan a column
loop          mov       outa,#0                         ' reset any residual capacitance by outputting 0 to all rows+cols
              mov       dira,scanmask                   ' all outputs
              mov       dira,scancol                    ' set one col as output only, all others inputs
              mov       outa,scanmask                   ' set the active col output =1 (all 1's but only one will be output)
              mov       t1,ina                          ' read rows 4:1 = P3-0
              and       t1,#%1111                       ' extract 4 row bits
              shl       keycode,#4                      ' shift accumulated row bits ready for next set
              or        keycode,t1                      '  & add new accumulated row bits
              shl       scancol, #1                     ' prepare for next column
              djnz      count,#loop
scan_ret      ret
scanmask      long      %0000_0000_0000_0000_0000_0000_1111_1111   'P7-0
scancol       long      0
count         long      0

Comments

  • MagIO2MagIO2 Posts: 2,243
    edited 2011-04-12 10:10
    Just as a feedback I want to say thank you for this thread!

    Hope I'll remember it once I stumble over a similar problem ;o)
  • tdlivingstdlivings Posts: 437
    edited 2011-04-12 10:45
    Cluso99
    Interesting post, thanks
    Instead of doing you fix of clearing the charge by switching to outputs and back to inputs could you have just scanned at a slower pace.
    Using waitcnt in you scanning loop. Do you have a reason for scanning the keyboard at light speed. Seems like pull ups work better for
    this but maybe I am just use to pull ups and not pull downs.

    Tom
  • Heater.Heater. Posts: 21,230
    edited 2011-04-12 10:58
    tdlivings,
    could you have just scanned at a slower pace.

    Heresy !

    I guess folks around here are so used to squeezing every last cycle out of their Prop code such a solution as "go slower" never comes to mind:)
  • localrogerlocalroger Posts: 3,452
    edited 2011-04-12 12:03
    I've confronted this several times in commercial products that did it wrong. One thing you should consider is driving the keyboard through series resistors (say 100 ohms) so you won't short the scan input to another output if someone holds down more than one key, and never make any of the scan pins an input. The other thing is to sample first then set the new scan value before leaving the interrupt dr...

    Never mind that last one.
  • Cluso99Cluso99 Posts: 18,069
    edited 2011-04-12 23:32
    localroger: Provided your code is correct, there is no possibility of damage as there is only one output at a time when the outputs are high. When all outputs are on they are all low. So a series resistor is an overkill which would actually decrease your MTBF (mean time between failures).

    As for pullups, the old method was to use pullups (for us oldies). However, when you use pullups you usually have to invert the data read. Nowadays, there is no difference between pullups and pulldowns, so I use pulldowns if it saves inversion. That said, you will often see pullups like on the -CS of the SD card and SRAMs because you want the default to be disabled.
  • bill190bill190 Posts: 769
    edited 2011-04-13 05:34
    There is a term for this called "parasitic capacitance".

    The problem is worse if you have several inch long traces on a keyboard with the lines running next to each other. So if you have a rectangular keyboard with the wires connected near the top, then the buttons near the bottom of the board might exhibit more of this problem because the traces going to those buttons are longer, running next to each other, and therefore have more capacitance.

    Another problem is when using ribbon cables to connect the keyboard to the microcontroller board. Notice that many keyboard ribbon cables have the wires spaced apart! --- As opposed to a ribbon cable used for disk drives, where the wires are right next to each other (greater capacitance between wires run next to each other).

    If you are using a ribbon cable with the wires close to each other, one solution is to use scissors and cut the wires apart. Leave them loose so it looks like a rat's nest, then this solves the capacitance problem with the ribbon cable. - A case where the "rat's nest prototype" works, but the "neat and tidy finished product" does not work! :smile:
  • tdlivingstdlivings Posts: 437
    edited 2011-04-13 20:52
    Cluso99
    I agree with localroger on the need for current limiting resistors if the port doing the scanning is
    not open drain or collector but a low impedence drive. If two switches on the same column are
    pressed you can walk through the switches and connect two port outputs together with one driving
    the level high and the other trying to drive it low and damage could occur to the port without current
    limiting resistors.
    keypad.png

    Assume (D0,D1,D2) are high and D3 is low in order to test the first column of switches to see if one
    is pressed. The red line shows the danger path if both the F and B key are pressed at same time.
    The drawing shows open drain port with pull ups and would not be a problem as you just tie two pull ups
    together and assuming the port can sink that current there is no issue , but if you were using a port that
    drove the output high or low there would be a fight .
    Your circuit maybe different.
    Tom
    900 x 600 - 70K
  • tdlivingstdlivings Posts: 437
    edited 2011-04-13 21:03
    A little on the small side so I attached pic.
    No matter how big I make the pic when I insert it, it gets rescalled small.

    Tom
    900 x 600 - 40K
  • Cluso99Cluso99 Posts: 18,069
    edited 2011-04-14 03:39
    My circuit only has 4 pulldown resistors on the set of rows. I read the 4 rows at one time. I only EVER have 1 column enabled at a time when I am using a high output to read the rows. I do make them all outputs when they are all low to kill the residual capacitance. So, there can never be a conflict unless you make a mistake in code.
  • localrogerlocalroger Posts: 3,452
    edited 2011-04-18 15:17
    Well the problem is that you are protecting your outputs from 3-key shorting by making them inputs instead of output low, but output low through a current limiting resistor solves the parasitic capacitance problem. Or just waiting a few milliseconds before reading after you assert a new column select output.
  • Cluso99Cluso99 Posts: 18,069
    edited 2011-04-18 18:51
    localroger: Yes, there are many solutions. I chose (as is usual) to minimise the hardware and do it in software. A delay would have worked also, and only a few us would have been enough. My solution is only one of a number.

    The intention of the thread was to state the potential problems and a solution.
Sign In or Register to comment.