In this tutorial, you will continue learning C++ by building a C++ class for controlling 8×8 LED Dot Matrix MAX7219 using Arduino.
MAX7219 8×8 LED Dot Matrix module
First, let’s have a look at the 8×8 LED dot matrix module which is controlled by MAX7219 chip. The module is shown in the below image
The module consists of a 8×8 LED matrix or 64 individual LEDs. These LEDs are arranged in rows and columns which are illustrated in the below figure.
The MAX7219 has 8 registers, numbered from 0 to 7, which are used to control 8 rows of LEDs. Register 0 (DIGIT_0 register) controls 8 LEDs of row 0, register 1 (DIGIT_1 register) controls 8 LEDs of row number 1, and so on. By writing to these 8-bit registers, you can make individual LED on or off. For example, to turn all LEDs off except one LED at row 1 and colum 1, you need to write to register 1 value 0b00000010 in binary or 0x02 in hexadecimal.
Now you have understand the module, the next step is to write a class to control it.
MAX7219 class
To control MAX7219, you will create a class MAX7219 as follow
class MAX7219 {
private:
int CS;
int CLK;
int MOSI;
void transfer(uint8_t * p_data, uint8_t len);
void write_reg(uint8_t reg, uint8_t value);
public:
MAX7219(int CS, int CLK, int MOSI);
void clear(void);
void set_led(uint8_t row_index, uint8_t col_index);
void set_row(uint8_t row_index);
void set_col(uint8_t col_index);
};
This class has a number of member variables and member methods:
Private member variables and methods
CS, CLK, MOSI
are private member variables. You use these variables to determine which IO pins of Arduino are used for chip select, clock, and Data In lines.transfer()
is a private member method to perform an SPI transfer to MAX7219. This function accepts a pointer to a data buffer and length of the data buffer.write_reg()
is a private member method to write to a MAX7219 register. The table below listed all MAX7219 8-bit registers
Register | Address | Note |
---|---|---|
No-Op | 0x00 | |
Digit 0 | 0x01 | Control LEDs in Row 0 |
Digit 1 | 0x02 | Control LEDs in Row 1 |
Digit 2 | 0x03 | Control LEDs in Row 2 |
Digit 3 | 0x04 | Control LEDs in Row 3 |
Digit 4 | 0x05 | Control LEDs in Row 4 |
Digit 5 | 0x06 | Control LEDs in Row 5 |
Digit 6 | 0x07 | Control LEDs in Row 6 |
Digit 7 | 0x08 | Control LEDs in Row 7 |
Decode mode | 0x09 | |
Intensity | 0x0A | |
Scan Limit | 0x0B | |
Shutdown | 0x0C | |
Display test | 0x0F |
Public methods
MAX7219()
is the constructor which is called whenever an instance of class MAX7219 is created. You will implement initialisation such as configuring IO pins as output ports, set default register values in this constructor method.clear()
is the method to turn all LEDs offset_led()
is the function to turn on an individual LED at position(row_index, col_index)
set_row()
is to turn on all LEDs in a rowset_col()
is to turn on all LEDs in a column
MAX7219 member methods
Constructor
The constructor member method is called whenever an object of class MAX7219 is instantiated. You will do the following initialisation steps inside this function:
- Assign the IO pins that are connected to MAX7219 to the member variables
CS, CLK and MOSI
. In this project, you will implement SPI transfer using software and you can use any available IO pins as Chip Select, Clock and Data lines. Note that in the below snippet you use C++this
keyword.this
is a special pointer that points to the current object, so you can usethis->CS
to refer to the member variableCS
. - Configure
CS
,CLK
andMOSI
pins as output port. You use ArduinopinMode()
API for this purpose. - Initialise those lines with
digitalWrite()
API - Call
write_reg()
to set up the MAX7219. You will implementwrite_reg()
in a little while. - Turn off all LEDs by calling
clear()
method.
#define DECODE_MODE_REG 0x09
#define INTENSITY_REG 0x0A
#define SCAN_LIMIT_REG 0x0B
#define SHUTDOWN_REG 0x0C
#define DISPLAY_TEST_REG 0x0F
MAX7219::MAX7219(int CS, int CLK, int MOSI) {
this->CS = CS;
this->CLK = CLK;
this->MOSI = MOSI;
pinMode(this->CS, OUTPUT);
pinMode(this->CLK, OUTPUT);
pinMode(this->MOSI, MOSI);
digitalWrite(this->CS, HIGH);
digitalWrite(this->CLK, LOW);
digitalWrite(this->MOSI, LOW);
write_reg(DISPLAY_TEST_REG, 0);
write_reg(SCAN_LIMIT_REG, 7);
write_reg(DECODE_MODE_REG, 0);
write_reg(SHUTDOWN_REG, 1);
clear();
}
SPI transfer
Next you implement transfer()
function to transfer data to MAX7219. You’ll need to refer to timing diagram in MAX7219 datasheet in order to understand this piece of code.
- Before transferring data, you need to pull
CS
line low. - You loop over each data byte in buffer and shift out each bit on
MOSI
line, starting with most signification bit (MSB), then generate clock signal onCLK
line. - A
mask
variable is used to determine the current bit being transferred - When all data has been transferred to the MAX7219, you pull the
CS
line high again.
void MAX7219::transfer(uint8_t * p_data, uint8_t len) {
uint8_t mask;
digitalWrite(CS, LOW);
delayMicroseconds(1);
for (int i = 0; i < len; i++) {
mask = 0x80;
do {
if (p_data[i] & mask) {
digitalWrite(MOSI, HIGH);
} else {
digitalWrite(MOSI, LOW);
}
delayMicroseconds(1);
digitalWrite(CLK, HIGH);
delayMicroseconds(1);
digitalWrite(CLK, LOW);
mask >>= 1;
} while (mask != 0);
}
digitalWrite(CS, HIGH);
}
Write MAX7219 register
To write to MAX7219 register, you need two parameters: one parameter is the address of the register to be written to, one parameter is the value to write to the register. You put these parameters in a buffer and send it out by calling transfer()
function.
void MAX7219::write_reg(uint8_t reg, uint8_t value) {
uint8_t tx_data[2] = { reg, value };
transfer(tx_data, 2);
}
Turn on a specific LED
To turn on a specific LED at row_index, col_index
, you need to write to the register address at row_index + 1
and value to be written is determined by 0x01 << col_index
void MAX7219::set_led(uint8_t row_index, uint8_t col_index) {
write_reg(row_index + 1, 0x01 << col_index);
}
Turn on a row of LEDs
To turn on a row of LEDs, you need to write to the register at address row_index + 1
with value 0xFF
.
void MAX7219::set_row(uint8_t row_index) {
write_reg(row_index + 1, 0xFF);
}
Turn on a column of LEDs
To turn on all LEDs in a column col_index
, you need to write to all 8 registers with the same value (1 << col_index)
. You can do it in a for
loop as follow
void MAX7219::set_col(uint8_t col_index) {
for (int i = 0; i < 8; i++) {
write_reg(i + 1, 0x01 << col_index);
}
}
Turn off all LEDs
By writing 0x00
to all 8 registers, you can turn off all 64 LEDs of the LED matrix
void MAX7219::clear(void) {
for (int i = 0; i < 8; i++) {
write_reg(i + 1, 0x00);
}
}
Main program
Now all the heavy work has been done, in the main program, you just need to declare an object of class MAX7219. The following example shows how you can call its member functions to do some interesting tasks.
MAX7219 max7219(10, 11, 12);
void setup() {
max7219.set_led(7, 7);
delay(2000);
}
void loop() {
for (int i = 0; i < 8; i++) {
max7219.clear();
max7219.set_row(i);
delay(1000);
}
for (int i = 0; i < 8; i++) {
max7219.clear();
max7219.set_col(i);
delay(1000);
}
}
Full project code can be seen on Github.
Testing the program
In this step, you’ll need a few components. This table describes hardware used in this project
QTY | Component |
---|---|
1 | Arduino Mega 2560 |
1 | MAX7219 8×8 LED Matrix Module |
1 | Jumper Wires |
Connect the MAX7219 module with Arduino as follow
Arduino Pins | MAX7219 pins |
5V | VDD |
GND | GND |
10 | CS/LOAD |
11 | CLK |
12 | DIN/MOSI |
Open Arduino IDE, set the board, set the port, then Compile and Upload the above program. You will see that each row of the LED matrix is turned on and then each column of the LED matrix is turned on repeated every 1 second.
Wrapping Up
In this article, you have learnt to write a C++ program with Arduino by going through a project with MAX7219. To be able to control MAX7219 module, you need to understand how it works by reading its datasheet, then build a class to control it.