Go Down

Topic: Brightness vs POV problem  (Read 893 times) previous topic - next topic

svenonderbeke@hotmail.com

Hi everybody,

I've been working to make a 8x8x8 led cube (kit reference below).
It came with a STC IC with preprogrammed animations but I wanted to control the cube myself using an Arduino Mega.

I did some research and this is how I think it's done:

To create a 3D image I have to draw a 2D image on a layer. and do this for each of the 8 layers but
at a high enough frequency so that the 2D images looks permanent.

It think to control that frequency using a delay in my LoadRow-function. But when I put the frequency high enough to have POV, the brightness of the leds is to dimmed to see it decently. When a lower the frequency this gets better but you can see each layer to be seperately written.

I think that I'm doing something fundamentally wrong but I have no idea.
I have pasted the code below!

Kind regards Sven!

Code: [Select]
//layers
#define LAY0 0
#define LAY1 1
#define LAY2 2
#define LAY3 3
#define LAY4 4
#define LAY5 5
#define LAY6 6
#define LAY7 7

//pins
#define PIN0 14
#define PIN1 15
#define PIN2 16
#define PIN3 17
#define PIN4 18
#define PIN5 19
#define PIN6 20
#define PIN7 21

//rows
#define ROW0 22
#define ROW1 24
#define ROW2 26
#define ROW3 28
#define ROW4 30
#define ROW5 32
#define ROW6 34
#define ROW7 36

int rowHigh;
 
int  hartje[8][8] = { //heart-matrix
    {0,0,0,0,0,0,0,0},
    {0,1,1,0,0,1,1,0},
    {1,1,1,1,1,1,1,1},
    {1,1,1,1,1,1,1,1},
    {0,1,1,1,1,1,1,0},
    {0,0,1,1,1,1,0,0},
    {0,0,0,1,1,0,0,0},
    {0,0,0,0,0,0,0,0}
   
};

int  rij[8][8] = { //diagonal-matrix
    {1,0,0,0,0,0,0,0},
    {0,1,0,0,0,0,0,0},
    {0,0,1,0,0,0,0,0},
    {0,0,0,1,0,0,0,0},
    {0,0,0,0,1,0,0,0},
    {0,0,0,0,0,1,0,0},
    {0,0,0,0,0,0,1,0},
    {1,0,0,0,0,0,0,1}
};

void setup() {
  pinMode(PIN0, OUTPUT);
  pinMode(PIN1, OUTPUT);
  pinMode(PIN2, OUTPUT);
  pinMode(PIN3, OUTPUT);
  pinMode(PIN4, OUTPUT);
  pinMode(PIN5, OUTPUT);
  pinMode(PIN6, OUTPUT);
  pinMode(PIN7, OUTPUT);

  pinMode(LAY0, OUTPUT);
  pinMode(LAY1, OUTPUT);
  pinMode(LAY2, OUTPUT);
  pinMode(LAY3, OUTPUT);
  pinMode(LAY4, OUTPUT);
  pinMode(LAY5, OUTPUT);
  pinMode(LAY6, OUTPUT);
  pinMode(LAY7, OUTPUT);

  pinMode(ROW0, OUTPUT);
  pinMode(ROW1, OUTPUT);
  pinMode(ROW2, OUTPUT);
  pinMode(ROW3, OUTPUT);
  pinMode(ROW4, OUTPUT);
  pinMode(ROW5, OUTPUT);
  pinMode(ROW6, OUTPUT);
  pinMode(ROW7, OUTPUT);
   
  digitalWrite(LAY0,LOW);
  digitalWrite(LAY1,LOW);
  digitalWrite(LAY2,LOW);
  digitalWrite(LAY3,LOW);
  digitalWrite(LAY4,LOW);
  digitalWrite(LAY5,LOW);
  digitalWrite(LAY6,LOW);
  digitalWrite(LAY7,LOW);

  digitalWrite(ROW0,LOW);
  digitalWrite(ROW1,LOW);
  digitalWrite(ROW2,LOW);
  digitalWrite(ROW3,LOW);
  digitalWrite(ROW4,LOW);
  digitalWrite(ROW5,LOW);
  digitalWrite(ROW6,LOW);
  digitalWrite(ROW7,LOW);

  setPins(0,0,0,0,0,0,0,0); //resetPins

}

void loop() {
 


}
void test2(){

   digitalWrite(LAY7, HIGH);
 writeSquare(rij,0);
 digitalWrite(LAY7, LOW);

  digitalWrite(LAY6, HIGH);
 writeSquare(rij,1);
 digitalWrite(LAY6, LOW);

  digitalWrite(LAY5, HIGH);
 writeSquare(rij,2);
 digitalWrite(LAY5, LOW);
 
  digitalWrite(LAY4, HIGH);
 writeSquare(rij,3);
 digitalWrite(LAY4, LOW);
 
  digitalWrite(LAY3, HIGH);
 writeSquare(rij,0);
 digitalWrite(LAY3, LOW);
 
  digitalWrite(LAY2, HIGH);
 writeSquare(rij,1);
 digitalWrite(LAY2, LOW);
 
  digitalWrite(LAY1, HIGH);
 writeSquare(rij,2);
 digitalWrite(LAY1, LOW);
 
  digitalWrite(LAY0, HIGH);
 writeSquare(rij,3);
 digitalWrite(LAY0, LOW);
 
}
void test(){
  for(int a=0;a<8;a++){
    digitalWrite(a, HIGH);
    for(int b=0;b<8;b++){
      if(b==a){
        loadRow(1,1,1,1,1,1,1,1,b);
      }
      else loadRow(0,0,0,0,0,0,0,0,b);
    }
    digitalWrite(a, LOW);
  }
}

void writeSquare(int square[8][8], int turns){ //write one layer , "turns" is the amount CW rotations of the matrix
 
   if(turns==0){
    for(int a=0;a<8;a++){
      loadRow(square[7][a],square[6][a],square[5][a],square[4][a],square[3][a],square[2][a],square[1][a],square[0][a],a);
    }
  }
  if(turns==1){
    for(int a=0;a<8;a++){
      loadRow(square[0][a],square[1][a],square[2][a],square[3][a],square[4][a],square[5][a],square[6][a],square[7][a],a);
    }
  }
  if(turns==2){
    for(int a=0;a<8;a++){
      loadRow(square[a][7],square[a][6],square[a][5],square[a][4],square[a][3],square[a][2],square[a][1],square[a][0],a);
    }
  }
  if(turns==3){
    for(int a=0;a<8;a++){
      loadRow(square[a][0],square[a][1],square[a][2],square[a][3],square[a][4],square[a][5],square[a][6],square[a][7],a);
    }
  }
 
}

void setPins(int zero, int one, int two, int three, int four, int five, int six, int seven){ //set the pins of
  digitalWrite(PIN0,zero);
  digitalWrite(PIN1,one);
  digitalWrite(PIN2,two);
  digitalWrite(PIN3,three);
  digitalWrite(PIN4,four);
  digitalWrite(PIN5,five);
  digitalWrite(PIN6,six);
  digitalWrite(PIN7,seven);
}

void loadRow(int zero, int one, int two, int three, int four, int five, int six, int seven, int row){ //display one row
  setPins(0,0,0,0,0,0,0,0);
  int rowHigh = 22 + row*2;
  digitalWrite(rowHigh, HIGH);
  setPins(zero,one,two,three,four,five,six,seven);
  delayMicroseconds(200); //arbitrary delay to create POV
  digitalWrite(rowHigh, LOW);
 
}



References:

Used kit :
http://www.ebay.com/itm/3D-LED-LightSquared-DIY-Kit-8x8x8-3mm-LED-Cube-White-LED-Blue-Green-Red-Yellow-/112166018887?var=&hash=item1a1d9d9f47:m:mAP4fpl1Wv0Jpqmdiz35H0Q

PaulRB

#1
Jun 18, 2017, 09:33 pm Last Edit: Jun 19, 2017, 07:48 am by PaulRB
Hi Sven. It's hard to guess what the problem is. You should post the schematics that came with the kit. My guess is that the kit achieved a 1:8 multiplex ratio, but your code achieves only 1:64 ratio, so it's 8 times less bright.

If the original kit also achieved only a 1:64 ratio, but it was still much brighter than with your Arduino circuit, then the problem might be that your circuit and code is not maximising the "on-time" that is possible. Give us all the detail you can, perhaps we can help.

Also please post the code you are testing with. The code above clearly does nothing:
Code: [Select]
void loop() {
 


}

svenonderbeke@hotmail.com

#2
Jun 19, 2017, 03:31 pm Last Edit: Jun 19, 2017, 05:37 pm by svenonderbeke@hotmail.com
Hi Paul,

Thanks for reading my problem!

I actually bought the same kit from Alibaba instead of Ebay and it didn't came with a schematic or any paper so a send an email to the seller and he send me some Chinese instructions and schematics (attachment 'info.rar').

So I searched online and found 'foto1' (attachment). It gave me an idea which pins to connect to my Arduino. I also found 'foto2' (attachment). It's a schematic that gives an idea about the schematic of my kit but it isn't exactly the same since I use an ULN2803AP6 to source the leds and they use transistors but it has the same concept I guess. The latches I used are 74HC573AN.

With my multi-meter a discovered connections from the 'Load enable' of each latch to the 'latch select' pins that can be seen on 'foto1'.

I made a little recording in which I tested several different values for the delay in the function 'loadRow(...)'. I tried them in the following order : 200 2000 1000 200 (microseconds).
You can find it in attachment 'test'.
Unfortunately you can't see much difference in brightness since the led cube is the only lightsource and its my phone camera but visually the are less brighter. 

This was my loop function :
Code: [Select]
void loop() {
test();
}


The function test2() is alike (forms a 3D image) and has the similar problems.

I understand that I multiplex at a 1:64 ratio but I do not know how this could be solved since I can only load one latch at a time (or not?).

So I hope this gives you more info.

Thanks a lot , Sven!

NOTE : attachments where too big. You can download them from my google drive :
https://drive.google.com/open?id=0BwCxkViKm3BmNVpQbTBtdHkyRW8

PaulRB

#3
Jun 20, 2017, 12:12 am Last Edit: Jun 20, 2017, 12:12 am by PaulRB

PaulRB

#4
Jun 20, 2017, 12:17 am Last Edit: Jun 20, 2017, 12:33 am by PaulRB
I could not open the rar file.

The schematic does not match the picture of the board. The picture shows a uln2803 chip which is not shown on the schematic. The schematic shows 16 npn transistors, which I cannot see on the board.

What I need to be able to help you is a schematic drawn by you showing your driver circuit. Not schematics and pictures of other circuits that you are not using.

From your description, it is hard to be sure, but I think with some changes to the code, you could achieve 1:8 multiplex which would be far brighter than the 1:64 multiplex I suspect your code achieves now.

PaulRB

#5
Jun 20, 2017, 11:41 am Last Edit: Jun 20, 2017, 01:23 pm by PaulRB
Quote
It's a schematic that gives an idea about the schematic of my kit but it isn't exactly the same since I use an ULN2803AP6 to source the leds and they use transistors but it has the same concept I guess.
ULN2803 can only sink current, it cannot source current. But yes, the principle is the same as the schematic with the transistors.

Quote
With my multi-meter a discovered connections from the 'Load enable' of each latch to the 'latch select' pins that can be seen on 'foto1'.
Are you certain it is the latch select pins and not the output enable pins on the latches that are connected? I hope so, otherwise 1:8 will not be possible.

Quote
I understand that I multiplex at a 1:64 ratio but I do not know how this could be solved since I can only load one latch at a time (or not?).
You can only load one latch at a time, but all 8 latches can be outputting high or low to leds at the same time. So you can light 64 leds at once. Your code mentions enabling and disabling the latches as though disabling switches leds off. It does not (assuming you have connected the latch enable pins and not the output enable pins). Latches like these copy the input data to the output data when the latch enable line is high. When the latch enable line is low, their outputs are frozen, so they retain the last data pattern.

Reading your code, I think this is the sequence you are following:
  • Enable layer 0
  • Load latch 0 with data
  • Enable latch 0
  • Delay
  • Disable latch 0
  • Repeat from step 2 for latches 1 to 7
  • Disable layer 0
  • Repeat from step 1 for layers 1 to 7

The result is a 1:64 multiplex ratio.

This is the sequence you should follow to achieve 1:8 ratio:
  • Enable latch 0
  • Load latch 0 with data
  • disable latch 0
  • Repeat from step 1 for latches 1 to 7
  • Enable layer 0
  • Delay
  • Disable layer 0
  • Repeat from step 1 for layers 1 to 7


Some other ideas to think about in the future, once you have the cube working with 1:8 ratio:
  • You appear to be using digital pins 0 and 1. This is not a good idea. Those pins are used for sketch upload, and also for serial communication with the PC, for example when debugging.
  • Rather than calculating pin numbers, put them into arrays. It will make your code shorter and more readable
  • Using type int for your pattern arrays is very inefficient, because int is 16 bits, but you only need to represent 0 and 1, which requires only 1 bit. Use byte instead of int. Better still, pack your data into bits and use bitRead() to extract them and 0b01010101 style to give values.
  • To get maximum brightness from your cube, even when you have achieved 1:8 multiplex, you need to keep the time that no layer is enabled to an absolute minimum. Look at using a PORT on the Atmega to load all 8 bits of data to a latch at the same time. Do not update or calculate new patterns while the layers are off. Instead, do this while the layers are on by using millis() instead of delay().

svenonderbeke@hotmail.com

I could not open the rar file.
I've uploaded the uncompressed folder to my google drive:

https://drive.google.com/open?id=0BwCxkViKm3BmNVpQbTBtdHkyRW8 (it does contain schematic files, but I haven't been able to open them with any program).

I also uploaded a drawing in which I sketch the schematic that I constructed using continuity-testing.
I think most important here is that OE of the latches is attached to ground so I can't control that. I can control the LE using the "latch select" pins.

I was wrong the ULN2803 is indeed used to sink current.

I'm a bit confused at first you say 1:8 will not be possible (since I can't control OE).
Quote
Are you certain it is the latch select pins and not the output enable pins on the latches that are connected? I hope so, otherwise 1:8 will not be possible.
But you also mention a sequence afterwards that could achieve 1:8 ratio.
I do understand both sequences you mention (indeed I used the first sequence in my sketch).

Next I will try the second method you mention (loading an entire layer and then enable that layer).

I really appreciate the ideas mentioned to improve the code! I like to make it more stable and efficient so this will certainly help me doing so!

Thanks again for putting so much time in helping me!







svenonderbeke@hotmail.com

#7
Jun 25, 2017, 12:07 am Last Edit: Jul 08, 2017, 08:33 pm by svenonderbeke@hotmail.com
[SOLVED]

It turned out that I didn't connect the VCC and GND from my arduino to the led cube. The cube was powered by a dedicated cable but I was controling the cube from the Arduino Mega. but the highs and lows from my pins had no reference. So the latches didn't load like they should and my code wasn't doing exactly what I thought is was doing but know it is fixed. If anyone else would try the same this is my (improved) code:

Code: [Select]
byte rowPin[8] = {22,24,26,28,30,32,34,36};
byte pinPin[8] = {14,15,16,17,18,19,20,21};
byte layPin[8] = {52,50,48,46,44,42,40,38};
 
byte  hartje[8] = { //heart-array    
   B00000000,
   B01100110,
   B11111111,
   B11111111,
   B01111110,
   B00111100,
   B00011000,
   B00000000
};
byte  rij[8] = { //diagonal-array
   B10000000,
   B11000000,
   B10100000,
   B10010000,
   B10001000,
   B10000100,
   B10000010,
   B10000001
};
byte  rij2[8] = { //diagonal-array
   B10000000,
   B10000000,
   B10000000,
   B10000000,
   B10000000,
   B10000000,
   B10000000,
   B10000000

};
byte  rij3[8] = { //diagonal-array
   B00000000,
   B00000000,
   B00000000,
   B00000000,
   B00000000,
   B00000000,
   B00000000,
   B00000000

};
byte image[8][8] = {
 { B00000000,
   B00000000,
   B00000000,
   B11000000,
   B11000000,
   B00000000,
   B00000000,
   B00000000},

 { B00000000,
   B00000000,
   B00000000,
   B11000000,
   B11000000,
   B00000000,
   B00000000,
   B00000000},

 { B00000000,
   B00000000,
   B00000000,
   B11000000,
   B11000000,
   B00000000,
   B00000000,
   B00000000},

 { B00000000,
   B00000000,
   B00000000,
   B11000000,
   B11000000,
   B00000000,
   B00000000,
   B00000000},

 { B00000000,
   B00000000,
   B00000000,
   B11000000,
   B11000000,
   B00000000,
   B00000000,
   B00000000},

 { B00000000,
   B00000000,
   B00000000,
   B11000000,
   B11000000,
   B00000000,
   B00000000,
   B00000000},

 { B00000000,
   B00000000,
   B00000000,
   B11111111,
   B11111111,
   B00000000,
   B00000000,
   B00000000},

 { B00000000,
   B00000000,
   B00000000,
   B11111111,
   B11111111,
   B00000000,
   B00000000,
   B00000000}
 
};

void setup() {

 for(int a=0;a<8;a++){ //set al used IO as OUTPUT and write them LOW
   pinMode(pinPin[a], OUTPUT);
   pinMode(layPin[a], OUTPUT);
   pinMode(rowPin[a], OUTPUT);
   
   digitalWrite(pinPin[a],LOW);
   digitalWrite(layPin[a],LOW);
   digitalWrite(rowPin[a],LOW);
 }
 
}

void loop() {
 writeImage(image);
}


void setPins(byte data){ //write the pins
 for(byte a=0;a<8;a++){
   digitalWrite(pinPin[a],bitRead(data,7-a));
 }
}

void loadRow(byte data, byte row){ //display one row
 setPins(data);
 digitalWrite(rowPin[row], HIGH);
 //delayMicroseconds(1);
 digitalWrite(rowPin[row], LOW);
}

void writeSquare(byte square[8]){ //write one layer
   for(byte a=0;a<8;a++){
     loadRow(square[a],a);
   }
}

void writeImage(byte image[8][8]){ //write image
 for(byte a=0;a<8;a++){
   digitalWrite(layPin[a],HIGH);
   writeSquare(image[7-a]);
   delay(1); //time to display one layer
   digitalWrite(layPin[a],LOW);
   
 }
}



PaulRB

#8
Jun 25, 2017, 11:26 am Last Edit: Jun 25, 2017, 12:11 pm by PaulRB
Good to see you followed some of my suggestions. But you forgot the code tags!

Another suggestion for the future: if you use the PORT technique I explained above, you can achieve a very high refresh rate for your cube > 200 per second. Why would you do this, given the human eye cannot perceive a difference? Because then you can use a technique called Bit Angle Modulation to individually fade each led in the matrix and create some subtle effects. For example, to have 16 levels of fading, you would refresh the cube 4 times, for periods of, say, 5000us, 2500us, 1250us and 625us. (Divide these times by 8 for the timing of each layer.)

svenonderbeke@hotmail.com

Hello PaulRB,

Quote
Good to see you followed some of my suggestions. But you forgot the code tags!
I do not understand what you mean by code tags? Does it mean that I have to add more commands to the code?

Yes I tried to improve the code but in the previously posted code I did not yet apply 2 of your recommendations:

- Use the PORT technique

- Use millis() instead of delay() to avoid calculating when layers are off.

The PORT technique is applied by now (thank you for that)
But I want to note that bit angle modulation won't be an option for me since I discovered that the brightness of the leds are already dependent on the amount of leds of the same layer that are turned on.
(due to hardware).

Using millis() looks very interesting since a now added a snake game to my code and every time my snake moves you see a flicker because the arduino is calculating.
I want to change that but I have no idea how I could implement that (probably not a hard thing to do but I have just no idea)

Code is attached.



PaulRB

Quote
I do not understand what you mean by code tags?
Yes you do. You used them in your first two posts in this thread! Now you are attaching .ino files which I cannot read on my phone. Please sort this out by editing both offending posts.

PaulRB

#11
Jul 02, 2017, 10:49 pm Last Edit: Jul 02, 2017, 10:55 pm by PaulRB
Quote
I discovered that the brightness of the leds are already dependent on the amount of leds of the same layer that are turned on.
That should not be. Something else is wrong, something we missed.

What value series resistors are you using? The 74hc574 probably has a total current limit of around 70mA per chip. If your series resistors are too low, you may be exceeding this, overloading the chips, causing the dimming effect when more leds are lit, and shortening the life of the chips in the long term.

Grumpy_Mike

The 74hc574 is an octal D-Type flip flop, it is not wired up like you show in the diagram.

svenonderbeke@hotmail.com

Oooh okay yes I know what you mean with code tags know, (I didn't get that they were called that way because I'm not native English.
I didn't mean no offence nor do I understand how it may be offensive. I edited the first post but in the second I attached the .ino file because I exceeded that maximum amount of characters I may use for a post.

The kit has 8 resistors. One for each layer with a value of 510 Ohms. It's a chinese kit so I guess that is it just a bad design to have one resistor per layer because the more leds per layer that are turned on, the less current each led will get

Quote
The 74hc574 is an octal D-Type flip flop, it is not wired up like you show in the diagram.
Indeed the diagram that I posted gave me some info on how multiplexing etc works but it is indeed not the diagram I used. I shouldn't have attached that diagram.

PaulRB

#14
Jul 08, 2017, 09:10 pm Last Edit: Jul 08, 2017, 09:16 pm by PaulRB
Quote
It's a chinese kit so I guess that is it just a bad design to have one resistor per layer because the more leds per layer that are turned on, the less current each led will get
Yes, congrats for figuring that out. A bad design. Can you fix it?

Quote
I didn't mean no offence nor do I understand how it may be offensive.
It's a figure of speech. I did not mean that you were acting in an offensive way, only that your posts were not following the forum guidelines.

Go Up