K115 8×8 dot led module shield for Arduino

In this project, we are going to show you how to make a shield for Arduino Uno with an 8×8 dot display, and how to program the Arduino in order to display shapes or scrolling messages. The 8×8 dot display is connected serially to Arduino board through the integrated circuit MAX7219 or MAX7221 LED display driver. In the following, first, we will describe the IC driver and how it communicates serially with the Arduino board.

About the MAX7219 and MAX7221 drivers

The MAX7219/MAX7221 are compact, serial input/output common-cathode display drivers that interface microcontrollers to 7-segment numeric LED displays of up to 8 digits or 64 individual LED’s. The MAX7219 is compatible with the standard serial protocol. The MAX7221 is compatible with SPI protocol and has slew rate-limited segment drivers to reduce EMI. The MAX7219/MAX7221 also allows the user to select code-B decoding or no-decode for each digit.

The microcontroller sends the 16-bit packet serially to the MAX7219/MAX7221 driver. Data bits are labelled D0-D15 (see Table 1), D8-D11 contain the register’s address, D0-D7 contain the data and D12-D15 are “don’t care” bits. The first received is D15, the most significant bit.

The MAX7219/MAX7221 has fourteen digit and control registers. Each register contains 8-bit data and has its own address (see Table 2). Digit registers are addressed directly so that individual digits can be updated. The control registers consist of decode mode, display intensity, scan limit (number of scanned digits), shutdown and display test (all LED’s on). On initial power-up all control registers are reset; the display is blanked and the MAX7219/MAX7221 enter shutdown mode.

The MAX7219 or MAX7221 serial data at DIN sent in 16-bit packets, is shifted into the internal 16-bit shift register with each rising edge of CLK. For the MAX7221 CS must be low to clock data in or out. The data is then latched into either the digit or control registers on the rising edge of LOAD/CS. LOAD/CS must go high concurrently with or after the 16th rising clock edge but before the next rising clock edge or data will be lost.

When the MAX7219/MAX7221 is in shutdown mode, the display is blanked. Data in the digit and control registers remains unaltered. Shutdown can be used to save power or to flash the display by entering and leaving shutdown mode. The shutdown register has Address 0x0C and data for shutdown mode 0x00 and normal operation 0x01.

The decode-mode register sets BCD code B or no-decode operation for each digit. Its address is 0x09. Each bit in the register corresponds to one digit. A logic high selects code B decoding while a logic low bypasses the decoder for this digit. (see datasheet for more details).

The MAX7219/MAX7221 allows display brightness to be controlled with an external resistor RSET connected between V+ and ISET (see datasheet for more details).

The scan-limit register sets how many digits are displayed from 1 to 8. It has address 0x0B and the register data is between 0x00  (digit 0 only) to 0x07  (all digits).

The display-test register (Address 0x0F) operates in two modes: normal (register data 0x00) and display test (register data 0x01). Display-test mode turns all LED’s on by overriding, all controls and digit registers.

How to program Arduino to communicate with MAX7219

We wire the serials inputs DIN, LOAD and CLK of the driver to any I/O pins of Arduino. In our sketch we insert the lines:

#define DIN 11     //e.g. we wire DIN to I/O pin 11
#define LOAD  7   //e.g. we wire LOAD to I/O pin 7
#define CLK  13     //e.g. we wire CLK to I/O pin 13

We define the following function:

void max7219Transfer(unsigned char address, unsigned char value){
    digitalWrite(LOAD, LOW);
    shiftOut(DIN, CLK, MSBFIRST, address);
    shiftOut(DIN, CLK, MSBFIRST, value);
    digitalWrite(LOAD, HIGH);
    delay(1);
}

Inside in this function we use the function:

   shiftOut(DIN, CLK, MSBFIRST, num);

to shift serially the 8-bit num through pin of DIN, with clock pin of CLK. In setup()  function we have firstly defined the I/O pins: DIN, CLK, LOAD as output pins.

To send the data byte e.g. 0x03 to digit 5 register which has Address 0x06, we use the following instruction:

   max7219Transfer(0x06, 0x03);

How to program Arduino to communicate with MAX7221

The driver MAX7221 uses SPI protocol to communicate with Arduino. So we wire the pins DIN and CLK of MAX7221 with the pins MOSI and SCK of Arduino respectively and the CS pin to any I/O pin of Arduino, e.g. pin 7.

In our sketch we include the SPI library and we define the CS_PIN:

#include  <SPI.h>
#define CS_PIN 7  //e.g. CS_PIN is I/O pin 7

We define the following function:

void max7221Transfer(unsigned char address, unsigned char value){
    digitalWrite(CS_PIN, LOW);
    SPI.transfer(address);
    SPI.transfer(value);
    digitalWrite(CS_PIN, HIGH);
    delay(1);
}

Inside the function we use the function:

  SPI.transfer(value);

to send the 8-bit value serially to MAX7221. Firstly in setup() function we have inserted the lines:

  SPI.begin();   //initialises the SPI connection
  pinMode(CS_PIN, OUTPUT);   //sets the pin CS_PIN as output

To send the data byte e.g. 0x03 to digit 5 register which has Address 0x06, we use the instruction:

   max7221Transfer(0x06, 0x03);

How we store data in program memory

If we have the following array of 8-bit data and we want to store it in program memory instead of SRAM, we define it as:

const uint8_t numSet[] PROGMEM = {0x00, 0x01, 0x02, 0x03, 0x04};

The PROGMEM keyword is a variable modifier, it should be used only with the datatypes defined in pgmspace.h. PROGMEM is part of the pgmspace.h library. It is included automatically in modern versions of the IDE, however if you are using an IDE version below 1.0 (2011), you’ll first need to include the library at the top your sketch, like this:

#include <avr/pgmspace.h>

To read the data from program memory back into SRAM we use the instruction:

uint8_t   k = 2;  //is the index of the number in the array
uint8_t myNum =  pgm_read_byte_near(numSet + k);
Serial.print(myNum);   //prints the 8-bit number:  0x02

if the array has 16-bit date we use the function: pgm_read_word_near(numSet + k);

Construction

The shield is an 6.0cm X 5.5cm pcb board which on it there are a 8X8 dot LED module (common cathode display), the integrated circuit MAX7219 or MAX7221 LED driver, the 10K resister for brightness control, a 220nF ceramic capacitor for voltage smoothing and two rows of pins.

The schematic of shield is shown below:

The construction of the shield is very easy because we use a little component and a small through hole pcb. We must give attention to the right placement of the dot LED module and the integrated circuit.

After we have assembled the shield we insert it to Arduino Uno and connect the USB cable. We open the Arduino IDE program on our PC and before typing one of the following sketches, we must choose the right board from the Menu: Tools>Board: “Arduino/Genuino Uno” and the right port from the Menu: Tools>Port>COM?

Example sketch 1: Displaying a smile image

In this example sketch a smile image is displaying on 8×8 dot LED module. Here we use the MAX7221 LED driver which communicates with Arduino via SPI protocol. The image is stored on byte image[8]  array.

How to make the image array of numbers: The array consists of eight number. The first number corresponds to the first column of 8×8 led matrix, the second number to the second column etc. Each column consists of eight dot led’s. Every led in the column has the corresponding “weight”. So the first led from the bottom has weight “1”, the second “2”, the third “4”, the quarter “8”, the fifth “16”, the sixth “32”, the seventh “64” and the eighth “128”. The number that corresponds the specific column of dot image is formed from the sum of “wights” of the led’s in which are switched on.

#include <SPI.h>
#define CS_PIN 7
byte image[8]={ 60, 66, 169, 133, 133, 169, 66, 60};
void max7221Transfer(byte address, byte value){
    digitalWrite(CS_PIN, LOW);
    SPI.transfer(address);
    SPI.transfer(value);
    digitalWrite(CS_PIN, HIGH);
    delay(1);
}
void setup() {
   SPI.begin();
   pinMode(CS_PIN, OUTPUT);
   digitalWrite(CS_PIN, HIGH);
   SPI.setBitOrder(MSBFIRST);
   SPI.setDataMode(SPI_MODE0);
   max7221Transfer(0x0C, 0x01); //Shutdown Register Format: Normal Operation
   max7221Transfer(0x0F, 0x01); //Display – Test Register Format: Display Test Mode
   delay(2000);
   max7221Transfer(0x0F, 0x00); //Display – Test Register Format: Normal Operation
   max7221Transfer(0x0B, 0x07); // Scan – Limit Register Format: Display digits 01234567
   max7221Transfer(0x09, 0x00); //Decode – Mode Register Format: No decode for digits 7-0
}
void loop() {
   for(byte j=1; j<=8; j++) max7221Transfer(j, 0);
   for(byte i=1; i<=8; i++) max7221Transfer(i, image[i-1]);
   for(byte m=1; m<=60; m++) delay(1000);
   max7221Transfer(0x0C, 0x01); //Shutdown Register Format: Shutdown Mode
   while(1);
}

Example sketch 2: Displaying a scrolling message

In this example sketch the message “HELLO WORLD! MY NAME IS ARDUINO” which is stored in char line[] array is running on 8X8 dot LED module. Here we use the MAX7221 LED driver which communicates with Arduino via SPI protocol.

#include <SPI.h>
#define CS_PIN 7
const byte letters[][5] = { { 31,36,68,36,31}, //A
           {127,73,73,73,54}, //B
           {62,65,65,65,34}, //C
           {127,65,65,34,28}, //D
           {127,73,73,73,65}, //E
           {127,72,72,72,64}, //F
           {62,65,65,73,46}, //G
           {127,8,8,8,127}, //H
           {0,65,127,65,0}, //I
           {2,1,1,1, 126}, //J
           {127,8, 20,34,65}, //K
           {127,1,1,1,1}, //L
           {127,32,16,32,127}, //M
           {127,32,16,8,127}, //N
           {62,65,65,65,62}, //O
           {127,72,72,72,48}, //P
           {62,65,69,67,63}, //Q
           {127,72,76,74,49}, //R
           {48,73,73,73,6}, //S
           {64,64,127,64,64}, //T
           {126,1,1,1,126}, //U
           {124,2,1,2,124}, //V
           {127,2,4,2,127}, //W
           {99,20,8,20,99}, //X
           {64,32,31,32,64}, //Y
           {67,69,73,81,97}, //Z
           {62,65,65,65,62}, //0
           {0,32,127,0,0}, //1
           {79,73,73,73,121}, //2
           {73,73,73,73,127}, //3
           {120,8,8,8,127}, //4
           {121,73,73,73,79}, //5
           {127,73,73,73,79}, //6
           {64,64,64,64,127}, //7
           {127,73,73,73,127}, //8
           {121,73,73,73,127}, //9
           {0,0,0,0,0}, //' '
           {0,0,121,0,0}, //!
           {32,64,69,72,48} //?};
};

byte matrix[300];
byte len, m;
char line[] = "HELLO WORLD! MY NAME IS ARDUINO";
char c, ch;

void max7221Transfer(unsigned char address, unsigned char value){
    digitalWrite(CS_PIN, LOW);
    SPI.transfer(address);
    SPI.transfer(value);
    digitalWrite(CS_PIN, HIGH);
    delay(1);
}

void setup() {
    SPI.begin();
    pinMode(CS_PIN, OUTPUT);
    digitalWrite(CS_PIN, HIGH);
    SPI.setBitOrder(MSBFIRST);
    SPI.setDataMode(SPI_MODE0);
    max7221Transfer(0x0F, 0x01); //Display – Test Register Format: Display Test Mode
    delay(2000);
    max7221Transfer(0x0F, 0x00); //Display – Test Register Format: Normal Operation
    max7221Transfer(0x09, 0x00); //Decode – Mode Register Format: No decode for digits 7-0
    max7221Transfer(0x0C, 0x01); //Shutdown Register Format: Normal Operation
    max7221Transfer(0x0B, 0x07); // Scan – Limit Register Format: Display digits 01234567

    len = strlen(line);
    for(m=0; m<len; m++){
       ch=line[m];
       if(ch>='A' && ch<='Z') c=ch-'A';
       else if(ch>='0' && ch<='9') c=26+(ch-'0');
       else if(ch==' ') c=36;
       else if(ch=='!') c=37;
       else c=38;
       for(byte n=0; n<=4; n++) matrix[(m+1)*6+n] = letters[n];
    }
matrix[(m+1)*6+5]=0;
}

void loop() {
    for(byte j=0; j<(len+1)*6; j++){
       for(byte i=0; i<8; i++) max7221Transfer(i+1, matrix[j+i]);
       delay(200);
    }
}

Example sketch 3: Displaying a message letter by letter

In this example sketch the message “?HELLO WORLD!” which is stored in char line[] array is passing letter by letter on 8X8 dot LED module. Here we use the MAX7219 LED driver which communicates with Arduino via simple serial protocol.

#include <avr/pgmspace.h>
#define DIN 11
#define LOAD 7
#define CLK 13
char c, ch;
const byte letters[] PROGMEM = {31,36,68,36,31, //A
                                127,73,73,73,54, //B
                                62,65,65,65,34, //C
                                127,65,65,34,28, //D
                                127,73,73,73,65, //E
                                127,72,72,72,64, //F
                                62,65,65,73,46, //G
                                127,8,8,8,127, //H
                                0,65,127,65,0, //I
                                2,1,1,1, 126, //J
                                127,8, 20,34,65, //K
                                127,1,1,1,1, //L
                                127,32,16,32,127, //M
                                127,32,16,8,127, //N
                                62,65,65,65,62, //O
                                127,72,72,72,48, //P
                                62,65,69,67,63, //Q
                                127,72,76,74,49, //R
                                49,73,73,73,70, //S
                                64,64,127,64,64, //T
                                126,1,1,1,126, //U
                                124,2,1,2,124, //V
                                127,2,4,2,127, //W
                                99,20,8,20,99, //X
                                64,32,31,32,64, //Y
                                67,69,73,81,97, //Z
                                62,65,65,65,62, //0
                                0,32,127,0,0, //1
                                79,73,73,73,121, //2
                                73,73,73,73,127, //3
                                120,8,8,8,127, //4
                                121,73,73,73,79, //5
                                127,73,73,73,79, //6
                                64,64,64,64,127, //7
                                127,73,73,73,127, //8
                                121,73,73,73,127, //9
                                0,0,0,0,0, //' '
                                0,0,121,0,0, //!
                                32,64,69,72,48 //?
                             };

byte matrix[300];
byte len;
char line[] = "?HELLO WORLD!";

void max7219Transfer(unsigned char address, unsigned char value){
    digitalWrite(LOAD, LOW);
    shiftOut(DIN, CLK, MSBFIRST, address);
    shiftOut(DIN, CLK, MSBFIRST, value);
    digitalWrite(LOAD, HIGH);
    delay(1);
}

void setup() {
    pinMode(DIN, OUTPUT);
    pinMode(CLK, OUTPUT);
    pinMode(LOAD, OUTPUT);
    digitalWrite(LOAD, HIGH);
    digitalWrite(DIN, LOW);
    digitalWrite(CLK, LOW);
    max7219Transfer(0x0F, 0x01);
    delay(2000);
    max7219Transfer(0x0F, 0x00);
    max7219Transfer(0x09, 0x00);
    max7219Transfer(0x0C, 0x01);
    max7219Transfer(0x0B, 0x07);
    for(byte j=1; j<=8; j++) max7219Transfer(j, 0);
    len = strlen(line);
}

void loop() {
    for(byte m=0; m<len; m++){
       ch=line[m];
       if(ch>='A' && ch<='Z') c=ch-'A';
       else if(ch>='0' && ch<='9') c=26+(ch-'0');
       else if(ch==' ') c=36;
       else if(ch=='!') c=37;
       else c=38;
       for(byte n=0; n<=4; n++){
           max7219Transfer(n+2, pgm_read_byte_near(letters+c*5+n));
       }
       delay(1000);
    }
    for(byte j=1; j<=8; j++) max7219Transfer(j, 0);
    delay(2000);
}