Noob needs some help with code

Sincere apologies for reposting this….but I though this got lost….would much appreciate help for all the kind people here…….

I have had good luck with fabricating the controller for my 4x4x4 bi-colored led cube with some really helpful inputs from here. While the actual cube is still to be made I wanted to give the code a thought. My set up is:

1.64 common cathode bi colored LEDs stacked in 4x4x4 form with layers sharing common cathodes and columns sharing anodes. So I have 4 ground pins and 32 anode pins (16x2)

2.I have daisy chained 4 74HC595s to be able to drive the anodes using discrete high side switches and 4 2N2222s driven straight from the Arduino for the layers

There is a fairly popular code that I found online and have used before for my single colored cube:

#include <avr/pgmspace.h> // allows use of PROGRAM to store patterns in flash

#define CUBESIZE 4
#define PLANESIZE CUBESIZE*CUBESIZE
#define PLANETIME 3333 // time each plane is displayed in us -> 100 Hz refresh
#define TIMECONST 20 // multiplies DisplayTime to get ms - why not =100?

// LED Pattern Table in PROGMEM - last column is display time in 100ms units
// TODO this could be a lot more compact but not with binary pattern representation
prog_uchar PROGMEM PatternTable[] = {
  // blink on and off

  B0001,B0000,B0000,B0000,B0001,B0000,B0000,B0000,B0000,B0000,B0000,B0000,B0000,B0000,B0000,B0000,10,
 
  // this is a dummy element for end of table (duration=0) aka !!!DO NOT TOUCH!!!
  B0000, B0000, B0000, B0000, B0000, B0000, B0000, B0000, B0000, B0000, B0000, B0000, B0000, B0000, B0000, B0000, 0
};

/*
** Defining pins in array makes it easier to rearrange how cube is wired
 ** Adjust numbers here until LEDs flash in order - L to R, T to B
 ** Note that analog inputs 0-5 are also digital outputs 14-19!
 ** Pin DigitalOut0 (serial RX) and AnalogIn5 are left open for future apps
 */

int LEDPin[] = {
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
int PlanePin[] = {
  16, 17, 18, 19};

// initialization
void setup()
{
  int pin; // loop counter
  // set up LED pins as output (active HIGH)
  for (pin=0; pin<PLANESIZE; pin++) {
    pinMode( LEDPin[pin], OUTPUT );
  }
  // set up plane pins as outputs (active LOW)
  for (pin=0; pin<CUBESIZE; pin++) {
    pinMode( PlanePin[pin], OUTPUT );
  }
}

// display pattern in table until DisplayTime is zero (then repeat)
void loop()
{
  // declare variables
  byte PatternBuf[PLANESIZE]; // saves current pattern from PatternTable
  int PatternIdx;
  byte DisplayTime; // time*100ms to display pattern
  unsigned long EndTime;
  int plane; // loop counter for cube refresh
  int patbufidx; // indexes which byte from pattern buffer
  int ledrow; // counts LEDs in refresh loop
  int ledcol; // counts LEDs in refresh loop
  int ledpin; // counts LEDs in refresh loop

  // Initialize PatternIdx to beginning of pattern table
  PatternIdx = 0;
  // loop over entries in pattern table - while DisplayTime>0
  do {
    // read pattern from PROGMEM and save in array
    memcpy_P( PatternBuf, PatternTable+PatternIdx, PLANESIZE );
    PatternIdx += PLANESIZE;
    // read DisplayTime from PROGMEM and increment index
    DisplayTime = pgm_read_byte_near( PatternTable + PatternIdx++ );
    // compute EndTime from current time (ms) and DisplayTime
    EndTime = millis() + ((unsigned long) DisplayTime) * TIMECONST;

    // loop while DisplayTime>0 and current time < EndTime
    while ( millis() < EndTime ) {
      patbufidx = 0; // reset index counter to beginning of buffer
      // loop over planes
      for (plane=0; plane<CUBESIZE; plane++) {
        // turn previous plane off
        if (plane==0) {
          digitalWrite( PlanePin[CUBESIZE-1], HIGH );
        } 
        else {
          digitalWrite( PlanePin[plane-1], HIGH );
        }

        // load current plane pattern data into ports
        ledpin = 0;
        for (ledrow=0; ledrow<CUBESIZE; ledrow++) {
          for (ledcol=0; ledcol<CUBESIZE; ledcol++) {
            digitalWrite( LEDPin[ledpin++], PatternBuf[patbufidx] & (1 << ledcol) );
          }
          patbufidx++;
        }

        // turn current plane on
        digitalWrite( PlanePin[plane], LOW );
        // delay PLANETIME us
        delayMicroseconds( PLANETIME );
      } // for plane
    } // while <EndTime
  } 
  while (DisplayTime > 0); // read patterns until time=0 which signals end
}

While I broadly understand the code I have a few specific questions so that I can modify it to be able to work on my shift registered setup:

1.In the line memcpy_P( PatternBuf, PatternTable+PatternIdx, PLANESIZE ) is the entire bit string (all 16 4-bit values) copied into the PatternBuf array?
2.If yes, then can someone please explain to me how are these values being loaded individually onto the output ports?
3.If the answer to the previous question is digitalWrite( LEDPin[ledpin++], PatternBuf[patbufidx] & (1 << ledcol) ); then I would really be grateful if someone could explain this to me.

My idea is to be able to modify the code to use 4 32-bit values (each representing one layer) and load them on the shift registers using SPI similar to what the line of code in #3 is doing (if I understand correctly). However, I am not sure if this approach makes sense. I would really appreciate if someone could help me out and point me in the right direction.

Also if this approach is practical then can someone give me an example about how can I modify the part of the code that loads data individually using digitalWrite() to use the SPI.transfer()

In the line memcpy_P( PatternBuf, PatternTable+PatternIdx, PLANESIZE ) is the entire bit string (all 16 4-bit values) copied into the PatternBuf array?

The 3rd argument is how many bytes to copy. So, yes, 16 bytes are being copied. They are 8 bit values, though, even if you are only using 4 bits per byte.

If yes, then can someone please explain to me how are these values being loaded individually onto the output ports?

I suppose that depends on what you mean by loading onto output ports. There is nothing in this code that uses ports. Everything is done with individual pins, even if all pins are on the same port. The Arduino (UNO and similar) only has 3 ports, none of which are collectively used in this code. Pins on all 3 ports are used.

Each 4 x 4 collection of LEDs is enabled using one plane pin and 16 pins, to allow individual addressing of LEDs on a plane.

If the answer to the previous question is digitalWrite( LEDPin[ledpin++], PatternBuf[patbufidx] & (1 << ledcol) ); then I would really be grateful if someone could explain this to me.

Mostly, yes, it is. The particular pin to use should be pretty obvious. The value from PatternBuf is selected, using patbufidx as the index, and it is logically anded with 1 left shifted ledcol times (resulting in 1, 2, 4, or 8 (B0001, B0010, B0100, or B1000)). So, the result is 0 or 1 for each LED. If PatternBuf[someValue] is B0010, for instance, and that is anded with B0001, the result is 0, so the led in column 1 is off. If the same pattern value was anded with B0010, the result would be 1, so the led in column 2 would be on.

I have daisy chained 4 74HC595s to be able to drive the anodes using discrete high side switches and 4 2N2222s driven straight from the Arduino for the layers

This allows you to send data to 32 things at one time. For a 4 x 4 x 4 cube, that seems either too many shift registers, or too few. What 32 things are you trying to control?

thank you paul.....i was getting worried tht no one would answer.....apologies for the naivety but by ports i meant individual pins so sincere apologies....i am a noob so do excuse.....so far as 4-shift registers are concerned i am doing a bi-colored cube so i have 32 anode bunches (16x2) to control.....i am still reading through your explanation so i hope i can rewrite to do this....although any suggestion would really help...

your explanation of the bit-wise operations really helps…….however I am now having a tough time visualizing how shld I be storing patterns for my bi-colored cube….can a 32-bit long value be stored as a single string and shifted out in on go or would it have to be necessarily divided into 4 byte string…..I feel like I am all over the place….

can a 32-bit long value be stored as a single string and shifted out in on go or would it have to be necessarily divided into 4 byte string…..I feel like I am all over the place….

There is no need to deal with strings. Period.

A 32 bit value can be stored in an unsigned long. That 32 bit value would define which LED on a plane was on, and what color(s) are on. It is up to you to define which bits correspond to row, which bits correspond to column, and which bits correspond to each color.

You might consider using two sets of 16 bit values - one for each color.

Have you seen this:
http://www.arduino.cc/en/Tutorial/ShiftOut

thanks paul....i did go through the tutorial but was a little confused about how to do this with a 32 bit value but i think your suggestion explains a lot.....i am goin to try to start with the 32 bit value approach and take it from there.....although one last question would be if i can declare PatternBuf as unsigned long and still use memcpy_P( PatternBuf, PatternTable+PatternIdx, 128 )?

or shld this be memcpy_P( PatternBuf, PatternTable+PatternIdx, 16)

if i can declare PatternBuf as unsigned long and still use memcpy_P( PatternBuf, PatternTable+PatternIdx, 128 )?
or shld this be memcpy_P( PatternBuf, PatternTable+PatternIdx, 16)

The current size of PatternBuf is one byte per element, and you want to copy 16 elements, so the value in the 3rd argument is 16.

The new size of PatternBuf is 4 bytes per element, and you want to copy 16 elements. Let me get my calculator out to multiply 16 by 4. Hmmm, its not 16 and it is not 128.

apologies paul but um jst gettin started so do excuse me.....but i thought i could use a 4 byte value to display the pattern for one entire layer....i wld need 4 such values for four planes so.....shld tht not be 16....please do excuse my limited experience.....i would sure noe who to thank whn i do get this finally.....

but i thought i could use a 4 byte value to display the pattern for one entire layer....i wld need 4 such values for four planes so.....shld tht not be 16

The existing code uses 16 values per plane. Your new code will also need to use 16 values per plane.

The existing code uses 4 bits per value. Your new code will need to use 8 bits per value, since you have 2 colors (or effectively 2 LEDs) at each location.

You could use the same code and use all 8 bits - the low nibble would be for one color and the high nibble for the other color.

But, you don't have enough pins to do that directly, so you need to use shift registers. Since there are 32 LEDs to light, and you need to shift out all the data at once, your data will need to be organized differently. There will be one long value (32 bits) per plane. If you put the data in PROGMEM, you'll need to copy it out 32 bits at a time (or 4 bytes). Your code will actually be more efficient than the code you showed.

You might consider copying the data out of PROGMEM in 16 byte increments, so you get data for the whole cube at once, rather than data for just one plane.

But, you don't have enough pins to do that directly, so you need to use shift registers. Since there are 32 LEDs to light, and you need to shift out all the data at once, your data will need to be organized differently. There will be one long value (32 bits) per plane. If you put the data in PROGMEM, you'll need to copy it out 32 bits at a time (or 4 bytes).

That was precisely what i was unsure of. But i think i understand now. I will use one 32-bit (4 byte) value to represent the pattern to be displayed on one plane. So that is sorted.

You might consider copying the data out of PROGMEM in 16 byte increments, so you get data for the whole cube at once, rather than data for just one plane.

I would indeed like to do that, be able to copy 16 bytes of info from PROGMEM to get the entire cube data at one go, instead going back an forth for each layer.

But this is where my logic loses footing. I am trying to understand that if i declare the PatterBuf as a unsigned lond and copy a piece of info 16 bytes long (4 bytes per plane) organized into 4, 32-bit values would the individual 32-bit long values get indexed automatically so that when i try to read PatternBuf[0] i get the first set of 32 bits which will then be shifted to the register and when i try to read PatternBuf[1] i get the second 32 bit value and so on and so forth.

Man i will thank you every time i switch on the cube. :slight_smile:

I am trying to understand that if i declare the PatterBuf as a unsigned lond and copy a piece of info 16 bytes long (4 bytes per plane) organized into 4, 32-bit values would the individual 32-bit long values get indexed automatically so that when i try to read PatternBuf[0] i get the first set of 32 bits which will then be shifted to the register and when i try to read PatternBuf[1] i get the second 32 bit value and so on and so forth.

Yes, that is what would happen. You just need to make sure that the bit pattern is such that when the data is shifted out, the correct LEDs light up.

thank you paul.....i will put up a video as soon as it's done......

ok so i finally started to do some code before actually having built the cube.....but have run into a small glitch.....i can not seem to find a way to tell the board that a 32-bit long value oganized as 1's and 0's is a binary value....i think it seems to interpret it as an integer and tells me that it is too long to be stored in any data type....i tried affixing a B before the value but the moment i type more than 8 bits the code does not recognize it as a binary value......for the time being i have split the 32 bit long value in 4 bytes.....so basically 4 bytes are shifted out and the relevant plane is activated and then move on to the next plane and so on and so forth.....can some one please tell me how can i store a 32-bit long value as a single set of 1's and 0's or is this not possible at all....if breaking the value up into 4 bytes is the only solution would be really thankful for any thoughts on the following code:

Thanks

#include <avr/pgmspace.h> // allows use of PROGMEM to store patterns in flash
#include <SPI.h>

#define CUBESIZE 4
#define PLANESIZE CUBESIZE*CUBESIZE
#define PLANETIME 1111 // time each plane is displayed in us -> 100 Hz refresh
#define TIMECONST 20 // multiplies DisplayTime to get ms - why not =100?
#define LATCHPIN 10
// LED Pattern Table in PROGMEM - last column is display time in 100ms units
// TODO this could be a lot more compact but not with binary pattern representation


PROGMEM prog_uchar PatternTable[] = {
  // blink on and off

  B00000000,B00000000,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111, 1,
  B11111111,B11111111,B00000000,B00000000,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111, 1,

  // this is a dummy element for end of table (duration=0) aka !!!DO NOT TOUCH!!!
  B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111,B11111111, 0,
};

/*
** Defining pins in array makes it easier to rearrange how cube is wired
 ** Adjust numbers here until LEDs flash in order - L to R, T to B
 ** Note that analog inputs 0-5 are also digital outputs 14-19!
 ** Pin DigitalOut0 (serial RX) and AnalogIn5 are left open for future apps
 */
int PlanePin[] = {  16, 17, 18, 19};

// initialization
void setup()
{
  int pin; // loop counter
  pinMode(LATCHPIN, OUTPUT);
  // set up plane pins as outputs (active LOW)
  for (pin=0; pin<CUBESIZE; pin++) {
    pinMode( PlanePin[pin], OUTPUT );
  }
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
}  

// display pattern in table until DisplayTime is zero (then repeat)
void loop()
{
  // declare variables
  byte PatternBuf[PLANESIZE]; // saves current pattern from PatternTable
  int PatternIdx;
  byte DisplayTime; // time*100ms to display pattern
  unsigned long EndTime;
  int plane; // loop counter for cube refresh
  int patbufidx; // indexes which byte from pattern buffer
  int bufstart; // buffer index counter

  // Initialize PatternIdx to beginning of pattern table
  PatternIdx = 0;
  // loop over entries in pattern table - while DisplayTime>0
  do {
    // read pattern from PROGMEM and save in array
    memcpy_P( PatternBuf, PatternTable+PatternIdx, PLANESIZE );
    PatternIdx += PLANESIZE;
    // read DisplayTime from PROGMEM and increment index
    DisplayTime = pgm_read_byte_near( PatternTable + PatternIdx++ );
    // compute EndTime from current time (ms) and DisplayTime
    EndTime = millis() + ((unsigned long) DisplayTime) * TIMECONST;

    // loop while DisplayTime>0 and current time < EndTime
    while ( millis() < EndTime ) {
      bufstart = 0; //reset buffercount to beginning
      for (plane=0; plane<CUBESIZE; plane++) {       // loop over planes
      
        digitalWrite(LATCHPIN, LOW); //take latchpins low to load data to '595
        for (patbufidx=0; patbufidx<CUBESIZE; patbufidx++) {

          SPI.transfer(PatternBuf[bufstart]); //shift out 4-bytes for the current plane
          bufstart++;
        }

        digitalWrite(LATCHPIN, HIGH); //take latch pin high to display data
        // turn current plane on
        digitalWrite( PlanePin[plane], HIGH ); //activate current plane
        // delay PLANETIME us
        delayMicroseconds( PLANETIME );
        digitalWrite(PlanePin[plane], LOW); //de-activate current plane
      } // for plane
    } // while <EndTime
  } 
  while (DisplayTime > 0); // read patterns until time=0 which signals end
}

You do not need to specify a binary initializer. A hex initializer is as easy to define and understand, and a lot shorter.

thanks paul but unfort i have never worked with hex values.....could you perhaps give me an example....i just liked the 32-bit binary representation because it was easy to understand (0 as on 1 as off and so on).....also does the code that i posted look ok to use if i make peace with using 4 1-byte values per layer?

Each 4 bits is one hex digit. If the value is supposed to be 00001111000011110000111100001111 for instance, that's 0x0F0F0F0F. The bits in 1111 represent 8, 4, 2, and 1, respectively. Add the values where the 1s are to get the hex value.

thanks paul and i have modified my code accordingly.....but i have another confusion and this might really be a stupid one and a result of the fact that i have never used hex representations but.....when i was using binary representations i would use 16 zeros or 16 ones to represent one color off and the other one on for one layer.....my question is when i shift out something like 0x0F0F0F0F to represent 00001111000011110000111100001111 do the 0s in the hex representation get converted to 4 zeros binary....i guess what i am trying to ask is if i shift out one zero in hex representation would the code actually shift out four zeros automatically.....so if i wanted to shift out 00000000000000001111111111111111 (basically one entire layer in one color) would i need to write it as 0x0000FFFF?

Whether you use B00000000, 0x00, or 0, the value stored in a variable is exactly the same.

so if i wanted to shift out 00000000000000001111111111111111 (basically one entire layer in one color) would i need to write it as 0x0000FFFF?

Yes.

thank you sir......i wonder where u kind gentlemen find the patience to deal with completely clueless people like me.....thank you again sir for all the help.......