0

MCP23017 with Keypad
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

MCP23017 with Keypad

by bbcentral on Fri Jan 17, 2014 12:04 pm

I'm currently working on an Arduino Uno project that uses the Adafruit Wave Shield, DS1307 RTC, shift registers and a 4x3 keypad.

The Wave Shield uses 8 pins, and the shift registers will need another 3. The 4x3 keypad takes up 7 pins, plus the 2 I2C pins needed for the RTC. I realised I was going to run out of pins so I purchased the MCP23017 from Adafruit with the intention of connecting the keypad to it.

I was able to get it working with buttons and LEDs using I2C, even with the RTC connected at the same time to the same pins (both on different addresses, of course). However the Arduino Keypad library doesn't support I2C out of the box, and my attempts at creating a modified keypad library were unsuccessful.

I found several forum topics on here pointing users to this I2C Keypad library for Arduino:
https://github.com/joeyoung/arduino_keypads

This is where I'm stuck, I haven't been able to get it working with the MCP23017, it's designed for the MCP23016. I was under the impression the 016 wasn't too different to the 017, I even tried changing the definitions in the library to match but that was unsuccessful too.

Here's how far I got with that:
Code: Select all | TOGGLE FULL SIZE
// New Code
#define GP0 0x12      //MCP23017 GPIO reg
#define GP1 0x13      //MCP23017 GPIO reg
#define IODIR0 0x00      //MCP23017 I/O direction register
#define IODIR1 0x01      //MCP23017 I/O direction register
#define IOCON0 0x0A      //MCP23017 I/O configuration register
#define IOCON1 0x0B      //MCP23017 I/O configuration register
// original Code
/*#define GP0 0x00      //MCP23016 GPIO reg
#define GP1 0x01      //MCP23016 GPIO reg
#define IODIR0 0x06      //MCP23016 I/O direction register
#define IODIR1 0x07      //MCP23016 I/O direction register
#define IOCON0 0x0a      //MCP23016 I/O configuration register
#define IOCON1 0x0b      //MCP23016 I/O configuration register*/


If I connect the keypad to the first 7 pins on the port expander, the serial output displays a "1", and keypresses don't do anything. I'm 99% sure this is a software/code issue rather than a hardware one (everything works until I use the I2C keypad library).

So my project has ground to a halt until I can figure this out. I've posted my request on the Github repository for the library, but I'm not confident I'll get a response (it looks dead quiet there).

I believe these are my options:
  • Hope that the developer releases an update
  • Ditch the MCP23017 and find an alternative which has a keypad library (PCF8574, PCF8575, MCP23008, MCP23016)
  • Buy an Arduino Mega with more pins (preferably not :P )
  • Write my own code for reading the keypad over I2C without all the benefits of the Arduino keypad library
  • See if anyone here has any other ideas (or knows how to fix the library)

Hope someone can help, please let me know if you need more information from me.

Thanks!

bbcentral
 
Posts: 20
Joined: Fri Jan 17, 2014 11:40 am

Re: MCP23017 with Keypad

by bbcentral on Fri Jan 17, 2014 7:17 pm

So, the keypad library developer replied to my question with some ideas to try to get me on the right track:
I don't have a MCP23017 to work with, so can't be completely sure what is causing the problem. The ..17 has features that support it's use as two independent 8-bit ports more completely than does the ..16, but it looks like it can be made compatible with the register access mode which is ASSUMED by the Keypad_MC16 library. That is, the library assumes auto-incrementing of the register address pointer so that two-byte accesses go to gp0 and gp1 as if they were a single 16-bit register. The ..17 datasheet identifies IOCON.BANK = 0 as the mode control bit that makes two-byte writes/reads act as 16-bit register access. This condition should be the power-up or reset default value, and the library setup should be leaving it alone. But maybe you need to also modify Keypad_MC16::begin( ) to explicitly write 0x00 (the library now writes 0x01 to IOCON0).

From my quick skim of the ..17 datasheet, it's not clear to me if the access sequence used by the library (write to the address pointer, followed by two more bytes on write, or followed by two-byte read) works the same way as it does for the ..16 -- it seems that setting the register pointer may not be needed for the ..17, so maybe doing it every time messes something up.


I'll see if I can decipher what it all means and create my own MCP23017 keypad library, I'll post my code here when I'm done (or when I'm stuck, which is a far more likely scenario ;) )

bbcentral
 
Posts: 20
Joined: Fri Jan 17, 2014 11:40 am

Re: MCP23017 with Keypad

by bbcentral on Fri Jan 17, 2014 11:53 pm

I'm completely lost now, this seems to be far beyond my skill set (I've only been working with C/Arduino for 6 months).
I literally have no idea where to start on this, I don't even really understand what each of the library functions is doing (which makes things much harder, of course).

I'm going to have to get some help, this may require somehow combining the Adafruit MCP23017 library with the I2CKeypads library.

bbcentral
 
Posts: 20
Joined: Fri Jan 17, 2014 11:40 am

Re: MCP23017 with Keypad

by hiduino on Sat Jan 18, 2014 11:28 pm

You could try the keypad direct using pins D6, D7, D8, D9 and A1, A2, A3. The analog pins can be used as digital i/o so you don't need to use the mcp23017 chip.

hiduino
 
Posts: 830
Joined: Sat Sep 01, 2012 7:05 pm

Re: MCP23017 with Keypad

by adafruit_support_mike on Sun Jan 19, 2014 12:43 am

First of all, your life will be much easier if you use our library for the MCP23017: https://github.com/adafruit/Adafruit-MC ... no-Library We've already done most of the datasheet-intensive stuff and created 'digitalRead()' and 'digitalWrite()' functions so code for the pins on the chip looks and acts pretty much like code for the Arduino's regular digital pins.

There are about three main tricks involved in reading a keyboard: setting up a group of input pins, managing a group of control pins, and using those two groups to read each possible key really quickly.

For simplicity's sake I'll describe a 2x2 keypad, but to control a larger keypad you just need to use bigger numbers.

First we need to set a pair of pins that can tell whether some button in a row has been pressed:

Code: Select all | TOGGLE FULL SIZE
#include <Wire.h>
#include "Adafruit_MCP23017.h"

Adafruit_MCP23017 mcp;
 
void setup() { 
  mcp.begin();

  mcp.pinMode(0, INPUT);  // reads row 0
  mcp.pullUp(0, HIGH);

  mcp.pinMode(1, INPUT);  // reads row 1
  mcp.pullUp(1, HIGH);
}

You generally need a pull-up resistor to read a switch, and the MCP23017 has pull-ups built in. They're only connected if you want them, and the 'pullUp()' function tells the chip that you do.

Next, you assign a couple of pins to control the columns:

Code: Select all | TOGGLE FULL SIZE
void setup() { 
  mcp.begin();

  mcp.pinMode(0, INPUT);  // reads row 0
  mcp.pullUp(0, HIGH);

  mcp.pinMode(1, INPUT);  // reads row 1
  mcp.pullUp(1, HIGH);

  mcp.pinMode(2, INPUT);  // controls column A
  mcp.pinMode(3, INPUT);  // controls column B
}

We'll actually use pins 2 and 3 for output, but setting them as INPUT lets us take advantage of a neat little quirk in how the chips work.

When a pin is configured for input, its effective resistance is really high.. 10M or more. In terms of resistance, the pin just isn't there any more, so the 100k pull-ups on pins 0 and 1 won't see anything pulling them up or down.

To control a column, we flip over to OUTPUT mode and send the pin LOW:

Code: Select all | TOGGLE FULL SIZE
void loop () {
  mcp.pinMode(2, OUTPUT);
  mcp.digitalWrite(2, LOW);  // create a GND connection for column A
}

Now each button in that column has an INPUT pin with a pull-up resistor on one side and what amounts to a direct connection to GND on the other.

That's your basic button-reading arrangement, so we check each row to see if the INPUT pins are HIGH (button open) or LOW (button closed):

Code: Select all | TOGGLE FULL SIZE
void loop () {
  mcp.pinMode(2, OUTPUT);
  mcp.digitalWrite(2, LOW);  // create a GND connection for column A

  if (LOW == mcp.digitalRead(0)) {
    // button A,0 has been pressed
  }
  if (LOW == mcp.digitalRead(1)) {
    // button A,0 has been pressed
  }
}

Then we disconnect that column's GND line and create one for the next column:

Code: Select all | TOGGLE FULL SIZE
void loop () {
  mcp.pinMode(2, OUTPUT);
  mcp.digitalWrite(2, LOW);  // create a GND connection for column A

  if (LOW == mcp.digitalRead(0)) {
    // button A,0 has been pressed
  }
  if (LOW == mcp.digitalRead(1)) {
    // button A,0 has been pressed
  }
  mcp.pinMode(2, INPUT);  // disconnect GND from column A

  mcp.pinMode(3, OUTPUT);
  mcp.digitalWrite(3, LOW);  // create a GND connection for column B

  if (LOW == mcp.digitalRead(0)) {
    // button B,0 has been pressed
  }
  if (LOW == mcp.digitalRead(1)) {
    // button B,0 has been pressed
  }
  mcp.pinMode(3, INPUT);  // disconnect GND from column B
}

And at that point you've tested every button in the matrix.

In real code you'd loop over the columns and rows, but that's the basic idea.
When you void a product warranty, you give up your right to sue the manufacturer if something goes wrong and accept full responsibility for whatever happens next. And then you truly own the product.

adafruit_support_mike
 
Posts: 50210
Joined: Thu Feb 11, 2010 2:51 pm

Re: MCP23017 with Keypad

by bbcentral on Sun Jan 19, 2014 2:42 am

hiduino wrote:You could try the keypad direct using pins D6, D7, D8, D9 and A1, A2, A3. The analog pins can be used as digital i/o so you don't need to use the mcp23017 chip.

Unfortunately pins D6, D7 and D8 are taken up by my shift register Clock, Latch, and Data pins.

bbcentral
 
Posts: 20
Joined: Fri Jan 17, 2014 11:40 am

Re: MCP23017 with Keypad

by bbcentral on Sun Jan 19, 2014 2:58 am

adafruit_support_mike wrote:First of all, your life will be much easier if you use our library for the MCP23017: https://github.com/adafruit/Adafruit-MC ... no-Library We've already done most of the datasheet-intensive stuff and created 'digitalRead()' and 'digitalWrite()' functions so code for the pins on the chip looks and acts pretty much like code for the Arduino's regular digital pins.


I've half managed to get that working, but I cloned the Keypad library (not the I2C keypad library I posted above, the standard Arduino one) and called it "Keypad2".
Inside that library I included the Adafruit MCP23017 library and updated all references from digitalRead and digitalWrite to mcp.digitalRead and mcp.digitalWrite respectively.
I also had to make some other changes to get it working, such as adding another function to run mcp.begin() at the right time and handle setting the pullups correctly.

It does work now, the code is a bit messy but I'll try to tidy it up at some stage.

My main reason for wanting to use the Keypad library is that it has some great debounce code, but I may try a more manual approach later on. I'm reaching my SRAM limit on the Arduino Uno (the Wave Shield uses more than 50%), so optimizing the code is going to be important.

What I hacked together is sitting here:
https://gist.github.com/anonymous/8501408

If I get more time I'll see if I can try again with the I2C Keypad library, but for now this is better than nothing.

Thanks for your help!

bbcentral
 
Posts: 20
Joined: Fri Jan 17, 2014 11:40 am

Re: MCP23017 with Keypad

by Killg0reNL on Tue Apr 10, 2018 6:47 am

Will the custom Keypad2 library be able to use 2 or more mcp's?
For now it only seems to use adress '0' or '0x20'

ie. would this work?:
#include "Adafruit_MCP23017.h"

Adafruit_MCP23017 mcp1;

Adafruit_MCP23017 mcp2;

Adafruit_MCP23017 mcp3;

#define addr1 0x00

#define addr2 0x01

#define addr3 0x02

void setup() {

mcp1.begin(addr1);

mcp2.begin(addr2);

mcp3.begin(addr3);

}



void loop() {





}

Killg0reNL
 
Posts: 1
Joined: Tue Apr 10, 2018 6:45 am

Please be positive and constructive with your questions and comments.