ATtiny85 + TLC5940

great :slight_smile: i was reading that there are some problems with SPI on attinys... Did you solved them? did you modify the TLC library for arduino or did you make a new one? Is that library public?
And Which core did you use to program attiny?
Thanks for the info :wink:

?R:
great :slight_smile: i was reading that there are some problems with SPI on attinys... Did you solved them?

The standard Arduino SPI library probably doesn't work, no. The tinys don't have SPI, they have USI (which is hardware support for SPI, I2C, etc.)

?R:
did you modify the TLC library for arduino or did you make a new one? Is that library public?

It's a new library. I can send you a copy, no problem...

you haven't published it in some website yet, do you?

It would be great if you could share the library.

?R:
you haven't published it in some website yet, do you?

Nope.

funkyguy4000:
It would be great if you could share the library.

Watch this space...

OK, here's a copy of my library...hope somebody finds it useful!

http://www.artlum.com/arduino/tinytlc.html

fungus:
The CPU spends most of its time generating a clock output on a pin at about 4MHz (the tlc5940 needs a clock signal for the PWM). That's 100% in my book...

Why not use one of the 2 counter/timers in the ATtiny85 to generate the 4MHz clock?

dc42:

fungus:
The CPU spends most of its time generating a clock output on a pin at about 4MHz (the tlc5940 needs a clock signal for the PWM). That's 100% in my book...

Why not use one of the 2 counter/timers in the ATtiny85 to generate the 4MHz clock?

V1.0 of the library did that... but using timers to generate the signals needs both timers and all five I/O pins to make it work. I wanted to get it down to three pins so I could attach a couple of potentiometers/switches/whatever. You could even use a timer to play music on one of the other pins if you wanted to.

Besides, what would you do with all those extra CPU cycles? :slight_smile: In real life you'd probably only use 1% of them anyway (especially if you can't attach any external devices because you ran out of I/O pins - that kinda limits your processing to basic color sequencing).

Edit: I guess if you want to chain a lot of TLC5940s together the CPU power needed to update them all might become significant - even for basic color fades. In that case I'd dig out the v1.0 version of the library (and use a Tiny84 if I needed some free pins).

fungus:
OK, here's a copy of my library...hope somebody finds it useful!
artlum.com - Arduino

thank you very much! i will try it asap! :wink:

well, i tried it and it worked very well: Rainbow effect with RGB LEDs using 3 TLC5940's controlled by an ATtiny85 ƎR - YouTube
thank you for sharing it! :wink:
i had some problems because you suggested not to use millis and delays, but i see that the problems are caused only by delays, so we can use millis and micros... we just need to do the update method very often, right?
i've a question, i'm not an expert of making libraries, but to make this library work in an attiny84 do i just need to add this line?

#if defined (__AVR_ATtiny85__) || (__AVR_ATtiny84__)

or is there some register settings to do?
thanks again :wink:

?R:
well, i tried it and it worked very well: Rainbow effect with RGB LEDs using 3 TLC5940's controlled by an ATtiny85 ƎR - YouTube
thank you for sharing it! :wink:

No problem.

It's fun to think that the teeny chip in the corner is driving something that many LEDs... :slight_smile:

?R:
i had some problems because you suggested not to use millis and delays, but i see that the problems are caused only by delays, so we can use millis and micros... we just need to do the update method very often, right?

I don't think millis() will do anything useful. I turned off the hardware timer in setup() (if it's ticking then there's a bug!)

Time spent in between calls to update() is time the LEDs aren't lit up. I think you'd have to do a lot before you notice them get visibly dimmer, but bear it in mind.

?R:
i've a question, i'm not an expert of making libraries, but to make this library work in an attiny84 do i just need to add this line?

#if defined (__AVR_ATtiny85__) || (__AVR_ATtiny84__)

or is there some register settings to do?
thanks again :wink:

I just tried it and there's a line in setup() that doesn't compile. The line is:

  TCCR1 = 0;

It should work if you replace that line with:

#if defined (__AVR_ATtiny85__)
  TCCR1 = 0;
#else
  TCCR1A = TCCR1B = 0;
#endif

fungus:
I don't think millis() will do anything useful. I turned off the hardware timer in setup() (if it's ticking then there's a bug!)

Time spent in between calls to update() is time the LEDs aren't lit up. I think you'd have to do a lot before you notice them get visibly dimmer, but bear it in mind.

yes i noticed that, and i used millis and micros to synchronize the duration of functions and other things, i think it easier to use than the method that you used in your example code... i do something like this, and it acts like a delay, but it's useful for other things too:

#define TIME 100
...
_time=millis();
while(millis()- _time < TIME) tlc5940.update();

fungus:
I just tried it and there's a line in setup() that doesn't compile. The line is:

  TCCR1 = 0;

It should work if you replace that line with:

#if defined (__AVR_ATtiny85__)

TCCR1 = 0;
#else
  TCCR1A = TCCR1B = 0;
#endif



ok thank you, after i'll try that if i've time ;)

Whoa ?R, are those 2pin rgb fade leds or are those 4 pin rgb terminated leds? They look to only have 2 pins.

funkyguy4000:
Whoa ?R, are those 2pin rgb fade leds or are those 4 pin rgb terminated leds? They look to only have 2 pins.

they are RGB LEDs with common anode, 4 pins, they are controlled by 3 TLC's

about the tiny84, i'm trying it, but i think there is a problem with the port mapping, because in the tiny85 you set the pin PB0, PB1 and and PB2 but in attiny84 should be PA6 PA5 and PA4, do you agree with me? or am i wrong?

i modified this part and it compiles:

#if defined (__AVR_ATtiny85__) || (__AVR_ATtiny84__)
/*------------------------------------------------------------------------
  Configuration
------------------------------------------------------------------------*/
// How many TLC chips are attached. Each chip needs 32 bytes of RAM.
// The ATtiny85 only has 512 bytes of RAM and you need some left over
// for the rest of your program. Do the math...
#ifndef NUM_TLC5940s
#define NUM_TLC5940s 3
#endif

/*------------------------------------------------------------------------
  Pin assignments
------------------------------------------------------------------------*/
// You could move the BLANK_XLAT_PIN if you wanted to
// but the USI pins are fixed.
#if defined (__AVR_ATtiny85__)
#define BLANK_XLAT_PIN PB0
#define USI_OUT_PIN PB1
#define USI_CLK_PIN PB2
#else
#define BLANK_XLAT_PIN PA6
#define USI_OUT_PIN PA5
#define USI_CLK_PIN PA4
#endif

after i'll try the hardware, but i think that with those modifications that library can be called "Tiny_TLC5940" :slight_smile:
what do you think about? am i wrong?

?R:

funkyguy4000:
Whoa ?R, are those 2pin rgb fade leds or are those 4 pin rgb terminated leds? They look to only have 2 pins.

they are RGB LEDs with common anode, 4 pins, they are controlled by 3 TLC's

about the tiny84, i'm trying it, but i think there is a problem with the port mapping, because in the tiny85 you set the pin PB0, PB1 and and PB2 but in attiny84 should be PA6 PA5 and PA4, do you agree with me? or am i wrong?

Oh, yes, those pins need to change, too. :slight_smile:

PA6 PA5 and PA4 is correct but you'll also have to change all references to PORTB, DDRB and PINB into PORTA, DDRA and PINA.

Maybe you can #define those, too:

#if defined (__AVR_ATtiny85__)
#define BLANK_XLAT_PIN PB0
#define USI_OUT_PIN PB1
#define USI_CLK_PIN PB2
#define USI_DDR DDRB
#define USI_PORT PORTB
#define USI_PIN PINB
#else
#define BLANK_XLAT_PIN PA6
#define USI_OUT_PIN PA5
#define USI_CLK_PIN PA4
#define USI_DDR DDRA
#define USI_PORT PORTA
#define USI_PIN PINA
#endif

Then use (eg.) "USI_DDR" instead of "DDRB" in the code...

The code then becomes:

/*------------------------------------------------------------------------
  Bitbang driver for ATtiny85+TLC5940 using only three pins
------------------------------------------------------------------------*/
#ifndef _TEENY5940_H
#define _TEENY5940_H

#if defined (__AVR_ATtiny85__) || (__AVR_ATtiny84__)
/*------------------------------------------------------------------------
  Configuration
------------------------------------------------------------------------*/
// How many TLC chips are attached. Each chip needs 32 bytes of RAM.
// The ATtiny85 only has 512 bytes of RAM and you need some left over
// for the rest of your program. Do the math...
#ifndef NUM_TLC5940s
#define NUM_TLC5940s 1
#endif

/*------------------------------------------------------------------------
  Pin assignments
------------------------------------------------------------------------*/
// You could move the BLANK_XLAT_PIN if you wanted to
// but the USI pins are fixed.
#if defined (__AVR_ATtiny85__)

#define BLANK_XLAT_PIN PB0
#define USI_OUT_PIN PB1
#define USI_CLK_PIN PB2

#define USI_DDR DDRB
#define USI_PORT PORTB
#define USI_PIN PINB

#else

#define BLANK_XLAT_PIN PA6
#define USI_OUT_PIN PA5
#define USI_CLK_PIN PA4

#define USI_DDR DDRA
#define USI_PORT PORTA
#define USI_PIN PINA

#endif

/*------------------------------------------------------------------------
  The TLC5940 controller
------------------------------------------------------------------------*/
#define NUM_LEDS (16*NUM_TLC5940s)   /* 16 LEDs per chip */

class Teeny5940 {
  typedef uint8_t byte;
  typedef uint16_t pwm_val;
  pwm_val pwmVals[NUM_LEDS];
public:
  // ----------------------------------------
  // Initialize - call this in your "setup()"
  // ----------------------------------------
  void init() {
    USI_DDR |= _BV(BLANK_XLAT_PIN);    // Set pin as 'output'
    USI_PORT |= _BV(BLANK_XLAT_PIN);   // BLANK pin high (stop the TLC5940, disable all output)
    // Set all PWM values to zero
    clear();
  }
  
  // --------------------------
  // Set all PWM values to zero
  // --------------------------
  void clear() {
    byte *p = (byte*)pwmVals;
    byte *e = p + (NUM_LEDS*sizeof(pwm_val));
    while (p!=e) {
      *p++ = 0;
    }
  }
  
  // ------------------------------------------------------------------------
  // Set the PWM value of one of the PWM channels, 'value' in range [0..4095]
  //
  // eg. "set(2,2048)" sets LED 2 to 50% on, 50% off
  // ------------------------------------------------------------------------
  void set(byte channel, pwm_val value) {
    if ((channel&1)!=0) {
      pwmVals[channel] = value<<4;    // update() expects data for the odd channels to be shifted...
    }
    else {
      pwmVals[channel] = value&0x0fff;
    }
  }

  // ------------------------------------------------------------------------
  // Call this in your main loop to perform one complete PWM cycle
  //
  // With the Tiny85's internal 8mHz clock this takes almost exactly
  // one millisecond to execute :-)
  // ------------------------------------------------------------------------
  void update() const {
    // Set BLANK and XLAT low for the PWM cycle
    USI_PORT &= ~_BV(BLANK_XLAT_PIN);

    // Set up USI (USI drives the TLC5940 clocks and serial data input)
    byte sOut = _BV(USI_CLK_PIN)|_BV(USI_OUT_PIN);
    USI_DDR |= sOut;         // Pins are outputs
    USI_PORT &= ~sOut;       // Outputs are low
    USICR = _BV(USIWM0);     // Three wire mode

// Macros to drive the USI clock
#define tick USICR=_BV(USIWM0)|_BV(USITC)
#define tock USICR=_BV(USIWM0)|_BV(USITC)|_BV(USICLK)
#define clock8()   tick; tock;  tick; tock;  tick; tock;  tick; tock;  tick; tock;  tick; tock;  tick; tock;  tick; tock;

    // We need to send 4096 clock pulses per PWM cycle, ie. 512 bytes of data.
    // There's only 24 bytes of real data per TLC5940 so we send some dummy data first.
    byte dummySize = (512-(24*NUM_TLC5940s))/2;
    while (dummySize != 0) {
      clock8();      // Send clock pulses for dummy data...
      clock8();
      --dummySize;
    }

    // Now send the real PWM data
    // nb. This sequence of instructions is the result of a lot of
    // compiles/disassembles to get the best possible code output
    // from the compiler. Don't change it...
    byte numPairs = 8*NUM_TLC5940s;
    const pwm_val *pwm = pwmVals+NUM_LEDS-2;
    while (numPairs != 0) {
      const pwm_val hi = pwm[1];
      USIDR = hi>>8;
      clock8();
      const pwm_val lo = pwm[0];
      USIDR = hi|(lo>>8);
      clock8();
      USIDR = lo;
      clock8();
      pwm -= 2;
      --numPairs;
    }

    // Set BLANK and XLAT high while you do your updates
    USI_PIN = _BV(BLANK_XLAT_PIN);
  }
};


// A driver for you to use
Teeny5940 tlc5940;


#else
  unsupported_chip;
#endif

#endif /*_TEENY5940_H*/

very well, after i'll try it :wink:

i just tried and it works with attiny84 too :wink:

This is going to sound really newbish but how do you guys program your ATtinys? I have tried the High Low Tech tutorial numerous times and it doesn't work. Also I have a hard time finding ATtiny85 cores.
Any help?

funkyguy4000:
This is going to sound really newbish but how do you guys program your ATtinys?

I use an ISP programmer I bought on eBay

eg.: arduino isp programmer for sale | eBay

When I started out I just poked wires into the cable (see photos at the start of this thread).

Now I use a little board I made, see here:

http://arduino.cc/forum/index.php/topic,134673.0.html

As an experiment I've designed/ordered some little PCBs for an ATtiny85 version of that (nb. they didn't arrive yet...)

funkyguy4000:
I have tried the High Low Tech tutorial numerous times and it doesn't work. Also I have a hard time finding ATtiny85 cores.
Any help?

I think that was the page I used when I started out but I admit I never did "Arduino as ISP", I bought an ISP programmer.

I usually don't bother with cores any more. The chips are very easy to program directly and writing the code is part of the fun!

The real problem would be in using other libraries that rely on having SPI, etc. Adapting code that needs those things would be a challenge. I don't use other code much though (it rarely fits exactly) and I can always use a Mega328 if I really need to...

PS: This is my PCB for the Tiny85duino... :slight_smile:

Tiny85duino.png

i use an arduino as ISP programmer and with this core: Google Code Archive - Long-term storage for Google Code Project Hosting.