18. Chapter LED Matrix
In the previous chapter, we have learned how to use some modules and sensors and shows some information on the computer through serial port. Now let us learn some modules which can output images and text.
In this chapter, we will learn how to use the LED matrix to output characters and images.
18.1. Project 74HC595
Firstly, let us learn how to use the 74HC595 chip, which is very helpful for us to control the LED matrix.
18.1.1. Component List
Control board x1
|
||||
Breadboard x1
|
GPIO Extension Board x1
|
|||
USB cable x1
|
Jumper M/M x15
|
|||
74HC595 x1
|
LED bar graph x1 |
Resistor 220Ω x8 |
||
18.1.2. Code Knowledge
18.1.2.1. Hexadecimal
The conversion between binary and decimal system has been mentioned before. When you write the code, the number is decimal by default. Hexadecimal numbers need to add the 0x prefix in the code, such as 0x01.
One Hexadecimal bit can present one number between 0-15. In order to facilitate writing, the numbers greater than 9 are written into the letter A-F (case-insensitive) such as 0x2A. The corresponding relationship is as follows:
Number |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Represent |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
A |
B |
C |
D |
E |
F |
Conversion between hexadecimal and decimal system is similar to the conversion between hexadecimal and binary such as the sixteen digit 0x12:
Sequence |
1 |
0 |
|---|---|---|
Number |
1 |
2 |
Conversion between hexadecimal and decimal system is similar to the conversion between hexadecimal and binary such as the sixteen digit 0x12:
Sequence |
1 |
0 |
|---|---|---|
Number |
1 |
2 |
When a hexadecimal number need to be converted to decimal number, first, the nth number of it need be multiplied by n power of 16, then sum all multiplicative results. Take 0x12 as an example:
1*16^1+2*16^0=18
When decimal number is converted to hexadecimal number, decimal number is divided by 16. Then we will get quotient and remainder, and quotient obtained will be continuously divided by 16 until quotient is zero. Arrange all remainders from right to left in a line. Then we complete the conversion. For example:
The result is of the conversion 0x12.
When you write code, sometimes it is convenient to use hexadecimal, especially involving bit operation, because 1 hexadecimal number can be expressed by 4 binary number (2^4=16). The corresponding relationship between 4 bit binary numbers and 1 hexadecimal number is shown as follows:
4 bit binary |
0000 |
0001 |
0010 |
0011 |
0100 |
0101 |
0110 |
0111 |
|---|---|---|---|---|---|---|---|---|
1 figure of hexadecimal |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
4 bit binary |
1000 |
1001 |
1010 |
1011 |
1100 |
1101 |
1110 |
1111 |
|---|---|---|---|---|---|---|---|---|
1 figure of hexadecimal |
8 |
9 |
A |
B |
C |
D |
E |
F |
For example, binary 00010010 is corresponding to hexadecimal 0x12.
18.1.3. Component Knowledge
18.1.3.1. 74HC595
A 74HC595 chip is used to convert serial data into parallel data. A 74HC595 chip can convert the serial data of one byte into 8 bits, and send its corresponding level to each of the 8 ports correspondingly. With this characteristic, the 74HC595 chip can be used to expand the IO ports the control board. At least 3 ports on the RPI board are required to control the 8 ports of the 74HC595 chip.
The ports of 74HC595 are described as follows:
Pin name |
Pin number |
Description |
|---|---|---|
Q0-Q7 |
15, 1-7 |
Parallel data output |
VCC |
16 |
The positive electrode of power supply, the voltage is 2~6V |
GND |
8 |
The negative electrode of power supply |
DS |
14 |
Serial data Input |
OE |
13 |
Enable output, When this pin is in high level, Q0-Q7 is in high resistance state When this pin is in low level, Q0-Q7 is in output mode |
ST_CP |
12 |
Parallel update output: when its electrical level is rising, it will update the parallel data output. |
SH_CP |
11 |
Serial shift clock: when its electrical level is rising, serial data input register will do a shift. |
MR |
10 |
Remove shift register: When this pin is in low level, the content in shift register will be cleared . |
Q7’ |
9 |
Serial data output: it can be connected to more 74HC595 in series. |
For more detail, please refer to the datasheet.
18.1.4. Circuit
Use pin 11, 12, 13 on the control board to control the 74HC595, and connect it to the 8 LEDs of LED bar graph.
Schematic diagram |
|
Hardware connection If you need any support, please feel free to contact us via: support@freenove.com |
|
18.1.5. Sketch
18.1.5.1. Sketch 74HC595
Now write code to control the 8 LEDs of LED bar graph through 74HC595.
1/**********************************************************************
2 Filename : Sketch_18.1.1_74HC595
3 Description : 74HC595
4 Auther : www.freenove.com
5 Modification: 2024/08/05
6**********************************************************************/
7
8int latchPin = 12; // Pin connected to ST_CP of 74HC595(Pin12)
9int clockPin = 13; // Pin connected to SH_CP of 74HC595(Pin11)
10int dataPin = 11; // Pin connected to DS of 74HC595(Pin14)
11
12void setup() {
13 // set pins to output
14 pinMode(latchPin, OUTPUT);
15 pinMode(clockPin, OUTPUT);
16 pinMode(dataPin, OUTPUT);
17}
18
19void loop() {
20 // Define a one-byte variable to use the 8 bits to represent the state of 8 LEDs of LED bar graph.
21 // This variable is assigned to 0x01, that is binary 00000001, which indicates only one LED light on.
22 byte x = 0x01;
23 for (int j = 0; j < 8; j++) {
24 // Output low level to latchPin
25 digitalWrite(latchPin, LOW);
26 // Send serial data to 74HC595
27 shiftOut(dataPin, clockPin, LSBFIRST, x);
28 // Output high level to latchPin, and 74HC595 will update the data to the parallel output port.
29 digitalWrite(latchPin, HIGH);
30 // make the variable move one bit to left once, then the bright LED move one step to the left once.
31 x <<= 1;
32 delay(100);
33 }
34}
In the code, we configure three pins to control the 74HC595. And define a one-byte variable to control the state of 8 LEDs through the 8 bits of the variable. The LED lights on when the corresponding bit is 1. If the variable is assigned to 0x01, that is 00000001 in binary, there will be only one LED on.
1byte x = 0x01;
In each loop, the x is sent to 74HC595. The sending process is as follows:
1// Output low level to latchPin
2digitalWrite(latchPin, LOW);
3// Send serial data to 74HC595
4shiftOut(dataPin, clockPin, LSBFIRST, x);
5// Output high level to latchPin, and 74HC595 will update the data to the parallel output port.
6digitalWrite(latchPin, HIGH);
The x will be shift 1 bit to left in each cycle, which makes the bright LED of the 8 LEDs move one bit.
1x <<= 1;
- << operator
“<<” is the left shift operator, which can make all bits of 1 byte shift by several bits to the left (high) direction and add 0 on the right (low). For example, shift binary 00000001 by 1 bit to left:
byte x = 1 << 1;
The result of x is 2(binary 00000010).
There is another similar operator” >>”. For example, shift binary 00000001 by 1 bit to right:
byte x = 1 >> 1;
The result of x is 0(00000000).
X <<= 1 is equivalent to x = x << 1 and x >>= 1 is equivalent to x = x >> 1
Verify and upload the code, and then you will see the LED bar graph with the effect of flowing water.
18.2. Project LED Matrix
In the previous section, we have used 74HC595 to control 8 LEDs of the LED bar graph. Now let’s use 74HC595 to control LED matrix.
18.2.1. Component List
Control board x1
|
||||
Breadboard x1
|
GPIO Extension Board x1
|
|||
USB cable x1
|
Jumper M/M x15
|
|||
74HC595 x1
|
LED matrix x1
|
Resistor 220Ω x8 |
||
18.2.2. Component Knowledge
18.2.2.1. LED matrix
An LED Matrix is a rectangular display module that consists of a uniform grid of LEDs. The following is an 8X8 monochrome (one color) LED Matrix containing 64 LEDs (8 rows by 8 columns).
In order to facilitate the operation and reduce the number of ports required to drive this component, the Positive Poles of the LEDs in each row and Negative Poles of the LEDs in each column are respectively connected together inside the LED Matrix module, which is called a Common Anode. There is another arrangement type. Negative Poles of the LEDs in each row and the Positive Poles of the LEDs in each column are respectively connected together, which is called a Common Cathode.
The LED Matrix that we use in this project is a Common Anode LED Matrix.
Here is how a Common Anode LED Matrix works. First, choose 16 ports on RPI board to connect to the 16 ports of LED Matrix. Configure one port in columns for low level, which makes that column the selected port. Then configure the eight port in the row to display content in the selected column. Add a delay value and then select the next column that outputs the corresponding content. This kind of operation by column is called Scan. If you want to display the following image of a smiling face, you can display it in 8 columns, and each column is represented by one byte.
Column |
Binary |
Hexadecimal |
|---|---|---|
1 |
0001 1100 |
0x1c |
2 |
0010 0010 |
0x22 |
3 |
0101 0001 |
0x51 |
4 |
0100 0101 |
0x45 |
5 |
0100 0101 |
0x45 |
6 |
0101 0001 |
0x51 |
7 |
0010 0010 |
0x22 |
8 |
0001 1100 |
0x1c |
Scanning rows is another option to display on an LED Matrix (dot matrix grid). Whether scanning by row or column, 16 GPIO is required. In order to save GPIO ports of control board, two 74HC595 IC Chips are used in the circuit.
18.2.3. Circuit
Use pin 11, 12, 13 on control board to control the 74HC595. And connect 74HC595 to the 8 anode pins of LED Matrix, in the meanwhile, connect 8 digitals port on control board to the 8 cathode pins of LED Matrix.
Schematic diagram |
|
Hardware connection If you need any support, please feel free to contact us via: support@freenove.com |
|
18.2.4. Sketch
18.2.4.1. Sketch LED_Matrix
Now write the code to drive LED dot matrix to display static and dynamic images, in fact, the dynamic image is formed by continuous static image.
1/**********************************************************************
2 Filename : Sketch_18.2.1_LED_Matrix
3 Description : LED Matrix
4 Auther : www.freenove.com
5 Modification: 2024/08/05
6**********************************************************************/
7
8int latchPin = 12; // Pin connected to ST_CP of 74HC595(Pin12)
9int clockPin = 13; // Pin connected to SH_CP of 74HC595(Pin11)
10int dataPin = 11; // Pin connected to DS of 74HC595(Pin14)
11int LEDPin[] = {2, 3, 4, 5, 6, 7, 8, 9}; // column pin (cathode) of LED Matrix
12
13// Define the pattern data for a smiling face
14const int smilingFace[] = {
15 0x1C, 0x22, 0x51, 0x45, 0x45, 0x51, 0x22, 0x1C
16};
17// Define the data of numbers and letters, and save them in flash area
18const int data[] PROGMEM = {
19 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // " "
20 0x00, 0x00, 0x21, 0x7F, 0x01, 0x00, 0x00, 0x00, // "1"
21 0x00, 0x00, 0x23, 0x45, 0x49, 0x31, 0x00, 0x00, // "2"
22 0x00, 0x00, 0x22, 0x49, 0x49, 0x36, 0x00, 0x00, // "3"
23 0x00, 0x00, 0x0E, 0x32, 0x7F, 0x02, 0x00, 0x00, // "4"
24 0x00, 0x00, 0x79, 0x49, 0x49, 0x46, 0x00, 0x00, // "5"
25 0x00, 0x00, 0x3E, 0x49, 0x49, 0x26, 0x00, 0x00, // "6"
26 0x00, 0x00, 0x60, 0x47, 0x48, 0x70, 0x00, 0x00, // "7"
27 0x00, 0x00, 0x36, 0x49, 0x49, 0x36, 0x00, 0x00, // "8"
28 0x00, 0x00, 0x32, 0x49, 0x49, 0x3E, 0x00, 0x00, // "9"
29 0x00, 0x00, 0x3E, 0x41, 0x41, 0x3E, 0x00, 0x00, // "0"
30 0x00, 0x00, 0x3F, 0x44, 0x44, 0x3F, 0x00, 0x00, // "A"
31 0x00, 0x00, 0x7F, 0x49, 0x49, 0x36, 0x00, 0x00, // "B"
32 0x00, 0x00, 0x3E, 0x41, 0x41, 0x22, 0x00, 0x00, // "C"
33 0x00, 0x00, 0x7F, 0x41, 0x41, 0x3E, 0x00, 0x00, // "D"
34 0x00, 0x00, 0x7F, 0x49, 0x49, 0x41, 0x00, 0x00, // "E"
35 0x00, 0x00, 0x7F, 0x48, 0x48, 0x40, 0x00, 0x00 // "F"
36};
37
38void setup() {
39 // set pins to output
40 pinMode(latchPin, OUTPUT);
41 pinMode(clockPin, OUTPUT);
42 pinMode(dataPin, OUTPUT);
43 for (int i = 0; i < 8; i++) {
44 pinMode(LEDPin[i], OUTPUT);
45 }
46}
47
48void loop() {
49 // Define a one-byte variable (8 bits) which is used to represent the selected state of 8 column.
50 int cols;
51 // Display the static smiling pattern
52 for (int j = 0; j < 500; j++ ) { // repeat 500 times
53 cols = 0x01; // Assign 0x01(binary 00000001) to the variable, which represents the first column is selected.
54 for (int i = 0; i < 8; i++) { // display 8 column data by scaning
55 matrixColsVal(cols); // select this column
56 matrixRowsVal(smilingFace[i]);// display the data in this column
57 delay(1); // display them for a period of time
58 matrixRowsVal(0x00); // clear the data of this column
59 cols <<= 1; // shift"cols" 1 bit left to select the next column
60 }
61 }
62 // Display the dynamic patterns of numbers and letters
63 for (int i = 0; i < 128; i++) { // "space,0-9,A-F"16 letters ,each letter hold 8 columns, total 136 columns. Firstly, display space ,then we need shift 128 times(136-8)
64 for (int k = 0; k < 10; k++) { // repeat image of each frame 10 times.
65 cols = 0x01; // Assign binary 00000001. Means the first column is selected.
66 for (int j = i; j < 8 + i; j++) { // display image of each frame
67 matrixColsVal(cols); // select this column
68 matrixRowsVal(pgm_read_word_near(data + j));// display the data in this column
69 delay(1); // display them for a period of time
70 matrixRowsVal(0x00); // close the data of this column
71 cols <<= 1; // shift"cols" 1 bit left to select the next column
72 }
73 }
74 }
75}
76
77void matrixRowsVal(int value) {
78 // make latchPin output low level
79 digitalWrite(latchPin, LOW);
80 // Send serial data to 74HC595
81 shiftOut(dataPin, clockPin, LSBFIRST, value);
82 // make latchPin output high level, then 74HC595 will update the data to parallel output
83 digitalWrite(latchPin, HIGH);
84}
85
86void matrixColsVal(byte value) {
87 byte cols = 0x01;
88 // Output the column data to the corresponding port.
89 for (int i = 0; i < 8; i++) {
90 digitalWrite(LEDPin[i], ((value & cols) == cols) ? LOW : HIGH);
91 cols <<= 1;
92 }
93}
In the code, use an array to define the column pins of LED Matrix.
1const int smilingFace[] = {
2 0x1C, 0x22, 0x51, 0x45, 0x45, 0x51, 0x22, 0x1C
3};
Use another array to define some numbers and letters, and every eight elements of the array represent a dot matrix pattern data of a number or a letter.
1const int data[] PROGMEM = {
2 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // " "
3 0x00, 0x00, 0x21, 0x7F, 0x01, 0x00, 0x00, 0x00, // "1"
4 0x00, 0x00, 0x23, 0x45, 0x49, 0x31, 0x00, 0x00, // "2"
5 0x00, 0x00, 0x22, 0x49, 0x49, 0x36, 0x00, 0x00, // "3"
6 0x00, 0x00, 0x0E, 0x32, 0x7F, 0x02, 0x00, 0x00, // "4"
7 0x00, 0x00, 0x79, 0x49, 0x49, 0x46, 0x00, 0x00, // "5"
8 0x00, 0x00, 0x3E, 0x49, 0x49, 0x26, 0x00, 0x00, // "6"
9 0x00, 0x00, 0x60, 0x47, 0x48, 0x70, 0x00, 0x00, // "7"
10 0x00, 0x00, 0x36, 0x49, 0x49, 0x36, 0x00, 0x00, // "8"
11 0x00, 0x00, 0x32, 0x49, 0x49, 0x3E, 0x00, 0x00, // "9"
12 0x00, 0x00, 0x3E, 0x41, 0x41, 0x3E, 0x00, 0x00, // "0"
13 0x00, 0x00, 0x3F, 0x44, 0x44, 0x3F, 0x00, 0x00, // "A"
14 0x00, 0x00, 0x7F, 0x49, 0x49, 0x36, 0x00, 0x00, // "B"
15 0x00, 0x00, 0x3E, 0x41, 0x41, 0x22, 0x00, 0x00, // "C"
16 0x00, 0x00, 0x7F, 0x41, 0x41, 0x3E, 0x00, 0x00, // "D"
17 0x00, 0x00, 0x7F, 0x49, 0x49, 0x41, 0x00, 0x00, // "E"
18 0x00, 0x00, 0x7F, 0x48, 0x48, 0x40, 0x00, 0x00 // "F"
19};
- PROGMEM keyword
Microprocessors generally have two storage areas, namely ROM and RAM. ROM is used to store code. And these stored data will not change with the execution of code until the code are uploaded. RAM is used to store data, for example, the variables we defined are stored here. The stored data will change in real time with the execution of the code. Generally, capacity of RAM is small. So we can use PROGMEM keyword to save the data that don’t change in ROM.
Define two functions, one of them uses control board port to select the column.
1void matrixRowsVal(int value) {
2 // make latchPin output low level
3 digitalWrite(latchPin, LOW);
4 // Send serial data to 74HC595
5 shiftOut(dataPin, clockPin, LSBFIRST, value);
6 // make latchPin output high level, then 74HC595 will update the data to parallel output
7 digitalWrite(latchPin, HIGH);
8}
Another one uses 74HC595 to write data of the row.
1void matrixColsVal(byte value) {
2 byte cols = 0x01;
3 // Output the column data to the corresponding port.
4 for (int i = 0; i < 8; i++) {
5 digitalWrite(LEDPin[i], ((value & cols) == cols) ? LOW : HIGH);
6 cols <<= 1;
7 }
8}
- ? : operator
“? :” operator is similar to conditional statements. When the expression in front of “?” is tenable, the statement in front of “:” will be executed. When the expression is not tenable, the statement behind “:” will be executed. For example:
int a = (1 > 0) ? 2: 3;
Because 1>0 is tenable, so “a” will be assigned to 2.
- Bitwise logical operation
There are many bitwise logical operators such as and (&), or (|), xor (^), negate (~). The result of exclusive or (^) is true only when two corresponding bit is not equal.
&, | and ^ is used to operate the corresponding bit of two numbers. Such as:
byte a = 1 & 2;
“a” will be assigned to 0. The calculation procedure is as follows:
1(00000001)
& 2(00000010)
0(00000000)
Negate (~) is used to negate a number, for example:
byte a = ~15;
“a” will be assigned to 240. The calculation procedure is as follows:
~ 15(00001111)
240(11110000)
In the loop () function, firstly, show the static smile pattern. Select one of the 8 columns circularly in turn to display each the result. Repeat 500 times the process, then we can see a static smile pattern.
1for (int j = 0; j < 500; j++ ) { // repeat 500 times
2 cols = 0x01; // Assign 0x01(binary 00000001) to the variable, which represents the first column is selected.
3 for (int i = 0; i < 8; i++) { // display 8 column data by scaning
4 matrixColsVal(cols); // select this column
5 matrixRowsVal(smilingFace[i]);// display the data in this column
6 delay(1); // display them for a period of time
7 matrixRowsVal(0x00); // clear the data of this column
8 cols <<= 1; // shift"cols" 1 bit left to select the next column
9 }
10}
Then display the dynamic pattern of the numbers and letters. We have defined space, 0-9, A-F, total of 16 characters (136 columns) in an array, among which 8 adjacent rows of data form one frame. Shift one column once. There are for 128 frames of image from the first frame (1-8) to the last frame (128-136 column). Each frame image is displayed 10 times, then display the next frame. Repeat the process above, then we can see the pattern of scrolling numbers and letters.
1for (int i = 0; i < 128; i++) { // "space,0-9,A-F"16 letters ,each letter hold 8 columns, total 136 columns. Firstly, display space ,then we need shift 128 times(136-8)
2 for (int k = 0; k < 10; k++) { // repeat image of each frame 10 times.
3 cols = 0x01; // Assign binary 00000001. Means the first column is selected.
4 for (int j = i; j < 8 + i; j++) { // display image of each frame
5 matrixColsVal(cols); // select this column
6 matrixRowsVal(pgm_read_word_near(data + j));// display the data in this column
7 delay(1); // display them for a period of time
8 matrixRowsVal(0x00); // close the data of this column
9 cols <<= 1; // shift"cols" 1 bit left to select the next column
10 }
11 }
12}
Verify and upload the code, then LED Matrix begins to display the static smile pattern. A few seconds later, LED Matrix will display the scrolling number 0-9 and the letter A-F.












