In this tutorial, I will explore the functionalities of a very interesting piece of hardware: an 8×8 LED Matrix. It is difficult not to see this type of hardware in our everyday lives, since it is used in industrial displays, destination signs in transports, danger signalization, clocks, and sports displays. So, if you want to understand how these work, you’re in the right place!
Parts Required
- Arduino Mega 2560;
- USB 2.0 Cable Type A/B;
- 1588BS 8×8 LED Matrix;
- 8 220Ω resistors;
- Male to male jumper wires;
- 1 Breadboard.
The LED Matrix
The LED Matrix used in this tutorial is a 1588BS module that encompasses 8 rows and 8 columns of LEDs, as shown in Figure 1.
In total, this module integrates 64 LEDs. However, as we can see in Figure 1, our LED matrix only has 16 pins. So you might be thinking: how can we power 64 LEDs using only 16 pins? To answer this question, we have to understand how the LEDs are connected. In Figure 2, it is possible to see that there is a grid connecting them all together, where the LEDs in each row share a common anode and the LEDs in each column share a common cathode. The 16 pins of the 1588BS module correspond to the 8 rows and 8 columns of the matrix.
Therefore, in order to power a specific LED, we just need to apply 5V and 0V to the correspondent row and column, respectively, (Figure 3). Moreover, since LEDs operational current is normally 20mA [2], we cannot forget to limit the current flowing through the LED, and so a 220Ω resistor is needed (the current value will be approximately 22mA according to Ohm’s law).
The way resistors are connected is not arbitrary. If one would want, for instance, to light on the fourth row of the LED matrix, the results from connecting the resistors to the rows (anodes) and columns (cathodes) would be significantly different. Since, in each row, the corresponding LEDs are connected in parallel, in the first scenario (Figure 4.a) the 22mA of current would be divided by the 8 LEDs of the row. Thus, the current flowing in each LED would be approximately 2.75mA and it would be difficult to see the row light on. As for the second scenario (Figure 4.b), the current flowing in each LED would be 22mA since each one has its own resistor. In this case, it would be perfectly possible to see the row.
Note: The 1588BS module has a common anode configuration (rows -> anodes). If the module you are using has a common cathode configuration, then you must connect the resistors to the rows of the LED matrix.
As the main purpose of this kind of hardware is to show information, it is important to know how to show characters or figures. However, a new challenge emerges when one tries to output these shapes in the LED matrix. Considering that one wants to output a very simple diagonal line that will start in the top left corner and finish in the bottom right corner of the matrix (Figure 5).
In this case, it would be necessary to connect all the rows to 5V and all the columns to ground (0V). By doing so, the output would not be the one desired. If we wanted to show the diagonal line row by row, the problem would appear the moment we connect the second row and column to 5V and ground, respectively. Because the first row and column were already connected, all LEDs shared by the two pairs of rows and columns would be on. Thus, as we can imagine, all LEDs from the matrix would be turned on at the end, as shown in Figure 6
To solve this issue, we need to apply a multiplexing method (or scanning), in order to alternately connect and disconnect the rows and the necessary columns to print the desired character. More specifically, and taking into consideration the diagonal line from Figure 5, we start by connecting the first row to 5V. Then, the LED we want to be on in this row is also in the first column, so we connect it to ground. Then, after a certain amount of time, we disconnect the previous row and column and repeat the process for all the remaining rows. The result obtained by applying this method can be seen in Figure 7. The speed at which we connect and disconnect the rows and columns can be set so that we are not able to detect the multiplexing effect.
This way, it is possible to print everything we want in the LED matrix!
Circuit
Due to its dimensions, it can be difficult to use the 1588BS module on most breadboards. Therefore, my advice is to start by setting the breadboard as shown in Figure 8. The LED matrix is inserted on top of the jumper wires (area marked by a black line) and all pin connections are made on the sides.
For the sake of convenience, I have connected the 1588BS pins to my Arduino Mega pins according to Table 1. You can connect your LED matrix to all digital pins from your board. In Figure 9 it is possible to see the circuit´s schematic.
ROWS | ARDUINO PINS | COLUMNS | ARDUINO PINS | |
1 | 37 | 1 | 44 | |
2 | 45 | 2 | 52 | |
3 | 30 | 3 | 53 | |
4 | 38 | 4 | 36 | |
5 | 50 | 5 | 32 | |
6 | 31 | 6 | 39 | |
7 | 51 | 7 | 46 | |
8 | 33 | 8 | 47 |
Code
Finally, it’s time for some code! In this work, the purpose is to display in the 8×8 LED matrix any message inserted by the user character by character. After defining the rows and columns’ pins, all letters and numbers were converted into an array of bytes corresponding to the LEDs to be on row by row. Then, the functions required to implement the multiplexing method are developed, in order to connect alternately all rows and the specific columns to form the desired character. Finally, the message inserted by the user in the Arduino IDE (Tools -> Serial Monitor)is treated and displayed in the LED matrix.
//Definition of the pins for the rows and columns #define ROW_1 37 // Pin 9 of the LED matrix #define ROW_2 45 // Pin 14 of the LED matrix #define ROW_3 30 // Pin 8 of the LED matrix #define ROW_4 38 // Pin 12 of the LED matrix #define ROW_5 50 // Pin 1 of the LED matrix #define ROW_6 31 // Pin 7 of the LED matrix #define ROW_7 51 // Pin 2 of the LED matrix #define ROW_8 33 // Pin 5 of the LED matrix #define COL_1 44 // Pin 13 of the LED matrix #define COL_2 52 // Pin 3 of the LED matrix #define COL_3 53 // Pin 4 of the LED matrix #define COL_4 36 // Pin 10 of the LED matrix #define COL_5 32 // Pin 6 of the LED matrix #define COL_6 39 // Pin 11 of the LED matrix #define COL_7 46 // Pin 15 of the LED matrix #define COL_8 47 // Pin 16 of the LED matrix const byte row[] = { ROW_1, ROW_2, ROW_3, ROW_4, ROW_5, ROW_6, ROW_7, ROW_8 }; const byte col[] = { COL_1, COL_2, COL_3, COL_4, COL_5, COL_6, COL_7, COL_8 }; // Definition of the leds to be ON for each row in order to display the desired caracter (1 = ON, 0 = OFF) byte NONE[] = {B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000}; byte A[] = {B00011000,B00111100,B01100110,B01100110,B01111110,B01111110,B01100110,B01100110}; byte B[] = {B00111110,B01100110,B01100110,B01111110,B00111110,B01100110,B01100110,B00111110}; byte C[] = {B01111100,B01111110,B00000110,B00000110,B00000110,B00000110,B01111110,B01111100}; byte D[] = {B00011110,B00111110,B01100110,B01100110,B01100110,B01100110,B00111110,B00011110}; byte E[] = {B01111110,B01111110,B00000110,B01111110,B01111110,B00000110,B01111110,B01111110}; byte F[] = {B01111110,B01111110,B00000110,B00111110,B00111110,B00000110,B00000110,B00000110}; byte G[] = {B00011100,B00111110,B00100110,B00000110,B01110110,B00100110,B00111110,B00011100}; byte H[] = {B01100110,B01100110,B01100110,B01111110,B01111110,B01100110,B01100110,B01100110}; byte I[] = {B01111110,B01111110,B00011000,B00011000,B00011000,B00011000,B01111110,B01111110}; byte J[] = {B01111110,B01111110,B00011000,B00011000,B00011000,B00011000,B00011110,B00001110}; byte K[] = {B01100110,B00110110,B00011110,B00001110,B00001110,B00011110,B00110110,B01100110}; byte L[] = {B00000110,B00000110,B00000110,B00000110,B00000110,B00000110,B01111110,B01111110}; byte M[] = {B01000010,B01100110,B01111110,B01011010,B01000010,B01000010,B01000010,B01000010}; byte N[] = {B01100110,B01100110,B01100110,B01101110,B01111110,B01110110,B01100110,B01100110}; byte O[] = {B00111100,B01111110,B01100110,B01100110,B01100110,B01100110,B01111110,B00111100}; byte P[] = {B00011110,B00111110,B01100110,B01100110,B00111110,B00011110,B00000110,B00000110}; byte Q[] = {B00111100,B01111110,B01100110,B01000110,B01010110,B01100110,B01111110,B10111100}; byte R[] = {B00111110,B01111110,B01100110,B00110110,B00011110,B00011110,B00110110,B01100110}; byte S[] = {B00111100,B01111110,B00000110,B00111110,B01111110,B01100000,B00111100,B00111110}; byte T[] = {B01111110,B01111110,B00011000,B00011000,B00011000,B00011000,B00011000,B00011000}; byte U[] = {B01100110,B01100110,B01100110,B01100110,B01100110,B01100110,B01111110,B00111100}; byte V[] = {B01100110,B01100110,B01100110,B01100110,B01100110,B01100110,B00111100,B00011000}; byte W[] = {B01000010,B01000010,B01000010,B01000010,B01000010,B01011010,B01011010,B00100100}; byte X[] = {B01100110,B01100110,B01100110,B00111100,B00011000,B00111100,B01100110,B01100110}; byte Y[] = {B01100110,B01100110,B01100110,B01111110,B00111100,B00011000,B00011000,B00011000}; byte Z[] = {B01111110,B01111110,B00001100,B00011000,B00110000,B01100000,B01111110,B01111110}; byte Zero[] = {B00111100,B01111110,B01100110,B01100110,B01100110,B01100110,B01111110,B00111100}; byte One[] = {B00111000,B00111100,B00111110,B00110000,B00110000,B00110000,B00110000,B00110000}; byte Two[] = {B00111100,B01111110,B01100010,B00110000,B00011000,B00001100,B01111110,B01111110}; byte Three[]= {B00111100,B01111110,B01100000,B00110000,B00110000,B01100000,B01111110,B00111100}; byte Four[] = {B00110000,B00111000,B00111100,B00110110,B01111110,B01111110,B00110000,B00110000}; byte Five[] = {B01111110,B01111110,B00000110,B00111110,B01111100,B01100000,B01111110,B00111110}; byte Six[] = {B00110000,B00011000,B00001100,B00111110,B01111110,B01100010,B01100010,B00111100}; byte Seven[]= {B01111110,B01111110,B01100000,B01100000,B00110000,B00011000,B00001100,B00000110}; byte Eight[]= {B00111100,B01100110,B01100110,B00111100,B01111110,B01100110,B01100110,B00111100}; byte Nine[] = {B00111100,B01111110,B01100110,B01100110,B01111100,B00110000,B00011000,B00001100}; // Variable that will store the inserted text String Str = ""; void setup() { // Set all pins as outputs for (byte i = 30; i <= 53; i++) pinMode(i, OUTPUT); // Initial message Serial.begin(9600); Serial.println ("Insert the text you want to show"); } void writeInMatrix(byte b[]){ for (byte i = 0; i < 8; i++) { setColumns(b[i]); // Set the columns for this specific row digitalWrite(row[i], HIGH); // Each row and column is set as HIGH and LOW, respectivelly, in a very small time interval // Set this delay to 100 to see the multiplexing effect! delay(2); digitalWrite(row[i], LOW); } } void setColumns(byte b) { // Depending on the digitalWrite(COL_1, (~b >> 0) & 0x01); // Get the 1st bit: 10000000 digitalWrite(COL_2, (~b >> 1) & 0x01); // Get the 2nd bit: 01000000 digitalWrite(COL_3, (~b >> 2) & 0x01); // Get the 3rd bit: 00100000 digitalWrite(COL_4, (~b >> 3) & 0x01); // Get the 4th bit: 00010000 digitalWrite(COL_5, (~b >> 4) & 0x01); // Get the 5th bit: 00001000 digitalWrite(COL_6, (~b >> 5) & 0x01); // Get the 6th bit: 00000100 digitalWrite(COL_7, (~b >> 6) & 0x01); // Get the 7th bit: 00000010 digitalWrite(COL_8, (~b >> 7) & 0x01); // Get the 8th bit: 00000001 } void loop() { if (Serial.available() > 0) { // Reads the inserted text and gets the upper-case version of it Str = Serial.readString(); Str.toUpperCase(); // Prints the text to be displayed in the LED matrix Serial.print("Text to be printed: "); Serial.println(Str); } if (Str != ""){ int Str_size = Str.length(); // Sets the speed at which each character is shown int t_letter = 100; // Checks each character of the inserted text and prints it for (int j = 0; j < Str_size; j++){ int t = 0; while(t < t_letter){ if (Str.charAt(j) == 'A'){ writeInMatrix(A); } else if (Str.charAt(j) == 'B'){ writeInMatrix(B); } else if (Str.charAt(j) == 'C'){ writeInMatrix(C); } else if (Str.charAt(j) == 'D'){ writeInMatrix(D); } else if (Str.charAt(j) == 'E'){ writeInMatrix(E); } else if (Str.charAt(j) == 'F'){ writeInMatrix(F); } else if (Str.charAt(j) == 'G'){ writeInMatrix(G); } else if (Str.charAt(j) == 'H'){ writeInMatrix(H); } else if (Str.charAt(j) == 'I'){ writeInMatrix(I); } else if (Str.charAt(j) == 'J'){ writeInMatrix(J); } else if (Str.charAt(j) == 'K'){ writeInMatrix(K); } else if (Str.charAt(j) == 'L'){ writeInMatrix(L); } else if (Str.charAt(j) == 'M'){ writeInMatrix(M); } else if (Str.charAt(j) == 'N'){ writeInMatrix(N); } else if (Str.charAt(j) == 'O'){ writeInMatrix(O); } else if (Str.charAt(j) == 'P'){ writeInMatrix(P); } else if (Str.charAt(j) == 'Q'){ writeInMatrix(Q); } else if (Str.charAt(j) == 'R'){ writeInMatrix(R); } else if (Str.charAt(j) == 'S'){ writeInMatrix(S); } else if (Str.charAt(j) == 'T'){ writeInMatrix(T); } else if (Str.charAt(j) == 'U'){ writeInMatrix(U); } else if (Str.charAt(j) == 'V'){ writeInMatrix(V); } else if (Str.charAt(j) == 'W'){ writeInMatrix(W); } else if (Str.charAt(j) == 'X'){ writeInMatrix(X); } else if (Str.charAt(j) == 'Y'){ writeInMatrix(Y); } else if (Str.charAt(j) == 'Z'){ writeInMatrix(Z); } else if (Str.charAt(j) == '0'){ writeInMatrix(Zero); } else if (Str.charAt(j) == '1'){ writeInMatrix(One); } else if (Str.charAt(j) == '2'){ writeInMatrix(Two); } else if (Str.charAt(j) == '3'){ writeInMatrix(Three); } else if (Str.charAt(j) == '4'){ writeInMatrix(Four); } else if (Str.charAt(j) == '5'){ writeInMatrix(Five); } else if (Str.charAt(j) == '6'){ writeInMatrix(Six); } else if (Str.charAt(j) == '7'){ writeInMatrix(Seven); } else if (Str.charAt(j) == '8'){ writeInMatrix(Eight); } else if (Str.charAt(j) == '9'){ writeInMatrix(Nine); } else { writeInMatrix(NONE); } t++; } } int t = 0; // Inserts a longer blank interval at the end of the text before it is repeated while(t < t_letter*3){ writeInMatrix(NONE); t++; } } }
Results
Here is the result when I type “geekering” in the Serial Monitor.
If you enjoyed this tutorial, you can visit our YouTube channel and watch all our videos. Thanks for following us and be sure to rate, comment and share our content.
Towards the Future !!!