The MAX7219 is a Integrated Circuit that can drive 64 individual LED's or up to 8 digits of 7-segment display's or a LED matrix of 8x8. The driver implements a SPI interface that can be controlled from a microcontroller using only three I/O pins.

The IC itself has 24 pins. An extensive datasheet for the IC is available from the Maxim homepage. The IC in this project is used to drive a 4 digit 7-segment display to show the time. The display is of the common caythode type. The ATMega328 microcontroller is used to control the 3 pin SPI interface.  


Below is the schematic that shows how the IC is wired to the ATMEGA328 microcontroller and the 4 digit 7-segment display with comon cathode.

led driver max7219 clock sch

Besides the MAX7219 you need only three other external components: two capacitors and one resistor.

The capacitors are here to reduce noise signals and cannot be ommited.

The resistor is used to set the limit for the LED's current. It should be at least 10kohm. The MAX7219 has to be powered with 5VDC. It is possible to use the +5V of the ATMEGA328 boerd. Both the ground pins of the MAX7219 have to be connected to the ATMEGA328 board.

The three signal lines (DIn,CLK,Load(/CS)) have to be connected to three digital outputs on the ATMEGA328 board.


 

The code is written in C with the WinAVR compiler.

// program name: LED driver MAX7219 - clock
// date: 2016.07.15
// author: www.avrprojects.net
// target device : ATMEGA328
// hardware: MAX7219, 7 segment LED display 4 digit

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define F_CPU = 8000000UL

#define clk_low 0b11111110
#define clk_high 0b00000001
#define load_low 0b00000000
#define load_high 0b00000010
#define din_low 0b11111011
#define din_high 0b00000100

// adress
#define noop 0x00
#define digit0 0x01
#define digit1 0x02
#define digit2 0x03
#define digit3 0x04
#define digit4 0x05
#define digit5 0x06
#define digit6 0x07
#define digit7 0x08
#define decode 0x09
#define intense 0x0A
#define scanlim 0x0B
#define shutdown 0x0C
#define distest 0x0F
#define dp 0b10000000


char second = 0;
int time_cntr = 0; //global variable for number of Timer 0

/// Timer 0 overflow interrupt service routine
ISR(TIMER0_OVF_vect)
{
//8.000.000hz/64=125000hz = 8µmS x 125 ticks = 1mS
//set timer/counter to 255-125 = 130
TCNT0= 130;
time_cntr++;
if (time_cntr == 250) /*check for one second, increment counter*/
{
time_cntr = 0 ;
second++;
}
}

void clk_pulse(void)
{
PORTC = PORTC & clk_low;
//_delay_ms(1);
PORTC = PORTC | clk_high;
//_delay_ms(1);
PORTC = PORTC & clk_low;
}

void send_bits(unsigned char data)
{
int i;
for (i = 0; i < 8; i++)
{
// consider leftmost bit
// set line high if bit is 1, low if bit is 0
if (data & 0x80)
PORTC = PORTC | din_high;
else
PORTC = PORTC & din_low;
// pulse clock to indicate that bit value should be read
clk_pulse();
PORTC = PORTC & din_low;
// shift byte left so next bit will be leftmost
data <<= 1;
}
}

void send_data(unsigned char adress, unsigned char data)
{
send_bits(adress);
send_bits(data);
PORTC = load_low;
PORTC = load_high;
}


void test(void) // test display
{
{
send_data(distest,0x01);
_delay_ms(1000);
send_data(distest,0x00);
}
}

int main (void)

{
// init
DDRC = 0xFF; //set PORTC as output
PORTC = 0; // reset PORTC

char hour = 11;
char minute= 18;

//set timer 0 prescaler to clk/256*/
TCCR0A = 0b00000000;
TCCR0B = 0b00000100;
// enable Timer 0 overflow interrupt*/
TIMSK0 = 0x01;
sei();

// init display
send_data(shutdown,0x01);
send_data(scanlim,0x03);
send_data(intense,0x04);
send_data(decode,0x0F);
test();

while(1)
{
{
send_data(digit0,hour / 10);
send_data(digit1,hour % 10 | dp);
send_data(digit2,minute / 10);
send_data(digit3,minute % 10);
if (second > 59)
{
second = 0;
minute++;
if (minute>59)
{
minute = 0;
hour++;
if (hour>23)
{
hour = 0;
}
}
}
}
}
}