Need to verify my idea is sound.

I have a new project I am working on and I would like to verify that my plan is sound. I am a programmer so when it comes to the code side of things I have no problem. But on the hardware side I need to make sure I am understanding things correctly.

I am going to make some animated LEDs. I plan on using three TLC5947 chips to control 24 rgb leds. I have the arduino communicating with the tlcs and doing some basic animations (solid color, fading, and random). I have a 4 button wireless remote I am using to cycle through colors and animations. Now I want to go to the next level.

I have been using a bare bone kit because the container I can put the arduino in is not very wide. the next step is I want to have frame based animation. The simple animations can be done in code, but others that I want to do will be easier if I can specify frames and delays.

So my idea for this, due to the arduino memory limitations, is to store the frame based animations on a microsd card. the file format will be simple. The first two bytes contain the number of frames and some 1 bit flags. The next section of bytes is one byte per frame specifying how long the frame will display in milliseconds. the rest of the file will be the frames. I have two formats either one byte per led or 3 bytes per led (one of the flags will determine this).

For hardware I plan on using this microsd adaptor and this library to do what I need.

With the 1k of memory the arduino hs that isn't enough to store animations more than about 30 frames int he worst case scenario. So I need alternate storage. After reading this blog I think I can figure out how to read and write from the 25AA512 chip. 64kbytes should be plenty of space.

The idea then is when I switch animation it will load the animations file into the eeprom at address 0. I can store address 2 for the beginning of the delays array. Then calculate and store the address of the beginning of the frames. I can make a c++ class to make this easy to use. A function that does simple array calculations to get a specific led on a specific frame should be easy.

The next step I want to include wifi so I can control the LEDs with my ipod touch. I am thinking about getting the new Yellow Jacket from AsyncLabs. What would be awesome is if they released wishield 2.0 they release a new Yellow jacket that has that serial flash chip. I would get a wishield but it is too wide for the container I am going to use.

What would be icing on the cake is if I can have music modes included. Have the leds animate to music. I have a couple of ideas for this, but it would require taking audio input and splitting it into lows, mids, and highs. This I am not sure how to do.

So, the pins needed on the arduino: The tlc5947 needs 4, I am not sure if any can be shared with other pins, but I doubt. The eeprom, microsd, and wifi use spi, so those need the 3 spi pins and one cs pin per device - so 6 more pins. Though the wishield uses pin 2 as an interrupt also, correct? If I keep the 4 button remote (don't have my ipod handy) that is 4 more pins.

That's a total of 11. That means I would have to use a couple of the analog pins as though they were digital. that also would leave me at least 3 analog pins to dot he music mode if I get that far.

Is there anything I missed, didn't think of?

Oh yeah, power. The power source is going to be a 12v scooter battery. Why? Because they are cheap, small (3"x3"x4"), have plenty of mah, and easily recharged with a car battery charger. The LEDs I am using are designed for automotive use. They come on strips that can be cut every 3 LEDs and take a 12v source. I believe everything else can operate at 5v so a voltages regulator that has enough amps should suffice.

For wifi access later on I am thinking about providing a WAP that will automatically forward connecting devices to the arduino web server. A Fonera router can run on 3.3v (or 5v if you don't bypass the internal regulator) and is supported by OpenWRT and DD-WRT. So having it do HTTP redirects should be easy and running from a battery pack should work well. Doing this will allow anyone with a wifi device to control the project without needing to know the IP of the arduino - which would be a cool experiment.

Edit: forgot the microsd when counting up pins - added.

Sounds like quite a project.

Do you need the SD card and eeprom? I would have thought you could read data out of the SD card fast enough for what you need.

Have you considered if the pins for the TLC5947 can be reallocated to free ones and don't require the same pins (because of hardware) as the SPI bus?

Does the EEPROM use SPI? I thought most used I2C.

The eeprom I linked to in that blog uses SPI. The TLC5947 does not use SPI. It has a data, clock, latch, and blank pin. I made a video several months ago when I was playing around with the chip.

The microsd thing is one thing I am not sure about. I know microsd has a limited duty cycle. So reading a file into memory minimized the number of times the card is accessed. Plus most of the arduino examples I find deal with writing to sd cards. I haven't seen a single project other than simple examples from the libraries on reading from sd cards. Most of the libraries are geared around writing to files.

The SdFat library I am looking at seems to have the best read support. Even so I still have to modify the library to read what I want to read. the library has two read functions I think. One that reads one byte at a time and one that reads a specified number of bytes. Both read functions move the file pointer to the next part of the files. I suppose I could make a read function that takes file pointer, frame, led, number of frames as params and returns the bytes needed.

The TLC5947 does not use SPI.

No but the libraries for the TLC5940 uses the timer output and the pins that the SPI uses.

I know microsd has a limited duty cycle.

That is only for write, (or rather erase), you can read them as often as you like.

Ok, how do you quote on this forum? Manually use the quote tag? I made my own function to output to the tlc5947 that I specify the pins used - based on the example code from the octobright.

So I shouldn't need the eeprom. As long as I can work with the sdfat library and make a read function that grabs the bytes I need I should be good.

I may need more memory when I get to the wifi part for the webpage. However, I should be able to use the microsd card for that also.

Ok, how do you quote on this forum?

The button to the right of the # button on the top row will insert tags. Paste the material to quote between those tags.

Oh, another question for anyone with tlc5947 experience. This should probably be asked as a separate thread. While the grayscale is from 0-4095 I find an odd behavior. It seems like I get max brightness at 2047. When the value is set between 2048 and 4095 it is like 0-2047. I hope that makes sense, the brightness seems to get reset.

That does seem like an off by one error with the bits. However, the function I am using is based of octobrite’s and as far as I can tell it works correctly.

Here’s my class I am using, you can set the _WriteChannel(uint16_t value) only shifts the 12 bits. If there was an off by one I would think the LED would all be different as the error gets push down the line. All the LEDs are the same color and brightness.

Anyone is free to use this class. If you use pins that aren’t in PORTD you will need to modify accordingly. You could change to using digitalWrites but the maker of Octobrite says this method is faster. For smooth animation that can make a difference.

Also note the class is designed for rgb leds. The code could also be made more robust, like making sure the led specified in SetLED is within range. The idea is in your setup() you alloc memory and SetPins. Then in loop() use SetLED to set the LEDs and Update() to update the tlc. Update() will only latch if the led data needs updating to minimize flicker.

Edit: #include “HardwareSerial.h” is probably not needed anymore. It was used for debugging the code.

LEDDriver.h

//Created by Kevin Jonas aka SirPoonga
#ifndef LEDDriver_H
#define LEDDriver_H

#include <stdint.h>

#define NUM_TLCS  1
#define NUM_LEDS (8 * NUM_TLCS)
#define PINS_PER_LED 3
#define NUM_PINS (NUM_TLCS * NUM_LEDS * PINS_PER_LED)

#define MAX_PULSE_STEPS 4095

#define RED 2
#define GREEN 1
#define BLUE 0

class LEDDriver 
{
  public:      
    //PORTD pins must be specified
    void SetPins(uint8_t pClock_pin, uint8_t pData_pin, uint8_t pBlank_pin, uint8_t pLatch_pin);
    void Update();      
    void ClearLEDS();            
    void SetLED(uint16_t led, uint16_t red, uint16_t green, uint16_t blue);
            
  private:
    uint16_t led_data[NUM_PINS];
            
    uint8_t clock_pin;
    uint8_t data_pin;
    uint8_t blank_pin;
    uint8_t latch_pin;
      
    bool needsUpdate;
      
    void _WriteChannel(uint16_t value);
    void _UpdateLEDS();
    void SetClock();
    void SetLatch();
};
#endif

LEDDriver.cpp

//Created by Kevin Jonas aka SirPoonga
#include "LEDDriver.h"

#include "wiring.h"
#include "HardwareSerial.h"

void LEDDriver::SetPins(uint8_t pClock_pin, uint8_t pData_pin, uint8_t pBlank_pin, uint8_t pLatch_pin)
{
  clock_pin = pClock_pin;
  data_pin = pData_pin;
  blank_pin = pBlank_pin;
  latch_pin = pLatch_pin;
}

void LEDDriver::Update()
{
  //compare with led_data to see if need to update.
  //if need to update then update led_data and SetLatch().  
  //This will minimize flickering.  Only latch if needed.
  if(needsUpdate)
  {
    _UpdateLEDS();
    SetLatch();
      needsUpdate = false;
  }
}

void LEDDriver::SetClock()
{
  PORTD |= (1 << clock_pin);
  PORTD &= ~(1 << clock_pin);
}

void LEDDriver::SetLatch()
{
  PORTD |= (1 << latch_pin);
  PORTD |= (1 << blank_pin); //per suggestion - this resets pwm clock providing less flicker
  delay(1);
  PORTD &= ~(1 << blank_pin);
  PORTD &= ~(1 << latch_pin);
}

void LEDDriver::ClearLEDS()
{
  for(uint16_t i = (NUM_PINS - 1); i >= 0; i--)
  {
    SetLED(i, 0, 0, 0);
  }
}

void LEDDriver::SetLED(uint16_t led, uint16_t red, uint16_t green, uint16_t blue)
{
  if(led_data[(led*PINS_PER_LED)+RED] != red)
  {
    needsUpdate = true;
      led_data[(led*PINS_PER_LED)+RED] = red;
  }
  if(led_data[(led*PINS_PER_LED)+GREEN] != green)
  {
    needsUpdate = true;
      led_data[(led*PINS_PER_LED)+GREEN] = green;
  }
  if(led_data[(led*PINS_PER_LED)+BLUE] != blue)
  {
    needsUpdate = true;
      led_data[(led*PINS_PER_LED)+BLUE] = blue;
  }
}

void LEDDriver::_WriteChannel(uint16_t value)
{
  int bit;
  // Write value, MSB first
  for (bit=0; bit<12; bit++)
  {
    if ((value >> (11 - bit)) & 1)
    {
      PORTD |= (1 << data_pin);
    }
    else
    {
      PORTD &= ~(1 << data_pin);
    }
    SetClock();
  }
}

void LEDDriver::_UpdateLEDS()
{
  for(int i = (NUM_PINS - 1); i >= 0; i--)
  {
    _WriteChannel(led_data[i]);
  }
  SetClock();
}

If there’s nothing wrong with your actual shift out routines, then you need to look at the code that is feeding data into those functions. That’s probably where the issue is now.

Do you need the SD card and eeprom? I would have thought you could read data out of the SD card fast enough for what you need.

I thought about this some more. Speed shouldn't be a problem hopefully. I will have to test it and make a large animation. The microsd will be accessed when the animation is initialized (get the the number of frames and flags). Then it will need to grab the frame delay and frame on each frame change.

I have two types of animations - coded and frames file. Frames file has two types - relative color and absolute color.

Relative color uses two system colors (out of 11 presets). One button on the remote will cycle through color1 and another through color2. This allows you to change the color of an animation - like a fade that fades between the two system colors. This can be done with one byte per led.

Other animations might needs to use specific colors - like a scrolling rainbow. I think my original calculations for this are off. For this type of animation I need to store the actual grayscale data. Since the grayscale data is 12 bits that 2 bytes per pin or 6 bytes per led. So 24*6=144 bytes is the size of one frame. That could be reduced using a packed array but since I am using a microsd card to store the data space isn't an issue. This will make loading the data easier since I don't have to do the packed array calculations.

So the file format consist of this: Header (2 bytes) 0002 1111 1111 1111 1) number of frames 2) 0 = relative, 1 = absolute

I am keeping the other three bits available for future use if I come up with other ideas. Though I could reduce the max number of frames, I doubt I will ever have 4096 frames.

Delays (1 byte * number of frames)

Frames Relative(1 byte per led): 3211 1111 1) Blending level 2) use color 1 or no color 3) use color 2 or no color For example 10100000 will blend color 2 with blank at level 32 out of 64 (half brightness).

Absolute (6 bytes per led): 0000 3333 3333 3333 0000 2222 2222 2222 0000 1111 1111 1111 1) red grayscale 2) green grayscale 3) blue grayscale

macegr, I've set the value in _WriteChannel(uint16_t value) to 2048 so it didn't matter that value was coming in. The brightness was set as if the value was 0. I then tried other values between 2048 and 4095. The brightness was as if 0-2047.

macegr - maybe another possibility is the current draw from the leds. According to my cheap multimeter a LED is right at the maximum the TLC5947 allows. Maybe if it is drawing more current that ends up being the behavior of the tlc?