TLC5940 matrix very dim

Hi guys.

So I’m still working on my 4x16 LED matrix, p-mos with TLC5940.

The schematic is quite easy, arduino pin to control p-mos in rows and a tlc5940 to control in columns.

first I tried to work with only 1 row and 1 column, it works ok, the LED is bright as hell.
The current is at 19ma which matches my 2k iref.

then I tried to work on 1 column with row scan, the problem is all LEDs are very very dim.

key part

ISR(TIMER1_COMPA_vect)
{ // Interrupt to count 4096 Pulses on GSLCK
  PORTD |= 1<<5;// write blank HIGH to reset the 4096 counter in the TLC
  turnoff(counter);// turn the current row off
  if (counter==3)
  {counter=0;}
  else
  {counter++;}
  PORTD |= 1<<2;// write XLAT HIGH to latch in data from the last data stream
  PORTD &= ~(1<<2);  //XLAT can go low now---
  PORTD &= ~(1<<5);//Blank goes LOW to start the next cycle
  SPI.end();//end the SPI so we can write to the clock pin
  PORTB |= 1<<5;// SPI Clock pin to give it the extra count
  PORTB &= ~(1<<5);// The data sheet says you need this for some reason?
  SPI.begin();// start the SPI back up
  for(SINData=23; SINData>=0; SINData--)// send the data out!
  SPI.transfer(transferbyte[counter][SINData]);
  turnon(counter);// turn the next row on
}

My idea is, arduino interrupts every 512us to reset TLC5940’s counter, (1/8MHz = 125ns, 4096count * 125ns = 512us)

so when it interrupts, I turn off the row, send a new value, turn on a new row.

but what I see is very dim, so to my understanding, it takes too much time on the row changing, so for every LED, the on duty time is way much less than 25%.

So is there any way to improve this?

Thanks for any help

whole code

#include <SPI.h> //Serial Peripheral Interface

byte ch=0, chbit=0, spibit=0, spibyte=0; //variables for tlc5940 transfer
int SINData; //variable for tlc5940 shift
byte transferbyte[4][24]; //variable to transfer to tlc5940
//16 channels in 12 bits, 16*12=192 bits
//192 bits / 8 = 24 bytes
byte DCvalue[16]; //variable to dot correction
int i, j, k, l, m, n; //variables misc
int x;//test usage
int counter=0;//cycle counter

void setup()
{
  pinMode(2, OUTPUT); //XLAT
  pinMode(3, OUTPUT); //OSC2B GSCLK
  pinMode(4, OUTPUT); //VPRG
  pinMode(11,OUTPUT); //SPI MOSI
  pinMode(13,OUTPUT); //SPI Clock
  //pin setup

  pinMode(A0,OUTPUT);
  digitalWrite(A0,LOW);
  pinMode(A1,OUTPUT);
  digitalWrite(A1,LOW);
  pinMode(A2,OUTPUT);
  digitalWrite(A2,LOW);
  pinMode(A3,OUTPUT);
  digitalWrite(A3,LOW);

  SPI.setBitOrder(MSBFIRST); //Most Significant Bit
  SPI.setDataMode(SPI_MODE0); //Rising keep clock low
  SPI.setClockDivider(SPI_CLOCK_DIV4); //Clock at 16 MHz / 4 = 4 MHz

  for (i=0; i<16; i++)
  {transferbyte[0][i]=0;}
  //clear Gray Scale Data

  for (i=0; i<16; i++)
  {DCvalue[i]=63;}
  //set dot correction to max

  DotCorrection();
  //set up dot correction

  noInterrupts();
  //stop interrupts temporary for counter setup

  TCCR2A = B00010010;
  //Timer 2 toggle @ 8MHz for pin 5;
  //GSCLK
  TCCR2B = B00000001;
  //Timer pre scale at 16MHz

  TCCR1A = B00000000;
  //Timer 1 no toggle

  TCCR1B = B00001100;
  //Timer 1 pre scale at /256
  //1/8MHz = 125ns, 4096count * 125ns = 512us
  //16Mhz/256 = 62.5kHz

  TIMSK1 = B00000010;

  OCR1A = 31;
  //512us interrupt

  interrupts();
  //setup done, take interrupt back

  for (j=0; j<4; j++)
  {for (i=0; i<16; i++)
  {tlc(j,i,0);}
  j++;}
  //clear all outputs

  pinMode(5,OUTPUT);
  //set up blank
}

void loop()
{
  
}

//interrupts
ISR(TIMER1_OVF_vect){}// Over Limit Flag Interrupt

ISR(TIMER1_COMPB_vect){}// Compare B - Not Used

ISR(TIMER1_COMPA_vect)
{ // Interrupt to count 4096 Pulses on GSLCK
  PORTD |= 1<<5;// write blank HIGH to reset the 4096 counter in the TLC
  turnoff(counter);// turn the current row off
  if (counter==3)
  {counter=0;}
  else
  {counter++;}
  PORTD |= 1<<2;// write XLAT HIGH to latch in data from the last data stream
  PORTD &= ~(1<<2);  //XLAT can go low now---
  PORTD &= ~(1<<5);//Blank goes LOW to start the next cycle
  SPI.end();//end the SPI so we can write to the clock pin
  PORTB |= 1<<5;// SPI Clock pin to give it the extra count
  PORTB &= ~(1<<5);// The data sheet says you need this for some reason?
  SPI.begin();// start the SPI back up
  for(SINData=23; SINData>=0; SINData--)// send the data out!
  SPI.transfer(transferbyte[counter][SINData]);
  turnon(counter);// turn the next row on
}
//interrupts end

void DotCorrection()
{
  PORTD |= 1<<4; //VPRG to high to DC Mode
  spibyte = 0;
  spibit = 0;
  //reset variables

  for (ch=0; ch<16; ch++) // 6 bit x 16 outputs
  {
    for (chbit=0; chbit<6; chbit++)
    {
      if (spibit == 8)
      {
        spibyte++;
        spibit=0;
      }
      if (bitRead(DCvalue[ch], chbit)) // through all 6 bits
      {bitSet(transferbyte[0][spibyte], spibit);} // set bit of transfer byte
      else
      {bitClear(transferbyte[0][spibyte], spibit);} // clear bit of transfer byte
      spibit++;
    }
  }
  SPI.begin();
  for (j=spibyte; j>=0; j--)
  {SPI.transfer(transferbyte[0][j]);}
  PORTD |= 1<<2; //latch up
  PORTD &= ~(1<<2); //latch down
  PORTD &= ~(1<<4); //VRPG back to normal
}

void tlc(int var_row, int channel, int value)
{
  delayMicroseconds(100);
  //speed control if necessary

  if (bitRead(channel, 0))
  {spibit = 4;}
  else
  {spibit = 0;}
  //the odd data always start at 4
  //the even data always start at 0

  spibyte = int(channel*3/2);
  //start bit

  for (chbit=0; chbit<12; chbit++, spibit++)
  {
    if (spibit == 8)
    {
      spibyte++;
      spibit = 0;
    }
    if (bitRead(value, chbit))
    {bitSet(transferbyte[var_row][spibyte], spibit);}
    else
    {bitClear(transferbyte[var_row][spibyte], spibit);}
  }
}

void turnoff(int var_r)
{
  switch (var_r)
  {
    case 1:
      PORTC |=  (1<<0);
      break;
    case 2:
      PORTC |=  (1<<1);
      break;
    case 3:
      PORTC |=  (1<<2);
      break;
    case 0:
      PORTC |=  (1<<3);
      break;
  }
}

void turnon(int var_x)
{
  switch (var_x)
  {
    case 1:
      PORTC &= ~(1<<0);
      break;
    case 2:
      PORTC &= ~(1<<1);
      break;
    case 3:
      PORTC &= ~(1<<2);
      break;
    case 0:
      PORTC &= ~(1<<3);
      break;
  }
}

so for every LED, the on duty time is way much less than 25%.

So is there any way to improve this?

Increase the current through the LED to compensate for the duty cycle of multiplexing. However with that chip I found that 4:1 multiplexing was about the most I could usefully use.