Daisychaining Max7221: When does data get propagated?

I’m writing code to display letters onto a 7x5 LED matrix. Currently, I’m working on a function that will let me write a letter pattern onto a designated chip (chip index starts with 0, with data line connecting to the 0th-indexed chip).

I have 2 LED displays wired up to their corresponding chips.

The following correctly displays the pattern for the letter ‘A’ on the second chip (and not on the first)

writeLet('A', 1); //Write to 2nd chip

But if I call

writeLet('A', 0); //Should write to first chip only

Both the first and second MX displays ‘A’.

And if I call

writeLet('A', 0); //Display 'A' on first chip
writeLet('B', 1); //Display 'B' on second chip

Each chip displays its letter correctly (first chip displays A, second displays B) but I can’t verify that B does not get propagated to the third chip (not wired).

The issue seems to be that the propagation occurs if I try to write to chip 1 where the chip “automatically” propagates the 16 bytes that I’ve written to it. This makes me wonder if my 2nd chip would propagate its data onto a third chip if I had it connected (I’m unable to wire it up atm).

Here is the call tree:

First parameter to writeLet() is a char A-Z whose corresponding Ascii values are [65-90], second parameter is a chip select indexed 0-1 (I have 2 wired up)

 m1.writeLet('B', 0);

The function maps ascii value 65 to index=3, 66 to index=13, which is the starting byte for what I want to display. writeRow() for i=7 is junk but it does not get displayed on my 7x5 MX.

addr is chipSelect

void MaxMtrxDrvr::writeLet(char asciiLetter, int addr)
{
   int offset = 3;

   //Mapping function depends on how chTbl is set up; map ascii value to starting byte of corresponding letter
   byte startByteIndex = (asciiLetter - 65)*10 + offset;  //3 is an offset number; it's a 5x7 array so we skip first 2 starting bytes

   for(int i=0; i<8; i++) //Write to each row; writeRow(index of row, byte encoding)
   {
      writeRow(i, pgm_read_byte_near(chTbl + startByteIndex + i), addr);
      //pgm_read_byte_near() reads the address of starting location of array; chTbl = 0; Note pointer arithmetic
   }
}

row designates which row to write the 5 MSBs of binRow to on the chip designated ‘addr’ (again 0-1)

void MaxMtrxDrvr::writeRow(int row, byte binRow, int addr)
{/*Translates and display binary-byte encoding of a row in to a 2-byte instructions for Max chip*/
    putBytes(addr, row+1, binRow); //0-6;
}
void MaxMtrxDrvr::putBytes(int addr, byte addrByte, byte dataByte)
{ /*Clocks 'data' byte into MAX's register; MSB first*/
  
  digitalWrite(ss, LOW);

  shiftOut(dataPin, clk, MSBFIRST, addrByte);
  shiftOut(dataPin, clk, MSBFIRST, dataByte);

  for(int i=0; i<addr*2; i++) //Pad with No-Ops
    shiftOut(dataPin, clk, MSBFIRST, 0x00);

  digitalWrite(ss, LOW);
  digitalWrite(ss, HIGH); 

}

And here is the table:

//(Sprite mapping)Top-Down/Small-Large Addr; (width, height) prefixes 8-byte
// Equivalence classes: i = width, i+1 = height, i+(2-9) = Byte encoding of sprite
// ASCII 65=A
const byte chTbl[] PROGMEM = {
  4, 6, B00000000, B00000000, B01100000, B10010000, B10010000, B11110000, B10010000, B10010000, //'A'
  4, 7, B00000000, B11100000, B10010000, B10010000, B11100000, B10010000, B10010000, B11100000, //'B'
  4, 7, B00000000, B01100000, B10010000, B10000000, B10000000, B10000000, B10010000, B01100000, //'C'
  4, 7, B00000000, B11000000, B10100000, B10010000, B10010000, B10010000, B10100000, B11000000, //'D'
  4, 6, B00000000, B00000000, B11110000, B10000000, B11100000, B10000000, B10000000, B11110000, //'E'
  4, 7, B00000000, B11110000, B10000000, B10000000, B11100000, B10000000, B10000000, B10000000, //'F'
  5, 7, B00000000, B01100000, B10010000, B10000000, B10111000, B10010000, B10010000, B01100000, //'G'
  4, 7, B00000000, B10010000, B10010000, B10010000, B11110000, B10010000, B10010000, B10010000, //'H'
  3, 6, B00000000, B00000000, B11100000, B01000000, B01000000, B01000000, B01000000, B11100000, //'I'
  5, 7, B00000000, B01111000, B00010000, B00010000, B00010000, B00010000, B10010000, B01100000, //'J'
  4, 7, B00000000, B10010000, B10010000, B10100000, B11000000, B10100000, B10010000, B10010000, //'K'
  4, 7, B00000000, B10000000, B10000000, B10000000, B10000000, B10000000, B10000000, B11110000, //'L'
  5, 7, B00000000, B10001000, B11011000, B10101000, B10101000, B10001000, B10001000, B10001000, //'M'
  5, 7, B00000000, B10001000, B11001000, B11001000, B10101000, B10011000, B10011000, B10001000, //'N'
  4, 6, B00000000, B00000000, B01100000, B10010000, B10010000, B10010000, B10010000, B01100000, //'O'
  4, 7, B00000000, B11100000, B10010000, B10010000, B11100000, B10000000, B10000000, B10000000, //'P'
  5, 7, B00000000, B01110000, B10001000, B10001000, B10001000, B10101000, B10010000, B01101000, //'Q'
  4, 7, B00000000, B11100000, B10010000, B10010000, B11100000, B10100000, B10010000, B10010000, //'R'
  5, 7, B00000000, B01110000, B10001000, B10000000, B01110000, B00001000, B10001000, B01110000, //'S'
  5, 7, B00000000, B11111000, B00100000, B00100000, B00100000, B00100000, B00100000, B00100000, //'T'
  4, 6, B00000000, B00000000, B10010000, B10010000, B10010000, B10010000, B10010000, B01100000, //'U'
  5, 7, B00000000, B10001000, B10001000, B10001000, B10001000, B01010000, B01010000, B00100000, //'V'
  5, 7, B00000000, B10001000, B10001000, B10001000, B10101000, B10101000, B11011000, B01010000, //'W'
  5, 7, B00000000, B10001000, B10001000, B01010000, B00100000, B01010000, B10001000, B10001000, //'X'
  5, 7, B00000000, B10001000, B10001000, B01010000, B00100000, B00100000, B00100000, B00100000, //'Y'
  5, 7, B00000000, B11111000, B00011000, B00010000, B00100000, B01000000, B11000000, B11111000, //'Z'
};

The data is shifted from the first into the second, second to third, etc. Think of it as a line of bits tharevare shuffled down the comms path. In order to target a specific chip, you need to send NOP Commands to those past your target, then the command for your target and then NOPs again for those before your target. In other words, you need to know then number of devices in the chain.

ahg

marco_c:
The data is shifted from the first into the second, second to third, etc. Think of it as a line of bits tharevare shuffled down the comms path. In order to target a specific chip, you need to send NOP Commands to those past your target, then the command for your target and then NOPs again for those before your target. In other words, you need to know then number of devices in the chain.

Aha! Ok. Then I’ll just tell each chip to do nothing first. Then address the chip I want changed. It works! Thanks!

void MaxMtrxDrvr::putBytes(int addr, byte addrByte, byte dataByte)
{ /*Clocks 'data' byte into MAX's register; MSB first*/
  
  digitalWrite(ss, LOW);

  for(int i=0; i<addr*2; i++) //Pad with No-Ops  //Tell all chips down the line - do nothing
    shiftOut(dataPin, clk, MSBFIRST, 0x00);

  shiftOut(dataPin, clk, MSBFIRST, addrByte);  //Send mutating bytes down the line
  shiftOut(dataPin, clk, MSBFIRST, dataByte);  

  for(int i=0; i<addr*2; i++)                       //Push the mutating bytes to the right chip
    shiftOut(dataPin, clk, MSBFIRST, 0x00);

  digitalWrite(ss, LOW);
  digitalWrite(ss, HIGH); 

}

Preliminary question: How many 16-bit registers does each chip contain? (1 16-bit reg per row?)

I’m still a little confused about how the registers work. This is because of a very peculiar behavior that I’m observing:

  1. writeRow(0, B10101000, 0); //(row# (indexing starts at 0), data byte, chip #0)
  2. writeRow(1, B11111000, 0);
  3. writeRow(0, B01010000, 0);

In (1) I tell it to write to the first row. Then (2) tells it to right to the second row. Then (3) I tell it to overwrite what was in the first row (with new data). What I observe after (1),(2), and (3) run is that the second row remains the same is preserved and the first row has been overwritten. How come the old data from (1) didn’t propagate?

When does propagation occur?

Preliminary question: How many 16-bit registers does each chip contain? (1 16-bit reg per row?)

1 only. 8 bits for command and 8 bits for data.

Every tie you push 16 bits in, the previous 16 bits are pushed out. If there is another in the chain it gets those 8 bits.

Read the datasheet for the MAX7219.