[SOLVED] Using SPI on Arduino Nano to control a shift register (TPIC6B595)

I'm using a TPIC6B595 to control a 12V RGB LED strip. It's wired with #G to ground, #SRCLR to +5V, and RCK/SRCK/SER IN to the Arduino Nano (on pins 10/13/11 respectively, which should be #CS/SCLK/MOSI in SPI mode).

This program using shiftOut does work (cycles between blue/green/red, as expected):

#define latchPin 10
#define dataPin 11
#define clockPin 13

//             BGR
#define valA 0b100
#define valB 0b010
#define valC 0b001

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

void go(byte b) {
  digitalWrite(latchPin, LOW);
  digitalWrite(clockPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, b);
  digitalWrite(latchPin, HIGH);

  delay(1000);
}

void loop() {
  go(valA);
  go(valB);
  go(valC);
}

This program using SPI, based on the few examples I could find, is meant to be equivalent but doesn't work — it flashes between white and all-dark every second instead:

#include <SPI.h>

#define latchPin 10

//             BGR
#define valA 0b100
#define valB 0b010
#define valC 0b001

void setup() {
  pinMode(latchPin, OUTPUT);
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
}

void go(byte b) {
  SPI.transfer(b);
  digitalWrite(latchPin, LOW);
  digitalWrite(latchPin, HIGH);

  delay(1000);
}

void loop() {
  go(valA);
  go(valB);
  go(valC);
}

I was confused about why the digitalWrite(latchPin, [...]); lines are necessary (shouldn't SPI.transfer take pin 10 low at the start and high at the end?), but removing them caused the lights to stay white, and moving the LOW line above SPI.transfer gave white-dark cycling again.

The white-dark cycling occurs even if I send only one value repeatedly (e.g. comment out the second two calls to go).

What's the more-correct way to use SPI here?

No, SPI does not operate the SS line in master mode. That's because you're free to connect multiple slaves (and thus need multiple SS lines) to a single master.

I big difference I see, with shiftOut() you make latch LOW before you start sifting and with SPI only after. I don't know if the TPIC6B595 cares but I do know that

  digitalWrite(latchPin, LOW);
  digitalWrite(latchPin, HIGH);

Makes a very very very small pulse. Probably to small.

I'm working on a project driving 12V strips with tpic6b595 and SPI at the moment, and its working. I can post example code later. But, right now, I can't see a problem in your code.

How much LED strip are you hoping to drive with that TPIC6B595? 350mA per channel doesn't get you very much LED strip...

Here's my working code example:

#include <SPI.h>

#define LATCH D8

SPISettings tpic6c595(10000000UL, MSBFIRST, SPI_MODE0);

byte digitPattern[10] = {
  0b0111111,
  0b0000110,
  0b1011011,
  0b1001111,
  0b1100110,
  0b1101101,
  0b1111101,
  0b0000111,
  0b1111111,
  0b1101111
};

void setup() {
  pinMode(LATCH, OUTPUT);
  Serial.begin(115200);
  SPI.begin();
  SPI.beginTransaction(tpic6c595);
}

void loop() {
  for (int i = 0; i <= 9; i++) {
    unsigned long startTime = micros();
    SPI.transfer(digitPattern[i]);
    Serial.println(micros() - startTime);
    digitalWrite(LATCH, HIGH);
    digitalWrite(LATCH, LOW);
    delay(500);
  }

}

As you can see, I change the latch from high to low with no delay between. What's more, I am running this on a esp8266 @ 80MHz.

*** EDIT **** You have no SPI.begin(). Maybe that's why your code does not work correctly? I'm not certain that using SPI.beginTransaction() means you don't also need to use SPI.begin().

Ahh, goo point! Yeah, SPI.begin() and SPI.beginTransaction() do different things. SPI.begin() actually set's up the hardware where SPI.beginTransaction() only sets the speed and mode and locks SPI from interrupts.

Adding SPI.begin(); was sufficient to fix it. Thanks!