Project help (MAX7219) related... (connecting/dis-connecting from main project)

got it.. (no worries)..

hopefully someone else will come along and take interest... provide a second set of eyes on the project.. and point me in the right direction.. :slight_smile:

Question.. was there a reason in your demo sketch you used the wire library,...and not the SPI library?

anyways.. anyone else around, maybe want to take a a peek at the LedControl lib for me? See if there is a problem with the class construtor portion? (why it doesnt overwrite? or why cant seem to re-define an object already defined previously in the sketch?... or why if you do NOT need to re-create the contructor again.. why only the first group of leds works?)

hell, if someone wants to take the time and has a solution.. I can throw ya few bucks for the time. :slight_smile:

thanks!

Since I've been prompted to get involved in this thread, can you please clearly explain, in a paragraph or so, what the current problem is?

You have two (or more?) MAX7219 chips you want to control at once, is that it? And something is going wrong?

Quick summary:

  • using the LedControl library
  • 2- MAX chips (chained) (they are actually two physical/separate pcb's)..
  • the 'second' MAX chip/pcb gets (physically) removed and re-inserted back into the running circuit/code

I need to re-initialize the 'second' MAX chip again, once it is connected back into the circuit.. (more specific, since they are chained I have been re-initializing them both. Targeting both or just the second chip, to shutdown = false; has made no difference, I have been sticking with going through both chips as that is what I initially in the sketch upon power-up)

Problem:

the second MAX chip/pcb is not behaving correctly upon re-inserting into the circuit.

I end up with 1 of two outcomes/scenarios:
a.) only the FIRST GROUP of 8 leds (DIG0 / SEGDP-G)... turns back on/displays & updates correctly (rest of the 30 leds are turned off)
b.) the bargraph does turn fully on and 'respond'.. (but sometimes immediately or after a couple of seconds,.. or few triggered events (button presses/fire) ...the project freezes, resets, audio and/or serial output stops when Im watching the code flow/execution)

-----------------------[detailed info below]-------------------------------

I can purposefully command results A or B by commenting out one line... but before that..

here is how I set up the chips when the project gets power..

LedControl lc=LedControl(3, 6, 5, 2); //scab board (2 max chips)

and then in the setup() loop...

byte devices=lc.getDeviceCount();
//we have to init all devices in a loop
for(byte address=0;address<devices;address++) {
	/*The MAX72XX is in power-saving mode on startup*/
	lc.shutdown(address,false);
	/* Set the brightness to a medium values */
	lc.setIntensity(address,15);
	/* and clear the display */
	lc.clearDisplay(address);
}

normal stuff.. and in no way a problem.. (just background info)..

however.. (as noted) the problem is when I take the pcb (chip) out of the rest of the circuit, and re-insert it.
I must obviously re-initialize the second chip (once it has gained power..etc).. because they start in shutdown mode..etc..

so I have stable code that tells me when the 'pcb/chip' has been re-inserted.. (because I am using another pin/switch to inform me when the pcb (connector/plug) connection has been made...(works fine.. letting you know I have a 'trigger' that tells me when to try and initialize that second chip again)

So when I get this 'trigger' letting me know I can attempt to re-initialize the second MAX chip and send it some data.....

I have tried this:

targetting ONLY the second chip by device ID (1)

lc.shutdown(1,false);
lc.setIntensity(1,15);
lc.clearDisplay(1);

and this: (thinking maybe I needed to re-initialize BOTH chips again being as they are daisy chained together)

byte devices=lc.getDeviceCount();
//we have to init all devices in a loop
for(byte address=0;address<devices;address++) {
	/*The MAX72XX is in power-saving mode on startup*/
	lc.shutdown(address,false);
	/* Set the brightness to a medium values */
	lc.setIntensity(address,15);
	/* and clear the display */
	//lc.clearDisplay(address); <-- do not clear out address 0/3-digit display
}

doing EITHER of the two approaches above yields the follow results:

  • the bargraph ONLY has the first 8 leds of the 30 total lit up/or displaying the correct 'amount' (as this is a counter)
  • rest of the 'code/sketch' is stable.. does NOT freeze, reset, loose audio or serial output

if I pull the pcb/chip and re-insert again.... (only that first group of 8 leds is back on/responsive)
thinking maybe not enough time for power up >> sending re-initialization code attempts.. I have thrown delays in there to debunk this. (waiting seconds even to try and re-initialize and send new data to it,...again only first 8)

If I add this line:

LedControl lc=LedControl(3, 6, 5, 2); //scab board (2 max chips)

as in

LedControl lc=LedControl(3, 6, 5, 2); //scab board (2 max chips)
byte devices=lc.getDeviceCount();
//we have to init all devices in a loop
for(byte address=0;address<devices;address++) {
	/*The MAX72XX is in power-saving mode on startup*/
	lc.shutdown(address,false);
	/* Set the brightness to a medium values */
	lc.setIntensity(address,15);
	/* and clear the display */
	//lc.clearDisplay(address); <-- do not clear out address 0/3-digit display
}

or

LedControl lc=LedControl(3, 6, 5, 2); //scab board (2 max chips)
lc.shutdown(1,false);
lc.setIntensity(1,15);
lc.clearDisplay(1);

the bargraph DOES all light back up (fully).. or correctly display the right amount..
but then either immediately.. freezes,, or when you press a button it resets, stop audio/serial output..etc..

it makes sens that I WOULDNT have to re-create the class/instance/constructor...

but if NOT there.. then the rest of the leds are not 'working'..

adding it obviously is NOT just overwriting the old one..and leading to a RAM problem?

hope that helps explain the issue.

thanks.

Hmm.

I've been playing with two MAX7219s while you wrote all that. Now I understand how the daisy-chaining works. :slight_smile:

This fairly simple sketch outputs a 16-digit number to both displays (chained together):

#include <SPI.h>

const int CHIP_COUNT = 2;  // how many MAX7219s

const byte MAX7219_REG_NOOP        = 0x0;
// codes 1 to 8 are digit positions 1 to 8
const byte MAX7219_REG_DECODEMODE  = 0x9;
const byte MAX7219_REG_INTENSITY   = 0xA;
const byte MAX7219_REG_SCANLIMIT   = 0xB;
const byte MAX7219_REG_SHUTDOWN    = 0xC;
const byte MAX7219_REG_DISPLAYTEST = 0xF;

void sendToAll (const byte reg, const byte data)
  {    
  digitalWrite (SS, LOW);
  for (int chip = 0; chip < CHIP_COUNT; chip++)
    {
    SPI.transfer (reg);
    SPI.transfer (data);
    }
  digitalWrite (SS, HIGH); 
  }  // end of sendToAll
 
void setup () 
  {
  SPI.begin ();
  sendToAll (MAX7219_REG_SCANLIMIT, 7);      // show 8 digits
  sendToAll (MAX7219_REG_DECODEMODE, 0xFF);  // use digits (not bit patterns)
  sendToAll (MAX7219_REG_DISPLAYTEST, 0);    // no display test
  sendToAll (MAX7219_REG_INTENSITY, 15);      // character intensity: range: 0 to 15
  sendToAll (MAX7219_REG_SHUTDOWN, 1);       // not in shutdown mode (ie. start it up)
}   // end of setup
 
void sendChar (const int which, const char c)
  {
  byte i;
  
  // segment is in range 1 to 8
  const byte segment = 8 - (which % 8);
  // for each daisy-chained display we need an extra NOP
  const byte nopCount = which / 8;
  // start sending
  digitalWrite (SS, LOW);
  // start with enough NOPs so later chips don't update
  for (int i = 0; i < CHIP_COUNT - nopCount - 1; i++)
    {
    SPI.transfer (MAX7219_REG_NOOP);
    SPI.transfer (MAX7219_REG_NOOP);  // need 16 bits of NOP
    }    
  // send the segment number and data
  SPI.transfer (segment);
  SPI.transfer (c);
  // send extra NOPs to push the data out to extra displays
  for (byte i = 0; i < nopCount; i++)
    {
    SPI.transfer (MAX7219_REG_NOOP);
    SPI.transfer (MAX7219_REG_NOOP);  // need 16 bits of NOP
    }    
  // all done!
  digitalWrite (SS, HIGH); 
  }  // end of sendChar
  
// write an entire null-terminated string to the LEDs
void sendString (const char * s)
{
  for (int pos = 0; *s; pos++)
    sendChar (pos, *s++);
}  // end of sendString
  
void loop () 
 {
 sendString ("012345678901234567");
 delay (100);
 }  // end of loop

As described in the datasheet to access further chips down the chain you have to:

  • Bring LOAD low
  • Send the register (eg. which segment number)
  • Send the data (eg. what to display in that segment)
  • Send x NOPs where x is the distance down the chain (eg. 1 for the 2nd chip)
  • Bring LOAD high to latch the data

I'm not really interested in debugging someone else's library, especially as I could drive two chips with minimal code above. I agree that if you add a new chip it needs to go through the initialization sequence. A quick test shows that my initialization in setup above does all chips at once. (Although this seems slightly odd, judging by the datasheet).

I don't see why (based on my test) that if you add a new chip you don't just initialize all of them, eg.

  sendByte (MAX7219_REG_SCANLIMIT, 7);      // show 8 digits
  sendByte (MAX7219_REG_DECODEMODE, 0xFF);  // use digits (not bit patterns)
  sendByte (MAX7219_REG_DISPLAYTEST, 0);    // no display test
  sendByte (MAX7219_REG_INTENSITY, 7);      // character intensity: range: 0 to 15
  sendByte (MAX7219_REG_SHUTDOWN, 1);       // not in shutdown mode (ie. start it up)

If that doesn't work, throw in enough NOPs to address the correct chip.

(edit) Updated sketch to work better.

I too can 'drive' both chips...

at least when I initially boot up. (maybe that wasnt clear)..

if I power-up the project with BOTH max chips connected (the first max chip/pc b is ALWAYS connected to be clear)..
it works... until I need to disconnected the second pcb... and the re-connect it.

(this is to simulate a magazine/clip in a sci-fi blaster..if that helps put some visual to the physical connection/events..being pulled..and re-inserted to 'reload' the bargraph)..

if I power up with BOTH connected, it works... until the 'bargaph' is depleted..and essentially needs to be pulled and re-inserted.

once that happens..
(regardless of if I initialize BOTH or just the SECOND one)...

if JUST initialize (shutdown = false, making it read to accept data, turns it on)... only the first group of 8 work.

if I re-create the constructor it breaks.

I guess if nobody has any suggestions.. I'll and try to use SPI lib...

having my large, multidimensional BINARY array in PROGMEM helps...

and might not be too hard to pass over tot he chip using SPI..?? (right?) lol..

but Im not sure how I can easily convert the 3-digit display function over that easily..

I guess its worth a shot.. and I can probably get better help using SPI transfers than the LedControl library?

if anyone can still save me the trouble of converting things.. (please) chime in! lol..

otherwise... I guess I'll see about converting.. or better yet, probably trying a test.. to see if it even initializes correct using the SPI library.. being inserted/re-inserted..while the first chip never loses power..etc..

Independent of your library, the 5 lines of code I gave above should reinitialize all the MAX7219s in the chain. Why not try that when you detect a reinsertion?

what do you mean by independent?

like not having the LedControl libray imported/used during this 'test'..?

that was the plan.. but I have a fairly big sketch.. to trim down and add this to work with the current hardware set-up..etc.

or we you saying import the SPI library..in my current sketch/project..

and then try those lines where I need to re-initialize? (worth a shot either way I guess) :slight_smile:

I added this to my current sketch:

#include <SPI.h>

const byte MAX7219_REG_NOOP        = 0x0;
// codes 1 to 8 are digit positions 1 to 8
const byte MAX7219_REG_DECODEMODE  = 0x9;
const byte MAX7219_REG_INTENSITY   = 0xA;
const byte MAX7219_REG_SCANLIMIT   = 0xB;
const byte MAX7219_REG_SHUTDOWN    = 0xC;
const byte MAX7219_REG_DISPLAYTEST = 0xF;

void sendByte (const byte reg, const byte data){    
  digitalWrite (SS, LOW);
  SPI.transfer (reg);
  SPI.transfer (data);
  digitalWrite (SS, HIGH); 
}  // end of sendByte

and then where was initializing before.. I commented that out and added this;

sendByte (MAX7219_REG_SCANLIMIT, 4);      // show 4 digits
      sendByte (MAX7219_REG_DECODEMODE, 0xFF);  // use digits    ---> (not bit patterns) the 3-digit display with use 'digits'..but thebargraph will use bit/binary patterns?
      sendByte (MAX7219_REG_DISPLAYTEST, 0);    // no display test
      sendByte (MAX7219_REG_INTENSITY, 15);      // character intensity: range: 0 to 15
      sendByte (MAX7219_REG_SHUTDOWN, 1);       // not in shutdown mode (ie. start it up)
      /*
      delay(loadDelay);
      //LedControl lc=LedControl(3, 6, 5, 2); //scab board (2 max chips)
      //Initialize MAX7219/7221 chip(s)
      byte devices=lc.getDeviceCount();
      //we have to init all devices in a loop
      for(byte address=0;address<devices;address++) {
        //The MAX72XX is in power-saving mode on startup
        lc.shutdown(address,false);
        //Set the brightness to a medium values 
        lc.setIntensity(address,15);
        //and clear the display 
        //lc.clearDisplay(address);
      }
      */

however.. Im a bit confused.. as I dnt see how i am just targetting the second chip?

and if re-initializing BOTH of them.. some defaults need to be different? how to target?

3-digit display will take

I use it (using the LedControl library like so)
breaking down my current ammoCount variable.. and sending it to each 'digit' to the correct 7-segment display (its a 3 digit in one housing 7-segment display, controlled by MAX CHIP #1)

void updateDisplay(){
  //split main ammo count into separate digits
  int dig1 = ammoCount / 100 % 10;//3 on start  
  int dig2 = ammoCount / 10 % 10;//0 on start
  int dig3 = ammoCount % 10; //0 on start 
  lc.setDigit(0, 0, dig1, false);
  lc.setDigit(0, 1, dig2, false);
  lc.setDigit(0, 2, dig3, false);  
};

maybe I'll just try this line:

sendByte (MAX7219_REG_SHUTDOWN, 1); // not in shutdown mode (ie. start it up)

how do I use the NO_OP to target the correct chip?

but I dont understand how its much different than doing the same with the library?

Im trying. but its not doing anything to the bargraph pcb/chip..when I get the trigger its been inserted.. I do a sendByte()..to shutdown = false.. but still is all blanked out..and I cant update it?

Does that library use SPI or not?

Granted that you have the right pin assignments, I think Nick is suggesting that you use your current code, but insert his initialisation code rather then attempting the re-instantiation. You will of course need to refresh the displays immediately afterward, but the annoying "library" will not notice the extra code.

Do you mean hardware SPI? I don't think we know. The problem is that it is being used as a "black box" and it simply does not work properly, and is too much trouble to analyse in such detail.

My suggestion has been to forget the library and write your own code to do what you want, and only what you want.

Paul__B:
Granted that you have the right pin assignments, I think Nick is suggesting that you use your current code, but insert his initialisation code rather then attempting the re-instantiation. You will of course need to refresh the displays immediately afterward, but the annoying "library" will not notice the extra code.

yeah..thats what Im currently trying/attempting to do.. however.. its either not working.. (or more likely....I'm not implementing it correctly?)

I have outlined above what I tried.. (using his lines of code... still un clear on how you separate/target the individual chips though through his approach?)

also.. to be more clear.. i made a quick video:

it shows how it works.. inotially then when disconnecting/re-inserting.. you see only the initial 8 leds light back up..

then I change the line of code.... (re-doing the constructor line..even though I think we all think that should need to be done)..

and you see all the leds in the bragraph light back up upon re-inserting.. but then after a few fire button presses.. you see it free/die.

here is YT link:

as far as it using SPI..

I believe so..

link to lib in playground
http://playground.arduino.cc/Main/LedControl

but back in post #18:
http://forum.arduino.cc//index.php?topic=193834.msg1434936#msg1434936

I posted some snippets from its library.. when looking into how it creates it class instances..etc..

so lets go back and see what Im doing/did wrong in adding in Nicks snippet of code?

hey gang-

wanted to update the thread..

I have 'solved' it...
and it is now working..

apparently.. I didnt need the SPI lib.. or any direct data dumps to reset anything..

I didnt need to loop through ALL (both) chips...

I didnt need to re-create/define the instance constructor..

when I was getting the first group of 8 leds to light up only...
I went back to that very first iteration of code..... where I was just doing this:

lc.setIntensity(1,15);
    lc.clearDisplay(1);
    lc.shutdown(1,false);

when the pcb/max chip was being re-inserted to the main circuit...

after a bunch of playing around.. messing with the library..looking at its init routine..

I added one line and change its value..

lc.setScanLimit(1,4);

so now I just have this:

lc.setScanLimit(1,4);
lc.setIntensity(1,15);
lc.clearDisplay(1);
lc.shutdown(1,false);

and its working great so far.. rock solid...

Im not sure in the beginning I read it defaults scan limit to 7..(and it works fine)....
but I need to define it scanLimit 4 to work? (I mean I only have 4 'sections'....but setting it to 7 should still work)....
I mean I dont set the 3-digit 7-segment display/max chip to scan limit 3? or anythign but default 7 (which means I dont set it at all..just defaults to 7 internally in the library)

anyways.. figured I'd post and share..

thanks for the time and the attention.. (not sure how this slipped by!) :slight_smile:

setting it back to 7..(or not at all)..gives me the results of only the first group of 8 leds being turned back on)

xl97:
but I dont see any mention of this in the .h file.. (so maybe its a 'private' function and we can not use it?....reaching/guessing) :slight_smile:

Why guess?

class LedControl {
 private :
    /* The array for shifting the data to the devices */
    byte spidata[16];
    /* Send out a single command to the device */
    void spiTransfer(int addr, byte opcode, byte data);

Judging by the source this library does not use the hardware SPI. Not that it is a huge issue.

What I don't like is this:

LedControl::LedControl(int dataPin, int clkPin, int csPin, int numDevices) {
    SPI_MOSI=dataPin;
    SPI_CLK=clkPin;
    SPI_CS=csPin;
    if(numDevices<=0 || numDevices>8 )
	numDevices=8;
    maxDevices=numDevices;
    pinMode(SPI_MOSI,OUTPUT);
    pinMode(SPI_CLK,OUTPUT);
    pinMode(SPI_CS,OUTPUT);
    digitalWrite(SPI_CS,HIGH);
    SPI_MOSI=dataPin;
    for(int i=0;i<64;i++) 
	status[i]=0x00;
    for(int i=0;i<maxDevices;i++) {
	spiTransfer(i,OP_DISPLAYTEST,0);
	//scanlimit is set to max on startup
	setScanLimit(i,7);
	//decode is done in source
	spiTransfer(i,OP_DECODEMODE,0);
	clearDisplay(i);
	//we go into shutdown-mode on startup
	shutdown(i,true);
    }
}

What you really need to do is what is done in the constructor:

    for(int i=0;i<maxDevices;i++) {
	spiTransfer(i,OP_DISPLAYTEST,0);
	//scanlimit is set to max on startup
	setScanLimit(i,7);
	//decode is done in source
	spiTransfer(i,OP_DECODEMODE,0);
	clearDisplay(i);
	//we go into shutdown-mode on startup
	shutdown(i,true);
    }

But you can't re-call the constructor. And spiTransfer is private. So you are going to have to modify the library to make spiTransfer not private, and make another function that sets up the display. Then call that.

hi Nick Gammon-

not it does use HARDWARE SPI pins.. you can define any you like..

I have to guess.. because I have never really written a library before.. and not experienced with all the private vs. public stuff..

however,.. I 'DID" in fact do exactly that before posting.. make the spiTransfer() public.....and go about trying to mirror the setup data being set during the constructor part..

in that end that how I stumbled upon the setScanLimit() function.. setting it back to 7 (like it does on default)...gave me the initial results of only the first group of 8 leds working/lighting up..
thinking I only had 4 'sections' of leds on the bargraph.. I set the scan limit to 4.. (things worked)..

so I removed everything.. and just set the scan limit through the LedControl library (before I wasnt..because it was set by the constructor)

so after deleting everything I had altered from the original code.. and used the LedControl function to set the scan limit to 4.. and I again was left with working code.. :slight_smile: (sweet!)

So in the end.. all I need was to change the scanLimit to 4 on the second max chip/pcb.. :slight_smile:

Question:
Why dont you like the constructor part of the code in the library? (just trying to learn)

From what I said in the link. It's doing too much. And in your case, it does stuff you might want to do later, so it would have made more sense to make a "begin" function and then you could have just called that.

If you do anything in a constructor that relies on other libraries (eg. pinMode) then you can't be sure that the other library has been initialized.

http://www.parashift.com/c++-faq/static-init-order.html

The static initialization order fiasco is a very subtle and commonly misunderstood aspect of C++. Unfortunately it's very hard to detect — the errors often occur before main() begins.

Now you don't want bugs that are hard to detect do you?

lol..

well yeah the 'begin' function is what I would have wanted to do.. and thats in fact what I have been working toward, (and posted on help working toward) :slight_smile:

** first time I had ever looked into a library.. (not even sure of the differences between the .h & .cpp file?.. or why there is 2 files to begin with?)

thanks for the link(s)!

The .h file gives the interfaces (ie. promises what functions the library implements).

The .cpp file actually implements them.

You can't copy the .cpp file into your own files or you would get multiple definitions if you did (if you had multiple files in your project).