MAX7219 Library selection

If you use the MAX7219 library, which one?

Which library for the MAX7219 is most popular?

Which library is the clearest or cleanest to use?

More importantly, which should be avoided?

What applications are you looking trying to implement? That could have an influence on which is the cleanest to use.

marco_c:
What applications are you looking trying to implement? That could have an influence on which is the cleanest to use.

Long story very short. I need to display the peak value of the ADC. Nothing more, nothing less.

Are you using 7 segment LED displays or 8x8 LED matrix modules or ...?

Good question. 7 segment display. 3 digit, homemade.

You don't need a library for that,

Call SPI.begin() in setup to use the default 4 MHz speed and SPI Mode 0.

Then add some SPI.transfer() calls like this:

digitalWrite (ssPin, LOW)
SPI.transfer(registerAddress);
SPI.transfer(data);
digitalWrite (ssPin, HIGH);

Put it in a function if you want.

Call it 5 times in setup() for:
normal mode (vs standby)
display test off (vs all LEDs on)
3 digits
brightness (0 to 15)
display mode (auto decode if just displaying 0,1,2,3,4,5,6,7,8,9,H,E,L,P,-). (vs no decode mode)

Then call it again 3 times in loop for registers 1,2,3 to send the 3 digits you want displayed.

It's pretty simple. The register addresses are in the data sheet for the 5 control bytes and the digits.

CrossRoads:
You don't need a library for that,

Call SPI.begin() in setup to use the default 4 MHz speed and SPI Mode 0.

Then add some SPI.transfer() calls like this:

digitalWrite (ssPin, LOW)
SPI.transfer(registerAddress);
SPI.transfer(data);
digitalWrite (ssPin, HIGH);

Put it in a function if you want.

Call it 5 times in setup() for:
normal mode (vs standby)
display test off (vs all LEDs on)
3 digits
brightness (0 to 15)
display mode (auto decode if just displaying 0,1,2,3,4,5,6,7,8,9,H,E,L,P,-). (vs no decode mode)

Then call it again 3 times in loop for registers 1,2,3 to send the 3 digits you want displayed.

It's pretty simple. The register addresses are in the data sheet for the 5 control bytes and the digits.

Woah, boy. Say that again. Kind of a right turn in my train of thought. Where did SPI come in?

I'm with CrossRoads. For a simple 7 segment display you don't need a library - adds unnecessary overheads and the chip already supports decoding digits into 7 segment display segments.

SPI comes from the fact that the MAX7219 communicates using SPI. You need 3 connections to the chip (CLOCK, DATA, LOAD) and you load the command registers using serial SPI output. Works a lot like using a 595 IC if you have done that before.

Still need more explanation on CrossRoads post (or an example). No I haven't used '595s. The datasheet says the MAX7221 is compatible with SPI, but nothing about the 7219. If it works great, but I need a little more info on how to get from here to there (as aforementioned, an example would be huge, I learn great from good examples)

Here's the code I'm using:
https://github.com/tttapa/Control-Surface/blob/master/src/Hardware/LEDs/MAX7219_Base.hpp

It uses raw decoding, if you just want to use digits, change the value for the decode mode in the init function to 0xFF instead of 0. Also change the scan limit to 2 if you're only going to be using 3 of the 8 digits.

(Also ignore the ExtIO namespace specifiers, just use digitalWrite and pinMode as usual.)

To display a number:

   /**
     * @brief   Display a long integer number to the display.
     *          The number will be right-aligned.
     * 
     * @param   number
     *          The number to display.
     * @param   startDigit
     *          The digit (zero-based, counting from the right) to start 
     *          printing the number.  
     *          Digits: 7 6 5 4 3 2 1 0
     * @param   endDigit
     *          The last digit (zero-based, counting from the right) that can 
     *          be printed.
     *          If the number is larger than `endDigit - startDigit`, the number
     *          is truncated on the left. If the number is smaller than 
     *          `endDigit - startDigit`, the leftmost digits including 
     *          `endDigit` are cleared.
     * @return  The number of digits that have been overwritten 
     *          (`endDigit - startDigit`).
     */
    uint8_t display(long number, uint8_t startDigit = 0, uint8_t endDigit = 7) {
        long anumber = abs(number);
        uint8_t i = startDigit + 1;
        endDigit++;
        do {
            sendRaw(i++, anumber % 10);
            anumber /= 10;
        } while (anumber && i <= endDigit);
        if (number < 0 && i <= endDigit)
            sendRaw(i++, 0xA); // minus sign
        while (i <= endDigit)
            sendRaw(i++, 0xF); // clear unused digits within range
        return endDigit - startDigit;
    }

Source and some other printing functions

Pieter

The decode mode of the max72xx can make the coding simpler but is compatible only with common cathode displays.

6v6gt:
The decode mode of the max72xx can make the coding simpler but is compatible only with common cathode displays.

That's what I have, if that's lucky for me.

Some code for a class I have been using in a small project. This displays 2 digits and does not use the built in number to seven segment conversion as I needed more 'characters' to be displayed that were available in the MAX7219. You should be able to simplify and adapt this to your needs, I think.

Header file

#pragma once
#include <Arduino.h>
#include <SPI.h>
// Class Definition for LED matrix display handling
// interfaced through the hardware SPI interface.
//
// SPI interface is through the SPI library and so uses the 
// native hardware interface and pin numbers.

// Define the number of digit in the display
const uint8_t NUM_DIGITS = 2;

#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))

// Displayable decimal point is mapped to this char
const char CH_DP = 0x21;

class cIRDisplay
{
public:
  cIRDisplay(uint8_t pinSS) : _pinSS(pinSS) { };

  void begin(void);
  void clear(void);
  void update(void);
  void intensity(uint8_t n) { sendData(OP_INTENSITY, (n > 15) ? 15 : n); }
  void sleep(bool b) { sendData(OP_SHUTDOWN, b ? 0 : 1); }
  
  void show(uint8_t d0, uint8_t d1);
  void show(char d0, char d1);
  void show(uint8_t d0, char d1);
  void show(char d0, uint8_t d1);

private:
  // Opcodes for the MAX7221 and MAX7219
  // All OP_DIGITn are offsets from OP_DIGIT0
  const uint8_t OP_NOOP = 0;         //< MAX72xx opcode for NO OP
  const uint8_t OP_DIGIT0 = 1;       //< MAX72xx opcode for DIGIT0
  const uint8_t OP_DIGIT1 = 2;       //< MAX72xx opcode for DIGIT1
  const uint8_t OP_DIGIT2 = 3;       //< MAX72xx opcode for DIGIT2
  const uint8_t OP_DIGIT3 = 4;       //< MAX72xx opcode for DIGIT3
  const uint8_t OP_DIGIT4 = 5;       //< MAX72xx opcode for DIGIT4
  const uint8_t OP_DIGIT5 = 6;       //< MAX72xx opcode for DIGIT5
  const uint8_t OP_DIGIT6 = 7;       //< MAX72xx opcode for DIGIT6
  const uint8_t OP_DIGIT7 = 8;       //< MAX72xx opcode for DIGIT7
  const uint8_t OP_DECODEMODE = 9;   //< MAX72xx opcode for DECODE MODE
  const uint8_t OP_INTENSITY = 10;   //< MAX72xx opcode for SET INTENSITY
  const uint8_t OP_SCANLIMIT = 11;   //< MAX72xx opcode for SCAN LIMIT
  const uint8_t OP_SHUTDOWN = 12;    //< MAX72xx opcode for SHUT DOWN
  const uint8_t OP_DISPLAYTEST = 15; //< MAX72xx opcode for DISPLAY TEST

  // Define the first and last ASCII Code in the data table
  const uint8_t ASCII_FIRST = ' ';  // space char
  const uint8_t ASCII_LAST = '_';   // underscore char

  uint8_t _pinSS;

  uint8_t _display[NUM_DIGITS]; // display buffer for display digits

  void sendData(uint16_t cmd, uint8_t data);  // send the data to the device
  void write(uint8_t dig, char ch);
};

CPP file

// Class Definition for 2 digit 7 segment display handling
// interfaced through a max 7219 IC using SPI.
//
// SPI interface is through the SPI library and so uses the 
// native hardware interface and pin numbers.

#include "TAD-PIRC-Display-Max7219.h"

// Display System characters set definitions and mapping to seven segment
// Map of the bits that make up the letters and numbers for the various alphabets
//   --a
// f|  |b
//   --g
// e|  |c
//  d--  o DP
//

// The mask for a segment's decimal point
// DP is last bit, which must be OR'ed in separately if included with character.
#define  DP_MASK  0b00000001
// Bit patterns for number and display pattern
const uint8_t PROGMEM mapChar[] =
{
  /* Hex Char   PABCDEFG (dp is last bit, which must be set separately) */
  /* 20 sp */ 0b00000000,
  /* 21 DP */ 0b10000000,
  /* 22 "  */ 0b00000000,
  /* 23 #  */ 0b00000000,
  /* 24 $  */ 0b00000000,
  /* 25 %  */ 0b00000000,
  /* 26 &  */ 0b00000000,
  /* 27 '  */ 0b00000000,
  /* 28 (  */ 0b01001110,
  /* 29 )  */ 0b01111000,
  /* 2A *  */ 0b00000000,
  /* 2B +  */ 0b00000000,
  /* 2C ,  */ 0b00000000,
  /* 2D -  */ 0b00000001,
  /* 2E .  */ 0b10000000,
  /* 2F /  */ 0b00000000,
  /* 30 0  */ 0b01111110,
  /* 31 1  */ 0b00110000,
  /* 32 2  */ 0b01101101,
  /* 33 3  */ 0b01111001,
  /* 34 4  */ 0b00110011,
  /* 35 5  */ 0b01011011,
  /* 36 6  */ 0b01011111,
  /* 37 7  */ 0b01110000,
  /* 38 8  */ 0b01111111,
  /* 39 9  */ 0b01111011,
  /* 3A :  */ 0b00000000,
  /* 3B ;  */ 0b00000000,
  /* 3C <  */ 0b00000000,
  /* 3D =  */ 0b00000000,
  /* 3E >  */ 0b00000000,
  /* 3F ?  */ 0b01100101,
  /* 40 @  */ 0b00000000,
  /* 41 A  */ 0b01110111,
  /* 42 B  */ 0b00011111,
  /* 43 C  */ 0b01001110,
  /* 44 D  */ 0b00111101,
  /* 45 E  */ 0b01001111,
  /* 46 F  */ 0b01000111,
  /* 47 G  */ 0b01011110,
  /* 48 H  */ 0b00110111,
  /* 49 I  */ 0b00110000,
  /* 4A J  */ 0b00111100,
  /* 4B K  */ 0b00000000,
  /* 4C L  */ 0b00001110,
  /* 4D M  */ 0b00000000,
  /* 4E N  */ 0b01110110,
  /* 4F O  */ 0b01111110,
  /* 50 P  */ 0b01100111,
  /* 51 Q  */ 0b01110011,
  /* 52 R  */ 0b00000101,
  /* 53 S  */ 0b01011011,
  /* 54 T  */ 0b00001111,
  /* 55 U  */ 0b00111110,
  /* 56 V  */ 0b00000000,
  /* 57 W  */ 0b00000000,
  /* 58 X  */ 0b00000000,
  /* 59 Y  */ 0b00111011,
  /* 5A Z  */ 0b01101101,
  /* 5B [  */ 0b01001110,
  /* 5C \  */ 0b00000000,
  /* 5D ]  */ 0b01111000,
  /* 5E ^  */ 0b00000000,
  /* 5F _  */ 0b00001000,
};

void cIRDisplay::begin(void)
{
  pinMode(_pinSS, OUTPUT);
  SPI.begin();
  sleep(false);
  sendData(OP_DECODEMODE, 0);
  sendData(OP_SCANLIMIT, NUM_DIGITS-1);
  sendData(OP_INTENSITY, 7);
  clear();
}

void cIRDisplay::sendData(uint16_t cmd, uint8_t data)
// Send the data to the MAX7219 IC using SPI protocol.
{
  uint16_t x = (cmd << 8) | data;

/*
  Serial.print(F("\nSD: C:"));
  Serial.print(cmd);
  Serial.print(F(" D: 0x"));
  Serial.print(data, HEX);
*/
  SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
  digitalWrite(_pinSS, LOW);
  SPI.transfer16(x);
  digitalWrite(_pinSS, HIGH);
  SPI.endTransaction();
}

void cIRDisplay::update(void)
{
  for (uint8_t i = 0; i < NUM_DIGITS; i++)
    sendData(OP_DIGIT0 + i, pgm_read_byte(mapChar + _display[i] - ASCII_FIRST));
}

void cIRDisplay::clear(void)
{
  for (uint8_t i=0; i < NUM_DIGITS; i++)
    _display[i] = ' ';
  update();
}

void cIRDisplay::write(uint8_t dig, char c)
{
  c = toupper(c);
  _display[dig] = (c >= ASCII_FIRST && c <= ASCII_LAST) ? c : ' ';
}

void cIRDisplay::show(uint8_t d0, uint8_t d1)
{
  write(0, d0 + '0');
  write(1, d1 + '0');
  update();
}

void cIRDisplay::show(char d0, char d1)
{
  write(0, d0);
  write(1, d1);
  update();
}

void cIRDisplay::show(uint8_t d0, char d1)
{
  write(0, d0 + '0');
  write(1, d1);
  update();
}

void cIRDisplay::show(char d0, uint8_t d1)
{
  write(0, d0);
  write(1, d1 + '0');
  update();
}

marco_c:
Some code for a class I have been using in a small project. This displays 2 digits and does not use the built in number to seven segment conversion as I needed more 'characters' to be displayed that were available in the MAX7219. You should be able to simplify and adapt this to your needs, I think.

Why not stick to the OP using a library in the Arduino IDE? Which circles back to which of the Arduino IDE libraries is best, or get more details on CrossRoads suggested method.

I think the point here is that controlling a MAX7219 is extremely easy. Once you've got the sendRaw(opcode, value) function working, you're almost done.
You can use whatever code you like.
If you want a review/comparison of specific libraries, just post a link, and we might be able to help.

adwsystems:
Why not stick to the OP using a library in the Arduino IDE? Which circles back to which of the Arduino IDE libraries is best

There is nothing special about "libraries". It's just code without setup and loop functions. Move your code to the ~/Arduino/libraries folder, and you have yourself a library.

adwsystems:
or get more details on CrossRoads suggested method.

An implementation of CrossRoads' method is in the first link in my reply #9.
Connect MOSI (11) to DIN, CLK (13) to CLK and CS (any digital output, 10 recommended) to LOAD (CS).

Seriously?

You asked

Still need more explanation on CrossRoads post (or an example).

The code I gave you was exactly what you asked for. It needs some effort from your side now.

PieterP:
An implementation of CrossRoads' method is in the first link in my reply #9.
Connect MOSI (11) to DIN, CLK (13) to CLK and CS (any digital output, 10 recommended) to LOAD (CS).

marco_c:
Seriously?

The code I gave you was exactly what you asked for. It needs some effort from your side now.

Yes, seriously. I don't see how they relate. Then again, I don't fully follow CrossRoads reply either.

PieterP:
There is nothing special about "libraries". It's just code without setup and loop functions. Move your code to the ~/Arduino/libraries folder, and you have yourself a library.

That part I know. Have known about libraries for decades. I choose the Arduino platform due to the large number of libraries already available (I don't miss the days of no internet and having to roll your own library for everything_. I have learned that not all of them are created equal (look at the top 3-4 contenders to drive the 16x2/20x4 LCDs). Why would I want to create my own?

PieterP:
If you want a review/comparison of specific libraries, just post a link, and we might be able to help.

I don't have links. They are in the Arduino Library Manager. When I punch in MAX7219, I get 6 results. See also previous comment.

adwsystems:
Yes, seriously. I don't see how they relate. Then again, I don't fully follow CrossRoads reply either.

The link I posted contains approximately 20 lines of actual code. Please read and try to understand them, and feel free to ask questions about it if you don't understand it.
Everything you need to know is in the MAX7219 datasheet: you assert the load line, shift out an op-code (using SPI or bit-banged), shift out a value, and de-assert the load line. That's it, and that's what the code does.

adwsystems:
I choose the Arduino platform due to the large number of libraries already available (I don't miss the days of no internet and having to roll your own library for everything_. I have learned that not all of them are created equal (look at the top 3-4 contenders to drive the 16x2/20x4 LCDs). Why would I want to create my own?

You showed interest in the method proposed by CrossRoads, so we posted an implementation.

PieterP:
The link I posted contains approximately 20 lines of actual code. Please read and try to understand them, and feel free to ask questions about it if you don't understand it.

The code in the post says nothing of SPI and doesn't compile, But I don't expect it too, as it is only a floating code snippet. So it is difficult to understand how it relates to anything (expect I assume it relates to the thread and the MAX7219).

PieterP:
Everything you need to know is in the MAX7219 datasheet: you assert the load line, shift out an op-code (using SPI or bit-banged), shift out a value, and de-assert the load line. That's it, and that's what the code does.
You showed interest in the method proposed by CrossRoads, so we posted an implementation.

As aforementioned, CrossRoads ost me with the reply to use SPI to control the MAX7219, with some high level details. Unfortunately between the datasheet and the SPI, the level of description is too high level for me to do anything with. There have been some before and after presented, but no explanation nor details enough for me to follow how to get from CrossRoads' post to using SPI to control the MAX7219. CrossRoads alludes to the fact that I could make a few simple functions using SPI and be on my way. There are libraries tossed out "here's the answer". Great, fine, well, and dandy. But still doesn't answer the question of how to get from CrossRoads post to his few simple functions, nor does it answer the question of the "best library" (now up to 9, 6 in the IDE, 2posted in replies, plus CrossRoads suggestion to roll my own). I'm asking for explanation, but getting the final answer. An answer and an explanation can be two different things (and are in this case).

Sounds like you still didn't read the link I keep referring to.