Go Down

Topic: Attiny13A + MM5481N LED driver? (Read 1 time) previous topic - next topic

smeezekitty

I was looking for a way to drive a 7 segment LED display with an attiny13A. As you probably know, the attiny only has 5 usable pins.
So, I went digging in the junk box for a solution.
I found a National  MM5481N that seems to be unused.
But the datasheet (http://pdf1.alldatasheet.com/datasheet-pdf/view/144761/NSC/MM5481N.html) for it is almost useless for connecting and programming so I was left to guessing wiring and programming. It seems to implement a form of SPI interface.
I am wiring it as attached in the schematic.
Code:
Code: [Select]

#define F_CPU 128000
#include <util/delay.h>
#include <avr/io.h>
void shreg(int x){
int z;
for(z = /*8192*/16384;z>1;z/=2){
PORTB = _BV(PB3);//(_BV(PB3) * !!(x&z)); //Debugging - should write all outputs to high
_delay_ms(1); //Wait
PORTB |= _BV(PB4); //SCK high
_delay_ms(1); //wait
PORTB &= _BV(PB4); //SCK low
_delay_ms(1);
PORTB = 0;
_delay_ms(1);
}
}
int main(){
DDRB = _BV(PB1) | _BV(PB3) | _BV(PB4);
PORTB = 0;
while(1)
shreg(0xffff);
}

I have tried different combinations of code.
Also I have tried tying the dataenable to 5v and ground.
All attempts have no outputs on any output pins on the LED driver.
Cannot be 100% sure the chip works but I think it does.
Avoid throwing electronics out as you or someone else might need them for parts or use.
Solid state rectifiers are the only REAL rectifiers.
Resistors for LEDS!

AndrewStone

The datasheet claims that its an MM5450 packaged in a 20 pin package.  So you can look at the code and connection diagrams here:
http://effluviaofascatteredmind.blogspot.com/2009/02/arduino-and-m5451-control-35-leds.html
http://code.google.com/p/arduino-m5451-current-driver/wiki/ArduinoM5451

One thing that's jumping out at me is that you have to clock in 35 bits even though your package does not have that many LEDs.  You aren't clocking all those bits in.

Also, the chip SINKS current, so connect your LED anode to 5v and cathode to the chip's pin.  In your schematic the LED is backwards and connected to GND.

Finally, the M5450 is a constant current driver.  So actually you DON'T need a resistor (ironic given your sig :-)!!).

Cheers!
Andrew
http://www.toastedcircuits.com Lightuino LED driver: 16 sources, 70 sinks, remote controlled.  Also high powered LED drivers.

smeezekitty


Quote


One thing that's jumping out at me is that you have to clock in 35 bits even though your package does not have that many LEDs.  You aren't clocking all those bits in.

Alittle odd but should be fixed now.
Quote

Also, the chip SINKS current, so connect your LED anode to 5v and cathode to the chip's pin.  In your schematic the LED is backwards and connected to GND.

I changed the LED correctly. This is alittle annoying though.
Quote

Finally, the M5450 is a constant current driver.  So actually you DON'T need a resistor (ironic given your sig :-)!!).

I'd rather keep it just in case.

I changed the LED wiring, wired out 3 LEDS and changed the code as follows:
Code: [Select]

#define F_CPU 128000
#include <util/delay.h>
#include <avr/io.h>
#include <inttypes.h>
#define SCK _BV(PB4)
void shreg(uint32_t a){
int z;
//Start sequence ? Is this necessary and correct?
PORTB = 0; // All pins low
_delay_ms(1);
PORTB |= SCK; //SCK high
_delay_ms(1);
PORTB &= SCK; //SCK low
_delay_ms(1);
PORTB |= _BV(PB3); //Serial data high
_delay_ms(1);
PORTB |= SCK; //Sck high
_delay_ms(1);
PORTB &= SCK; //SCK LOW
_delay_ms(1);
for(z = 0;z<35;z++){
if(z < 14){
PORTB = (_BV(PB3) * !!(a&(z<<1))); //Output bit if < 14
} else {
PORTB = 0; //Otherwise output nothing
}
_delay_ms(1);
PORTB |= _BV(PB4); //SCK high
_delay_ms(1);
PORTB &= _BV(PB4); //SCK low
_delay_ms(1);
PORTB = 0; //All pins off
_delay_ms(1);
}

}
int main(){
DDRB = _BV(PB3) | _BV(PB4);
PORTB = 0;
while(1)
shreg(0x0);
}

Problem is all the LEDS always light up regardless of the value I pass to shreg().
Thanks for the help.
I am alittle confused by the code at this point.
I also grounded data enable.
Avoid throwing electronics out as you or someone else might need them for parts or use.
Solid state rectifiers are the only REAL rectifiers.
Resistors for LEDS!

Coding Badly

Quote
PORTB &= SCK; //SCK low


The comment doesn't match the code.  I believe you're missing a bitwise unary operator.   ;)

smeezekitty


Quote
PORTB &= SCK; //SCK low


The comment doesn't match the code.  I believe you're missing a bitwise unary operator.   ;)


LOL thanks for the catch. That was stupid of me  :(
Now its working "better" but still not right.
I changed the code as follows:
Code: [Select]

#define F_CPU 128000
#include <util/delay.h>
#include <avr/io.h>
#include <inttypes.h>
#define SCK _BV(PB4)
void shreg(uint16_t a){
int z;
//Start sequence ?
PORTB = 0; // All pins low
_delay_ms(1);
PORTB |= SCK; //SCK high
_delay_ms(1);
PORTB &= ~SCK; //SCK low
_delay_ms(1);
PORTB |= _BV(PB3); //Serial data high
_delay_ms(1);
PORTB |= SCK; //Sck high
_delay_ms(1);
PORTB &= ~SCK; //SCK LOW
_delay_ms(1);
for(z = 0;z<35;z++){
if(z < 14){
PORTB = (_BV(PB3) * (a&1)); //Output bit if < 14
a>>=1;
} else {
PORTB = 0; //Otherwise output nothing
}
_delay_ms(1);
PORTB |= _BV(PB4); //SCK high
_delay_ms(1);
PORTB &= ~_BV(PB4); //SCK low
_delay_ms(1);
/* PORTB = 0; //All pins off
_delay_ms(1);*/
}

}
int main(){
unsigned int cat = 0;
DDRB = _BV(PB3) | _BV(PB4);
PORTB = 0;
while(1){
shreg(0xFFFF);
_delay_ms(500);
}
}

With shreg(0xFF), it correctly lights all LEDS and shreg(0) turns them all off.
The problem is, shreg(1) or shreg(2) does not light the first or second LED.
Avoid throwing electronics out as you or someone else might need them for parts or use.
Solid state rectifiers are the only REAL rectifiers.
Resistors for LEDS!

Coding Badly


Andrew Stone's code is clocking out an extra two bits when the LED driver is "cleared"...

Quote
#define M5451_NUMOUTS 35

...

  // Clear out the device so we can clock in items
  digitalWrite(serDataPin,LOW);
  for (i=0;i< M5451_NUMOUTS+2;i++)
    {
    mydelay(M5451_CLK);
    digitalWrite(clockPin,HIGH);
    mydelay(M5451_CLK);
    digitalWrite(clockPin,LOW);   
    }



And, there's this mumbo jumbo when the driver is fed data...

Quote
// Write the initial "start" signal
  digitalWrite(clockPin,LOW);
  digitalWrite(serDataPin,LOW);
  ...



Looks like there are two start bits (lo then hi; or a start bit and a "space").

smeezekitty



Andrew Stone's code is clocking out an extra two bits when the LED driver is "cleared"...

Quote
#define M5451_NUMOUTS 35

...

  // Clear out the device so we can clock in items
  digitalWrite(serDataPin,LOW);
  for (i=0;i< M5451_NUMOUTS+2;i++)
    {
    mydelay(M5451_CLK);
    digitalWrite(clockPin,HIGH);
    mydelay(M5451_CLK);
    digitalWrite(clockPin,LOW);   
    }



And, there's this mumbo jumbo when the driver is fed data...

Quote
// Write the initial "start" signal
  digitalWrite(clockPin,LOW);
  digitalWrite(serDataPin,LOW);
  ...



Looks like there are two start bits (lo then hi; or a start bit and a "space").


For some reason, clocking in two extra bits helped. But the bits are still not in the right place.
I think I can pull it off with a look up table but it still is acting 100% correctly.
Avoid throwing electronics out as you or someone else might need them for parts or use.
Solid state rectifiers are the only REAL rectifiers.
Resistors for LEDS!

Coding Badly


You need to spend more time with the datasheet...
http://datasheet.elcodis.com/pdf/10/22/102280/mm5481n.pdf

After about a minute I found these on page 3...

Quote
Using a format of a leading "1'' followed by the 35 data bits allows data transfer without an additional load signal. The 35 data bits are latched after the 36th bit is complete,


Quote
To blank the display at any time, (i.e., power on), clock in 36 or more zeroes, followed by a `one' (start bit), followed by 36 or more zeroes.


Quote
Figure 5 shows the Output Data Format for the MM5481.  Because it uses only 14 of the possible 34 outputs, 20 of the bits are `Don't Cares'. Note that only alternate groups of 4 outputs are used.


AndrewStone

#8
Jan 20, 2012, 05:59 pm Last Edit: Jan 20, 2012, 07:13 pm by Coding Badly Reason: 1
The code in the blog works but is pretty old; it may have an extra bit bang in there.  Here's the "in-use" version: http://code.google.com/p/arduino-m5451-current-driver/source/browse/trunk/latest/libraries/lightuino5_lib_dev/lightuinoSink.cpp.  But note that it drives 2 chips at the same time using the same clock but separate data pins.  It should be really easy to reduce this down to 1 chip though.  Just remove all logic pertaining to serDataPin[1].  There is also a "fastSet" version in that file that does the same thing with direct register access.


Code: [Select]
void LightuinoSink::safeSet(unsigned long int a[3])
{
 int i;

 // Write the initial "start" signal
 digitalWrite(clockPin,LOW);
 digitalWrite(serDataPin[0],LOW);
 digitalWrite(serDataPin[1],LOW);

 mydelay(M5451_CLK);
 digitalWrite(clockPin,HIGH);
 mydelay(M5451_CLK);
 digitalWrite(clockPin,LOW);
 mydelay(M5451_CLK/2);
 digitalWrite(serDataPin[0],HIGH);
 digitalWrite(serDataPin[1],HIGH);
 mydelay(M5451_CLK/2);
 digitalWrite(clockPin,HIGH);

 mydelay(M5451_CLK);
 digitalWrite(clockPin,LOW);

 unsigned long int d2[2];
 d2[0] = a[1]>>3 | (a[2] << 29);
 d2[1] = a[2]>>3;
 // Write the bits
   
 
 for (i=0;i<M5451_NUMOUTS;i++)
 {
   int serDataVal[2];
   if (i<32) { serDataVal[0] = (a[0]&1); a[0]>>=1;}
   else { serDataVal[0] = (a[1]&1); a[1]>>=1;}
   if (i<32) { serDataVal[1] = (d2[0]&1); d2[0]>>=1;}
   else { serDataVal[1] = (d2[1]&1); d2[1]>>=1;}
   
   mydelay(M5451_CLK/2);
   digitalWrite(serDataPin[0],serDataVal[0]);
   digitalWrite(serDataPin[1],serDataVal[1]);
   mydelay(M5451_CLK/2);
   digitalWrite(clockPin,HIGH);
   mydelay(M5451_CLK);
   digitalWrite(clockPin,LOW);
 }
}




Also, an extremely fast version that drives 2 chips simultaneously fast enough for 8 bit PWM exists here http://code.google.com/p/arduino-m5451-current-driver/source/browse/trunk/latest/libraries/lightuino5_lib_dev/lightuinoPwm.cpp

Code: [Select]
#if defined(__AVR_ATmega328P__)
#define THEPORT PORTD
#define ARDUINO_NUMBERING_ADJUST 0
#elif (defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__))
#define THEPORT PORTA
#define ARDUINO_NUMBERING_ADJUST 22
#else
#error Fast mode is not supported on your processor.  Hack near this line of code to add it!!
#endif

#define CDELAY(x) call(x)
#define PREPDELAY(x) call(x)
//delayMicroseconds(x);
#define DELAYTIME 1
#define PDELAYTIME 1

//#define CHK() { if (rframe < *bri) {datain=LFT;} else datain=0; if (rframe < *bri2) {datain |=RGT;}; bri--; bri2--;    }
//#define WRI() { THEPORT = regVal; CDELAY(DELAYTIME); THEPORT = regVal | datain; CDELAY(DELAYTIME); THEPORT |= CLK; CDELAY(DELAYTIME); }

#define DoOne() { THEPORT = regVal; temp = *bri2; if ((temp>minBrightness)&&(rframe < temp)) {datain=LFT;} else datain=0; temp=*bri; if ((temp>minBrightness)&&(rframe < temp)) {datain |=RGT;}; CDELAY(DELAYTIME); THEPORT = regVal | datain;  CDELAY(DELAYTIME); bri++; THEPORT |= CLK; rframe -= Lightuino_MAX_BRIGHTNESS/(Lightuino_NUMOUTS); rframe&=Lightuino_MAX_BRIGHTNESS-1; CDELAY(DELAYTIME); bri2++; }

//rframe -= Lightuino_MAX_BRIGHTNESS/(Lightuino_NUMOUTS/2); rframe&=Lightuino_MAX_BRIGHTNESS-1;

static void call(unsigned char loop)
{
 for(unsigned char i=0;i<loop;i++)
   {
   asm("nop");
   }
}


void FlickerBrightness::loop(void)
{
 lock();
 //char i=Lightuino_NUMOUTS;
 //char pos;
 register unsigned char CLK = 1 << brd.clockPin - ARDUINO_NUMBERING_ADJUST;
 register unsigned char LFT = 1 << brd.serDataPin[0] - ARDUINO_NUMBERING_ADJUST;
 register unsigned char RGT = 1 << brd.serDataPin[1] - ARDUINO_NUMBERING_ADJUST;
 register int temp;
 unsigned char datain=0;
 register int* bri = &brightness[(Lightuino_NUMOUTS/2)];
 register int* bri2 = &brightness[0];
 
 frame++;
 if (frame>=Lightuino_MAX_BRIGHTNESS) frame=0;
 unsigned int rframe = reverseframe(frame);

 // Write the initial "start" signal
 THEPORT &= ~(CLK | LFT | RGT);  // all low
 uint8_t regVal  = THEPORT;      // remember all the other bits in the register
 PREPDELAY(PDELAYTIME);
 THEPORT = regVal | CLK;         // toggle clock
 PREPDELAY(PDELAYTIME);
 THEPORT = regVal;

 PREPDELAY(PDELAYTIME);
 THEPORT = regVal | LFT | RGT;   // raise the data line on all the chips
 PREPDELAY(PDELAYTIME);
 THEPORT = regVal | CLK | LFT | RGT;         // toggle clock
 PREPDELAY(PDELAYTIME);
 THEPORT = regVal; // & (~CLK);
 PREPDELAY(PDELAYTIME);
 
 DoOne();
 DoOne();
 DoOne();
 DoOne();
 DoOne();
     
 DoOne();
 DoOne();
 DoOne();
 DoOne();
 DoOne();

 DoOne();
 DoOne();
 DoOne();
 DoOne();
 DoOne();

 DoOne();
 DoOne();
 DoOne();
 DoOne();
 DoOne();

 DoOne();
 DoOne();
 DoOne();
 DoOne();
 DoOne();

 DoOne();
 DoOne();
 DoOne();
 DoOne();
 DoOne();
   
 DoOne();
 DoOne();
 DoOne();
 DoOne();
 DoOne();
 THEPORT &= ~(CLK | LFT | RGT);  // all low
 unlock();
}
#endif


Please post anything you get running on the TINY13 since I just bought a couple!  

Cheers!
Andrew


Moderator edit: [code] [/code] tags added.
http://www.toastedcircuits.com Lightuino LED driver: 16 sources, 70 sinks, remote controlled.  Also high powered LED drivers.

smeezekitty

#9
Jan 20, 2012, 09:39 pm Last Edit: Jan 21, 2012, 12:03 am by smeezekitty Reason: 1


You need to spend more time with the datasheet...
http://datasheet.elcodis.com/pdf/10/22/102280/mm5481n.pdf

After about a minute I found these on page 3...

Quote
Using a format of a leading "1'' followed by the 35 data bits allows data transfer without an additional load signal. The 35 data bits are latched after the 36th bit is complete,

The datasheet I found from national semiconductor had very little technical information.


Quote
Figure 5 shows the Output Data Format for the MM5481.  Because it uses only 14 of the possible 34 outputs, 20 of the bits are `Don't Cares'. Note that only alternate groups of 4 outputs are used.



Thats a pain to deal with. Why couldn't they just used the lowest 14 bits?

In any case, I have been able to drive a 7 segment display with an attiny13.
May upload a video later to youtube.
-----edit-----
Code: [Select]

#define F_CPU 128000
#include <util/delay.h>
#include <avr/io.h>
#include <inttypes.h>
#define SCK _BV(PB4)
void shreg(uint16_t a){
int z;
//Start sequence ?
PORTB = 0; // All pins low
_delay_ms(1);
PORTB |= SCK; //SCK high
_delay_ms(1);
PORTB &= ~SCK; //SCK low
_delay_ms(1);
PORTB |= _BV(PB3); //Serial data high
_delay_ms(1);
PORTB |= SCK; //Sck high
_delay_ms(1);
PORTB &= ~SCK; //SCK LOW
_delay_ms(1);
for(z = 0;z<37;z++){
if(z < 15){
PORTB = (_BV(PB3) * (a&1)); //Output bit if < 14
a>>=1;
} else {
PORTB = 0; //Otherwise output nothing
}
_delay_ms(1);
PORTB |= _BV(PB4); //SCK high
_delay_ms(1);
PORTB &= ~_BV(PB4); //SCK low
_delay_ms(1);
/* PORTB = 0; //All pins off
_delay_ms(1);*/
}
             
}
#define BOTTOM 16//-
#define BOTTOMLEFT 32//-
#define BOTTOMRIGHT 64//-
#define CENTER 128//-
#define TOPLEFT 4096//-
#define TOP 8192//-
#define TOPRIGHT 16384//-
void ledprint(int n){
switch(n){
case 0:
shreg(BOTTOM | BOTTOMLEFT | BOTTOMRIGHT | TOPRIGHT | TOPLEFT | TOP);
break;
case 1:
shreg(TOPRIGHT | BOTTOMRIGHT);
break;
case 2:
shreg(TOP | TOPRIGHT | CENTER | BOTTOMLEFT | BOTTOM);
break;
case 3:
shreg(TOP | TOPRIGHT | BOTTOMRIGHT | BOTTOM | CENTER);
break;
case 4:
shreg(TOPLEFT | TOPRIGHT | CENTER | BOTTOMRIGHT);
break;
case 5:
shreg(TOP | TOPLEFT | CENTER | BOTTOMRIGHT | BOTTOM);
break;
case 6:
shreg(TOP | TOPLEFT | CENTER | BOTTOMLEFT | BOTTOMRIGHT | BOTTOM);
break;
case 7:
shreg(TOP | TOPRIGHT | BOTTOMRIGHT);
break;
case 8:
shreg(0xFFFF);
break;
case 9:
shreg(TOPRIGHT | TOPLEFT | TOP | CENTER | BOTTOMRIGHT | BOTTOM);
break;
}
}
int main(){
int cat = 9;
DDRB = _BV(PB3) | _BV(PB4);
PORTB = 0;
while(1){
ledprint(cat--);
if(cat < 0){cat = 9;}
_delay_ms(100);
}
}

Note, I used way long 1ms delays between operations. Much shorter delays would probably work fine.
Even a simple
Code: [Select]

asm ("nop");
asm ("nop");
might be enough since the Attiny13 is clock with the 128khz internal oscillator.

Avoid throwing electronics out as you or someone else might need them for parts or use.
Solid state rectifiers are the only REAL rectifiers.
Resistors for LEDS!

smeezekitty

Avoid throwing electronics out as you or someone else might need them for parts or use.
Solid state rectifiers are the only REAL rectifiers.
Resistors for LEDS!

Go Up