fern2411
Posts: 20
Joined: Wed Oct 09, 2013 4:38 am

SPI communication with MCP23S17 not working

Mon Sep 21, 2015 1:43 am

Hi all,

I am trying interface MCP23S17 with RPI using SPI, i want to lit the LEDs connected to port A of MCP23S17.
I am using the following C Code.

C Code for SPI interface with 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>

/* change CMD_WRITE and CMD_READ addrs to match MCP23S17 address */
#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;

/* 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 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;
}
else
{
printf("device: %s\n", spiDevice);
}

writeByte (0X0A, 0x80) ; // enable MCP23S17 addresses
writeByte (0X00, 0x00) ; // Set Port A -> Outputs
writeByte (0X14, 0xFF) ; // turn PORT A outputs "on"

close(spi_fd);
return 0;
}


After running this code no LED connected to port A is turn on.
I am not understanding, where i am going wrong ?

Any help will be appreciated.

ghp
Posts: 1558
Joined: Wed Jun 12, 2013 12:41 pm
Location: Stuttgart Germany
Contact: Website

Re: SPI communication with MCP23S17 not working

Mon Sep 21, 2015 5:01 am

Hello,
for readability, you should use the defines made early in the code:
writeByte (0X0A, 0x80) ; // enable MCP23S17 addresses
writeByte (0X00, 0x00) ; // Set Port A -> Outputs
writeByte (0X14, 0xFF) ; // turn PORT A outputs "on"

goes to
writeByte (IOCON , 0x80) ; // enable MCP23S17 addresses
writeByte (IODIRA, 0x00) ; // Set Port A -> Outputs
writeByte (OLATA, 0xFF) ; // turn PORT A outputs "on"

The problem is the IOCON write where you set the bank flag to 1, but your addresses are for bank flag 0. Try writing a bank flag 0, so the commands go for
writeByte (IOCON , 0x00) ; // Bank 0

Hope this helps,
Gerhard

fern2411
Posts: 20
Joined: Wed Oct 09, 2013 4:38 am

Re: SPI communication with MCP23S17 not working

Thu Sep 24, 2015 4:18 am

Thanks for the reply.

I have done the following modification in the code
writeByte (IOCON , 0x00) ; // Bank 0

After still, i am not able to get the LEDs turn on.
I am using Raspberry Pi 2 and the spi module is spi_bcm2835.
Is there any other way to get it working?

User avatar
DougieLawson
Posts: 39799
Joined: Sun Jun 16, 2013 11:19 pm
Location: A small cave in deepest darkest Basingstoke, UK
Contact: Website Twitter

Re: SPI communication with MCP23S17 not working

Thu Sep 24, 2015 7:46 pm

Change

Code: Select all

  struct spi_ioc_transfer spi;
to

Code: Select all

  struct spi_ioc_transfer spi;
  memset (&spi, 0, sizeof(spi));
Note: Any requirement to use a crystal ball or mind reading will result in me ignoring your question.

Criticising any questions is banned on this forum.

Any DMs sent on Twitter will be answered next month.
All fake doctors are on my foes list.

ghp
Posts: 1558
Joined: Wed Jun 12, 2013 12:41 pm
Location: Stuttgart Germany
Contact: Website

Re: SPI communication with MCP23S17 not working

Thu Sep 24, 2015 8:01 pm

Hello,
difficult to do remote debugging
- check wiring. Reset to high, address lines to low, gnd connected. SI to MOSI, SO to MISO; chip not hot or smoking.
- do you use GPIO10, 9, 11, 8 for the SPI ?
- doublecheck it is a MCP23S17 and not a MCP23017
- LED with series resistor to GND ?

Perhaps it helps to see some other code. It is in python, and was my first example to get this device running

Code: Select all

import spidev
import time

class MCP23S17:
    READ = 1
    WRITE = 0
    # Bank 0 Addresses
    IODIRA = 0x00
    IODIRB = 0x01
    
    IODIR_BIT_READ = 1
    IODIR_BIT_WRITE = 0
    
    GPIOA = 0x12
    GPIOB = 0x13
    
    max_speed_hz = None
    
    portA_value = None
    portB_value = None
   
    
    def __init__(self, bus=0, device=1, addr=0):
        self.spi = spidev.SpiDev()
        self.spi.open(bus, device)
            
        self.max_speed_hz = 10000000
        self.addr = addr
    
        self.portA_value = 0
        self.portB_value = 0
            
    def clearPortA(self):

        addr1 = 0x40 | self.addr << 1 | self.WRITE
        addr2 = self.GPIOA
        self.portA_value = 0
        
        self.spi.max_speed_hz = self.max_speed_hz
        self.spi.xfer2 (list([addr1, addr2, self.portA_value]))

    def clearPortB(self):

        addr1 = 0x40 | self.addr << 1 | self.WRITE
        addr2 = self.GPIOB
        self.portB_value = 0
        
        self.spi.max_speed_hz = self.max_speed_hz
        self.spi.xfer2 (list([addr1, addr2, self.portB_value]))

    def setPortA(self, port, value):

        addr1 = 0x40 | self.addr << 1 | self.WRITE
        addr2 = self.GPIOA
        if value:
            self.portA_value |= (1 << port)
        else:
            self.portA_value &= ~(1 << port)
        
        self.spi.max_speed_hz = self.max_speed_hz
        self.spi.xfer2 (list([addr1, addr2, self.portA_value]))

    def setPortA_dir(self, value):
        addr1 = 0x40 | self.addr << 1 | self.WRITE
        addr2 = self.IODIRA
        cmdValue = value;
        
        self.spi.max_speed_hz = self.max_speed_hz
        self.spi.xfer2 (list([addr1, addr2, cmdValue]))
        
    def setPortB_dir(self, value):
        addr1 = 0x40 | self.addr << 1 | self.WRITE
        addr2 = self.IODIRB
        cmdValue = value;
        
        self.spi.max_speed_hz = self.max_speed_hz
        self.spi.xfer2 (list([addr1, addr2, cmdValue]))


    def setPortB(self, port, value):
        addr1 = 0x40 | self.addr << 1 | self.WRITE
        addr2 = self.GPIOB
        if value:
            self.portB_value |= (1 << port)
        else:
            self.portB_value &= ~(1 << port)
        
        self.spi.max_speed_hz = self.max_speed_hz
        self.spi.xfer2 (list([addr1, addr2, self.portB_value]))

    def getPortB(self, port):
        addr1 = 0x40 | self.addr << 1 | self.READ
        addr2 = self.GPIOB

        self.spi.max_speed_hz = self.max_speed_hz
        r = self.spi.xfer2 (list([addr1, addr2, 0]))
        if r[2] & (1 << port) != 0:
            return True
        return False
   
   
mcp23s17 = MCP23S17(0,0,0) 

mcp23s17.setPortA_dir( 0b00000000 )  
mcp23s17.setPortB_dir( 0b00000000 )

t = 0.2
while True:
    
    mcp23s17.clearPortB()
    
    for i in range(8):
        mcp23s17.clearPortA() 
        mcp23s17.setPortA(i, 1)
        time.sleep(t)
    
    mcp23s17.clearPortA()
    
    for i in range(8):
        mcp23s17.clearPortB() 
        
        mcp23s17.setPortB(i, 1)
        time.sleep(t)
         
    t -= 0.01
    if t < 0.04:
        t = 0.2     
 
      
As I said, not a perfect sample code. I do not set IOCON here, as 0x00 is default for this register.

Hope it helps
Datasheet is http://ww1.microchip.com/downloads/en/D ... 21952b.pdf

Gerhard

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

Re: SPI communication with MCP23S17 not working

Thu Sep 24, 2015 8:55 pm

I appreciate that you may ultimately not want to use wiringPi, but the gpio utility can be helpful here as it can poke stuff like this from the command line:

Code: Select all

  gpio -x mcp23s17:200:0 mode 200 out
  gpio -x mcp23s17:200:0 write 200 1
That makes the first pin of port A on the mcp23s17 appear to be pin 200 to wiringPi, sets the mode to output and sets it to 1.


Then check the source code (wiringPi/mcp23s17.c) to see how its doing it and compare with your code.

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

fern2411
Posts: 20
Joined: Wed Oct 09, 2013 4:38 am

Re: SPI communication with MCP23S17 not working

Fri Sep 25, 2015 5:46 am

Thanks all for reply.

I rewired the MCP23S17 on breadboard and double checked the wiring.

I used Gordon's code to check the wiring and mcp chip status.

>> gpio -x mcp23s17:200:0 mode 200 out
>> gpio -x mcp23s17:200:0 write 200 1


After this, the LED connected to GPA0 turned ON. This made me conclude that the wiring is proper and mcp chip is OK.

I went through wiringPi library. I modified my earlier C code and used wiringPi in it.
I executed it and the spi interface with MCP23S17 is working. The working C code is as follows.

Code: Select all

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

// Register Addresses of MCP23S17
#define CMD_WRITE 	0x40
#define IOCON 		0x0A
#define IODIRA		0x00
#define OLATA		0x14

int main (void)
{
	uint8_t data[3];
	int i, speed, csPin;
	speed = 500000;		//CLK setting, set to 500KHz 
	csPin = 5;
	
	// Init
	wiringPiSetup();
	wiringPiSetupGpio();
	pinMode(csPin, OUTPUT);
	wiringPiSPISetupMode(0, speed, 0);
	
	digitalWrite(csPin, HIGH); // CS pin high
	
	printf("Raspberry Pi - MCP23S17\n");
	
	data[0] = CMD_WRITE;
	data[1] = IOCON;
	data[2] = 0x00;
	
	digitalWrite(csPin, LOW); // CS pin low
	wiringPiSPIDataRW(0, data, 3);	//Sending SPI data
	delay(1);
	digitalWrite(csPin, HIGH); // CS pin high
	delay(1);

	data[0] = CMD_WRITE;
	data[1] = IODIRA;
	data[2] = 0x00;		//Setting all PortA pins as outputs
	
	digitalWrite(csPin, LOW); // CS pin low
	wiringPiSPIDataRW(0, data, 3);	//Sending SPI data
	delay(1);
	digitalWrite(csPin, HIGH); // CS pin high
	delay(1);

	data[0] = CMD_WRITE;
	data[1] = OLATA;
	data[2] = 0x80;		//Setting GPA7 High

	digitalWrite(csPin, LOW); // CS pin low
	wiringPiSPIDataRW(0, data, 3);	//Sending SPI data
	delay(1);
	digitalWrite(csPin, HIGH); // CS pin high
	delay(1);
	
	printf("done\n");
}
I use Raspberry Pi B+ and the spi module is spi_bcm2708.

fern2411
Posts: 20
Joined: Wed Oct 09, 2013 4:38 am

Re: SPI communication with MCP23S17 not working

Sat Sep 26, 2015 6:32 am

Hi Gordon

I used interface MCP23S17 to Raspberry Pi 2 it has spi module spi_bcm2835.
I used the following code to test the setup, but no output.

>> gpio -x mcp23s17:200:0 mode 200 out
>> gpio -x mcp23s17:200:0 write 200 1


But when i used the same code on Raspberry Pi B+ which has spi module spi_bcm2708, there it gives proper output.
Also my earlier C code which worked successfully on Raspberry Pi B+, is not giving any output on Raspberry Pi 2.

Do i have to update/install any library on Raspberry Pi 2 ?
Is spi module spi_bcm2835 not working ?

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