Wednesday, 12 October 2016

Messing about with MAX7219 common anode 8x8 LED matrix and Arduino

Many years ago, we built a couple of word clocks. They were pretty good fun and we used both 74HC595 shift registers and the MAX7219 LED driver chip. As we've been working with Arduino so much in recent months, we figured it'd be interesting to port some earlier code to create a working prototype.

So here it is:

Firstly, we had no information on our LED matrix. It was in a drawer somewhere so we had to work out the connections. This involved nothing more than taking a 3v lead and a ground lead and touching them to each of the pins on the matrix, to see which LEDs lit up.
As each LED  lit up, we noted which pin was grounded and which had power, and from this we were able to calculate which pin was for which row and which pin was for which column.


Once we had worked out the rows and columns on the 8x8 matrix, it was a case of hooking up a few wires and giving it a try!


According to the MAX7219 datasheet, turning LEDs on and off involves writing data to memory addresses using a two-byte 16-bit value. Luckily for us, it's a relatively simple protocol.

The first byte is the memory address - or row - to write the "image data" to. Memory address 0x01 corresponds to row 1, 0x02 to row 2 etc.

The second byte contains the LED on/off data in binary format, where 1 = LED on and 0 = LED off. So if we wanted to switch on just the first two LEDs in row 3, we would send the two byte message

0x03, 0xC0 (where 0xC0 is binary 11000000)

So we built some simple bit-banging routines (no nasty libraries here!) to pump data into the MAX7219 chip.





#define data_pin    7
#define clock_pin 8
#define load_pin    9
#define led_pin    13
bool led = false;

void sendData(int k){
    int j;

    Serial.print(F("sending: "));
    digitalWrite(load_pin,LOW);
   
    for(int i=0; i<=15; i++){
        j = k & 0x8000;       
       
        if(j==0){
            digitalWrite(data_pin,LOW);
            Serial.print("0");
        }else{
            digitalWrite(data_pin,HIGH);
            Serial.print("1");
        }

        digitalWrite(clock_pin,HIGH);       
        delayMicroseconds(20);
        digitalWrite(clock_pin,LOW);       
        k = k << 1;               
    }
    Serial.println("");   
    toggleLoadPin();       
}

void toggleLoadPin(){
    digitalWrite(load_pin,HIGH);
    delay(1);
    digitalWrite(load_pin,LOW);   
}

void sendValue(int ad, int v){
    int k=0;   
    k=ad;
    k=k<<8;
    k=k|v;   
    sendData(k);   
}

void setup() {
    // put your setup code here, to run once:

    pinMode(data_pin,OUTPUT);
    pinMode(clock_pin,OUTPUT);
    pinMode(load_pin,OUTPUT);   

    Serial.begin(19200);
   
    // this turns off the LEDs on the matrix
    // (though we can still send data to the max7219)   
    // lc.shutdown(0,false);// turn off power saving, enables display
    // lc.setIntensity(0,15);// sets brightness (0~15 possible values)
    // lc.clearDisplay(0);// clear screen
   
    int k=0;
    int addr;
    int val;

   
    // no decode mode
    sendValue(0x09, 0x00);    // do not use BCD

    // full brightness
    sendValue(0x0A, 0x0F);

    // scan limit = 8 (use 8 rows)
    sendValue(0x0B, 0x07);

    // normal operation
    sendValue(0x0C, 0x01);    // display on
    sendValue(0x0F, 0x00);    // no test mode

    // display test
    sendValue(0x0F, 0x01);
    delay(1500);
    sendValue(0x0F, 0x00);
   
    // now light up all rows   
    sendValue(0x01,0x55);
    sendValue(0x02,0xAA);
    sendValue(0x03,0x0F);
    sendValue(0x04,0xF0);
    sendValue(0x05,0x00);
    sendValue(0x06,0xFF);
    sendValue(0x07,0x3C);
    sendValue(0x08,0xC3);
   
}

void loop(){   
    // put your main code here, to run repeatedly:

}

To pump data onto the LED display, simply call the function SENDVALUE (row, led_data)