More than 8 MCP23017

Status
Not open for further replies.

uezi

Member
Hello,
how can I use more than 8 pcs. of MCP23017... First 8 MCP connected by SDA0 + SCL0 on 18/19 on my Teensy 3.2 ...
I want to connect again 8 MCP to SDA1 + SCL1 on 29/30.

Anybody a solution how I can programm that?

Thanks for repleys
 
You just do it, really. SCL0 and SDA0 belong to Wire0, SCL1 and SDA1 belong to Wire1 (they are broken out to the bottom layer, though, so they are harder to reach). You can use both busses in the same application.
 
You can also connect 8 more to pins 16 & 17. Use Wire.setSCL(pin) and Wire.setSDA(pin) to switch between those pins and the default pins 18 & 19.

To use SCL1 & SDA1 on the bottom side, you'll need to make a copy of whatever code or library you have, and change all the Wire to Wire1. Or maybe the library already has a way to change the port? (you didn't give the code or a link to it, so this message can't be more specific about exactly what needs to be done specific to the code you're using)
 
Hello, thanks for repley...

haven´t any code with wire1 ... or something...
have at the moment only code connection on 18/19... do you need it?
 
Well another way to solve the problem is an i2c multiplexer, such as:

Though I suspect when you get up to 16 MCP23017's that you may have to be more careful about wiring issues, etc.

What Paul means is if you use something like Adafruit's MCP23017 to talk to the MCP23017:

You will need to copy the library to another name (such as Adafruit-MCP23017B). Then change all of the Wire references in the new libraries .h or .cpp files from Wire to Wire1/ This is due to the Arduino libraries believing there is only one SPI, I2C, etc. bus. Include both libraries, and use the first library to talk to the first 8 MCP23017's and the second library to talk to the second 8 MCP23017's.

I don't recall if the MCP23017 has pull-up resistors on board. If not, you will need pull up resistors for each i2c pair between SCL/SDA and 3.3v. Typically, you would want 2.2K resistors.

If you are just using the MCP23017 to light up 256 LEDs, it may be simpler to use WS2812B/neopixel or APA102/dotstar LED strings.
 
yes I use this adafruit library...
this is all what I have to do? Only copy this library and rename wire to wire1?
 
yes I use this adafruit library...
this is all what I have to do? Only copy this library and rename wire to wire1?

In theory yes (and solder wires to solder pads 29/30 underneath the Teensy, and and install pull-up resistors for both 18/19 and 29/30).
 
Do not work... here is my sketch... can´t upload to teensy... comes a error message

Code:
#include <i2c_t3.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_MCP230171.h>



//int Tasten 1-10 ------------------------------------------------------
int taste1 = LOW;
int taste1Alt = LOW;
int taste2 = LOW;
int taste2Alt = LOW;

//ENDE Block ------------------------------------------------------

Adafruit_MCP23017 mcp1; // Create MCP 1
Adafruit_MCP23017 mcp2; // Create MCP 2
Adafruit_MCP23017 mcp3; // Create MCP 3
Adafruit_MCP23017 mcp4; // Create MCP 4
Adafruit_MCP23017 mcp5; // Create MCP 5
Adafruit_MCP23017 mcp6; // Create MCP 6
Adafruit_MCP23017 mcp7; // Create MCP 7
Adafruit_MCP23017 mcp8; // Create MCP 8

  
void setup() {  


  mcp1.begin(0);      
  mcp2.begin(1);
  mcp3.begin(2);      
  mcp4.begin(3);
  mcp5.begin(4);      
  mcp6.begin(5);
  mcp7.begin(6);      
  mcp8.begin(7);

  mcp1.pinMode  (0, INPUT);
  mcp1.pullUp   (0, HIGH);  // turn on a 100K pullup internally
  mcp2.pinMode  (0, INPUT);
  mcp2.pullUp   (0, HIGH);  // turn on a 100K pullup internally
  mcp3.pinMode  (0, INPUT);
  mcp3.pullUp   (0, HIGH);  // turn on a 100K pullup internally
  mcp4.pinMode  (0, INPUT);
  mcp4.pullUp   (0, HIGH);  // turn on a 100K pullup internally
  mcp5.pinMode  (0, INPUT);
  mcp5.pullUp   (0, HIGH);  // turn on a 100K pullup internally
  mcp6.pinMode  (0, INPUT);
  mcp6.pullUp   (0, HIGH);  // turn on a 100K pullup internally
  mcp7.pinMode  (0, INPUT);
  mcp7.pullUp   (0, HIGH);  // turn on a 100K pullup internally
  mcp8.pinMode  (0, INPUT);
  mcp8.pullUp   (0, HIGH);  // turn on a 100K pullup internally



  pinMode(13, OUTPUT);  // use the p13 LED as debugging
}



void loop() {

// MCP1 Input Taste 1 - 16

// Tastenblock 1 (Taste 1 - 10)
taste1 = mcp1.digitalRead(0);
taste2 = mcp1.digitalRead(1);




// START Tastenblock 1 - Note, Velocity, Channel ------------------------------------------------

          if (taste1 == HIGH && taste1Alt == LOW) {
              usbMIDI.sendNoteOn(1,127,1); 
              taste1Alt = taste1;
           }
           if (taste1 == LOW && taste1Alt == HIGH) {
              usbMIDI.sendNoteOff(1,0,1); 
              taste1Alt = taste1;
          }
          
          if (taste2 == HIGH && taste2Alt == LOW) {
              usbMIDI.sendNoteOn(2,127,1); 
              taste2Alt = taste2;
           }
           if (taste2 == LOW && taste2Alt == HIGH) {
              usbMIDI.sendNoteOff(2,0,1); 
              taste2Alt = taste2;
          }
          
  
   
// ENDE Tastenblock 12 ------------------------------------------------ 
   
   usbMIDI.send_now();

  
  digitalWrite(13, mcp1.digitalRead(0));
}
 
Please also show us the modified library code. It's part of the code we can't get from any other source.
 
Code:
/*************************************************** 
 This is a library for the MCP23017 i2c port expander

 These displays use I2C to communicate, 2 pins are required to
 interface
 Adafruit invests time and resources providing this open source code,
 please support Adafruit and open-source hardware by purchasing
 products from Adafruit!

 Written by Limor Fried/Ladyada for Adafruit Industries.
 BSD license, all text above must be included in any redistribution
 ****************************************************/

#ifdef __AVR_ATtiny85__
  #define wire1 Tinywire1M      //Tinywire1M is now part of Adafruit wire1 library
#else
  #include <wire1.h>
#endif


#ifdef __AVR
  #include <avr/pgmspace.h>
#elif defined(ESP8266)
  #include <pgmspace.h>
#endif
#include "Adafruit_MCP23017.h"

#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

// minihelper to keep Arduino backward compatibility
static inline void wire1send(uint8_t x) {
#if ARDUINO >= 100
	wire1.write((uint8_t) x);
#else
	wire1.send(x);
#endif
}

static inline uint8_t wire1recv(void) {
#if ARDUINO >= 100
	return wire1.read();
#else
	return wire1.receive();
#endif
}

/**
 * Bit number associated to a give Pin
 */
uint8_t Adafruit_MCP23017::bitForPin(uint8_t pin){
	return pin%8;
}

/**
 * Register address, port dependent, for a given PIN
 */
uint8_t Adafruit_MCP23017::regForPin(uint8_t pin, uint8_t portAaddr, uint8_t portBaddr){
	return(pin<8) ?portAaddr:portBaddr;
}

/**
 * Reads a given register
 */
uint8_t Adafruit_MCP23017::readRegister(uint8_t addr){
	// read the current GPINTEN
	wire1.beginTransmission(MCP23017_ADDRESS | i2caddr);
	wire1send(addr);
	wire1.endTransmission();
	wire1.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
	return wire1recv();
}


/**
 * Writes a given register
 */
void Adafruit_MCP23017::writeRegister(uint8_t regAddr, uint8_t regValue){
	// Write the register
	wire1.beginTransmission(MCP23017_ADDRESS | i2caddr);
	wire1send(regAddr);
	wire1send(regValue);
	wire1.endTransmission();
}


/**
 * Helper to update a single bit of an A/B register.
 * - Reads the current register value
 * - Writes the new register value
 */
void Adafruit_MCP23017::updateRegisterBit(uint8_t pin, uint8_t pValue, uint8_t portAaddr, uint8_t portBaddr) {
	uint8_t regValue;
	uint8_t regAddr=regForPin(pin,portAaddr,portBaddr);
	uint8_t bit=bitForPin(pin);
	regValue = readRegister(regAddr);

	// set the value for the particular bit
	bitWrite(regValue,bit,pValue);

	writeRegister(regAddr,regValue);
}

////////////////////////////////////////////////////////////////////////////////

/**
 * Initializes the MCP23017 given its HW selected address, see datasheet for Address selection.
 */
void Adafruit_MCP23017::begin(uint8_t addr) {
	if (addr > 7) {
		addr = 7;
	}
	i2caddr = addr;

	wire1.begin();

	// set defaults!
	// all inputs on port A and B
	writeRegister(MCP23017_IODIRA,0xff);
	writeRegister(MCP23017_IODIRB,0xff);
}

/**
 * Initializes the default MCP23017, with 000 for the configurable part of the address
 */
void Adafruit_MCP23017::begin(void) {
	begin(0);
}

/**
 * Sets the pin mode to either INPUT or OUTPUT
 */
void Adafruit_MCP23017::pinMode(uint8_t p, uint8_t d) {
	updateRegisterBit(p,(d==INPUT),MCP23017_IODIRA,MCP23017_IODIRB);
}

/**
 * Reads all 16 pins (port A and B) into a single 16 bits variable.
 */
uint16_t Adafruit_MCP23017::readGPIOAB() {
	uint16_t ba = 0;
	uint8_t a;

	// read the current GPIO output latches
	wire1.beginTransmission(MCP23017_ADDRESS | i2caddr);
	wire1send(MCP23017_GPIOA);
	wire1.endTransmission();

	wire1.requestFrom(MCP23017_ADDRESS | i2caddr, 2);
	a = wire1recv();
	ba = wire1recv();
	ba <<= 8;
	ba |= a;

	return ba;
}

/**
 * Read a single port, A or B, and return its current 8 bit value.
 * Parameter b should be 0 for GPIOA, and 1 for GPIOB.
 */
uint8_t Adafruit_MCP23017::readGPIO(uint8_t b) {

	// read the current GPIO output latches
	wire1.beginTransmission(MCP23017_ADDRESS | i2caddr);
	if (b == 0)
		wire1send(MCP23017_GPIOA);
	else {
		wire1send(MCP23017_GPIOB);
	}
	wire1.endTransmission();

	wire1.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
	return wire1recv();
}

/**
 * Writes all the pins in one go. This method is very useful if you are implementing a multiplexed matrix and want to get a decent refresh rate.
 */
void Adafruit_MCP23017::writeGPIOAB(uint16_t ba) {
	wire1.beginTransmission(MCP23017_ADDRESS | i2caddr);
	wire1send(MCP23017_GPIOA);
	wire1send(ba & 0xFF);
	wire1send(ba >> 8);
	wire1.endTransmission();
}

void Adafruit_MCP23017::digitalWrite(uint8_t pin, uint8_t d) {
	uint8_t gpio;
	uint8_t bit=bitForPin(pin);


	// read the current GPIO output latches
	uint8_t regAddr=regForPin(pin,MCP23017_OLATA,MCP23017_OLATB);
	gpio = readRegister(regAddr);

	// set the pin and direction
	bitWrite(gpio,bit,d);

	// write the new GPIO
	regAddr=regForPin(pin,MCP23017_GPIOA,MCP23017_GPIOB);
	writeRegister(regAddr,gpio);
}

void Adafruit_MCP23017::pullUp(uint8_t p, uint8_t d) {
	updateRegisterBit(p,d,MCP23017_GPPUA,MCP23017_GPPUB);
}

uint8_t Adafruit_MCP23017::digitalRead(uint8_t pin) {
	uint8_t bit=bitForPin(pin);
	uint8_t regAddr=regForPin(pin,MCP23017_GPIOA,MCP23017_GPIOB);
	return (readRegister(regAddr) >> bit) & 0x1;
}

/**
 * Configures the interrupt system. both port A and B are assigned the same configuration.
 * Mirroring will OR both INTA and INTB pins.
 * Opendrain will set the INT pin to value or open drain.
 * polarity will set LOW or HIGH on interrupt.
 * Default values after Power On Reset are: (false,flase, LOW)
 * If you are connecting the INTA/B pin to arduino 2/3, you should configure the interupt handling as FALLING with
 * the default configuration.
 */
void Adafruit_MCP23017::setupInterrupts(uint8_t mirroring, uint8_t openDrain, uint8_t polarity){
	// configure the port A
	uint8_t ioconfValue=readRegister(MCP23017_IOCONA);
	bitWrite(ioconfValue,6,mirroring);
	bitWrite(ioconfValue,2,openDrain);
	bitWrite(ioconfValue,1,polarity);
	writeRegister(MCP23017_IOCONA,ioconfValue);

	// Configure the port B
	ioconfValue=readRegister(MCP23017_IOCONB);
	bitWrite(ioconfValue,6,mirroring);
	bitWrite(ioconfValue,2,openDrain);
	bitWrite(ioconfValue,1,polarity);
	writeRegister(MCP23017_IOCONB,ioconfValue);
}

/**
 * Set's up a pin for interrupt. uses arduino MODEs: CHANGE, FALLING, RISING.
 *
 * Note that the interrupt condition finishes when you read the information about the port / value
 * that caused the interrupt or you read the port itself. Check the datasheet can be confusing.
 *
 */
void Adafruit_MCP23017::setupInterruptPin(uint8_t pin, uint8_t mode) {

	// set the pin interrupt control (0 means change, 1 means compare against given value);
	updateRegisterBit(pin,(mode!=CHANGE),MCP23017_INTCONA,MCP23017_INTCONB);
	// if the mode is not CHANGE, we need to set up a default value, different value triggers interrupt

	// In a RISING interrupt the default value is 0, interrupt is triggered when the pin goes to 1.
	// In a FALLING interrupt the default value is 1, interrupt is triggered when pin goes to 0.
	updateRegisterBit(pin,(mode==FALLING),MCP23017_DEFVALA,MCP23017_DEFVALB);

	// enable the pin for interrupt
	updateRegisterBit(pin,HIGH,MCP23017_GPINTENA,MCP23017_GPINTENB);

}

uint8_t Adafruit_MCP23017::getLastInterruptPin(){
	uint8_t intf;

	// try port A
	intf=readRegister(MCP23017_INTFA);
	for(int i=0;i<8;i++) if (bitRead(intf,i)) return i;

	// try port B
	intf=readRegister(MCP23017_INTFB);
	for(int i=0;i<8;i++) if (bitRead(intf,i)) return i+8;

	return MCP23017_INT_ERR;

}
uint8_t Adafruit_MCP23017::getLastInterruptPinValue(){
	uint8_t intPin=getLastInterruptPin();
	if(intPin!=MCP23017_INT_ERR){
		uint8_t intcapreg=regForPin(intPin,MCP23017_INTCAPA,MCP23017_INTCAPB);
		uint8_t bit=bitForPin(intPin);
		return (readRegister(intcapreg)>>bit) & (0x01);
	}

	return MCP23017_INT_ERR;
}


Code:
/*************************************************** 
  This is a library for the MCP23017 i2c port expander

  These displays use I2C to communicate, 2 pins are required to  
  interface
  Adafruit invests time and resources providing this open source code, 
  please support Adafruit and open-source hardware by purchasing 
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.  
  BSD license, all text above must be included in any redistribution
 ****************************************************/

#ifndef _Adafruit_MCP23017_H_
#define _Adafruit_MCP23017_H_

// Don't forget the wire1 library
//#ifdef __AVR_ATtiny85__           //Tinywire1M is now part of
//#include <Tinywire1M.h>            //   Adafruit version of wire1 Library
//#else
#include <wire1.h>
//#endif

class Adafruit_MCP23017 {
public:
  void begin(uint8_t addr);
  void begin(void);

  void pinMode(uint8_t p, uint8_t d);
  void digitalWrite(uint8_t p, uint8_t d);
  void pullUp(uint8_t p, uint8_t d);
  uint8_t digitalRead(uint8_t p);

  void writeGPIOAB(uint16_t);
  uint16_t readGPIOAB();
  uint8_t readGPIO(uint8_t b);

  void setupInterrupts(uint8_t mirroring, uint8_t open, uint8_t polarity);
  void setupInterruptPin(uint8_t p, uint8_t mode);
  uint8_t getLastInterruptPin();
  uint8_t getLastInterruptPinValue();

 private:
  uint8_t i2caddr;

  uint8_t bitForPin(uint8_t pin);
  uint8_t regForPin(uint8_t pin, uint8_t portAaddr, uint8_t portBaddr);

  uint8_t readRegister(uint8_t addr);
  void writeRegister(uint8_t addr, uint8_t value);

  /**
   * Utility private method to update a register associated with a pin (whether port A/B)
   * reads its value, updates the particular bit, and writes its value.
   */
  void updateRegisterBit(uint8_t p, uint8_t pValue, uint8_t portAaddr, uint8_t portBaddr);

};

#define MCP23017_ADDRESS 0x20

// registers
#define MCP23017_IODIRA 0x00
#define MCP23017_IPOLA 0x02
#define MCP23017_GPINTENA 0x04
#define MCP23017_DEFVALA 0x06
#define MCP23017_INTCONA 0x08
#define MCP23017_IOCONA 0x0A
#define MCP23017_GPPUA 0x0C
#define MCP23017_INTFA 0x0E
#define MCP23017_INTCAPA 0x10
#define MCP23017_GPIOA 0x12
#define MCP23017_OLATA 0x14


#define MCP23017_IODIRB 0x01
#define MCP23017_IPOLB 0x03
#define MCP23017_GPINTENB 0x05
#define MCP23017_DEFVALB 0x07
#define MCP23017_INTCONB 0x09
#define MCP23017_IOCONB 0x0B
#define MCP23017_GPPUB 0x0D
#define MCP23017_INTFB 0x0F
#define MCP23017_INTCAPB 0x11
#define MCP23017_GPIOB 0x13
#define MCP23017_OLATB 0x15

#define MCP23017_INT_ERR 255

#endif
 
I think it's going to be a bit more difficult. Both libraries (the standard Adafruit_MCP21037 and your custom Adafruit_MCP210371) define the same class named "Adafruit_MCP23017", so the names clash. When you create an instance of it, which will it be? But you don't even get that far because the library code tries to include "wire1.h" which doesn't exist in teensyduino.

So either:
  • create a new class with a new name that correctly uses Wire1 and includes i2c_t3 correctly, or
  • write your own library - the adafruit library provides all the details - and give it a reference to Wire or Wire1 in the object constructor. This is what I'd do, because the chip is simple and code to start from is already there

I'm pretty sure that at the end of this thread we've hand-holded you through one of these paths.
 
For use with an Adafruit library that's not designed to use 2 different Wire ports, you're probably much better off using the 2 pairs of pins for Wire and not bother with Wire1 at all. Using Wire1 would require editing Adafruit's library.

You can use Wire.setSDA(pin) and Wire.setSCL(pin) to connect 8 of those chips to each set of pins.

Or perhaps even easier would be using this Adafruit product. It does pretty much the same thing as changing between those 2 sets of pins. The key difference is it has a very nice Adafruit tutorial, and Adafruit's support (on their forum) will have experience with using it, so you can probably also get some help from them if you get stuck.
 
Status
Not open for further replies.
Back
Top