Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 7 of 7

Thread: Need help on using SPI library

  1. #1
    Senior Member
    Join Date
    Sep 2015
    Location
    Taiwan, Asai. (Traditional Chinese)
    Posts
    166

    Need help on using SPI library

    Hello community

    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-t...akout/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

    EDIT: IDE informations
    Last edited by Po Ting; 02-20-2017 at 07:01 AM. Reason: EDIT: IDE informations

  2. #2
    Senior Member
    Join Date
    Sep 2015
    Location
    Taiwan, Asai. (Traditional Chinese)
    Posts
    166
    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?

    TLC5947tests.zip

  3. #3
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,502
    Perhaps these pinMode lines are the problem?

    Code:
      SPI.begin();
      //tlc.begin();
      pinMode(latch, OUTPUT);
      digitalWrite(latch, LOW);
      pinMode(clock, OUTPUT);
      digitalWrite(clock, LOW);
      pinMode(data, OUTPUT);
      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.

  4. #4
    Senior Member
    Join Date
    Sep 2015
    Location
    Taiwan, Asai. (Traditional Chinese)
    Posts
    166
    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!!!!
    Click image for larger version. 

Name:	16936037_1539883889364318_1927981681_o.jpg 
Views:	116 
Size:	60.8 KB 
ID:	9833

    found that they really were the problem,

    thank you Paul!!!!!
    now I can move on to further

  5. #5
    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.

  6. #6
    Senior Member
    Join Date
    Sep 2015
    Location
    Taiwan, Asai. (Traditional Chinese)
    Posts
    166
    Quote Originally Posted by nicnut View Post
    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.

  7. #7
    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.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •