Shift out using SPI doesn't work.

Actually, I already questioned this on Arduino StackExchange,
but got nothing so I'm asking this again ;-(

I made a sample using two 74HC595 on Arduino UNO.
So I did that like this:


(There isn't any resistor on that image, but there are 220ohm resistor on every LEDs.)
And uploaded this code.

//1.6us+62.5ns
#include <SPI.h>

#define sbi(port, bit) (port) |= (1 << (bit))
#define cbi(port, bit) (port) &= ~(1 << (bit))

int latchPin = 12;
int clockPin = 13;
int dataPin = 11;

byte data[]={ 0b10101010,0b11001100 };

void _595_out() {
    cbi(PORTB, 4);
    SPI.transfer(data[0] );
    SPI.transfer( data[1] );
    sbi(PORTB, 4);
}

void setup() {
    pinMode(latchPin, OUTPUT);
    pinMode(clockPin, OUTPUT);
    pinMode(dataPin, OUTPUT);

    SPI.setClockDivider(SPI_CLOCK_DIV2);
    SPI.setBitOrder(MSBFIRST);
    SPI.setDataMode(SPI_MODE0);
    SPI.begin();
}

void loop() {
    _595_out();
}

And this worked as I expected. But, when I changed sketch like this,

//1.6us+62.5ns
#include <SPI.h>

#define sbi(port, bit) (port) |= (1 << (bit))
#define cbi(port, bit) (port) &= ~(1 << (bit))
#define CHIPNO 2
int latchPin = 12;
int clockPin = 13;
int dataPin = 11;

byte data[]={ 0b10101010,0b11001100 };

void _595_out() {
    cbi(PORTB, 4);
    SPI.transfer(data[0]);
    SPI.transfer(data[1]);
    sbi(PORTB, 4);
}

void setup() {
    pinMode(latchPin, OUTPUT);
    pinMode(clockPin, OUTPUT);
    pinMode(dataPin, OUTPUT);

    SPI.setClockDivider(SPI_CLOCK_DIV2);
    SPI.setBitOrder(MSBFIRST);
    SPI.setDataMode(SPI_MODE0);
    SPI.begin();
    Serial.begin(9600);
}

void loop() {
    for(unsigned int z = 0; z < 65535; z++) {
        data[0] = z & 255;
        data[1] = (z & 65280) >> 8;
        Serial.println("A");
        Serial.print(data[0]);
        Serial.print("/");
        Serial.println(data[1]);
        Serial.println("B");
        _595_out();
        Serial.println("C");
        delay(500);
    }
}

74HC595s aren't working. Any LEDs aren't on.
Printing Serial works well. Only 74HC595s aren't working.
And Removing all Serial-related thing doesn't solved this problem.
Where did I do wrong?

MUSoftware:
And uploaded this code.

int latchPin = 12;

int clockPin = 13;
int dataPin = 11;



Where did I do wrong?

You can't use the MISO pin as your SlaveSelect (latch) pin. Try using Pin 10 which is the default SlaveSelect for SPI.

Thank you, johnwasser! Moving latch to Pin10(PORTB 2) fixed this.
But, don't know why can't I use that and why did first code work :stuck_out_tongue:

Even if you don't use pin 10 for chip select, it must be set to OUTPUT for SPI to work as a master.

All AVR based boards have an SS pin that is useful when they act as a slave controlled by an external master. Since this library supports only master mode, this pin should be set always as OUTPUT otherwise the SPI interface could be put automatically into slave mode by hardware, rendering the library inoperative.

Try this. I simplified it some, got rid of the default SPI settings, put the SPI clock after SPI.begin, made D10 an output (needed to be SPI master), and got rid of pin assignments that SPI.begin takes care of. You should have the shift register clock (SRCLK) on D13, the data to the first device on D11, and the output latch (SRCK) on D4, but I would use D10 as it has to be an output anyway, why not use it?

//1.6us+62.5ns
 #include <SPI.h>
 
 byte ssPin = 10; // must be output for SPI master
 byte latchPin = 4;


 byte data[]={ 0b10101010,0b11001100 };
 
 void _595_out() {
 PORTD = PORTD & 0b11101111; // D4 low
 SPI.transfer(data[0]);
 SPI.transfer(data[1]);
 PORTD = PORTD | 0b00010000; // D4 high
 }
 
 void setup() {
 pinMode(latchPin, OUTPUT);

 pinMode (ssPin, OUTPUT);


 SPI.begin();

SPI.setClockDivider(SPI_CLOCK_DIV2); // these follow SPI.begin().
 Serial.begin(9600);
 }
 
 void loop() {
 for(unsigned int z = 0; z < 65535; z++) {
 data[0] = z & 255;
 data[1] = (z & 65280) >> 8;
 Serial.println("A");
 Serial.print(data[0]);
 Serial.print("/");
 Serial.println(data[1]);
 Serial.println("B");
 _595_out();
 Serial.println("C");
 delay(500);
 }
 }

groundfungus, That's quite important information to me :slight_smile:
As johnwasser and CrossRoads said, changing latch to Pin10(PORTB 2) worked well.
And also I simplified the code thanks to CrossRoads.

Thank you all of you guys!

That's not really a SPI device. Wondering if shiftOut() would be preferable:

74HC595 is not really an SPI device? Sure it is. I use SPI.transfer() with 595 shift registers exclusively, it is much faster to send data out than shiftOut().
Especially at 8 MHz:
SPI.setClockDivider(SPI_CLOCK_DIV2);
shiftOut() can't touch that.

gfvalvo, shiftOut is toooooo slow on my project so I had to use SPI.
In a theory, using SPI and that code only takes 1.6us+62.5ns to send all data.

CrossRoads:
74HC595 is not really an SPI device? Sure it is. I use SPI.transfer() with 595 shift registers exclusively, it is much faster to send data out than shiftOut().
Especially at 8 MHz:
SPI.setClockDivider(SPI_CLOCK_DIV2);
shiftOut() can't touch that.

Yea, you’re right. I was incorrectly thinking of more specialized SPI protocols where specific data needs to be shifted into MISO right after commands go out on MOSI.

You can send data out in 17 clocks per byte if you want to go even faster:

 PORTD = PORTD & 0b11101111; // D4 low
 SPDR = data[0];nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; // 17 clocks!
 SPDR = data[1];nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
 PORTD = PORTD | 0b00010000; // D4 high

Need this at the top of your sketch

#define nop asm volatile ("nop")

MUSoftware:
Thank you, johnwasser! Moving latch to Pin10(PORTB 2) fixed this.
But, don't know why can't I use that and why did first code work :stuck_out_tongue:

Because the SPI hardware uses all of SS,MISO,MOSI,SCLK pins when operational. That's
the SPI bus.