Arduino DUE and MCP23S17 - software SPI and hardware SPI

Hello to everyone and greetings from Romania.
I am posting a solution for software SPI and MPC23S17 that avoids libraries.
The software problem is solved and I am posting it here for everybody to see
I have issues with hardware SPI. As I do not require high speed transfer, I work with software transfer.

For now I am posting software SPI output transfer. I have issues with software input transfer and I will post it here as soon as I have it working.


  • 1 x Arduino DUE;
  • 1 x MCP23S17;
  • Datasheets that people are lazy to read;
  • 1 x two inputs digital oscilloscope to view and manually interpret the data stream with pen and paper.

My project is to output data on MCP23S17 via software. The GPIO pins are then used to select other MCP23S17 and i/o data via hardware SPI. Soft SPI works, hard SPI does not.

Hardware SPI test first, done by the book and NOT WORKING. Copy-paste from official web site.

void setup() {
  // Set clock divider on pin 10 to 84
  SPI.setClockDivider(10, 84);

void loop() {
                delay(200);  // pause to see
                SPI.transfer(10,0xFF);  //transfer the number 255
                 SPI.transfer(10, 0x00); // transfer zero
} // back to the zoo

I connected the oscilloscope on the port marked as SPI. MISO stays low, MOSI stays high, RST stays HIGH, clock stays HIGH.
NO CHANGES. No pulses. Nothing.
I thought I burned the port because my MCPs are powered with 5 volts, so I tested the pins:

void setup() {
   pinMode(74, OUTPUT);  // pin 74 is MISO, see arduino due pinout
   pinMode(75, OUTPUT); //pin 75 is MOSI
   pinMode(76, OUTPUT) // pin 76 is SCK
   pinMode(10, OUTPUT) // slave select pin 10, as they say on the web site

void loop() {


Oscilloscope shows pulses, so the CPU is OK.


Here is the example code to output data on MCP23S17 GPIOs. You must be careful in function setup() where I defined the pins.
No libraries. Just shiftin/shiftout.
Tested on Arduino Mega and Arduino Due. Just copy-paste, compile, do the wiring and start playing.

// MCP23S17 registers setup - (do not) see data sheet (if lazy)
const int MCPWRT = 0x40;    //Command to write in MCP23S17 Address $00
const int MCPRED = 0x41;    //Command to read from MCP23S17 Address $00
const int IOCON  = 0x0A;    //Register address to MCP23S17 Config
const int IODIRA = 0x00;    //Register address to MCP23S17 PortA(1)
const int IODIRB = 0x01;    //Register address to MCP23S17 PortB(2)
const int GPIOA  = 0x12;    //Register address to MCP23S17 GPIOA(1)
const int GPIOB  = 0x13;    //Register address to MCP23S17 GPIOB(2)
const int OLATA  = 0x14;    //Register address to MCP23S17 LATCH-A(1)
const int OLATB  = 0x15;    //Register address to MCP23S17 LATCH-B(2)

// SPI software variables
// Warning - the software SPI pins must be defined
// init with zeros so the internal memory is alocated at startup
int  DataPin = 0, DataInPin=0, ClockPin = 0;
byte DataOut=0, MCPReg=0;
int softreset=0;
int SEL_PEX0=0, SEL_PEX1=0;

int i=0;   // stuff I want to send to MCP23S17 

// send data to MCP23S17 (software SPI)

void soft_send (int MCPREG, int DATAOUT, int SS) {   // 
                                                  digitalWrite(SS, LOW);   // select the chip
                                                  shiftOut(DataPin, ClockPin, MSBFIRST, MCPWRT);  // tell it I wish to send
                                                  shiftOut(DataPin, ClockPin, MSBFIRST, MCPREG); // tell it where I wish to send
                                                  shiftOut(DataPin, ClockPin, MSBFIRST, DATAOUT); // and what I wish to send
                                                  digitalWrite(SS, HIGH);                                                  

void init_soft_bus() {
            // define the Arduino Due pins
             softreset = 14;   // software SPI reset pin
             DataPin = 20;  // pin 20,  MOSI
             ClockPin = 18; // pin 18, SCK
             DataInPin = 19; // pin 19, MISO
             SEL_PEX0=16;   // Select one MCP23S17 Port EXpander
             SEL_PEX1=17;   // Select another MCP23s17 Port EXpander

             pinMode(softreset, OUTPUT);             // reset is an output pin
             pinMode(DataPin, OUTPUT);            // data output pin - output
             pinMode(DataInPin, INPUT);        // data input pin - this is an input for incoming data
             pinMode(ClockPin, OUTPUT);     // again output - I have to pulse the MCPs in order to get them working
             pinMode(SEL_PEX0, OUTPUT);   // select pins also outputs
             pinMode(SEL_PEX1, OUTPUT);
             digitalWrite(softreset, HIGH);    // put the reset in high mode
             digitalWrite(SEL_PEX0, LOW);  // low the select pins
             digitalWrite(SEL_PEX1, LOW);
             digitalWrite(SEL_PEX0, HIGH);  // then high them 
             digitalWrite(SEL_PEX1, HIGH);
             digitalWrite(softreset, LOW);   // reset the MCPs
             digitalWrite(softreset, HIGH);  // and signal them to be ready for transfer

            soft_send(IOCON, 0x20, SEL_PEX0);    // init PEX0
             soft_send(IODIRA, 0x00, SEL_PEX0);   // PEX0 PORTA = OUTPUT
             soft_send(IODIRB, 0x00, SEL_PEX0);  // PEX0 PORTB = OUTPUT
             soft_send(IOCON, 0x20, SEL_PEX1);   // init PEX1
             soft_send(IODIRA, 0x00, SEL_PEX1);  // PEX1 PORTA = output
             soft_send(IODIRB, 0x00, SEL_PEX1);  // PEX1 PORTB = output

            // read datasheet or trust this

void setup() {
                init_soft_bus();   // configure the MCP23S17 for the GPIOA = output and GPIOB = output

void loop() {
             delay(200); // pause to see this on oscilloscope
           // send data to port expander 0, output latch A
             soft_send(OLATA, i, SEL_PEX0);   // send "i" to output latch A (GPIOA) on the SELected MCP

          // send data to port expander 0, output latch B
             soft_send(OLATB, i, SEL_PEX0);   // send "i" to output latch B (GPIOB) on the SELected MCP
             i = i + 1;
             if (i == 256) i=0;