MCP23017 I/O Expander Help

I'm trying to learn about the MCP23017 I/O Expander using my Uno board.

I'm following the tutorial here: http://www.learningaboutelectronics.com/Articles/MCP23017-IO-port-expander-circuit-with-arduino.php

There is a statement: "And we connect the address pins, pins A0, A1, and A2, to ground. This makes the address of these 3 pins 000".

I understand the concept of using these three pins to chain more Expanders and how their binary value determines which expander board is the active board, etc. But in the example he is also connecting LEDs to these 3 ports.

How can I connect these ports to ground AND use them for controlling LEDs? It seems to me I can't do both at the same time?

Thanks..

You're getting A0~A2 (the address pins, physical pins 15-17) confused with GPA0~GPA2 (the first three I/O pins of the MCP23017's port A, physical pins 21~23)

Ahhh yes…I see that now…thanks…

Well, I've run into a problem trying to use that tutorial...because I'm new to the Ardunio world and electronics, I can't figure out how to wire up my breadboard.

What I'm trying to do is start with one button controlling one LED (push and the LED lights up). If I can get that working, then I can add a second button, etc.

So, I looked around but couldn't find a MCP23017 tutorial showing a button and LED with a Fritzing diagram to help me understand how to wire it up.

I think I understand the basics of the MCP23017 but still need some help with how to wire it up on a breadboard.

Thanks for any pointers to a tutorial showing me this.

The wiring part is the same as if you were wiring a button to an Arduino pin, and an LED to another Arduino pin. The pins on the MCP23017 act a lot like Arduino pins, electrically speaking (you of course control and read them differently from the software side)

I see…so I wire up my button on the MCP to pin A7 and the LED to pin A0, then in my code, I read pin A7 in MCP fashion to see if the button has been pressed. If it has, then I set pin A0 high to light up the LED.

I think I understand the wiring of the MCP now…thanks.

(here is the Fritzing diagram I am trying to follow but I am not using their code)

I’ve got two questions regarding my code below.

I’m trying to use the code I found in a tutorial on the MCP27017 chip.

Question (1) – are these two lines correct? They are both the same so I’m thinking the second line must need a different value?

Question (2) – my button is on pin A7 and my LED is on pin A0 so what is the binary value to set A7 as an input pin and A0 as an output pin?

Thanks…

void setup()
{
  Wire.begin(); //creates a Wire object
  Wire.beginTransmission(0x20); //begins talking to the slave device

  // ??? (1) are both of these lines corrext?
  Wire.write(0x00); //selects the IODIRA register: selects all 8 pins in port A
  Wire.write(0x00); //this sets all 8 port A pins to outputs
  // ???

  // (2) What values to set as commented?
  Wire.write(00000000); //set port A pin A7 to input
  Wire.write(00000000); //set port A pin A0 to output
  
  Wire.endTransmission(); //stops talking to device
}

So, I found out how to set port A pin A7 for input and port A pin A0 for output.

When I run my code below, the serial window displays only two lines:
255
255

and then nothing else.

Have I set something wrong in the MCP23017 pins somewhere?

Thanks…

#include <Wire.h>
int button = 0;
void setup()
{
  Serial.begin(9600);
  Wire.begin(); //creates a Wire object
  Wire.beginTransmission(0x20); //begins talking to the slave device
  Wire.write(0x00); //selects the IODIRA register: selects all 8 pins in port A
  Wire.write(10000000); //set port A pin A7 to input, the rest to output
  Wire.endTransmission(); //stops talking to device
}

void loop()
{
  Wire.beginTransmission(0x20); //starts talking to slave device
  Wire.requestFrom(0x20,1);
  byte waitfor = Wire.read();
  button = waitfor; //this converts the byte into a decimal value.
  Serial.println(button);
  Wire.endTransmission(); //ends communication with the device
  delay(100);
}
[\code]

Wire.write(10000000); is definitely wrong.

You're sending the number 10,000,000 to it, which will get truncated to a byte and be something non-sensical. You want to send it a byte where the high bit is 1 and all others are 0

You can do 0B10000000, or 0x80, or _BV(7), etc, however you want to express it.

I'm not sure what's going wrong that's making it stop though....

Well, I changed the line to 0B10000000.

However now when I run my sketch, the loop() doesn’t seem to run.

Below the loop() runs ok but I have all the Wire. statements remmed out of it.

If I only have these two Wire lines in the loop():
Wire.beginTransmission(0x20);
Wire.endTransmission();
then the loop doesn’t seem to run - that is, the print “loop” doesn’t display in the serial window.

If I remm out those two Write. lines, then the loop spits out “loop” endlessly.

So something isn’t working with the Write. statements.

I don’t think its the 0B10000000 because it does the same thing with 0x80.

Do I have the wrong library?

#include <Wire.h>
void setup()
{
  Serial.begin(9600);
  Wire.begin(); //creates a Wire object
  Wire.beginTransmission(0x20); //begins talking to the slave device
  Wire.write(0x00); //selects the IODIRA register: selects all 8 pins in port A
  Wire.write(0B10000000); //set port A pin A7 to input, the rest to output
  Wire.endTransmission(); //stops talking to device
}

void loop()
{
  Serial.println("loop");
  //Wire.beginTransmission(0x20); //starts talking to slave device
  //Wire.requestFrom(0x20,1);
  //byte waitfor = Wire.read();
  //int button = waitfor; //this converts the byte into a decimal value.
  //Serial.println(button);
  //Wire.endTransmission(); //ends communication with the device
  delay(100);
}

Did you forget pullup resistors on SDA and SCL?

You need a resistor, like 4.7 or 10k or something around there, between SDA and Vcc and between SCL and Vcc. Without that, I2C won't work correctly. I don't know off the top of my head if you can make an Atmel processor use the internal pullups for that.

Wire.h turns on the internal pullups by defuallt, but they are very weak. External 4.7k will give much better results.

I added the resistors.

I notice in my serial window I see 255’s scrolling…should be 0’s right?

When I press my button, nothing changes in the serial window.

#include <Wire.h>
int button = 0;
void setup()
{
  Serial.begin(9600);
  Wire.begin(); //creates a Wire object
  Wire.beginTransmission(0x20); //begins talking to the slave device
  Wire.write(0x00); //selects the IODIRA register: selects all 8 pins in port A
  //Wire.write(0B10000000); //set port A pin A7 to input, the rest to output
  Wire.write(0x80); //set port A pin A7 to input, the rest to output
  Wire.endTransmission(); //stops talking to device
}

void loop()
{
  Wire.beginTransmission(0x20); //starts talking to slave device
  Wire.write(0x12); //selects the IODIRA register: selects all 8 pins in port A
  Wire.endTransmission(); //ends communication with the device
  Wire.requestFrom(0x20,1);
  byte waitfor = Wire.read();
  button = waitfor; //this converts the byte into a decimal value.
  Serial.println(button);
  //Wire.endTransmission(); //ends communication with the device
  delay(200);
}

Hmm.... I don't see the smoking gun. But it's weird that you're getting 255 back... It's like it's reading from the wrong location.

I just checked the code I wrote for that on Espruino, and you're doing the right thing.

I'd ask if the switch was wired right (is it? You do have the pullup or pulldown resistor as appropriate, right? You haven't enabled the pullup on the MCP23017, so you do need an external resistor on that button), but it doesn't even matter at this point, since the results you're getting wouldn't make sense if the switch was wired incorrectly either - since you have the pins set to output and OLATA is in it's default state where everything is low. So from whence cometh the 255?

There's only one register that should be all 1's though, and that's IODIRB (and IODIRA if for some reason it's not getting set)... I'd try reading a bunch more bytes, and printing them out, and see if it's reading registers, just the wrong ones, or not reading them at all... Maybe a dozen bytes there? That way, if it was starting out pointed at IODIRB, the last one you got back would be GPIOA.

Lol well, I am new to all of this, but I think the switch is wired right. I certainly could be wrong.

I’m attaching a picture.

Looks good. (note that that there is a way to make the MCP23017 use it's internal pullups, like Arduino's INPUT_PULLUP, but solve the more pressing issue first)

I decided to go back to just lighting up one LED.

I’m using this code I found on setting all the pins as outputs. I’m not using the B side but I’m setting that side as well as the A side.

My resistor is 330 ohms, plugged into the out side of A07 pin, going into the long leg part of the LED first, then to ground.

But the LED doesn’t light up at all. Picture attached to show my wiring.

#include <Wire.h>

void setup()
{
  Serial.begin(9600);
  Wire.begin(); // wake up I2C bus
  Wire.beginTransmission(0x20);
  Wire.write(0x00); // IODIRA register
  Wire.write(0x00); // set all of bank A to outputs
  Wire.endTransmission();
  Wire.beginTransmission(0x20);
  Wire.write(0x01); // IODIRB register
  Wire.write(0x00); // set all of bank B to outputs
  Wire.endTransmission();
}

void loop()
{
 Wire.beginTransmission(0x20);
 Wire.write(0x12); // GPIOA
 Wire.write(0xFF); // bank A
 Wire.endTransmission();
 Wire.beginTransmission(0x20);
 Wire.write(0x13); // GPIOB
 Wire.write(0xFF); // bank B
 Wire.endTransmission();
 delay(500); 
 Wire.beginTransmission(0x20);
 Wire.write(0x12); // GPIOA
 Wire.write(0x00); // bank A
 Wire.endTransmission();
 Wire.beginTransmission(0x20);
 Wire.write(0x13); // GPIOB
 Wire.write(0x00); // bank B
 Wire.endTransmission();
 delay(500); 
}

One of the 4.7k pullups isn't going to Vcc, but to an empty set of holes in the breadboard.

Add some serial print statements so you can be sure it's actually running, not hanging itself up somewhere.

Good eye…I inserted the end of the 4.7k res into the power line. See attachment.

I added print statements and they are all printing out in the serial window but still no LED lighting up.

#include <Wire.h>

void setup()
{
  Serial.begin(9600);
  Wire.begin(); // wake up I2C bus
  Wire.beginTransmission(0x20);
  Wire.write(0x00); // IODIRA register
  Wire.write(0x00); // set all of bank A to outputs
  Wire.endTransmission();
  Wire.beginTransmission(0x20);
  Wire.write(0x01); // IODIRB register
  Wire.write(0x00); // set all of bank B to outputs
  Wire.endTransmission();
}

void loop()
{
 Serial.println("1111");
 Wire.beginTransmission(0x20);
 Wire.write(0x12); // GPIOA
 Wire.write(0xFF); // bank A
 Wire.endTransmission();
 Serial.println("2222");
 Wire.beginTransmission(0x20);
 Wire.write(0x13); // GPIOA
 Wire.write(0xFF); // bank B
 Wire.endTransmission();
 Serial.println("3333");
 delay(1000); 
 Wire.beginTransmission(0x20);
 Wire.write(0x12); // GPIOA
 Wire.write(0x00); // bank A
 Wire.endTransmission();
 Wire.beginTransmission(0x20);
 Wire.write(0x13); // GPIOA
 Wire.write(0x00); // bank B
 Wire.endTransmission();
 Serial.println("4444");
 delay(1000); 
}

Success! I got the LED to light up!

I decided to double check all of my wires and discovered from the MCP I had plugged in the SCA and SDL wires into the wrong pins on my Teensy board. I was off by one pin slot. The teensy board is damn teensy and hard to count those pins...but a good lesson learned.

Thanks for all the help.