Tuesday, March 18, 2014

Moar LEDs!

Phew! 400 done, just a bit over 100 to go until I have 512 perfectly shaped LEDs. That's one bag and a dozen :)

Sunday, March 16, 2014

Bending LEDs

Another weekend well spent :)

I've soldered three more pillars and straightened around fifty wires. I've also started bending the leads on all LEDs, a really boring job, but doing this beforehand will make the soldering go much faster. As it turns out, bending one LED costs about as much time as soldering one. And the more I can do in batch, the better!

Here's what I started with: around 600 LEDs
And the result after an evening on the couch, watching TV while bending leads.
200 done, 400 to go :)

I've marked the green cathode with a marker on each LED, that lead will have point up when the LED is inserted into the jig. It's pretty easy to spot the correct lead by just looking into the package, but more on that later.

All leads are at a 90 degree angle of each other and the LED sits perfectly flat on a flat surface (my hand isn't flat obviously).  It takes about 30 to 40 seconds to bend the leads on one LED, I could've done this faster but I'm a bit of a perfectionist, and I was watching TV :) It costs about 1 hour per 100 LEDs, but hopefully it'll save some time when soldering.

The great thing about this project is that it's easy to spend anywhere from 15 minutes to a few hours on it; you can just grab a box with all the tools/parts, do some work, and put it away when you're done or out of time. Cleaning up and getting started only takes a minute or two.

In other news, the PCBs I've ordered have been shipped last Friday! They'll probably arrive somewhere next week, but at least before the end of the month :)

Update 17-03: and a few more LEDs done, halfway there!

Tuesday, March 11, 2014

Cutting and stripping the time away

2.5 hours well spent. I've cut and stripped about 150 wires. I guess I still have a few wires left to cut and strip, but I'm not going to count them right now. I guess I'll find out when I run out of wires :)

I also just learned that the order for my boards have been sent to the fab. It's expected to arrive at OSH Park in about a week, not bad :)

Theory: shift registers, and the test jig code explained!

Ahh... Shift registers. The heart of most LED cubes.

So, what is a shift register and what does it do? Basically it's a serial input, parallel output IC; you send a stream of ones and zeroes to it on one pin and this makes a bunch of output pins go high or low. Usually you have two more pins: a clock pin and a latch pin. The clock pin is used to tell the shift register when to read a new bit on its input pin. The latch pin is used to tell it when to make the read bits appear on the output pins.

In my cube I use the 74HC595 shift register which is an 8 bit shift register. It simply means that it has 8 outputs. You can find these for almost a dime a dozen on eBay. If you need the smaller SMD type, search for 74HC595D.
The 595 (as I'll refer to it from now on) has two internal registers: the shift register and the storage register. We'll start at the beginning - the input pin - and work our way down to the output pins.

When you want to send data to the 595 you start by setting the DS pin (Serial Data Input) high or low, depending on whether you want to write a one or zero. Then you make the SHCP pin (Shift Register Clock Input) go from low to high to low again. The shift register will update whenever that pin goes from low to high. It will store whatever value is at DS and put it in bit 0 of the shift register. All the other bits of the register are shifted to the next one. So bit 1 will get the old value of bit 0, bit 2 the old value of bit 1, etc etc. The cool thing about this is that the old value of bit 7 will appear at a pin called Q7S. If you connect this pin to the DS pin of a second 595, and also connect the SHCP pins of both 595s, then bit 7 of the first 595 will be shifted to bit 0 of the second 595. You can chain a whole bunch of registers like this, effectively forming one large shift register.

Great, now that you've shifted 8 bits into the 595, you'll want to have them appear at the output pins. To do this you pull the STCP (Storage Register Clock Input, otherwise known as Latch) pin from low to high to low again. Whenever this pin goes from low to high all values from the shift register will be copied to the storage register.
This storage register is connected to the output pins, so any value in it will appear on the output pins. But only if the OE (Output Enable) pin is pulled low. If OE is high all output pins will be low.

One thing that's important to know is that when both the STCP and SHCP pins go high simultaneously, the shift will take place after the contents of the shift register have been copied to the storage register. This means that when you have STCP and SHCP connected to eachother, and you shift in a bit, that this bit will not appear on the output directly (since the content of the shift register has been copied to the output register before the new bit was shifted in).

Let's have another look at my pillar testing code. For this I have connected STCP and SHCP to the same pin on the controller (B4) and DS to B3. OE has been connected to ground.


1:  /*  
2:   * TinyPillarTester.c  
3:   *  
4:   * Created: 6-3-2014 21:48:11  
5:   * Author: Daniel  
6:   */   
7:    
8:    
9:  #include <avr/io.h>  
10:  #include <avr/interrupt.h>  
11:    
12:  int main(void)  
13:  {  
14:      PORTB = 7;  
15:      DDRB = _BV(DDB0) | _BV(DDB1) | _BV(DDB2) | _BV(DDB3) | _BV(DDB4);  
16:    
17:      TCNT0 = 0;  
18:      TIMSK0 = (1 << OCIE0A); // interrupt on compare match  
19:      TCCR0A = _BV(WGM01);  
20:      TCCR0B = _BV(CS01); // F_CPU / 8  
21:      OCR0A = 250;  
22:      asm("sei");  
23:    while(1)  
24:    {  
25:        // do nothing!  
26:        asm("sleep");  
27:    }  
28:  }  
29:    
30:  uint8_t cathodeCounter = 4;  
31:  uint8_t anodeCounter = 0;  
32:    
33:  ISR(TIM0_COMPA_vect, ISR_NAKED)  
34:  {  
35:      uint8_t sreg = SREG;  
36:      static uint8_t counter = 0;  
37:      ++counter;  
38:      if ( counter >= 75 )  
39:      {  
40:          counter = 0;  
41:          ++anodeCounter;  
42:          if ( anodeCounter == 8 )  
43:          {  
44:              anodeCounter = 0;  
45:              PORTB |= _BV(3);  
46:          }  
47:          else if ( anodeCounter == 1 )  
48:          {  
49:              PORTB |= cathodeCounter;  
50:              cathodeCounter <<= 1;  
51:              if ( cathodeCounter >= 8 )  
52:              {  
53:                  cathodeCounter = 1;  
54:              }  
55:              PORTB &= ~cathodeCounter;  
56:              PORTB &= ~_BV(3);  
57:          }  
58:          PORTB |= _BV(4);  
59:          PORTB &= ~_BV(4);  
60:      }  
61:      SREG = sreg;  
62:      asm("reti");  
63:  }  
64: 

Let's go through this line by line.

12:  int main(void)  
13:  {  
14:      PORTB = 7;  
15:      DDRB = _BV(DDB0) | _BV(DDB1) | _BV(DDB2) | _BV(DDB3) | _BV(DDB4);  
Line 14: Pins B0,B1 and B2 are set high, since they are connected to the cathodes of the LEDs this will make the LEDs go off. B3 (DS) and B4 (SHCP, STCP) are set low.
Line 15: All pins are configured as output pins (except B5, which is the reset pin)

17:      TCNT0 = 0;  
18:      TIMSK0 = (1 << OCIE0A); // interrupt on compare match  
19:      TCCR0A = _BV(WGM01);  
20:      TCCR0B = _BV(CS01); // F_CPU / 8  
21:      OCR0A = 250;  
Line 17-21: Timer initialization, it's set to generate an interrupt on compare match. The value to match is 250. The counter will reset when the match occurs (line 19) and its speed is 1/8th of the cpu speed (line 20).

22:      asm("sei");  
23:    while(1)  
24:    {  
25:        // do nothing!  
26:        asm("sleep");  
27:    }  
Line 22: Inline assembler, the "sei" command will enable interrupts
Line 23 - 27: Do nothing. The "sleep" command puts the cpu in idle state until there's an interrupt. This also reduces power consumption from 3 mA to 0.5 mA.

33:  ISR(TIM0_COMPA_vect, ISR_NAKED)  
34:  {  
35:      uint8_t sreg = SREG;  
Line 33: Start of interrupt handler for timer0 compare match. ISR_NAKED means the compiler won't put any code at the start and end specific to handling interrupts. One of the things you need do do is store the SREG register and restore it at the end, as you can see on line 35
Let's skip some stuff

61:      SREG = sreg;  
62:      asm("reti");  
63:  }  
64: 
Line 61: Restore SREG, not really necessary since we don't have any code in our main loop. But hey, it's good practice.
Line 62: Return from interrupt. Required special command if you exit an interrupt handler.

36:      static uint8_t counter = 0;  
37:      ++counter;  
38:      if ( counter >= 75 )  
39:      {  
40:          counter = 0;  
Line 36-40: The timer is configured to generate 600 interrupts per second, I only need 8, so this slows it down a bit :)

41:          ++anodeCounter;  
42:          if ( anodeCounter == 8 )  
43:          {  
44:              anodeCounter = 0;  
45:              PORTB |= _BV(3);  
46:          }  
Line 41: A counter to run through all the anodes. When it reaches 8, I reset it to 0. I also set B3 (DS) high (line 45).
47:          else if ( anodeCounter == 1 )  
48:          {
56:              PORTB &= ~_BV(3);  
57:          }  
Line 47: On the first interrupt (or the first one after the anode counter has been reset) I set B3 low again (line 56). It'll stay that way until the counter reaches 8 again. So it's high for 1 update, and low for 7.

58:          PORTB |= _BV(4);  
59:          PORTB &= ~_BV(4);  
Line 58: Set B4 (SHCP and STCP) high, this will cause the contents of the shift register to be copied to the storage register, and then shift a new bit from DS into the shift register.
Line 59: Set B4 low again.

And that's pretty much it. This clocks one bit to the shift register every 8th of a second, one one and seven zeroes. So one pin will be high and the others low, and the high pin will shift to the next every 8th of a second.

Now there's just a small piece of code left:
49:              PORTB |= cathodeCounter;  
50:              cathodeCounter <<= 1;  
51:              if ( cathodeCounter >= 8 )  
52:              {  
53:                  cathodeCounter = 1;  
54:              }  
55:              PORTB &= ~cathodeCounter;  
Line 49-55: This handles switching between red green and blue (cathodes). The cathodeCounter variable is a bit mask, only one bit will be set, which corresponds to one of the output pins B0, B1 or B2. I first apply this mask to the pins, which makes the current active pin go high (line 49) turning the LED off. Then I shift the variable left one bit to move to the next pin (line 50). If it goes past the last pin (line 51) I reset it back to the first pin (line 53). Finally I negate the mask and apply it, setting the new active pin low (line 55) turning the LED on.

The reason this piece of code sits inside the anodeCounter == 1 block is because the actual output of the 595 lags one clock cycle behind the content of the shift register. If I had put this code in the anodeCounter == 8 block then the jig would switch colors while the last LED was on. Since we don't want that, we have to wait one more cycle.

That's it! I hope this gives a better understanding of the 595 and the test jig :)

Sunday, March 09, 2014

4 down, 60 to go!

This project is going to take some time... I've just finished the fourth pillar and did some timing. I first cut, stripped and straightened 24 wires (for eight columns). This took about an hour, 7.5 minute per pillar. Then I started building pillars.
  • Bend the legs on eight LEDs
  • Trim the leads on the LEDs
  • Insert them in the jig
  • Insert the wires
  • Solder the LEDs
  • Trim the leads on the LEDs further
  • Cut the wires
  • Take the pillar out of the jig
  • Test it using the test jig
That took well over half an hour for the first pillar, 30 minutes flat for the second, and 20 minutes for the third. I did things slightly different for the last pillar. Instead of trimming the leads after bending them I just inserted the LEDs in the jig. I start by slightly pressing the top wire down on the jig and trim the top leads (green) before soldering them. Then I solder the top leads. After that I trim the remaining leads (red and blue). I then slightly bend the leads so they (almost) touch the wires. This makes soldering a bit easier, and you don't risk damaging the solder joint when trimming the leads.

All in all it takes about half an hour to create one pillar. I must admit I'm not that experienced when it comes to soldering, so I might be able to shave a few minutes off on that. It also helps to do things in batches and minimize switching between tools; I've first cut 24 wires, then stripped them and finally straightened them, instead of performing 3 steps on 1 wire after another.

Anyway, at this speed it's going to take about a month to finish all the pillars if I spend one hour a day. Damn...

Saturday, March 08, 2014

You've got mail!

The mailman doesn't seem to be liking me much lately, annoying bubble envelopes every couple of days, and often multiple on the same day. Having delivered mail myself for a few years I can relate to that, but nevertheless it makes me happy when I hear big things fall on the doormat :D
Today's "harvest": 200 LEDs, 5000 91 Ohm resistors, 5000 150 Ohm resistors.

Also visible on the photo are some wires I stripped and straightened. A colleague of mine gave me a golden tip on how to straighten those wires easily, but that's worth its own post and instructional video ;)

Friday, March 07, 2014

Cathode Board ordered!

I decided to go ahead and order a few cathode boards from OSH Park. It's the first time I've ordered a PCB I designed so I'm pretty excited about this. I've ordered six for now, for a total cost of $14.40, just to see if the design is actually going to work or not. Six of these boards is what I need to build a 4x4x4 cube as a proof-of-concept. If they work I can recycle them for the 8x8x8 cube.
OSH Park gives you a rendered preview of what the board is going to look like, pretty neat. Here's the bottom
 And the top:
And now we wait. The boards have been assigned to a panel, once the panel is full it will be manufactured. I believe it takes about 12 days for the board to be shipped, and then another 2 weeks or so before it actually arrives in the mail. So I guess I'll receive them somewhere early April.