Decoding the results of an MCP23016

Steve2381

Senior Member
Hello

I have an MCP23016 I/O expander chip running on I2c. Its got 16 input/outputs. I have these wired as an 8x8 button matrix (8x outputs, and 8x inputs). I am using 8 pins as outputs for columns and 8 pins for inputs as rows. 64 detectable buttons.

I am sequentially taking each of the 8 of the outputs high, and then reading the results of that line of 8 buttons using the other 8 input pins. It returns a value according to the button that is pressed.

so for each column of buttons, I get the results (if the are pressed individually)

1= button 1
2= button 2
4= button 3
8= button 4
16= button 5
32= button 6
64= button 7
128= button 8

This all works fine (if not a bit slow).

My question is, does anyone know of a nice smart way of deducting what button has been pressed, considering it might be a combination of buttons.

The result ideally would be a something like Button1=1 when its been pressed.
But, there are obviously a large number of possible outcomes. E.G. Buttons 1, 4 and 7 would equal 39. That is a lot of 'if' statements. And that is just one of the 8 rows of results to check.

I am then reading the final button press result from another chip using this 40x2 as a slave I2c.

I think by the time I get it scanning and deducing all 8 rows, this may have to be bumped off the 40x2 onto an Arduino simply due to the speed issue, but I will deal with that later.

When i get in tonight, I will lift the code and post it if I can.
 

AllyCat

Senior Member
Hi,

I think you may need to put at least a diode (or resistor) in series with each output pin (unless you can tristate them rather than switching to a low level) to prevent damaging the expander if two buttons (in different columns) are pressed at the same time.

To actually decode multiple buttons pressed at the same time, I think you need to put a diode in series wth every pushbutton. Otherwise, I don't believe it's possible to distinguish (e.g.) row2:col2 + row4:col4 pressed from row2:col4 + row4:col2 (or any combination of those four buttons) pressed at the same time.

The actual decoding shouldn't be difficult; whenever a "pulse" gets through the diode matrix (i.e. a bit set in the input byte), sum the column number with 8 times the row number (or vice versa), both counted from zero, and use that value (0 - 63) in a LOOKUP, a DATA array READ, or an ON GOTO command.

Cheers, Alan.
 
Last edited:

Steve2381

Senior Member
I can distinguish the button that is pressed, as I get a 0-255 result for each row of 8 buttons. Therefore each keypad scan 'pass', I end up with 8 variables between 0-255.

This variable is read off the MCP23016 by I2c, so the Picaxe isn't in any danger.

I think I should explain the connections better. The 16 pins on the MCP23016 are I/O and divided into 2x banks of 8 pins. I have allocated 8 of them inputs and 8 as outputs.

The outputs are the 8x commons of the 8x8 button matrix. The remaining 8x MCP23016 inputs are the 8x commons from the other side of the buttons. Standard matrix button wiring.

By I2c, I tell the MCP23016 to switch on (5v high) output 1 (column 1) - that is the common for the first column of 8 buttons.
It then reads back over I2c to the Picaxe the state of the 8x inputs. This is a value 0-255 depending on the state of the pins. This gives me the result of column 1.

I then tell the MCP23016 to switch off output 1 and switch on output 2 (column 2). I then again read the 8x inputs and get a result for that column of buttons 0-255.
I do this for all 8 columns (very quickly). If nothing on that column is pressed, you get a 0. Press them all and you get 255.

Its working very well, but I end up with 8x variables that then need efficiently decoding into the state of the buttons.

With crude if statements, i can determine individual button presses fine . If the result of column1=1 then button 1 was pressed, if column2=1 then button 9 was pressed etc.

Ideally, I would be able to determine if more than one button is pressed.
This is where I go into stuff I don't understand. In Binary the result for button 4 being pressed is going to be 00001000. If you pressed button 4 and 8, then its 10001000 (I think!).

How would I convert that 10001000 into

Button1=0
Button2=0
Button3=0
Button4=1
Button5=0
Button6=0
Button7=0
Button8=1

Out of my depth! I am amazed I managed to get it working as far as this to be honest
 

lbenson

Senior Member
Put the 1-byte input from the MCP23016 into b0 (or b1, b2, or b3).

Then you can use the bit variables. For instance:
Code:
symbol  Button1=bit0
symbol  Button2=bit1
symbol  Button3=bit2
symbol  Button4=bit3
symbol  Button5=bit4
symbol  Button6=bit5
symbol  Button7=bit6
symbol  Button8=bit7
Then you can just say "IF Button6 = 1 then ...".
 

AllyCat

Senior Member
Hi,

Have you actually checked that pressing (say) buttons 2:2 + 4:4 at the same time gives different "scan" values to pressing buttons 2:4 + 4:2 ? If not, then there is no way to write a program to decode them !

It's much easier if you number the rows and colums from 0 to 7 and in ascending sequence from right to left (i.e. columns: 7 6 5 4 3 2 1 0). If the hardware is wired differently then, if necessary, it could be corrected with software instructions, particularly with an X2. Don't bother with the "decimal" values of any scan (input) byte, just check if each particular "bit x" is set, or check for non-zero if "masked" (ANDed) with 1, 2, 4, 8, 16 ... etc..

Then, it depends what all the buttons actually represent: For an ASCII "Keyboard" you'd use a LOOKUP, but a PICaxe LOOKUP for 64 bytes is rather slow, so probably better handled as 8 LOOKUPs, one for each row or column. If the buttons perform completely different functions (in code) then use an ON .. GOTO, which is quite fast, so you can divide it up (or not) in the most convenient way.

Cheers, Alan.
 

Steve2381

Senior Member
Thats it. The symbol Button1=bit0 method was what I was looking for. Many thanks.

Not sure what you mean by the decoding issue Allycat? I know what buttons are being pressed, because it only checks a column of 8 buttons at a time.
Therefore, I know if they were on different lines? I know what I mean.

Anyway, it appears to work. I am sure I can tidy it all up now.

To be honest, I am amazed I got this far
 
Last edited:

AllyCat

Senior Member
Hi,

Ah yes, it looks as if "diagonal" pairs of buttons should work correctly. But (depending on the actual hardware configuration) there might be "issues" with two buttons pressed in the same row, or with some combinations of three buttons together.

Cheers, Alan.
 

lbenson

Senior Member
I think the issue with decoding is based on a misperception--that your outputs and inputs are wired like a numeric keypad--but I think you have (for instance), a single switch between an output and an input. If that's not the case, the misperception is on my part.

A circuit diagram would clear things up if there is further confusion.

If it is the case that your circuits are OUTPUT -> switch -> to INPUT, why not just make it 5V -> switch -> INPUT (and better if there is a, say, 1K resistor between the switch and the input or between the 5V and the switch (or 1 1K resistor and the + side of the switches all wired together)?

And by the way, congratulations on getting as far as you have.
 

Steve2381

Senior Member
Bit of an update, and also now stuck... I will try and explain, but please bear with me.

I have had to bump over from the Picaxe to an Arduino purely for speed reasons, but that isn't the problem.

This is how the 16 i/o chip is set up and used.

A 'for loop' scans the 8x bank 0 pins that are set up as outputs. This takes high the 8x commons of my 8x8 button matrix one at a time in sequence. Call these the columns of the 8x8 button matrix. So each column of 8x buttons gets a high pulse every loop.

It then on each pass also reads the 0-255 result of bank 1 which are set up as 8x inputs. These are then stored as the returned results of the 8 rows of buttons (make sense?). So I then end up with 8x values of between 0-255 for each row.

For instance, press button 1 on row 1 and you get a 1 in return. Press button 2 and you get a 2. Press button 3 and you get a 4. Press button 4 and you get an 8.... and so on. All 8x buttons pressed on the row returns 255.

Great... its fast, and it gives me 64 buttons.

Then it goes to pot....

I noticed that pressing more than 3 buttons on different columns was giving me the wrong result. The code seems fine and its not a timing issue (debugged all of that).

I think its something to do with the ability of the 23016 to provide enough output when more than 2x rows are being used. Let me explain.
The return bank of 8x inputs are all held low with a 10k resistor, so that I don't get floating values.

I think the problem might be that when you press the buttons in more than 2 separate rows, you are tying the output down to ground with more than one 10k resistor when it reads it. If you held down a button on each of the 8x rows, you would be effectively adding 8x 10k pulling down to ground.

This then appears to effect drop the output low enough to render the reading of the values incorrect.

Hmm,, I hope I explained that well enough. Any ideas how to get around that one? I am thinking switching the button commons with a transistor. Think that will fix it?
Terrible quick drawing attached (I can never get images to upload correctly here)

Thanks, Steve
 
Last edited:

Steve2381

Senior Member
:( Well... Tried driving a transistor on the output of column 1 to provide a low common on on the switch matrix (instead of a high) and reading the reverse results (you start with 255 and the value drops depending on the button pressed).

Fried channel 1 on the chip. No idea how i might add. That pin now appears to be dead.

No spare chip so game over until further notice. Its so annoying to get so close and then it all goes to ####.

I think I may just use a spare Arduino mini pro that has 16 pins and write a short bit of code to scan the buttons and report it over I2c. This has taken up way too much time!
 

hippy

Technical Support
Staff member
The problem is inherent with the design chosen; a matrix with multiple drives active at the same time and/or without blocking diodes is not intended to be able to cater for more than one button pushed at a time.

Multiple button pushes can be detected but only in circumstances where they do not create current paths which lead to phantom button pushes or shorts between high and low drives.

Unfortunately the bottom line is you chose a flawed design and have revealed its flaw.
 

Steve2381

Senior Member
Well that's helpful.
It turned out to be a rogue delay statement in the code I had forgotten about and that was missing the first returned value.
Now it switches each of the column supplies through a BC548 and reads the result as a low instead of a high... it seems to work very well.

Not sure how there are multiple paths when only one output is ever high at a time and one return read at a time? And how do diodes help me in this instance?

But I won't bore you with with this any more then. Wow I hate asking anything in forums
 
Last edited:

techElder

Well-known member
Well, this is a PICAXE forum. We do try to help, but it seems a bit contrary to complain about the help you do get.

Perhaps post on the Volkswagen forum and see if that's better?
 

Steve2381

Senior Member
Well it was connected to a Picaxe 40x2. Still is on one of my development boards. I am not complaining about the help, its the sarcastic unhelpful stuff.
Instead of putting down my ideas (or anyone else's), how about explaining the issues or problems you foresee. If I knew what the problems were going to be, I would not be asking for help.

If this design is so appalling flawed, then what method is suggested to read 64 button inputs quickly? I thought I had a good idea. Apparently not (even though it appears to work fine now).

IBenson gave me some valuable assistance, but apart from that its basically been 'you pillock'.

Don't worry yourselves oh clever folk. I won't bother your forum with my petty questions again
 

hippy

Technical Support
Staff member
Apologies for not having the time to explain what appeared to be wrong with your setup when I posted.

This will hopefully better explain the flaws which appears to exist.

When output A is high and all other outputs are low, pressing the top left button will pass the high from A to input 1. That is as expected ( click on image for better view ) -

Short.jpg

But if one presses any of the other buttons in the top row, that will short the high from A which has been put on the input 1 line to the lows on the B, C or D outputs. That creates a high to low short which may draw too much current and can damage the device.

The second problem is phantom button presses -

Phantom.jpg

Pressing the top left button puts the high from A onto the input 1 line, but also passes that to all top row buttons. If the top right is pressed that passes the signal down as if from output D, and if the bottom right is pressed, that passes the high on to input 4. That makes it appear as if the A signal had activated inputs 1 and 4, the bottom left button appears as a phantom button push.
 

hippy

Technical Support
Staff member
I have had more time to detail the solutions to the above problems. For the 'shorting high to low' problem there are two options.

1) Ensure that there is only one high output at a time with all other 'output lines' set as input rather than output low.

2) Add diodes into the output drive lines -

DIODE.jpg

That makes the circuit electrically safe but does not solve the problem of phantom button pushes. To avoid that, to allow any number and any combination of simultaneous button pushes, a diode must be added for every button. Those allows high outputs to pass through it, but prevent propagation of the input line going high -

BUTTONS.jpg
 

johnlong

Senior Member
Nice explination Hippy
So by using the diode approch to each button then if I have undersrood it right
gives you a trueism its high so must be high as there is no backfeed
so any combination of multiple pushes can be recorded (scanned for) and
inacted apon
regards john
 

hippy

Technical Support
Staff member
That's correct. Only buttons in the column being driven will pass a high onto the row input pins. All potential propagation through other buttons, even if pushed, is prevented by the bocking diodes; current can only flow in the direction of the diodes.

This is how an electronic keyboard would be wired up, or a matrix made of switches rather than push buttons. All simultaneous closures can be read with no errors, no phantom closures.

Telephone keypads, alarm panels, and the like can use the simpler 'no diode matrix' because their use case is one push at a time.

It is possible to have a 4x4 'no diode matrix' as a 3x4 number pad plus 4 'shift buttons' modifiers if wired appropriately ( modifiers all on the same row or column ) but only if one numeric and only one 'shift button' is used at a time. With three pushed phantom pushes can occur.

If it is necessary to have three or more simultaneous closures detected without error or phantom closures, one has to include diodes on each button or switch.

Added : It should be noted that AllyCat mentioned these issues in post #2.
 
Last edited:
Top