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: 3257
Joined: Wed Jun 12, 2013 12:41 pm
Location: Stuttgart Germany

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: 42481
Joined: Sun Jun 16, 2013 11:19 pm
Location: A small cave in deepest darkest Basingstoke, UK

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));
Languages using left-hand whitespace for syntax are ridiculous

DMs sent on https://twitter.com/DougieLawson or LinkedIn will be answered next month.
Fake doctors - are all on my foes list.

The use of crystal balls and mind reading is prohibited.

ghp
Posts: 3257
Joined: Wed Jun 12, 2013 12:41 pm
Location: Stuttgart Germany

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: 2024
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK

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.)”