MAX7219 and non-standard matrix sizes (10 x 20)

Hello all,

I'm trying to design a 10 column x 20 row monochrome LED matrix for use as a spectrum analyzer, and after some research have settled on using multiplexing via the MAX7219 IC. I think I need 4 of them, since ceil(200 / 64) = 4.

Usually these are used to drive 8 x 8 matrices or segmented displays, and I'm trying to figure out if I need to change how I wire my matrix, or if I can just connect all the cathodes / anodes in rows and columns as usual, and use software to alter the display parameters.

I'm thinking the latter should be possible if I do the following:

For columns:

Column 1: Digit 1 on IC1 Column 2: Digit 2 on IC1 etc. Column 9: Digit 1 on IC2 Column 10: Digit 2 on IC2

For rows:

Row 1: Segment 1 on IC1 Row 2: Segment 2 on IC1 etc. Row 9-16: Segment 1-8 on IC2 Row 17-20: Segment 1-4 on IC3

The bottom left 64 LEDs should be easy to do, as they correspond with the digits and segments in the first chip. However, if I wanted to light the LED in the (10,20) position, I would have to send a signal equivalent to digit 2 on IC2 and segment 4 on IC3.

Now that I think about it, since there's only 30 channels (rows + columns), could I theoretically use only 2 chips (16 outputs each) to drive the whole matrix? Or will it be dim if it's multiplexed that way?

Basically, I'm wondering if I'll run into difficulties if the display isn't wired up in 8 x 8 chunks.

Thanks for your time!

edit: ok with a bit more research, I found that the digit drive lines sink current and the segment drive lines source current, so it looks like I'll need 3 ICs at least, if I can mix signals... if not, I'd love some suggestions on how to break up 200 LEDs into 4 x 50 mini matrices.

if I can just connect all the cathodes / anodes in rows and columns as usual, and use software to alter the display parameters

Yes, This would be the way I would do it.

I'm wondering if I'll run into difficulties if the display isn't wired up in 8 x 8 chunks.

No, it's just a matter of how the software manages the array.

I would be giving some thought about whether it would be easier to have the display size as multiples of 8 (ie, 16x24) as the mapping would become a lot easier in the software. Alternatively you can just 'throw away' the additional capability and use the mapping as it the display was that size without the physical LEDs to display the output.

Its not clear if you are looking to use standard modules or a custom matrix for this. If you are looking for standard modules, then you may be able to use this work I have previously done: https://arduinoplusplus.wordpress.com/2018/07/13/fun-and-games-and-a-new-library/

Thank you for the reply and information! Here's some clarification and some followup questions I have:

I'm planning on creating a custom matrix using single 5 mm LEDs. The matrix has to be 10 x 20 for my project.

In regards to the 16 x 24 matrix and throwing away the extra pixels, how many chips would be needed to control it? I haven't used the MAX7219 before, so I'm unsure of its limitations. I'm hoping I can wire it up with just 3 ICs, but I'm guessing worse case scenario is I would need 6.

For example, if I had 2 MAX7219 and a small 8 x 16 matrix with all cathodes and anodes connected in rows/columns, could I hook up all 8 columns to source from chip 1, and sink 8 rows into each chip? Or would I have to run the 8 columns to source parallel from chip 1 and 2? I feel like this is going to cause unwanted LEDs to light up if there are multiple connections though, for example sending (1,1) and (8,16) at the same time would cause (1,16) and (8,1) to light up since all the rows and columns are connected (unless the chips multiplex those signals and only send one "digit" at a time?)

Thanks again for everyone's time :)

10 x 20 = 200 LEDs MAX7219 controls 8x8 = 64 LEDs 200/64 = 3 1/8 MAX7219 = 4 chips. Or 3, and one shift register for the last 8 bits. Each group of 8 LEDs in a MAX7219 is controlled by one internal shift register, each is individually accessible. The MAX7219 takes care of the multiplexing between columns. (common cathode per column - or rows, if you prefer to picture the device as common cathode per row).

For example, I offer a board that lets you break out each LED with its own pair of wires. |500x374 Thus you can take your 200 LEDs and arrange physically as you wish, you just need to keep track in software which register an LED is being controlled from. For example, say Row1 LED0 to LED19 and Row2 LED0 to LED 19 are 'mapped' in your software as 5 bytes (5 x 8 = 40), then Rows 3,4 Rows 5,6 Rows 7,8 and Rows 9,10 can be treated the same as well. Or do the same with columns - columns 1-4 = 40 LEDs, 5-8, 9-12, 13-16, and 17-20 are also 40 LEDs.

Or, use an int for each column of 10, and have a function that does the mapping for you, and ignoring the extra 6 bits.

If you want the matrices to be ordered, six MAX7219 (they chain of course, to just three control pins).

You need two wide to get the ten, and three long to get the 20, thus six.

If you broke up the matrix into smaller groups of LEDs, then four MAX7219s could control 256 LEDs, three could only control 192 and you want 200.

To even out the current distribution, you would want to use each of the "wide" groups driving five cathodes with the scan limit register set to 4. You might as well have the first two "long" pairs driving the full eight anodes and the third just four for neatness of the code.

Wow ok, lots of great info - thanks!

Mapping 5 bytes at a time or using an INT for 10 are some interesting options… I’ll definitely keep those in mind.

From this info, it sounds like I won’t be able to simply wire up the LEDs in a grid with all anodes/cathodes connected, and will need to break them out into groups of (max) 64, or 4 x 50 to even out the current distribution, as noted above.

Hence, my board will eventually have groups of 50 LEDs wired into each chip separately, and the chips connected to each other through the data lines? Then I can worry about software that maps the position of LEDs in the overall layout to the digit/segment assignment in each chip.

Based on this info, I’ve also created two different layouts for the LEDs (attached), with each colour representing a different MAX7219. I’m feeling like the left layout will benefit my application more (where LEDs are mostly lighting up together in columns), but perhaps the right layout has some benefits too? Codewise I’m thinking I could either do something like “light up all digits ending in 1” (x % 10 = 1) to get a whole column to light up in the left layout… though the right layout would be easier to use inequalities to get parts of columns to light up (ie: x < 40 or whatnot).

Layouts

Any thoughts on these or other potential layouts would be appreciated :slight_smile:

layout.PNG

Any thoughts on these

I think there must be something you have still not quite grasped yet. The coding to achieve those patterns would be very dificult. Not impossible, but not elegant or efficient. But the wiring to achieve them.... absolute nightmare. Just try drawing the schematic and you will soon see.

You might be thinking this is an interesting challenge. Life is too short!

If it were my project, I might use 3 x ht16k33 chips.

Im making this exact thing rn.
Youll need 6 7219s as it is not possible to use unused anodes and cathodes from one chip to the next.
The wiring is a nightmare so i opted to design pcbs to deal with this. This makes it costly when you make a mistake hardware wise.
The layout is going to be funky because youll only have 2 full 8x8 chips the others will be oddballs. Mine goes bottomA bottom B, midA etc… So when you call on a column all the way down you have to address all “A” sections of x column. Etc.
Right now mine is struggling with some kind of either noise interfence from lengthy data lines and not enough caps /too far etc… Or some other janky wiring I probably botched. Doubting software because even simple instructions act funny. Point being. Its gonna be a ride but Im sure well get through it, take your time and be patient.

PaulRB: I think there must be something you have still not quite grasped yet.

You're probably right, but that's why I'm asking all these questions :)

PaulRB: If it were my project, I might use 3 x ht16k33 chips.

That's a good suggestion, I've got a few coming for a different project so I'll try those out too :)

I think you would be better off making your matrix from a WS2812 addressable strip. That would be cheaper and a lot easier to control in the software.

And you have the advantage of using colour if you want it.

This is my 12 by 20 matrix.

EDIT Yes PaulRB it does do an auto insert. It didn’t do it at 5:00 this morning.

If you want to try the approach using 4 x max7219, how about this? Its awkward, but hopefully not too bad, from both wiring and coding point of view.

EDIT: Hey! That image inserted itself! Normally you have to do that yourself by copying the address and modifying the post.

The wiring for each chip’s part of the matrix would be:

Capture.PNG

Capture2.PNG

PaulRB: EDIT: Hey! That image inserted itself! Normally you have to do that yourself by copying the address and modifying the post.

Just noticed that! New feature(?) it seems, but I also make the image a link when I do it.

counterfeit Max7219 cost literally "nothing". I would buy two of these 8x32 Modules and use 6 MAX for the Matrix and keep two as spare and play around. Easy Wiring, easy program logic, no headbanging regarding, how to wire. Project can be started tomorrow.

noiasca: I would buy two of these 8x64 Modules

They are of course, 8 by 32. :grinning: Excellent idea! These are - if the images are to be believed - the good modules with the matrices socketed. The non-socketed ones are difficult to use.

It is clearly the case that changing the original proposal from a 10 by 20 to a 16 by 32 display would make things vastly easier! :roll_eyes:

noiasca: and use 6 MAX for the Matrix and keep two as spare and play around.

What? You want to saw off the fourth display? Dremel with a diamond disc.

noiasca: Project can be started tomorrow.

Really? They take a month or three to arrive!

Thanks for all the feedback and tips, everyone!

I've got 4 x MAX7219s arriving that I got as a free sample from maxim, and I really appreciate the layout explanation that you posted, PaulRB. I'll probably mock that one up using a breadboard and test with just 1 chip to see if I can get it to do some things I want to do.

As for using other sized matrices or pre-built ones, I know it would make the project easier but unfortunately it's not an option, as this is going to be a replica of a movie prop. Using addressable WS2812-compatible LEDs like PL9823s might be an option but it would be sacrificing some accuracy and brightness.

Cheers for all the great discussion, and I'll post again when I have some new developments :)

As for using other sized matrices

it's just about your final layout. You can remove the 8x8 LED modules and use other LEDs like you want to.

but even if you insist to build your own layout - I still would use 6. And pay attention on the different colors of the LEDs, to get red yellow and green to the same brightness.

by the way, if you want to rebuild a SID: http://www.bloody-plastic.com/props/sidpanel.html

petriomelony: Using addressable WS2812-compatible LEDs like PL9823s might be an option but it would be sacrificing some accuracy and brightness.

You can get these leds in 5mm round shape with diffusing lenses: search for apa106. So they could look accurate. And brightness would certainly not be a problem. Apa106 would be much brighter than using max7219 because no multiplexing is involved. You might need to turn the brightness down!

And be aware if this is for a movie then watch out for the strobe effect between the multiplexing and the shutter speed of the camera. The brightness might also be a problem as LEDs are very difficult to photograph without the LEDs looking burned out.

Do some tests before you commit to building the whole thing.

Apa106 would be much brighter than using max7219 because no multiplexing is involved.

Other than the PWM used in the dimming. Here the APA106 is much faster than the WS2812.

Grumpy_Mike: The brightness might also be a problem as LEDs are very difficult to photograph without the LEDs looking burned out.

Well that is a problem with amateur photographers on these boards, but I suspect much less of a problem on a properly-lit movie set - unless it is a dark scene!

Ok, it took my now some hours, but here comes my “Take it easy with 6 max7219” proof of concept.

/*
   This sketch simulates a spectrum analyzer with MAX7219
   e.g. the SID from the movie Back to the Future  
   or an AudioControl SA-3052
   based on the idea of: https://forum.arduino.cc/index.php?topic=704923.0
   
   hardware:
   - 8 MAX7219 with each 8x8 LED module, e.g. https://s.click.aliexpress.com/e/_dUpAJp9

   library:
   I'm using my Noiasca LED Control: https://werner.rothschopf.net/201904_arduino_ledcontrol_max7219.htm as it also supports Hardware SPI.
   But the sketch might work with the common known "LED Control" also.

   by noiasca 
   2020-09-19
*/

const uint8_t LEDDATA_PIN = D8;  // LED DATA IN
const uint8_t LEDCLK_PIN  = D7;  // LED Clock
const uint8_t LEDCS_PIN   = D6;  // LED CS or LOAD
const uint8_t LED_MODULES = 8;

const uint8_t noOfColumns = 10;  // columns to show
const uint8_t ledsPerColumn = 20;// 
const uint16_t speed = 250;      // refresh rate of display
const uint16_t highwater = 250;  // how long should the highwater mark stay

#include "NoiascaLedControl.h"  // download from https://werner.rothschopf.net/201904_arduino_ledcontrol_max7219.htm 
LedControl lc = LedControl  (LEDDATA_PIN, LEDCLK_PIN, LEDCS_PIN, LED_MODULES);  // Software Bitbang - use this if you can't use Hardware SPI
//LedControl lc = LedControl  (LEDCS_PIN, LED_MODULES); // Hardware SPI (UNO/NANO: 13 CLK, 11 DATA_IN/MOSI)

struct Coordinate
{
  byte device;
  byte row;
  byte col;
};

struct Column
{
  byte previousMax;          // stores the highest value
  uint32_t previousMillis;   // timestamp of previousMax
} column[noOfColumns];

// Maps one pixel of the column to a technical device/col/row position
// the calculation has to fit to your layout - and could differ based on the column you hand over in the parameter
Coordinate getCoordinate(byte actualColumn, byte value)
{
  Coordinate actual;
  if (actualColumn < 8)
  {
    actual.device = value / 8;
    actual.col = 7 - value % 8;   // "in my hardware implementation" I needed the reverse order of the column...
    actual.row = actualColumn;
  }
  else
  {
    actual.device = (value / 8) + 3;  // "in my hardware implementation" the 9th column (and further) start at a different MAX7219 device
    actual.col = 7 - value % 8;
    actual.row = actualColumn - 8;
  }

// Serial.print (actual.device); Serial.print("\t"); // activate if you need to check your wiring
// Serial.print (actual.col); Serial.print("\t");
// Serial.print (actual.row); Serial.println();
  return actual;
}

void showColumn(byte actualColumn, byte value)
{
  Coordinate coordinate;
  for (byte i = 0; i < ledsPerColumn; i++)
  {
    coordinate = getCoordinate(actualColumn, i);
    lc.setLed(coordinate.device, coordinate.row, coordinate.col, i <= value); // Set or delete the pixel
  }
  // show a delaying highwater mark
  uint8_t currentMillis = millis();
  if (value < column[actualColumn].previousMax && currentMillis - column[actualColumn].previousMillis < (speed + highwater))
  {
    coordinate = getCoordinate(actualColumn, column[actualColumn].previousMax);
    lc.setLed(coordinate.device, coordinate.row, coordinate.col, true);         // Set pixel
  }
  else
  {
    column[actualColumn].previousMax = value;
    column[actualColumn].previousMillis = currentMillis;
  }
}


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println(F("BTTF SDI with MAX7219"));
  lc.begin();  /* The MAX72XX needs to initialize hardware */
  for (byte i = 0; i < LED_MODULES; i++)
  {
    lc.shutdown(i, false);
    lc.setIntensity(i, 4);  /* Set the brightness to a medium value, possible is 0-15 */
  }
  lc.clearDisplay(); /* and clear all displays */
  // test the  display to ensure your wiring is correct:
  showColumn(4, 4);
  showColumn(4, 8);
  showColumn(5, 9);
  showColumn(6, 12);
  showColumn(7, ledsPerColumn - 1);
  delay(3000);
  lc.clearDisplay();

  showColumn(9, ledsPerColumn - 1);
}

void loop() {
  for (byte i = 0; i < noOfColumns; i++)
  {
    byte value = random(1, ledsPerColumn);                        // simulate with random values
    Serial.print(i); Serial.print("\t"); Serial.println(value);
    showColumn(i, value);
  }
  delay(speed);  // dirty delay
}

In the end, it’s just a question of how you map your visual “column” pixels to your real hardware.
The more complex your hardware wiring is, the more effort is needed for getCoordinate().