LED Multiplexer acting weird

Hi,
I need a fresh pair of eyes on my code. I am multiplexing a 43-segment display and turning segments on and off randomly. To achieve this, I am performing my own PWM by splitting each cycle into 64 “frames” and copying the previous one into the next each cycle so that new segments added to frame 0 are able to “fade in” by being copied into frames 1, 2, 3, 4 and so on as the cycles progress. When the segment is randomly selected again, I use an XOR function to flip the segment OFF for frame 0, and this OFF state gets copied to frames 1, 2, 3, 4 and so on so the segment fades out.

The problem is, something is illuminating segments that are 1 column over from activated segments. In other words, another segment illuminates exactly 1 column over from any segments properly in an ON state. The code is not complicated so I must be missing something straightforward and I’m just about at my wit’s end.

A few theories I have are: (1) the random number generator is not truly “random” and is generating the same segments, albeit one column over. I am calling random numbers every cycle so the sheer sample size may be revealing the pseudorandom nature of the generators. but I have tried this generator (2’s carry) as well as Arduino’s built in function random() and the phenomenon persists. (2) There is some rogue code somewhere copying columns to the next one over. But like I said the code is fairly simple and I can’t find anywhere where something like that could be happening.

const int columnPins[] = {3, A0, A1, A2, A3, A4, A5};
const int rowPins[] = {13, 5, 6, 9, 10, 11, 12};



byte colSegD[7];
int randGen;
byte colSegF[7];

byte fadeUpD[64][7];
byte fadeUpC[64][7];
byte fadeUpB[64][7];

byte newPatternD[7];
byte newPatternC[7];
byte newPatternB[7];

unsigned long m_w = 1;
unsigned long m_z = 2; 

unsigned long getRandom()
{
    m_z = 36969L * (m_z & 65535L) + (m_z >> 16);
    m_w = 18000L * (m_w & 65535L) + (m_w >> 16);
    return (m_z << 16) + m_w;  /* 32-bit result */
} 



void setup() {
  m_w = analogRead(A8);
  m_z = analogRead(A8);
  //Serial.begin(9600);
 // while (! Serial);
  pinMode(A8, INPUT);                 //set random seed pin as input
randomSeed(analogRead(A8)); 
 
for (int i = 0; i < 7; i++)       //set pins as outputs
  { 
    pinMode(rowPins[i], OUTPUT);
    pinMode(columnPins[i], OUTPUT);
  }
  
  colSegD[0] = B00000001;
  colSegF[1] = B10000000;
  colSegF[2] = B01000000;
  colSegF[3] = B00100000;
  colSegF[4] = B00010000;
  colSegF[5] = B00000010;
  colSegF[6] = B00000001;
  //set initial pattern
  for (int colCount = 0; colCount < 7; colCount++)
      {
        for (int rowCount = 0; rowCount < 7; rowCount++)
        {

          if (getRandom() % 10 == 1)   //futz with mod value to change periodicity of segments being toggled
          {
            if (rowCount == 0)  {  //if segment is activated, program direct port map into segment array
            fadeUpC[0][colCount] |= B10000000;
            
            }
            if (rowCount == 1) {
            fadeUpC[0][colCount] |= B01000000;
            
            }
            if (rowCount == 2) {
            fadeUpD[0][colCount] |= B10000000;
            
            }
            if (rowCount == 3) {
            fadeUpB[0][colCount] |= B00100000;
            
            }
            if (rowCount == 4) {
            fadeUpB[0][colCount] |= B01000000;
            
            }
            if (rowCount == 5) {
            fadeUpB[0][colCount] |= B10000000;
            
            }
            if (rowCount == 6) {
            fadeUpD[0][colCount] |= B01000000;
            
            }
          }
        }
      }
}

void loop() {
for (int masterLoop = 1; masterLoop < 32000; masterLoop++) {
 
  
  for (int level = 0; level < 64; level++)  //At each power level, the segment will be activated/deactivated.
  {
    for (int col = 0; col < 7; col++)
    {

      //Main multiplexing port mapping--turn segments on and off per column
      PORTF = colSegF[col];         
      PORTD = (fadeUpD[level][col] | colSegD[col]);   
      PORTB = fadeUpB[level][col];
      PORTC = fadeUpC[level][col];
    }

  }
//turn off all segments during new segment generation and frame copying
    PORTF = 0;
    PORTD = 0;
    PORTB = 0;
    PORTC = 0;

  
  //shift levels up and generate new pattern
  for (int i = 63; i > 0; i--)      //shift segments to next-longest illuminated slot
    {
      for (int x = 0; x < 7; x++)
      {
        fadeUpB[i][x] = fadeUpB[i-1][x];    
        fadeUpC[i][x] = fadeUpC[i-1][x];
        fadeUpD[i][x] = fadeUpD[i-1][x];
        //delay(10);
      }
    }
   
    for (int x = 0; x < 7; x++) //reset new pattern arrays
    {
      newPatternB[x] = 0;
      newPatternC[x] = 0;
      newPatternD[x] = 0;
    }
    
    //Generate new pattern for frame 0
    for (int colCount = 0; colCount < 7; colCount++)
      {
        for (int rowCount = 0; rowCount < 7; rowCount++)
        {
          
          if (getRandom() % 800 == 0)   
          {
            if (rowCount == 0)  {  //if segment is activated, program direct port map into fade up matrix
            newPatternC[colCount] |= B10000000;
            
            }
            if (rowCount == 1) {
            newPatternC[colCount] |= B01000000;
            
            }
            if (rowCount == 2) {
            newPatternD[colCount] |= B10000000;
            
            }
            if (rowCount == 3) {
            newPatternB[colCount] |= B00100000;
            
            }
            if (rowCount == 4) {
            newPatternB[colCount] |= B01000000;
            
            }
            if (rowCount == 5) {
            newPatternB[colCount] |= B10000000;
            
            }
            if (rowCount == 6) {
            newPatternD[colCount] |= B01000000;
            
            }
          }
        }
      }

      //toggle segments
       for (int col =0; col < 7; col++) {
        fadeUpB[0][col] ^= newPatternB[col];
        fadeUpC[0][col] ^= newPatternC[col];
        fadeUpD[0][col] ^= newPatternD[col];
      }
    }
  
}

Edit: I think I’ve narrowed it down to this bit of code that copies the levels

for (int i = 63; i > 0; i--)      //shift segments to next-longest illuminated slot
    {
      for (int x = 0; x < 7; x++)
      {
        fadeUpB[i][x] = fadeUpB[i-1][x];    
        fadeUpC[i][x] = fadeUpC[i-1][x];
        fadeUpD[i][x] = fadeUpD[i-1][x];
        //delay(10);
      }
    }

But I don’t understand?! This code doesn’t change the column x?

Hi, can you post a copy of your circuit please, CAD or picture of hand drawn circuit would be fine.
How are you powering the LEDs.

Tom..... Hope to help... :slight_smile:

Hi Tom,
I've been lucky enough to have been able to narrow this to a programming issue and not a hardware one. I've tested this circuitry over many months and written countless projects for it. If you need specific information about the setup please do ask but I don't have a schematic available (that is legible, anyway)

It is 7x7, Rows are GND connected to a CAT4101's and columns are VCC connected to MC33152's. This is on an Arduino Micro.

column pins are 3, A0, A1, A2, A3, A4, A5
row pins are 13, 5, 6, 9, 10, 11, 12

I've used the direct port mapping many times before, so I know the mapping is good.

Hi, no problems, but have you got bypass caps on the mutliplexing IC's you use to interface?

Tom... :slight_smile:

I've tested this circuitry over many months and written countless projects for it.

Did those countless projects include a walking bit test to illuminate the rows or columns in a sequential pattern to verify that the wiring is correct ?

Can you draw a schematic by hand on paper for one row and one column and post a photo of it ?

TomGeorge:
Hi, no problems, but have you got bypass caps on the mutliplexing IC’s you use to interface?

Tom… :slight_smile:

Hi Tom,
I do. Like I said I know it’s not an interference or hardware issue.

My next theory is that there is some kind of memory overflow. But the biggest arrays add up to 3x64x7 = 1344 which still leaves plenty of room before hitting the 2048 max. What do you think?

raschemmel:

I've tested this circuitry over many months and written countless projects for it.

Did those countless projects include a walking bit test to illuminate the rows or columns in a sequential pattern to verify that the wiring is correct ?

Hi raschemmel,
Yes. The wiring is correct. All segments light up individually with no problems in other projects.

Ghosting is usually a wiring issue, but I promise it's been thoroughly tested and the hardware is not the problem, or my EE degree ain't worth nuthin'!

Can you add comments to explain the code ?
Have you tried adding debug serial prints ?

raschemmel:
Can you add comments to explain the code ?
Have you tried adding debug serial prints ?

I have tried adding serial prints, but they slow down the code so much, so as to make multiplexing worthless and impossible to debug.

I’ve done my best to comment the code here:

/* LED Multiplexing code for 43-segment display
Random segment fade in-fade out using a first-in first-out 
array with direct port mapping                               */

const int columnPins[] = {3, A0, A1, A2, A3, A4, A5};
const int rowPins[] = {13, 5, 6, 9, 10, 11, 12};



byte colSegD[7];
int randGen;
byte colSegF[7];

//First in/First out direct port map arrays
byte fadeUpD[7][64];
byte fadeUpC[7][64];
byte fadeUpB[7][64];

//Holder for randomly generated pattern
byte newPatternD[7];
byte newPatternC[7];
byte newPatternB[7];

unsigned long m_w = 1;
unsigned long m_z = 2; 

//random routine
unsigned long getRandom()
{
    m_z = 36969L * (m_z & 65535L) + (m_z >> 16);
    m_w = 18000L * (m_w & 65535L) + (m_w >> 16);
    return (m_z << 16) + m_w;  /* 32-bit result */
} 

void setup() {
    pinMode(A8, INPUT);    
  //seed randomizer
  m_w = analogRead(A8);
  m_z = analogRead(A8);
  //Serial.begin(9600);
 //while (! Serial);
 
for (int i = 0; i < 7; i++)       //set pins as outputs
  { 
    pinMode(rowPins[i], OUTPUT);
    pinMode(columnPins[i], OUTPUT);
  }
  
  //Assign column ports for direct port mapping
  
  colSegD[0] = B00000001;
  colSegF[1] = B10000000;
  colSegF[2] = B01000000;
  colSegF[3] = B00100000;
  colSegF[4] = B00010000;
  colSegF[5] = B00000010;
  colSegF[6] = B00000001;
  
  //Generate first random pattern
  for (int colCount = 0; colCount < 7; colCount++)
      {
        for (int rowCount = 0; rowCount < 7; rowCount++)
        {

          if (getRandom() % 4000 == 1)   //Change mod value to alter activity
          {
            if (rowCount == 0)  {  //if segment is activated, program direct port map into fade up matrix
            fadeUpC[colCount][0] |= B10000000;
            
            }
            if (rowCount == 1) {
            fadeUpC[colCount][0] |= B01000000;
            
            }
            if (rowCount == 2) {
            fadeUpD[colCount][0] |= B10000000;
            
            }
            if (rowCount == 3) {
            fadeUpB[colCount][0] |= B00100000;
            
            }
            if (rowCount == 4) {
            fadeUpB[colCount][0] |= B01000000;
            
            }
            if (rowCount == 5) {
            fadeUpB[colCount][0] |= B10000000;
            
            }
            if (rowCount == 6) {
            fadeUpD[colCount][0] |= B01000000;
            
            }
          }
        }
      }
}

void loop() {
for (int masterLoop = 1; masterLoop < 32000; masterLoop++) {
 
  //MULTIPLEXING LOOP
  for (int level = 0; level < 64; level++)  //Cycle through 64 power levels per PWM cycle
  {
    for (int col = 0; col < 7; col++) //Multiplex by column
    {
      PORTF = colSegF[col];          //Turn on columns at T = 0.
      PORTD = (fadeUpD[col][level] | colSegD[col]);   //This will be all columns for the pattern.
      PORTB = fadeUpB[col][level];
      PORTC = fadeUpC[col][level];
      
    delayMicroseconds(10);
    PORTF = 0;
    PORTD = 0;
    PORTB = 0;
    PORTC = 0;
      /*Serial.println(PORTF, BIN);
      Serial.println(PORTD, BIN);
      Serial.println(PORTB, BIN);
      Serial.println(PORTC, BIN);
      Serial.println(' ');*/
    }

  }


  
//COLUMN SHIFT
  for (int i = 63; i > 0; i--)      //shift segments to next-longest illuminated slot
    {
      for (int x = 6; x >= 0; x--)
      {
        fadeUpB[x][i] = fadeUpB[x][i-1];    
        fadeUpC[x][i] = fadeUpC[x][i-1];
        fadeUpD[x][i] = fadeUpD[x][i-1];
        //delay(10);
      }
    }
   
   //CLEAR NEW PATTERN ARRAYS
   for (int x = 0; x < 7; x++)
    {
      newPatternB[x] = 0;
      newPatternC[x] = 0;
      newPatternD[x] = 0;
    }
    
    //GENERATE NEW PATTERN
    for (int rowCount = 0; rowCount < 7; rowCount++)
      {
        for (int colCount = 0; colCount < 7; colCount++)
        {
          
          if (getRandom() % 6000 == 0)   
          {
            if (rowCount == 0)  {  //if segment is activated, program direct port map into fade up matrix
            newPatternC[colCount] |= B10000000;
            
            }
            if (rowCount == 1) {
            newPatternC[colCount] |= B01000000;
            
            }
            if (rowCount == 2) {
            newPatternD[colCount] |= B10000000;
            
            }
            if (rowCount == 3) {
            newPatternB[colCount] |= B00100000;
            
            }
            if (rowCount == 4) {
            newPatternB[colCount] |= B01000000;
            
            }
            if (rowCount == 5) {
            newPatternB[colCount] |= B10000000;
            
            }
            if (rowCount == 6) {
            newPatternD[colCount] |= B01000000;
            
            }
          }
        }
      }
      
      //Toggle a segment in the new pattern on or off depending on its previous state
       for (int col =0; col < 7; col++) {
        fadeUpB[col][0] ^= newPatternB[col];
        fadeUpC[col][0] ^= newPatternC[col];
        fadeUpD[col][0] ^= newPatternD[col];
      }
    }
  
}

Why am I getting “A8 was not declared in this scope” compiler error ?

    m_w = analogRead(A8);
   m_z = analogRead(A8);

These are ALL your declarations:

 const int columnPins[] = {3, A0, A1, A2, A3, A4, A5};
const int rowPins[] = {13, 5, 6, 9, 10, 11, 12};



byte colSegD[7];
int randGen;
byte colSegF[7];

byte fadeUpD[64][7];
byte fadeUpC[64][7];
byte fadeUpB[64][7];

byte newPatternD[7];
byte newPatternC[7];
byte newPatternB[7];

unsigned long m_w = 1;
unsigned long m_z = 2; 

unsigned long getRandom()
{
    m_z = 36969L * (m_z & 65535L) + (m_z >> 16);
    m_w = 18000L * (m_w & 65535L) + (m_w >> 16);
    return (m_z << 16) + m_w;  /* 32-bit result */
}

Where’s “A8” ? (are you using a mega because an UNO only goes up to A5)

Hmm, I don’t know, but I have solved my problem! (Solved it…but still haven’t figured it out)

This is the main multiplexing code that changes the port maps.

for (int level = 0; level < 64; level++)  //Cycle through 64 power levels per PWM cycle
  {
    for (int col = 0; col < 7; col++) //Multiplex by column
    {
               //Turn on columns at T = 0.
        //This will be all columns for the pattern.
      PORTB = fadeUpB[col][level];
      PORTC = fadeUpC[col][level];
      PORTF = colSegF[col]; 
      PORTD = (fadeUpD[col][level] | colSegD[col]); 
    delayMicroseconds(10);
    PORTF = 0;
    PORTD = 0;
    PORTB = 0;
    PORTD = 0;
    delayMicroseconds(15);
    }

  }

Check out that small 15 microsecond delay after I shut them off. Adding that delay fixed my problem!

Figure THAT one out! Got any theories as to why that was happening?

After watching the behavior for a while the problem is still happening, but only on the first row! Odd!

Finally figured it out. The problem lay in the order in which the ports were manipulated.

 PORTB = fadeUpB[col][level];
      PORTC = fadeUpC[col][level];
      PORTD = (fadeUpD[col][level] | colSegD[col]);
      PORTF = colSegF[col]; 
    delayMicroseconds(10);
    PORTF = 0;
    PORTD = 0;
    PORTC = 0;
    PORTB = 0;
    delayMicroseconds(10);

Turning on the columns first allowed row segments from the previous column to be illuminated before they were switched off.

The corrected code flips on the columns only after selecting the rows, and turns the columns off immediately after the delay.