MCP23S17 Pinextender addressing confusion

Hello everyone,

my current setup consists of an Arduino Nano and the MCP23S17 (datasheet) as digital pin extender. Since I use the SPI-Bus already on this print it is a better fit than the MCP23017 or a common shift register. I am trying to add a DIP switch for the addressing pins but cant seem to find the correct impedance values of the input pins.

The IC is addressed via A0,A1,A2 either connecting them to +5V or GND (no floating). My planned setup looks like this

I assume a high impedance on the input pins and therefore would sink 0.5mA over the resistors R5,R6 or R7. On case of an open DIP the address pins are well grounded.

Is this assumption correct?

How many MCP23S17 devices are you intending to integrate in your project.?
If it is only one then I think you can ignore the address pins A0, A1 and A2. When transmitting to the device you simply bring /CS low , put commands and data on the SPI bus then bring /CS high again.
If you want to use the address pins with the MCP23S17, you have to set a register entry IOCON.HAEN

1 Like

So far there are 3 SPI devices (MCP23s17 included). I want to leave out an additional circuitry for an MCP23s17 which is not equipped yet and was wondering how to diferentiate between the two.

You are right. I think it is easier to differentiate between the two devices using CS rather than setting an unique address. Especially when the data packages to\from the IC are the same.

Yes, if your intention is to integrate three MCP23S17 devices in your project then you appear to have two choices:

  1. Have a separate /CS for each MCP23S17 and use that to determine which chip you are writing to.
    or
  2. Set the address pins so these are unique on each chip then you need only one /CS line.

Obviously the first option uses more arduino pins.

In this case I prefer the separate CS lines since all other ICs already have separat CS and unique address.

I would disagree. You can have a maximum of 8 of these devices using a normal one ~CS pin. I have used 6 of these chips in a system with hard wired addresses for each. Note you have to include the appropriate address in your SPI data package you send. Also A0, A1 & A2 are the three lower bits of the address, you have to also use the fixed portion to make up the address you use.

Yes.

This is some code I wrote to show you how to access two of these chips.

/* Routines for accessing MCP23S17 SPI Port expander - Mike Cook
  * CS - to digital pin 10  (SS pin)
  * SDI - to digital pin 11 (MOSI pin)
  * CLK - to digital pin 13 (SCK pin)
*/
const int slaveSelectPin = 10;
const byte expander0_write = 0x40, expander0_read = 0x41; 
const byte expander1_write = 0x42, expander1_read = 0x43; 

#include <Spi.h>
void setup(){
    // set the slaveSelectPin as an output:
  pinMode (slaveSelectPin, OUTPUT);
  // initialise SPI:
  SPI.begin();
  expanderW(expander0_write, 0x0A, SEQOP | HAEN); // this sets both / all expanders to use address pins
  expanderW(expander0_write, 0x0A, SEQOP | HAEN); // this sets both / all expanders to use address pins        
  expanderW(expander0_write,IODIRA, 0xff);      // Data direction register A all inputs
  expanderW(expander0_write,IODIRB, 0xff);      // Data direction register B all inputs
  expanderW(expander0_write,IPOLA, 0xff);       // Input polarity read an earth (press) as a one
  expanderW(expander0_write,IPOLB, 0xff);       // Input polarity read an earth (press) as a one
  expanderW(expander0_write,INTCONA, 0x00);     // Notify on change
  expanderW(expander0_write,INTCONB, 0x00);     // Notify on change
  expanderW(expander0_write,GPINTENA, 0xff);    // enable notifacation on pins
  expanderW(expander0_write,GPINTENB, 0xff);    // enable notifacation on pins

  expanderW(expander1_write, 0x0A, SEQOP | HAEN); // this sets both / all expanders to use address pins
  expanderW(expander1_write, 0x0A, SEQOP | HAEN); // this sets both / all expanders to use address pins        
  expanderW(expander1_write,IODIRA, 0xff);      // Data direction register A all inputs
  expanderW(expander1_write,IODIRB, 0xff);      // Data direction register B all inputs
  expanderW(expander1_write,IPOLA, 0xff);       // Input polarity read an earth (press) as a one
  expanderW(expander1_write,IPOLB, 0xff);       // Input polarity read an earth (press) as a one
  expanderW(expander1_write,INTCONA, 0x00);     // Notify on change
  expanderW(expander1_write,INTCONB, 0x00);     // Notify on change
  expanderW(expander1_write,GPINTENA, 0xff);    // enable notifacation on pins
  expanderW(expander1_write,GPINTENB, 0xff);    // enable notifacation on pins
}

void loop(){
}

byte expanderR(byte deviceOpcode,byte regAddress){ // expander read
  byte value;
  digitalWrite(slaveSelectPin, LOW);
  Spi.transfer(deviceOpcode);  // address read
  Spi.transfer(regAddress);   //  register address
  value = Spi.transfer(0x0);   //  dummy data for read
  digitalWrite(slaveSelectPin, HIGH);
  return value;
}

byte expanderW(byte deviceOpcode, byte regAddress, byte data){ // expander write
  digitalWrite(slaveSelectPin, LOW);
  Spi.transfer(deviceOpcode);  // address write
  Spi.transfer(regAddress);   //  register address
  Spi.transfer(data);   //  register data
  digitalWrite(slaveSelectPin, HIGH);
}

OK. I can only go by the data sheet which says this:
image

What I didn't notice before is that even if you have disabled the address pins, they should be biased to a power rail.

Edit
It looks like you've edited your post in the meantime.

Yes. Sorry about that you were right and I forgot. I only noticed when I included the example code I wrote and I saw it there commented.

@Grumpy_Mike your code is saved. Nice work.

Normally I would agree with you, but I recently was reminded that I only need the simplest circuit for the current application, rather than a universal solution for all possibilities. Since I have multiple free digital ports I will use separate CS lines. Merging multiple CS lines is the more elegant solution.

Hi Mike, these two lines give compiler error: 'SEQOP was not declared in this scope'

0x0A I understand is the IOCON register, SEQOP is bit 5 and HAEN is bit 3.

But how do these two lines of code work and why does this not compile?

It does not compile because you have not told the compiler what they are in your sketch. You need to add
#define SEQOP 0x20
#define HAEN 0x08
These are given on page 10 of the chip's data sheet.

SEQOP is defined as the byte number 0x20 or in binary 0010 0000
HAEN is defined as the byte number 0x08 or in binary 0000 1000
The | symbol between those byte names is the inclusive OR operator this puts a logic bit 1 into the result if either of the two arguments has a logic 1 in the corresponding bit.
So SEQOP | HAEN results in the value 0x28 or in binary 0010 1000, which is placed in the register 0x0A of the MCP23S17 which is the ICON register.

The Sequential Operation (SEQOP) controls the incrementing function of the Address Pointer.
The Hardware Address Enable (HAEN) bit enables/disables hardware addressing on the MCP23S17 only. The address pins (A2, A1 and A0) must be externally biased, regardless of the HAEN bit value.
If enabled (HAEN = 1), the device’s hardware address matches the address pins.
If disabled (HAEN = 0), the device’s hardware address is A2 = A1 = A0 = 0.
All other bits in the ICON register are zero.
See page 17 of the data sheet for the implications of that.

2 Likes

Why would you write these twice to 0x0A?

because it auto increments

Would this do the same (writing 2 bytes instead of one)? Would this work?

Datasheet 3.2.3.1 SPI Write Operation: The SPI write operation is started by lowering !CS. The Write command (slave address with RW bit cleared) is then clocked into the device. The opcode is followed by an address and at least one data byte.

```
byte expanderWriteBoth(byte deviceOpcode, byte regAddress, byte data){ // expander write
  digitalWrite(slaveSelectPin, LOW);
  Spi.transfer(deviceOpcode);  // address write
  Spi.transfer(regAddress);   //  register address
  Spi.transfer(data);   //  register data to A
  Spi.transfer(data);   //  register data to B
  digitalWrite(slaveSelectPin, HIGH);
}
```

Don't know, try it.

1 Like

Yes it does :grin:

But why would IOCONA and IOCONB both need to be written to?

For example, if mirroring is used (bit 6 = 1), then both A and B are "twins", so if IOCONA is written to and, say, ODR is set to "1", then this applies to both A and B. So why duplicate?

Sorry but I didn't design the chip so I can't say why they designed, unless it took less silicon to do it that way.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.