User avatar
Richard-TX
Posts: 1549
Joined: Tue May 28, 2013 3:24 pm
Location: North Texas

SPI MCP23s17 addressing

Tue Jun 25, 2013 4:46 pm

I have two MCP23S17 chips connected to the SPI bus on my Pi. I will have more connected in the future.

One of the chips is configured with address 0 and the other at address 1.

The two chips share the save CS/CE line as well as the other lines.

Below is the program that I am using.

The issue is that regardless of the address of the MCP23S17, both chips respond.

I have tried to change the CMD_WRITE and CMD_READ values to no avail.

What am I missing?

I am running the latest Wheezy release.

/****************************************
* basic SPI demo for mcp23s17 *
*****************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdint.h>
#define CMD_WRITE 0x40
#define CMD_READ 0x41
static char *spiDevice = "/dev/spidev0.0" ;
static uint8_t spiMode = 0 ;
static uint8_t spiBPW = 8 ;
static uint32_t spiSpeed = 5000000 ;
static uint16_t spiDelay = 0;
// MCP23S17 Registers
#define IOCON 0x0A
#define IODIRA 0x00
#define IPOLA 0x02
#define GPINTENA 0x04
#define DEFVALA 0x06
#define INTCONA 0x08
#define GPPUA 0x0C
#define INTFA 0x0E
#define INTCAPA 0x10
#define GPIOA 0x12
#define OLATA 0x14
#define IODIRB 0x01
#define IPOLB 0x03
#define GPINTENB 0x05
#define DEFVALB 0x07
#define INTCONB 0x09
#define GPPUB 0x0D
#define INTFB 0x0F
#define INTCAPB 0x11
#define GPIOB 0x13
#define OLATB 0x15
int spi_fd;
static uint8_t readByte (uint8_t reg)
{
uint8_t tx [4] ;
uint8_t rx [4] ;
struct spi_ioc_transfer spi ;
tx [0] = CMD_READ ;
tx [1] = reg ;
tx [2] = 0 ;
spi.tx_buf = (unsigned long)tx ;
spi.rx_buf = (unsigned long)rx ;
spi.len = 3 ;
spi.delay_usecs = spiDelay ;
spi.speed_hz = spiSpeed ;
spi.bits_per_word = spiBPW ;
ioctl (spi_fd, SPI_IOC_MESSAGE(1), &spi) ;
return rx [2] ;
}
static void writeByte (uint8_t reg, uint8_t data)
{
uint8_t spiBufTx [3] ;
uint8_t spiBufRx [3] ;
struct spi_ioc_transfer spi ;
spiBufTx [0] = CMD_WRITE ;
spiBufTx [1] = reg ;
spiBufTx [2] = data ;
spi.tx_buf = (unsigned long)spiBufTx ;
spi.rx_buf = (unsigned long)spiBufRx ;
spi.len = 3 ;
spi.delay_usecs = spiDelay ;
spi.speed_hz = spiSpeed ;
spi.bits_per_word = spiBPW ;
ioctl (spi_fd, SPI_IOC_MESSAGE(1), &spi) ;
}
/*spi_open
* - Open the given SPI channel and configures it.
* - there are normally two SPI devices on your PI:
* /dev/spidev0.0: activates the CS0 pin during transfer
* /dev/spidev0.1: activates the CS1 pin during transfer
*
*/
int spi_open(char* dev)
{
if((spi_fd = open(dev, O_RDWR)) < 0){
printf("error opening %s\n",dev);
return -1;
}
return 0;
}
int main(int argc, char* argv[])
{
unsigned char data = 0xAF;
if(argc <= 1){
printf("too few args, try %s /dev/spidev0.0\n",argv[0]);
return -1;
}
// open and configure SPI channel. (/dev/spidev0.0 for example)
if(spi_open(argv[1]) < 0){
printf("spi_open failed\n");
return -1;
}
writeByte (IODIRA, 0x00) ; // Port A -> Outputs
writeByte (GPIOA, 0xFF) ;
sleep (2);
writeByte (GPIOA, 0x00) ;

//writeByte (IPOLB, 0x01) ; // invert lsb
//writeByte (GPPUB, 0xFF) ; // enable pullups.
writeByte (IODIRB, 0xFF) ; // Port B -> Inputs
data = readByte (GPIOB) ;
printf("RECEIVED: %.2X\n",data);
close(spi_fd);
return 0;
}
Richard
Doing Unix since 1985.
The 9-25-2013 image of Wheezy can be found at:
http://downloads.raspberrypi.org/raspbian/images/raspbian-2013-09-27/2013-09-25-wheezy-raspbian.zip

User avatar
Grumpy Mike
Posts: 936
Joined: Sat Sep 10, 2011 7:49 pm
Location: Manchester (England England)
Contact: Website

Re: SPI MCP23s17 addressing

Tue Jun 25, 2013 6:07 pm

The issue is that regardless of the address of the MCP23S17, both chips respond.
How are you deducing this?

User avatar
Richard-TX
Posts: 1549
Joined: Tue May 28, 2013 3:24 pm
Location: North Texas

Re: SPI MCP23s17 addressing

Tue Jun 25, 2013 7:15 pm

When I connect a led to port a-1 of both chips, both LEDs light up and turn off simultaneously..
Richard
Doing Unix since 1985.
The 9-25-2013 image of Wheezy can be found at:
http://downloads.raspberrypi.org/raspbian/images/raspbian-2013-09-27/2013-09-25-wheezy-raspbian.zip

User avatar
Richard-TX
Posts: 1549
Joined: Tue May 28, 2013 3:24 pm
Location: North Texas

Re: SPI MCP23s17 addressing

Tue Jun 25, 2013 8:04 pm

I found out what is wrong.

I needed to set HAEN

writeByte (IOCON, 0x08) ; // set HW addressing.
Richard
Doing Unix since 1985.
The 9-25-2013 image of Wheezy can be found at:
http://downloads.raspberrypi.org/raspbian/images/raspbian-2013-09-27/2013-09-25-wheezy-raspbian.zip

User avatar
gordon@drogon.net
Posts: 2020
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: SPI MCP23s17 addressing

Wed Jun 26, 2013 6:34 pm

Richard-TX wrote:I found out what is wrong.

I needed to set HAEN

writeByte (IOCON, 0x08) ; // set HW addressing.
You may want to look at wiringPi - it has an extension for the mcp23s17 chips on the SPI bus and handles this situation - that command gets picked up by all the mcp23s17s concurrently, but after that, they then look at the 3-bit inputs - combine that with both select lines and in-theory you can have 16 chips giving 256 bits of GPIO...

the wiringPi extensions allows you to extend the normal pins with the GPIO chips, then you can use digitalRead()/digitalWrite() as normal just with the new base pin on the mcp23s17.

See: http://wiringpi.com/extensions/spi-mcp23s08-mcp23s17/ for the details an example.

-Gordon
--
Gordons projects: https://projects.drogon.net/

User avatar
Arjan
Posts: 265
Joined: Sat Sep 08, 2012 1:59 pm

Re: SPI MCP23s17 addressing

Wed Jun 26, 2013 7:03 pm

Hi Richard,

I have some other sample code available at : https://github.com/vanvught/OpenILDA/bl ... mcp23s08.c

It provides you the functions similar to Mike's library bcm2835_gpio_* functions :

*.mcp23s08_gpio_fsel(uint8_t pin, uint8_t mode);
*.mcp23s08_gpio_set(uint8_t pin);
*.mcp23s08_gpio_clr(uint8_t pin);

Mike's library can be found at : http://www.airspayce.com/mikem/bcm2835/

Regards, Arjan
http://www.raspberrypi-dmx.org/
Open Source DMX/RDM/MIDI/OSC/Art-Net/sACN solutions

User avatar
Richard-TX
Posts: 1549
Joined: Tue May 28, 2013 3:24 pm
Location: North Texas

Re: SPI MCP23s17 addressing

Sat Jun 29, 2013 7:47 am

Here is the corrected program. The command needed to enable HW addressing of the MCP23S17 is in bold.

/****************************************
* basic SPI demo for mcp23s17 with addressing
* now with interrupts.
*****************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdint.h>
/* change CMD_WRITE and CMD_READ addrs to match MCP23S17 address */
#define CMD_WRITE 0x40
#define CMD_READ 0x41
/* if MCP23S17 addr == 1 then cmd_write = 0x42 and cmd_read = 0x43 */
static char *spiDevice = "/dev/spidev0.0" ;
static uint8_t spiMode = 0 ;
static uint8_t spiBPW = 8 ;
static uint32_t spiSpeed = 5000000 ;
static uint16_t spiDelay = 0;

/* all of the MCP23S17 Registers */
#define IOCON 0x0A
#define IODIRA 0x00
#define IPOLA 0x02
#define GPINTENA 0x04
#define DEFVALA 0x06
#define INTCONA 0x08
#define GPPUA 0x0C
#define INTFA 0x0E
#define INTCAPA 0x10
#define GPIOA 0x12
#define OLATA 0x14
#define IODIRB 0x01
#define IPOLB 0x03
#define GPINTENB 0x05
#define DEFVALB 0x07
#define INTCONB 0x09
#define GPPUB 0x0D
#define INTFB 0x0F
#define INTCAPB 0x11
#define GPIOB 0x13
#define OLATB 0x15

int spi_fd;
static uint8_t readByte (uint8_t reg)
{
uint8_t tx [4] ;
uint8_t rx [4] ;
struct spi_ioc_transfer spi ;
tx [0] = CMD_READ ;
tx [1] = reg ;
tx [2] = 0 ;
spi.tx_buf = (unsigned long)tx ;
spi.rx_buf = (unsigned long)rx ;
spi.len = 3 ;
spi.delay_usecs = spiDelay ;
spi.speed_hz = spiSpeed ;
spi.bits_per_word = spiBPW ;
ioctl (spi_fd, SPI_IOC_MESSAGE(1), &spi) ;
return rx [2] ;
}
static void writeByte (uint8_t reg, uint8_t data)
{
uint8_t spiBufTx [3] ;
uint8_t spiBufRx [3] ;
struct spi_ioc_transfer spi ;
spiBufTx [0] = CMD_WRITE ;
spiBufTx [1] = reg ;
spiBufTx [2] = data ;
spi.tx_buf = (unsigned long)spiBufTx ;
spi.rx_buf = (unsigned long)spiBufRx ;
spi.len = 3 ;
spi.delay_usecs = spiDelay ;
spi.speed_hz = spiSpeed ;
spi.bits_per_word = spiBPW ;
ioctl (spi_fd, SPI_IOC_MESSAGE(1), &spi) ;
}
/*spi_open
* - Open the given SPI channel and configures it.
* - there are normally two SPI devices on your PI:
* /dev/spidev0.0: activates the CS0 pin during transfer
* /dev/spidev0.1: activates the CS1 pin during transfer
*
*/
int spi_open(char* dev)
{
if((spi_fd = open(dev, O_RDWR)) < 0){
printf("error opening %s\n",dev);
return -1;
}
return 0;
}
int main(int argc, char* argv[])
{
unsigned char data = 0xAF;
if(argc <= 1){
argv[1]=spiDevice;
}
// open and configure SPI channel. (/dev/spidev0.0 for example)
if(spi_open(argv[1]) < 0){
printf("spi_open failed\n");
return -1;
}

writeByte (IODIRA, 0x00) ; // Set Port A -> Outputs
writeByte (IOCON, 0x08) ; // enable MCP23S17 addresses
/* writeByte (IOCON, 0x0a) ; /* enable HW addresses + flip interrupt polarity */
writeByte (GPIOA, 0xFF) ; /* turn PORT A outputs "on" */
sleep (1); /* */
writeByte (GPIOA, 0x00) ; /* turn outputs "off" */


//writeByte (IPOLB, 0x01) ; // invert lsb
writeByte (GPINTENB, 0xFF) ; /* enable int on change . */
/* writeByte (INTCONB, 0xFF) ; /* int on change comp to defval. */
/* writeByte (DEFVALB, 0xff) ; /* set Default value for interrupt . */
/* writeByte (GPPUB, 0xFF) ; /* enable pullups on Port B */
writeByte (IODIRB, 0xFF) ; /* Port B -> Inputs */
data = readByte (GPIOB) ; /* */
printf("RECEIVED: %.2X\n",data);
/* printf ("sleep 1 before read of INTCAPB\n"); /* */
/* sleep (1); /* */
data = readByte (INTCAPB) ;
printf("INTCAPB: %.2X\n",data);
data = readByte (GPIOB) ;
printf("GPIOB: %.2X\n",data);
data = readByte (INTFB) ;
printf("INTFB: %.2X\n",data);
close(spi_fd);
return 0;
}
Last edited by Richard-TX on Sat Jun 29, 2013 12:46 pm, edited 2 times in total.
Richard
Doing Unix since 1985.
The 9-25-2013 image of Wheezy can be found at:
http://downloads.raspberrypi.org/raspbian/images/raspbian-2013-09-27/2013-09-25-wheezy-raspbian.zip

User avatar
gordon@drogon.net
Posts: 2020
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: SPI MCP23s17 addressing

Sat Jun 29, 2013 7:53 am

How to use wiringPi to setup an mcp23s17:

This outputs a 10-bit incrementing number on the first 10 output pins on an mcp12s17, and if pin 15 is held low (input), it stops for the duration.

Code: Select all

/*
 * 23s17.c:
 *      WiringPi test with an MCP23S17 SPI GPIO expander chip
 *
  ***********************************************************************
 */

#include <stdio.h>
#include <wiringPi.h>
#include <mcp23s17.h>

#define BASE    123

int main (void)
{
  int i, bit ;

  wiringPiSetup () ;
  mcp23s17Setup (BASE, 0, 0) ;

  printf ("Raspberry Pi - MCP23S17 Test\n") ;

  for (i = 0 ; i < 10 ; ++i)
    pinMode (BASE + i, OUTPUT) ;

  pinMode         (BASE + 15, INPUT) ;
  pullUpDnControl (BASE + 15, PUD_UP) ;

  for (;;)
  {
    for (i = 0 ; i < 1024 ; ++i)
    {
      for (bit = 0 ; bit < 10 ; ++bit)
        digitalWrite (BASE + bit, i & (1 << bit)) ;
      delay (5) ;
      while (digitalRead (BASE + 15) == 0)
        delay (1) ;
    }
  }
  return 0 ;
}
writeByte and readByte in the 2.1 release in a week or so.

-Gordon
--
Gordons projects: https://projects.drogon.net/

User avatar
Richard-TX
Posts: 1549
Joined: Tue May 28, 2013 3:24 pm
Location: North Texas

Re: SPI MCP23s17 addressing

Sat Jun 29, 2013 12:36 pm

Gordon and Arjan,

With all respect I appreciate the links to the libraries but I chose not to use them for a few reasons.
1 - if I were to use them I would learn little.
2 - This application has to be fast and a library may add latency.
3 - It is uncertain if a library would be suitable for my application.

My problem is that sometimes I miss things in the published documents and this is one example.

Adding 16 mcp23S17 chips to the SPI bus is almost trivial. I have experimentally added 8 MCP23017 chips to each of the I2C buses with no issues.
Richard
Doing Unix since 1985.
The 9-25-2013 image of Wheezy can be found at:
http://downloads.raspberrypi.org/raspbian/images/raspbian-2013-09-27/2013-09-25-wheezy-raspbian.zip

Return to “Interfacing (DSI, CSI, I2C, etc.)”