The MAX7219 is a common and inexpensive IC used for controlling up to eight 7-segment LED displays (or 64 individual LEDs).
Beginners are often intimidated by how to use this versatile component with a microcontroller, turning to libraries to help them solve the problem. In most simple cases, it is more efficient to run the IC directly. Here’s what you need to know to get the job done.
How the IC works
As for any device, to understand this IC a good starting point is the manufacturer’s datasheet (get a copy here). As with most datasheets it contains an abundance of information but written in a way that can be confusing!
The introduction to the data sheet explains most of the functionality provided:
“The MAX7219/MAX7221 are compact, serial input/output common-cathode display drivers that interface microprocessors (μPs) to 7-segment numeric LED displays of up to 8 digits, bar-graph displays, or 64 individual LEDs. Included on-chip are a BCD code-B decoder, multiplex scan circuitry, segment and digit drivers, and an 8x8 static RAM that stores each digit. Only one external resistor is required to set the segment current for all LEDs. The MAX7221 is compatible with SPI™, QSPI™, and MICROWIRE™, and has slewrate-limited segment drivers to reduce EMI.”
Let’s break that down:
- serial input/output – communications to the IC are via serial stream (SPI™, QSPI™, and MICROWIRE™). The most common Arduino interface is SPI, which is discussed below.
- common-cathode display drivers – 7-segment LEDs need to be common cathode type. LED matrices can work equally as row or column cathode type.
- a BCD code-B decoder – for 7-segment displays the IC can translate from the BCD digits to the 7 segment representation. This is a good feature for digit displays but not used for LED matrices.
- multiplex scan circuitry – the IC multiplexes the LEDs at 800Hz (ie, each led will be switched on and off 800 times in one second). This is more than enough for Persistence of Vision (PoV) to make it look like the LED is always on. The current supplied to each LED is controlled by one resistor selected to complement the LED reverse voltage and desired current/brightness.
- segment and digit drivers, and an 8×8 static RAM that stores each digit – once the microprocessor tells the display what to show, the IC will run the display independently. The microprocessor can do its thing and move on to other tasks.
We need to think of comms in 2 parts – the hardware interface and the messages transferred over the interface.
The MAX7219 connects to the microprocessor using the Serial Peripheral Interface (SPI). Serial communications is a one-way channel from the microcontroller to the MAX7219.
Much has already been written about SPI, so I will not repeat it here – this article by Sparkfun is a good basic introduction.
The basics relevant for this explanation are that:
- The interface requires 3 microprocessor pins – Slave Select (SS), System Clock (SCK) and Master Out Slave In (MOSI) are each connected to the MAX7219 LD (Load) or /CS(Chip Select), Clock (CLK) and Data In (DIN) pins respectively.
- SS is used to ‘activate’ the slave device and make it pay attention to the CLK and MOSI signals. This allows more than one slave to share the SPI interface. SS is normally HIGH and set LOW to make it pay attention with the new data loaded into the IC when SS transitions back to HIGH.
- CLK and MOSI work together to transfer the data as a serial stream to the MAX7219 registers, as in the figure above.
- The Data Out (DOUT) can be chained from one MAX7219 to the next MAX7219’s DIN, allowing the serial data to be streamed from the first DIN along a series of daisy-chained ICs.
I would recommend using the Arduino SPI library to implement this interface:
- it uses the hardware hardware supported CLK and MOSI SPI pins and will run much more efficiently than a ‘bit-bashed’ approach using arbitrary pins. SS remains an arbitrary pin you need to control.
- it is implemented for all microprocessor hardware supported by the Arduino IDE and is therefore architecture independent.
The online reference for the SPI library can be found here. SPI is such a common interface for many Arduino peripherals that it is worth spending time to understand how it works.
The message interface to each IC consist of fixed 16 bit messages organised as two 8-bit values.
The first byte transferred (D15 to D8) is the address for the data that follows. Only the lower 4 bits are significant (0x0 to 0xf). You can think of this byte as the either a command for the IC or the memory address for the relevant display ‘digit’.
The second byte (D7 to D0) is the data to support the command/address.
The list of command registers is found in Table 2 of the datasheet (shown below) and a full explanation of the data required for each is also listed in the datasheet.
If more than one IC is in the serial chain, enough data for the entire chain must be pushed into the first device e.g. for 4 devices, 4 messages (8 bytes) must be transmitted. The old contents of each IC in the chain is pushed along until it ‘drops off’ at the end of the chain.
Important for the controlling software is that data pushed in first will finish up at the end of the chain. Partially filling the pipeline results in the old data for the first module in the sequence re-appearing somewhere before the end of the chain.
In the next part we’ll see how these concepts translate into software for controlling 7 Segment and LED matrix displays.