Creating a MAX7219 LED Matrix driver

Following the post about the HC-SR04 Driver creation I decided to post about a new driver I created recently: the MAX7219 used to control 8×8 LED Matrix.

A single MAX7219 chip can be used to control 8 7-segment displays.

This is a 7-segment display representation:

          G
  A__     |
 F|__|B <-+ 
 E|__|C
    D  ° DP

So we have 7 segments (A, B, C, D, E, F, G) and more the Display Point (DP) used to represent fractional place.

This chip is controlled over SPI interface up to 10MHz. Also it can be connected in a chain because the DIN is propagated to DOUT after 16 clock pulses.

But the MAX7219 has more some flexibility: it allow you do define your digits. That means you can define which segments you want to turn ON/OFF when a digit is selected. Even the DP can also be turned on.

Also it does the refresh at 800Hz for you for all digits you represented in it internal memory.

So someone realized: “if I have 8 lines to select up to 8 7-segment displays and I can choose which segments to control (up to 8 segments: A-G + DP), then I can use it to control a 8×8 LED Matrix.”

And this is the nice thing about the MAX7219. You can find some low cost 8×8 LED Matrix at Aliexpress or eBay as a module with MAX7219.

You can find the MAX7219 datasheet here:

Click to access MAX7219-MAX7221.pdf

Let me explain now how you control this device:

This device expect a 16-bit data sequence:

D15 D14 D13 D12 D11 D10 D09 D08 D07 D06 D05 D04 D03 D02 D01 D00
 X   X   X   X  OP3 OP2 OP1 OP0 DP   G   F   E   D   C   B   A

It has 16 registers encoded in the 16-bit as OP3-OP2-OP1-OP0 (although registers #13 and #14 are not documented in the datasheet).

The register #0 is not operation (does nothing). Register #1 represents first display (Digit0), then when it receives OP3-OP0 = 0001 it will save the value of each segment (D07-D00) to represent the digit to show in the first display and when the Chip Select pin (CS/LOAD) changes to high level the programmed segments (D08-D00) will be presented in the display. Same apply to register #2 (second display digit: Digit1) up to register #8 (Digit7).

Now forget about 7-segment display and think about the 8×8 LED Matrix it will be controlled similar way. The Digit0-Digit7 will control which row of the LED Matrix is selected and D07-D00 will control which LED(s) of this row needs to be enabled.

You just need to program each row with the LEDs you want to turn-on/off and the MAX7219 will keep refreshing it for you automatically.

At this point you know how the MAX7219 works, now it is time to know how to create a driver for NuttX.

The module I was testing has 4 clusters: 4 8×8 LED Matrix chained. Initially I was thinking to create a driver where I could send the text to exhibit in the display, but it is very inflexible. What if you want to exhibit Italic text or even some graphic symbols.

Then I decided to create a driver to let applications “see” the MAX7219 as a LCD device. This way we can exhibit anything in the display and also we can to chain many 8×8 LED Matrices together to create big panels with higher resolutions.

Before coming with the final solution used in this driver I tested many options:

I started moving the CS/LOAD pin to low-level and sending the 16-bit data to represent the 8 pixels of the beginning of current row and moving the CS/LOAD pin to high level. This approach didn’t work well and I could see the pixels moving in the display.

Then I tested sending all pixels of current row and then move the CS/LOAD pin back to high level. This approach worked better, but the image was mirrored and upside-down.

So we need to send a whole row by time, but we need to send the last pixels first, because the LED matrices are chained and we need to send from last row to the first.

All NuttX LCD drivers has a putrun() function that receives the raster pixels from NX Graphic framework. For LCD displays that you cannot read its RAM pixel memory back, you need to create a “framebuffer” (fb[]) buffer memory to hold the pixels.

Normally this buffer memory will have the same “row x column” pixel organization and dimension as the display its control. But for the MAX7219 panel displays we have something different.

Imagine a MAX7219 panel formed by 4 8×8 LED Matrices, if you connect these 4 modules horizontally you will have 32×8 pixels (32 columns by 8 rows). But you can have other options to organize these modules, for example: 2 modules in the horizontal and more 2 modules below it forming a 16×16 pixels arrangement. Or you can put 1 module below each one, forming a 8×32 pixel display.

No matter how you physically organized these LED matrices, it needs to be controlled as a single LED strip of (N*8) x 8 pixels. It means that the NX Graphic resolution needs to be converted to a strip of 8 row pixels in the driver before sending it to MAX7219.

So imagine the resolution of 16×16 pixels. The driver needs to convert it to 32×8 pixels before sending it to display. So it creates 8 “long row” to fit the display resolution.

Then in the max7219_putrun() I need to convert any row greater than 7 to its equivalent between 0 up to 7 and draw it after the end of Xn column resolution:

  /* Get real row position in the strip */

  newrow = (int) (row % 8);

  /* Divide row by 8 and multiply by X to get right column to draw the pixels */

  row = (row >> 3);

  col = col + (row * MAX7219_XRES);

  row = newrow;

After this conversion the driver will draw the pixels at right position in the strip and we just need send a single “long row” by time:

  /* Lock and select the device */

  max7219_select(priv->spi);

  /* We need to send last row/column first to avoid mirror image */

  for (i = (MAX7219_XSTRIDE * MAX7219_YSTRIDE) - 1; i >= 0; i--)
    {
      /* Setup the row data */

      data = (8 - row) | (*(fbptr + i) spi);

The MAX7219_XSTRIDE is the number of horizontal 8×8 LED Matrix and MAX7219_YSTRIDE is the number of vertical LED Matrix.

As you can see I’m sending the “long row” of the LED Matrix starting from the last LED Matrix to avoid the image mirroring and also I’m inverting the row position (8 – row) to avoid the image upside-down.

Side note for Arduino people: there is a library to control MAX7219 called MaxMatrix (see this tutorial: http://howtomechatronics.com/tutorials/arduino/8×8-led-matrix-max7219-tutorial-scrolling-text-android-control-via-bluetooth/ ), but this MaxMatrix will not work with these modules with clusters of 8×8 matrices as single board because it expects each module to be rotated 90 degrees.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s