MAX7219 Example (8 digit segment driver)
Source: Dev.to
Hardware
The board I used contains an 8‑digit 7‑segment display and a MAX7219 driver.

Pinout
| Pin | Function |
|---|---|
| GND | Power ground |
| VCC | Power supply (5 V) |
| DIN | Serial data input |
| CLK | Serial clock |
| LOAD / CS | Latch (chip‑select) |
Although the pins look like a SPI interface, the MAX7219 only uses a simple 16‑bit serial protocol (the MAX7221 is the SPI‑compatible variant).
Serial Data Format
Each transaction consists of 16 bits:
- Upper 8 bits – Register address
- Lower 8 bits – Data for that register
The datasheet shows the bits being shifted MSB first (the functional diagram mistakenly mentions LSB first).

Register Address Map
| Address (hex) | Register name | Description |
|---|---|---|
| 0x01‑0x08 | Digit 0‑7 | Segment data for each digit |
| 0x09 | Decode Mode | Enable/disable BCD decoding per digit |
| 0x0A | Intensity | Brightness (0x0 … 0xF) |
| 0x0B | Scan‑Limit | Number of digits displayed (0‑7) |
| 0x0C | Shutdown | Normal operation (0x01) / shutdown (0x00) |
| 0x0F | Display Test | Force all segments on at full intensity |

The Display Test register lights every segment of every digit at maximum intensity.
The Shutdown register uses 0x01 to enable normal operation (the name is a bit confusing).
Register: Scan‑Limit
The Scan‑Limit register selects how many digits are active:
0x00– Only digit 0 is displayed (default after reset)0x07– All eight digits are displayed
If you want no output at all, either:
- Put the device into Shutdown (
0x0C 0x00), or - Set the first digit to
0x00(no segments on) in No‑Decode mode, or0x0Fin BCD mode.
Register: Digit
Each digit register holds 8 bits that control the individual segments (or a BCD value when decoding is enabled).
1. No‑Decode (raw segment) mode
Bits correspond to segments A‑G and the decimal point DP:
| Bit | Segment |
|---|---|
| D7 | DP |
| D6 | A |
| D5 | B |
| D4 | C |
| D3 | D |
| D2 | E |
| D1 | F |
| D0 | G |
Example – to display the numeral 1 you would write 0x30 (D5=1, D4=1, i.e. segments B and C).

2. BCD (decode) mode
When the corresponding bit in the Decode Mode register is set, the driver interprets the lower‑nibble (0x0‑0x9) as a decimal digit and automatically lights the proper segments.
Special BCD codes (0xA‑0xF):
| Code | Character |
|---|---|
| 0xA | dash (-) |
| 0xB | E |
| 0xC | H |
| 0xD | L |
| 0xE | P |
| 0xF | blank (all segments off) – same effect as 0x00 in No‑Decode mode |
You can even spell “HELP” by sending the sequence 0x040C 0x030B 0x020D 0x010E (digit 3 → H, digit 2 → E, digit 1 → L, digit 0 → P).
Register: Decode Mode
The Decode Mode register is a mask; each bit enables BCD decoding for the corresponding digit:
| Bit | Digit |
|---|---|
| D0 | Digit 0 |
| D1 | Digit 1 |
| … | … |
| D7 | Digit 7 |
0x00– No decoding for any digit (raw segment mode).0x01– Enable BCD decoding for Digit 0 only.0x03– Enable BCD decoding for Digits 0 & 1, etc.
Loading Data (Serial Write)
Data is clocked in on the rising edge of CLK. The driver samples DIN and shifts the bit into an internal 16‑bit shift register (MSB first).
Sending a single bit (Arduino‑style pseudocode)
digitalWrite(PIN_CLK, LOW); // prepare for rising edge
if (cmd & 0x8000) { // MSB of the 16‑bit word?
digitalWrite(PIN_DIN, HIGH);
} else {
digitalWrite(PIN_DIN, LOW);
}
digitalWrite(PIN_CLK, HIGH); // latch the bit
Sending a full 16‑bit command
// Example: enable Display‑Test (register 0x0F, data 0x01)
uint16_t cmd = 0x0F01; // high byte = address, low byte = data
for (int i = 0; i (reg)
Note: This can be converted into a macro if you need better performance.
Building Commands
The commands are very easy to build. For example, to send the number 6 to digit 0 (using BCD):
// 0x1 is the register for digit 0
uint16_t cmd = buildCmd(0x1, 6);
// send the command to the module
sendCmd(cmd);
If you are not using BCD:
// 0x1 is the register for digit 0
// 6 uses segments A, C, D, E, F, G → 0b0101 1111 = 0x5F
uint16_t cmd = buildCmd(0x1, 0x5F);
sendCmd(cmd);
Example
The example below uses test mode, then resets most registers. Resetting the module alone would be sufficient, but resetting everything gives you a clean slate before configuring it.
// Enable all 8 digits
cmdSetScanLimit(7);
// Set lowest intensity
cmdSetIntensity(0);
// Use BCD decoding for all digits
cmdSetDecodeMode(0xFF);
// Blank all segments
for (uint8_t i = 0; i < 8; i++) {
uint16_t cmd = buildCmd(REGISTER_DIGIT0 + i, 0xF);
sendCmd(cmd);
}
// Show all possible BCD values, finishing with a blank
for (uint8_t i = 0; i < 8; i++) {
for (uint8_t j = 0; j < 16; j++) {
uint16_t cmd = buildCmd(REGISTER_DIGIT0 + i, j);
sendCmd(cmd);
delay(200);
}
}
Example Repository
https://github.com/danguer/arduino-examples/tree/main/max7219
Datasheet
https://www.analog.com/media/en/technical-documentation/data-sheets/max7219-max7221.pdf