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 (MM5481N pdf, MM5481N Description, MM5481N Datasheet, MM5481N view ::: ALLDATASHEET :::) 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:
#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.
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 :-)!!).
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.
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.
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:
#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.
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.
Andrew Stone's code is clocking out an extra two bits when the LED driver is "cleared"...
#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...
// 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.
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,
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.
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.
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: Google Code Archive - Long-term storage for Google Code Project Hosting.. 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.
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);
}
}