Pages: [1]   Go Down
Author Topic: Attiny13A + MM5481N LED driver?  (Read 1056 times)
0 Members and 1 Guest are viewing this topic.
Washington
Offline Offline
God Member
*****
Karma: 38
Posts: 789
Firefox & Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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


* mewmatic.jpg (72.08 KB, 788x489 - viewed 32 times.)
Logged

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!

Boston, MA
Offline Offline
Jr. Member
**
Karma: 0
Posts: 83
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

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

Washington
Offline Offline
God Member
*****
Karma: 38
Posts: 789
Firefox & Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


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

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!

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 197
Posts: 12744
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
   PORTB &= SCK; //SCK low

The comment doesn't match the code.  I believe you're missing a bitwise unary operator.   smiley-wink
Logged

Washington
Offline Offline
God Member
*****
Karma: 38
Posts: 789
Firefox & Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
   PORTB &= SCK; //SCK low

The comment doesn't match the code.  I believe you're missing a bitwise unary operator.   smiley-wink

LOL thanks for the catch. That was stupid of me  smiley-sad
Now its working "better" but still not right.
I changed the code as follows:
Code:
#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.
Logged

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!

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 197
Posts: 12744
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


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").
Logged

Washington
Offline Offline
God Member
*****
Karma: 38
Posts: 789
Firefox & Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


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

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!

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 197
Posts: 12744
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


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

Boston, MA
Offline Offline
Jr. Member
**
Karma: 0
Posts: 83
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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:
#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.
« Last Edit: January 20, 2012, 01:13:26 pm by Coding Badly » Logged

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

Washington
Offline Offline
God Member
*****
Karma: 38
Posts: 789
Firefox & Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


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:
#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:
asm ("nop");
asm ("nop");
might be enough since the Attiny13 is clock with the 128khz internal oscillator.

« Last Edit: January 20, 2012, 06:03:43 pm by smeezekitty » Logged

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!

Washington
Offline Offline
God Member
*****
Karma: 38
Posts: 789
Firefox & Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Youtube video:
Logged

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!

Pages: [1]   Go Up
Jump to: