Need help on using SPI library

Status
Not open for further replies.

Po Ting

Well-known member
Hello community :D

I'm a hobbyist in Taiwan , my project is trying to make POV props (with as many pixels as possible) for flow art
here's a quick data flow : micro SD - > (MCU)teensy 3.5 -> LED driver -> next picture cycle

My POV project uses from teensy 3.2 or 3.5
reads pictures (.bmp) with time settings (.txt) from SD card (via SdFat/ SDIO)

and send the data to LED arrays
I uses dotstars (apa102) for last few months,
and now decides to make my own LED array for higher density LED layout.

So I started on modifying the example/library from Adafruit TLC5947 tutorial, with their TLC5947 breakout hardware
https://learn.adafruit.com/tlc5947-tlc59711-pwm-led-driver-breakout/overview

the modified example tlc5947test.ino worked fine , with hardware connects
clock to teensy pin 13, data to 11, oe(blank) to 15 and latch to 10
and a external Lipo battery for the tlc5947 breakout , in case the USB power couldn't afford the LEDs.
-> I'm using arduino IDE v1.80 with teensyduino v1.34

as I later planned to use hardware SPI, the pins were same to CS, MOSI & SCK

Code:
/*************************************************** 
  This is an example for our Adafruit 24-channel PWM/LED driver

  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/products/1429

  These drivers uses SPI to communicate, 3 pins are required to  
  interface: Data, Clock and Latch. The boards are chainable

  Adafruit invests time and resources providing this open source code, 
  please support Adafruit and open-source hardware by purchasing 
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.  
  BSD license, all text above must be included in any redistribution
 ****************************************************/

#include "Adafruit_TLC5947.h"

// How many boards do you have chained?
#define NUM_TLC5974 1
#define clock 13  
#define data 11
#define oe  15 
#define latch  10 
//#define oe  -1  // set to -1 to not use the enable pin (its optional)

Adafruit_TLC5947 tlc = Adafruit_TLC5947(NUM_TLC5974, clock, data, latch);

void setup() {
  Serial.begin(9600);
  
  Serial.println("TLC5974 test");
  tlc.begin();
  if (oe >= 0) {
    pinMode(oe, OUTPUT);
    digitalWrite(oe, LOW);
  }
}

void loop() {
  colorWipe(4095, 0, 0, 100); // "Red" (depending on your LED wiring)
  delay(200);
  colorWipe(0, 4095, 0, 100); // "Green" (depending on your LED wiring)
  delay(200);
  colorWipe(0, 0, 4095, 100); // "Blue" (depending on your LED wiring)
  delay(200);
  rainbowCycle(10);
}


// Fill the dots one after the other with a color
void colorWipe(uint16_t r, uint16_t g, uint16_t b, uint8_t wait) {
  for(uint16_t i=0; i<8*NUM_TLC5974; i++) {
      tlc.setLED(i, r, g, b);
      tlc.write();
      delay(wait);
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
  uint32_t i, j;

  for(j=0; j<4096; j++) { // 1 cycle of all colors on wheel
    for(i=0; i< 8*NUM_TLC5974; i++) {
      Wheel(i, ((i * 4096 / (8*NUM_TLC5974)) + j) & 4095);
    }
    tlc.write();
    delay(wait);
  }
}

// Input a value 0 to 4095 to get a color value.
// The colours are a transition r - g - b - back to r.
void Wheel(uint8_t ledn, uint16_t WheelPos) {
  if(WheelPos < 1365) {
    tlc.setLED(ledn, 3*WheelPos, 4095 - 3*WheelPos, 0);
  } else if(WheelPos < 2731) {
    WheelPos -= 1365;
    tlc.setLED(ledn, 4095 - 3*WheelPos, 0, 3*WheelPos);
  } else {
    WheelPos -= 2731;
    tlc.setLED(ledn, 0, 3*WheelPos, 4095 - 3*WheelPos);
  }
}


and from the Adafruit_TLC5947.cpp and the TLC5947 schematics,

I guess the TLC5947 driver chip reads 12 bit a channel (a color), and 24 channel per driver chip,
that makes 8 pixels(3 channel per RGB-LED) per driver chip.

Code:
void Adafruit_TLC5947::write(void) {
  digitalWrite(_lat, LOW);
  // 24 channels per TLC5974
  for (int16_t c=24*numdrivers - 1; c >= 0 ; c--) {
    // 12 bits per channel, send MSB first
    for (int8_t b=11; b>=0; b--) {
      digitalWrite(_clk, LOW);
      
      if (pwmbuffer[c] & (1 << b))  
        digitalWrite(_dat, HIGH);
      else
        digitalWrite(_dat, LOW);

      digitalWrite(_clk, HIGH);
    }
  }
  digitalWrite(_clk, LOW);
  
  digitalWrite(_lat, HIGH);  
  digitalWrite(_lat, LOW);
}

I found the refreshing rate is slow (~1Mhz tested)
changing digitalWrite to digitalWriteFast speeds up a lot, but not enough,(~3Mhz)
I use dotstar LED strips with FASTLED library,
with SPI pins used, it refreshes much faster than not SPI pins
and from the Adafruit_TLC5947.cpp, i didn't see it use SPI.
I think maybe I should use SPI port? so I tried to build a SPI version.


My question finnaly starts here: my SPI version isn't working properly.

while the software bit-bang version above works with the same pin connections.

tested on teensy 3.2/3.5 ,they both work with example
but both don't with the SPI version

SPI Version::
I added a function in "Adafruit_TLC5947.cpp" to return LED data in int, for byte recombination

Code:
    uint16_t Adafruit_TLC5947::getCHAN(uint16_t channel) {
   if (channel > 24*numdrivers) return 0;
   return pwmbuffer[channel];
  }
and a line under class "Adafruit_TLC5947.h"
Code:
    uint16_t getCHAN(uint16_t channel);

and from the tlc5947test.ino code above,
I replace function tlc.write() to SPIwrite() , include "SPI.h" ,
some serial.print and whatever needed,

the last function using SPI is at the bottom of the code


Code:
#include "Adafruit_TLC5947.h"
#include "SPI.h"

// How many boards do you have chained?
#define NUM_TLC5974 1
#define clock 13
#define data 11
#define oe  15
#define latch  10
//#define oe  -1  // set to -1 to not use the enable pin (its optional)

const int slaveSelectPin = latch;
Adafruit_TLC5947 tlc = Adafruit_TLC5947(NUM_TLC5974, clock, data, latch);

void setup() {


  SPI.setCS(latch);
  SPI.setMOSI(data);
  SPI.setSCK(clock);
  SPI.begin();
  //tlc.begin();
  pinMode(oe, OUTPUT);
  digitalWrite(oe, LOW);
  pinMode(latch, OUTPUT);
  digitalWrite(latch, LOW);
  pinMode(clock, OUTPUT);
  digitalWrite(clock, LOW);
  pinMode(data, OUTPUT);
  digitalWrite(data, LOW);
  Serial.begin(9600);

  Serial.println("TLC5974 test");
  //tlc.begin();
  if (oe >= 0) {
    pinMode(oe, OUTPUT);
    digitalWrite(oe, LOW);
  }
}

void loop() {
  Serial.println("RED");
  colorWipe(4095, 0, 0, 100); // "Red" (depending on your LED wiring)
  delay(200);
  Serial.println("GREEN");
  colorWipe(0, 4095, 0, 100); // "Green" (depending on your LED wiring)
  delay(200);
  Serial.println("BLUE");
  colorWipe(0, 0, 4095, 100); // "Blue" (depending on your LED wiring)
  delay(200);
  rainbowCycle(10);
}


// Fill the dots one after the other with a color
void colorWipe(uint16_t r, uint16_t g, uint16_t b, uint8_t wait) {
  for (uint16_t i = 0; i < 8 * NUM_TLC5974; i++) {
    tlc.setLED(i, r, g, b);
    SPIwrite();
    delay(wait);
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
  uint32_t i, j;

  for (j = 0; j < 4096; j++) { // 1 cycle of all colors on wheel
    for (i = 0; i < 8 * NUM_TLC5974; i++) {
      Wheel(i, ((i * 4096 / (8 * NUM_TLC5974)) + j) & 4095);
    }
    SPIwrite();
    delay(wait);
  }
}

// Input a value 0 to 4095 to get a color value.
// The colours are a transition r - g - b - back to r.
void Wheel(uint8_t ledn, uint16_t WheelPos) {
  if (WheelPos < 1365) {
    tlc.setLED(ledn, 3 * WheelPos, 4095 - 3 * WheelPos, 0);
  } else if (WheelPos < 2731) {
    WheelPos -= 1365;
    tlc.setLED(ledn, 4095 - 3 * WheelPos, 0, 3 * WheelPos);
  } else {
    WheelPos -= 2731;
    tlc.setLED(ledn, 0, 3 * WheelPos, 4095 - 3 * WheelPos);
  }
}


void SPIwrite() {
#define TSPEED 4000000 //4Mhz
  unsigned int chan1 = 0;
  unsigned int chan2 = 0;
  byte address1 = 0;
  byte address2 = 0;
  byte address3 = 0;

  // packing each 2 channel (12bit*2) to 3 byte (8bit*3) for transfering
  SPI.beginTransaction(SPISettings(TSPEED , MSBFIRST, SPI_MODE0));
  digitalWriteFast(latch, LOW);
  for (unsigned int ledpos = (24 / 2) * NUM_TLC5974  - 1; ledpos > 0; ledpos--) {
    chan1 = tlc.getCHAN(ledpos);
    chan2 = tlc.getCHAN(ledpos - 1);
    address1 = (byte)(chan1 >> 4) ;
    address2 = (byte)((chan1 << 4) & (B11110000)) + (byte)((chan2 >> 8) & (B00001111));
    address3 = (byte)chan2;
    SPI.transfer(address1);
    SPI.transfer(address2);
    SPI.transfer(address3);
  }

  SPI.endTransaction();
  digitalWriteFast(latch, HIGH);
  digitalWriteFast(latch, LOW);
}


void loop() {
  colorWipe(4095, 0, 0, 100); // "Red" (depending on your LED wiring)
  delay(200);
  colorWipe(0, 4095, 0, 100); // "Green" (depending on your LED wiring)
  delay(200);
  colorWipe(0, 0, 4095, 100); // "Blue" (depending on your LED wiring)
  delay(200);
  rainbowCycle(10);
}


// Fill the dots one after the other with a color
void colorWipe(uint16_t r, uint16_t g, uint16_t b, uint8_t wait) {
  for (uint16_t i = 0; i < 8 * NUM_TLC5974; i++) {
    tlc.setLED(i, r, g, b);
    tlc.write();
    delay(wait);
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
  uint32_t i, j;

  for (j = 0; j < 4096; j++) { // 1 cycle of all colors on wheel
    for (i = 0; i < 8 * NUM_TLC5974; i++) {
      Wheel(i, ((i * 4096 / (8 * NUM_TLC5974)) + j) & 4095);
    }
    tlc.write();
    delay(wait);
  }
}

// Input a value 0 to 4095 to get a color value.
// The colours are a transition r - g - b - back to r.
void Wheel(uint8_t ledn, uint16_t WheelPos) {
  if (WheelPos < 1365) {
    tlc.setLED(ledn, 3 * WheelPos, 4095 - 3 * WheelPos, 0);
  } else if (WheelPos < 2731) {
    WheelPos -= 1365;
    tlc.setLED(ledn, 4095 - 3 * WheelPos, 0, 3 * WheelPos);
  } else {
    WheelPos -= 2731;
    tlc.setLED(ledn, 0, 3 * WheelPos, 4095 - 3 * WheelPos);
  }
}


void SPIwrite() {
#define TSPEED 4000000 //4Mhz
  unsigned int chan1 = 0;
  unsigned int chan2 = 0;
  byte address1 = 0;
  byte address2 = 0;
  byte address3 = 0;

  // packing each 2 channel (12bit*2) to 3 byte (8bit*3) for transfering
  SPI.beginTransaction(SPISettings(TSPEED , MSBFIRST, SPI_MODE0));
  digitalWriteFast(latch, LOW);
  for (unsigned int ledpos = (24 / 2) * NUM_TLC5974  - 1; ledpos > 0; ledpos--) {
    chan1 = tlc.getCHAN(ledpos);
    chan2 = tlc.getCHAN(ledpos - 1);
    address1 = (byte)(chan1 >> 4) ;
    address2 = (byte)((chan1 << 4) & (B11110000)) + (byte)((chan2 >> 8) & (B00001111));
    address3 = (byte)chan2;
    SPI.transfer(address1);
    SPI.transfer(address2);
    SPI.transfer(address3);
  }

  SPI.endTransaction();
  digitalWriteFast(latch, HIGH);
  digitalWriteFast(latch, LOW);
}

the code results in LEDs stuck in random situation, unchanging,
I'm not familiar with hardware SPI and not sure what is going wrong....need some guidance, :(

Thanks, Thanks for helping,

this could be the last software problem (since the SDIO works properly and fast) I encounter
at least, before I start doing PCB layouts :p

EDIT: IDE informations
 
Last edited:
sorry for the messing details,
i should have uploaded the files
the attachment is one modified library and two sketches

one with software SPI works normally (LEDs changing from red to green to blue to white)
and the hardware SPI version (shows no data refreshing, LEDs not changing at all.)

the software SPI version still works today, proves the hardware connections should be fine?

View attachment TLC5947tests.zip
 
Perhaps these pinMode lines are the problem?

Code:
  SPI.begin();
  //tlc.begin();
  pinMode(latch, OUTPUT);
  digitalWrite(latch, LOW);
  [B]pinMode(clock, OUTPUT);[/B]
  digitalWrite(clock, LOW);
  [B]pinMode(data, OUTPUT);[/B]
  digitalWrite(data, LOW);

After SPI.begin() the pins are controlled by SPI. Do not use pinMode, because that puts the pin back under GPIO control. The SPI port can't use the pin.
 
I just uploaded with the pinModes moved before SPI.begin()
void setup() {
pinMode(latch, OUTPUT);
digitalWrite(latch, LOW);
pinMode(clock, OUTPUT);
digitalWrite(clock, LOW);
pinMode(data, OUTPUT);
digitalWrite(data, LOW);
/*
SPI.setCS(10);
SPI.setMOSI(11);
SPI.setSCK(13);*/
SPI.begin();
.....;
the sketch starts working!!!!
16936037_1539883889364318_1927981681_o.jpg

found that they really were the problem,

thank you Paul!!!!!
now I can move on to further :eek:
 
Hi Po Ting,

Can you post the example code that worked for hardware SPI?

I have been trying to figure out how to use the TLC5947 with hardware SPI and it is really difficult. Your post is giving me hope it is possible.

Thank You.
 
Hi Po Ting,

Can you post the example code that worked for hardware SPI?

I have been trying to figure out how to use the TLC5947 with hardware SPI and it is really difficult. Your post is giving me hope it is possible.

Thank You.

Hi, My code attachment on #3 should work, please give it a try
I no longer use the TLC5947 or TLC59711 driver in my later version of POV.
but the library and code should work, you may look into it and see the code, the SPI of the driver is different and works 12bit for a LED,
if you don't understand you may look into the datasheet of the part or the library, it will help.
 
Hi Po Ting,

Thanks for the info. I got it to work!

I'm having trouble controlling the LEDs, but I think I can figure that out, it's a coding issue.
 
Status
Not open for further replies.
Back
Top