This is the tutorial to show 25 on the cc-type multiplexed display unit of Fig-1 being driven by MAX7219 Display Controller. The theory of operation/programming of MXA7219 chip will be found at the end of this tutorial starting from Step-3.

Figure-1:
1. Make connection among UNO, MAX7219, and Display Unit as per Fig-1. Please, palce C1 very close to Pin-19 of MAX7219 to kill the switching noise.
2. Upload the following sketch and check that 25 has appeared on the display unit.
#include<SPI.h>
#define LOAD 10
byte registerAddress[] = {0x09, 0x0A, 0x0B, 0x0C};//Decode Mode,Intensity,Scan Limit,Shutdown
byte registerData[] = {0x00, 0xFF, 0x07, 0x01}; //data for above registers respectively
byte digitAddress[] = {0x01, 0x02};//address for digitas of positions DP0-DP1
byte lupTable[] = //No-decode B-Font cc-code (p a b c d e f g) for charcaters: 0 - 9, A - F
{
0x7E, 0x30, 0x6D, 0x79, 0x33, 0x5B, 0x5F, 0x70,
0x7F, 0x7B, 0x77, 0x1F, 0x4E, 0x3D, 0x4F, 0x47
};
//--------------------------------------------------------------
void setup()
{
Serial.begin(9600);
SPI.begin();
digitalWrite(LOAD, LOW); //auotomatically OUTPUT due to SPI.begin()
//----MAX7219 initialization----------------
for (int i = 0; i < 4; i++)
{
SPI.transfer(registerAddress[i]);//address of Control Registers
SPI.transfer(registerData[i]); //data for Control Registers
dataLoad(); //generates L-H-L pulse at the LOAD pin of MAX7219
}
SPI.transfer(digitAddress[0]); //address of DP0-digit
SPI.transfer(lupTable[2]); //B-Font code for digit-2
dataLoad();
//---------------------------------------------------
SPI.transfer(digitAddress[1]); //address of DP1-digit
SPI.transfer(lupTable[5]); //B-Font code for digit-5
dataLoad();
}
void loop()
{
}
void dataLoad()
{
digitalWrite(LOAD, HIGH);
delayMicroseconds(1);
digitalWrite(LOAD, LOW);
}
3. Working Principle
CC-type 7-segment display devices can be directly connected with MAX7219 controller; where, the segment currents are controlled by Rex1 (Fig-1) resistor and the content of Intensity Register.
A single MAX7219 can handle as many as 8 digits in multiplexed mode. Multiple MAX7219 chips can be cascaded using DOUT-pin to handle bigger display unit (Fig-4).
For each digit there is an 8-bit wide RAM location (with unique address) inside the controller. The MCU can write new data at any time into this RAM location, and the data is immediately transferred into the target digit and refreshed with the help of built-in scanning circuit.
Before the controller is put into operation, it needs to be configured/initialized by storing appropriate data (see sketch) into the following registers: No-decode Mode, Intensity, Scan Limit, and Shutdown Registers.
The controller accepts 16-bit "command word (Fig-2)" from the host MCU. The upper 8-bit of the command word is the "address" of the target register and the lower 8-bit is the "data" for that register.
The 16-clocking pulses (required to push-out command word) can be genrated by the SCK-pin of the SPI Port in two transaction cycles. The data enters into a temporary buffer (Fig-2) and then into the respective registers when L-H-L pulse is asseted on the LOAD-pin of the controller. For example: to store 0x01 into the Shutdown Register, we may execute the following codes:
SPI.transger(0x0C); //0x0C is the address of Shutdown Register
SPI.transfer(0x01); //0x01 is the data for Shutdown Register.
digitalWrite(10, HIGH); //asserting L-H-L pulse at the LOAD-pin of MAX7219
delayMicroseconds(1);
digitalWrite(10, LOW);

Figure-2:
4. No-decode B-Font CC-codes for charcaters 0 - 9, A - F
The active bits for the segments of the cc-type 7-segment device are arranged as per Fig-3 and the 8-bit codes are formed accordingly (known as no-decode B-Font). For example: the B-fornt for charcater 2 is: 0x6D (0110 1101 = DP A B C D E F G)
Figure-3:
5. Cascade Operaion of two MAX7219 Controllers

Figure-4:
(1) Codes to initialize both controllers.
byte registerAddress[] = {0x09, 0x0A, 0x0B, 0x0C};//Decode Mode,Intensity,Scan Limit,Shutdown
byte registerData[] = {0x00, 0xFF, 0x07, 0x01}; //data for above registers respectively
for (int i = 0; i <= 3; i++)
{
SPI.transfer(registerAddress[i]);//address of Control Registers
SPI.transfer(registerData[i]); //data for Control Registers
dataLoad();
SPI.transfer(registerAddress[i]);//(0x09); //decode mode
SPI.transfer(registerData[i]);//(0x00); //no-decode mode
SPI.transfer(0x00); //address of No-Op Register
SPI.transfer(0xFF); //data (any value) for No-Op Register
dataLoad();
}
(2) Role of No-Op (No Operation) Register in Cascading
Figure-5:
Initialization of the second MAX7219 is done with the help of No-Op (No Operation) Register. Command word 0x0900 (0900 = 0x00 data for Decode Mode Register with address 0x09) is transferred into temporary buffer of U1 (Fig-5). After that load pulse (L-H-L) is asserted and the data is latched in the Decode Mode Register of U1.
The same command word (0900) is again loaded into temporary buffer of the first MAX7219 chip (U1, Fig-5). Now, it has to be transferred into the temporary buffer of the second MAX7219 chip (U2, Fig-5) using DOUT-pin of U1; where, DOUT-pin of U1 is connected with DIN-line of U2. To do it, we need to generate 16 CLK pulses which are generated by sending command word 0x00FF (00FF = 0xFF data byte for No-Op Register with address 0x00) into the temporary buffer of first MAX7219 (U1). Now, load pulse is asserted; as a result, 0xFF enters into No-Op Register of U1 without affecting the contents of other registers, and 0x00 enters into the Decode Mode Register of U2.

