Posts tagged ·

leds

·...

DIY Arduino Debug Shield

Comments Off

Debugging with LEDs; it’s probably even more primitive than debugging with printf statements. However, to get a very immediate feedback on what’s happening, or which pins are in use, it can be useful. So what better way then than a ~$2 homemade shield to pop on top of your existing project.

DealExtreme supplies all the parts needed: A versatile prototyping shield board with holes and wires in sensible locations; a bag of assorted LEDs; resistors; and header pins. (The board and headers in this project used 6x and 8x header pins, to fit with the older Duemilanove. The Uno another other boards have slightly different pin layouts, so plan ahead).

Now, I’m not an expert at soldering, nor product design, but this shield does the job, and already helped in programming my next project. Lesson learnt: Lay out the components all the way before heading off with the iron. I should have gone with the 3mm LEDs all the way. Or, maybe surface mount could have worked. Next time.

Comments Off

3x3x3 LED cube with fewer wires

Comments Off

A few weeks ago, I mentioned that it would be possible to optimize the number of wires and components needed for a 3x3x3 single colour LED cube by using “the 9th bit” (the output pin, intended for daisy chaining) on a 8 bit serial in / parallel out shift register. Other examples of the 33 cube I’ve seen are simply pulling 12 wires (9 LEDs on each layer, and 3 layers) back to the Arudino. At that scale, it is of course feasible, but still, when it’s possible to get by with half, why not? So I put together a small 33 cube doing just that.

Previously, I made the claim that the 8 bit shift register retains 17 bits of data. I’ll have to revise that down, to 16.5 bit. It is possible, as already mentioned, to use the output pin as a 9th visible output, as seen in the images and code below. However, the value of the output pin is shared with the 8th element of internal register, thus 17 distinct bits are not realised. It becomes more clear when considering the timing diagram of the of 595. Below is an extract and cut-out from the Texas Instruments data sheet [PDF].

The diagram above is truncated both in the horizontal and vertical axis, to highlight the last cycles, when the output pin, denoted QH’ in the digram, gets its value. As can be seen, when the 7th pin, or QG, is shifted up one (the first red line, when RCLK goes from high to low), QH’ gets that bit set as well. Note that the internal states of the shift register is not shown here. In the next cycle, SRCLK is latched, and the internal value of the 8th pin is made visible on QH, on the yellow line. The result is, that either QG + QH’ are on at the same time, or QH + QH’. This was frustratingly easy to reproduce in code, but not what I wanted.

To control all the nine LEDs of a layer independently, I had to set the output pin after the other eight pins were latched. However, if cycling quickly, it would then not get enough time on, and thus would look very dim. The final trick was to insert a short delay after all pins were set correctly, as seen in the code and discussion on timing below.

For the construction of the cube, I used LEDs from a 100-pack for $4.30 from DealExtreme, soldered onto a small 4x6cm (14×20 points) PCB. Following “fruitkid101″ video instructions, I drilled holes for a template in a piece of wood. As it was all by hand, it turned out a bit skewed, but for this small project I found it didn’t matter so much. Next time I’ll be a bit more precise. Soldering the legs together turned out be easy. I made the spacing between the LEDs small enough for one cathode leg to reach across its two neighbours, which meant that I only had to bend the ones in the corner. I soldered the corners first, and then the ones in between, finishing off with the centre LED. Positioning the anode legs between the layers was a bit more tricky, but with a double bend, as seen in the picture below, it was easy to make the leg from the layer beneath stay close enough to solder to its upstairs neighbour. Since the joints were for both the electrical contact, as well as the structural, I applied quite a bit of solder.

The interface to the Arduino is through the three direct pins which controls the sinks for each layer, and three pins for data, clock and latch of the shift register. The overview can be seen in this picture, and below. The small breadboard with the shift register also contains a 100 Ohm resistor for each pin, including the output pin.


As mentioned above, part of the motivation for this project was to control the cube with fewer pins, and use the output pin of a shift register for the 9th LED. That led to another interesting revelation in the code, and how to control brightness. As seen in the snippet below, which includes the display() method and the external fields used, the first inner for-loop for the shift register is all normal. It shifts the first 8 bits, with Most Significant Bit in the 8th element (index 7) of the display buffer buf[]. Surrounding that for-loop is the latch, however, notice that on the first line of the outer for-loop, the layer pin is set HIGH, which means, it is turned off. Before the layer we operate is turned on, the output pin is set to the value of buf[8] (and equivalent for higher layers). This is done within the second inner for-loop, inside the if-block. Strictly speaking, the if was not necessary; it would have been fine to shift out the extra bits every time.

Only after every pin is set to its correct state, is the layer turned on and the LEDs lit. However, if doing this without a delay, the LEDs would seem very dim, or almost off. Even if running the display() method only takes 400 micro seconds without the delay, for a good 2500 iterations per seconds, the problem would be that the LEDs would be turned off most of the time. To fix that, the delay is inserted, which holds the LEDs for a little while per iteration over a layer. With a one millisecond delay, the full method takes 3.4 ms, and can be executed ~300 times per second or a 300 Hz refresh rate of full cube.

However, with a delay of only 1 millisecond the LEDs are still somewhat dim. The fraction of time where the LEDs are off is still significant: For each layer 400/3 = 133 µs off vs. 1000 µs on, or about 12% of the time. If the delay is increased to 2 milliseconds, that ratio goes down to 6%, and the LEDs are noticeable brighter. The refresh rate goes down to ~150 Hz, but we are still far from the 50 Hz limit which is required to avoid flickering. In fact, we can go all the way up to a 6 ms delay before the limit is near (1000 ms / 50 / 3 = ~6 ms). However, it is hard to notice a difference between 3 and 6 ms delay. It would have to be measured. The lesson learnt then, is that it is not the refresh rate which matters the most when multiplexing LEDs, but rather the ratio of time the LEDs are on. Furthermore, with a 16 MHz MCU like the ATmega328, there is plenty of room for doing other stuff, and little need for premature optimization of code.

Future work will include utilizing the Arduino timer library for controlling the display buffer. Currently, that bit of the code is rather clunky. I would also like to experiment with fading and controlled blinking. Again, the timer library will be useful, but I’d probably also use the buffer to hold an intensity value, rather than just on/off. Also, I would like to measure the brightness with the different delays discussed above. One way would be using an LDR (light dependent resistor), but I might also look at the cheap $30 lux meters from DealExtreme. Finally, I’ll of course have to make some interesting patterns and animations, which should lead to an API for some level of abstraction. There’s lots to do!


Extract from code. See the full file for further details and GPL 3 license.

// Digital output pins

const int data =  5;
const int latch = 6;
const int clock = 7;

const int layer0 =  9;
const int layer1 = 10;
const int layer2 = 11;

const int layers[3] = {layer0, layer1, layer2};

const int outputPinCount = 6;
int outputPins[outputPinCount] = {
  data, clock, latch, layer0, layer1, layer2};

// Frame buffer
const int lights = 27;
int buf[lights];


void display() {
  for(int l = 0; l < 3; l++) {
     // Turns the layer off
     digitalWrite(layers[l], HIGH);
 
     // Shifts out the first 8 bits
     digitalWrite(latch, LOW);
     for(int i = 0; i <= 7; i++) {
       digitalWrite(clock, LOW);
       int bit = buf[l*9 + 7-i];
       digitalWrite(data, bit);
       digitalWrite(clock, HIGH);
     }
     digitalWrite(latch, HIGH);
 
     // Shifts 9 more bits to set the output pin
     if (buf[l*9 + 7] != buf[l*9 + 8]) {
       digitalWrite(data, buf[l*9 + 8]);
       for(int i = 0; i <= 8; i++) {
 	digitalWrite(clock, LOW);
 	digitalWrite(clock, HIGH);
       }
     }
 
     // Turns the layer on, and hold for 3 ms
     digitalWrite(layers[l], LOW);
     delay(3);
     // Turns the layer off again
     digitalWrite(layers[l], HIGH);
   }
 }
 


Comments Off

Simple multiplexing with two shift registers

Comments Off

There are many ways to drive multiple LEDs, which in sum would require more IO pins and more current than is available on the control chip. The simplest way is perhaps multiplexing, and in this example I’ve used two 8 bits serial-in/parallel-out shift registers to achieve that. I’ll skip the theory for now, but I think the structure and layout of the LED matrix is worth highlighting.

Below the 64 LEDs are shown in a grid of 8 columns and 8 rows. To light a specific LED, there has to be a potential difference between its two pins, thus the pin connected to a column should be set HIGH, and the other row pin to LOW. To individually control all the LEDs, the active row is cycled very quickly. For each step of the cycle, the specific LEDs of that row is set, and then changed for the next row. If done quickly enough (typically at least 50 times per second; 50 Hz), it will appear as if all the LEDs are always on.

The example in the figure shows register A set to 0000 0011, for the two right-most LEDs. Register B is set to 1011 1111, thus activating the second row from the top. In theory, the settings of the two registers could have been reversed, and then the two bottom LEDs of the second column had been lit. However, since LEDs are directional, and does not let current through when the polarity is reversed, this is not possible (at least not with the simple diagram shown below).

Please note that the drawing glosses over a few details: First, the pin-placements of the chips are not as per specification; the right most pin, in case of register A and top for B, are in fact ground. Pin 0, for the first bit of the register is actually on the opposite side (which is always rather annoying and confusing). See this post for details.. Secondly, there should be an array of resistors: one between each column line and chip A (see this picture as an example). Finally, although it is possible to drive the LEDs from the Arduino, as seen in the pictures further down, external power will improve brightness, thus a set of resistors would also be required.

8x8 LED matrix with two shift registers

Once the LED side of the registers are hooked up, there are a few wires to connect to the Arudino: Each shift register needs at least its data-in, clock and latch pins connected (plus pins which need high or ground). The data pin sets the next bit to be pushed onto the register. The clock pin does one shift on its transition to HIGH. Finally, the latch pin copies the content of the internal shift register onto the externally visible storage register. See Mike Szczys’s video for details on how this works.

There are a few ways these three wires from each register can be hooked up. One way, as seen in the pictures and code below, is to connect each data, clock, and latch pin separately, for a total of six IO pins on the Arduino. It is also possible to sync the latch of both registers, thus combining both of these pins onto a single IO pin. The code below could have benefited from this optimization (see the comments in the code). Secondly, it is possible to sync both latch and clock pins of both registers, and shift out two bits in parallel. In code, this would thus only required a single for-loop, however, an extra call to digitalWrite() (for the second data bit) would be required. I’ve not measured what difference this makes, so any insight here is welcome. Finally, it would be possible to daisy chain the two shift registers together. This would only require three IO pins, however would waste a lot of time shifting redundant bits onto the second shift register.

// Code under GPL; please see full file for details.

// Digital pins for two independent shift registers.
const int dataA =  5;
const int latchA = 6;
const int clockA = 7;

const int dataB =  8;
const int latchB = 9;
const int clockB = 10;

const int outputPinCount = 6;
const int outputPins[outputPinCount] = {
  dataA, clockA, latchA, dataB, clockB, latchB};

// Frame buffer
const int lights = 64;
int buf[lights];

void setupPins() {
  for (int i = 0; i < outputPinCount; i++) {
    pinMode(outputPins[i], OUTPUT);
  }
  clearAll();
}

void setup() {
  Serial.begin(9600);
  Serial.println("reset");

  setupPins();
  setAll(1);
}

void loop() {
  display();
}

void clearAll() {
  setAll(0);
}

void setAll(int v) {
  for (int i = 0; i < lights; i++) {
    buf[i] = v;
  }
}

void display() {
  for(int grp = 0; grp < 8; grp++) {
    digitalWrite(latchB, LOW);

    // Writes a single bit to the B register.
    // 0 means that group is active / on, 1 means off.
    // At the beginning of the loop, one 0 is shifted
    // onto the first bit, and later shifted upwards
    // as 1s turn the previous groups off.
    digitalWrite(clockB, LOW);
    digitalWrite(dataB, grp == 0 ? 0 : 1);
    digitalWrite(clockB, HIGH);
    digitalWrite(latchA, LOW);

    // Depending on the arrangement of the LEDs, and which
    // bits are conceptually most/least significant, shift in
    // increasing or reverse order.
    for(int i = 7; i > -1; i--) {
      digitalWrite(clockA, LOW);

      int bit = buf[grp * 8 + i];
      digitalWrite(dataA, bit);

      digitalWrite(clockA, HIGH);
    }

    // Here and above, both latches are set to the same
    // value at the same time, this could have been combined
    // onto the same IO pin.
    digitalWrite(latchA, HIGH);
    digitalWrite(latchB, HIGH);
  }
}

In the pictures below, the LEDs are on a single line, rather than in a grid, however, the grouping seen above still applies. Furthermore, each LED in my project is dual colour, so there only 32 of them, but still 64 pins to control. On my LED "breakout board" I've added a double set of headers for each pin on each LED, so I can easily connect them in any combination. The 8 pin jumper-wires from this post really came in handy.

For my custom dual shift register breakout board, I used this 4 x 6 cm PCB, and made the pins I needed available through headers. Please note that I made a mistake when placing the output headers, and for some reason assumed the pin on the opposite side was Q8 or QH, rather than 0 / A. On the shift register without resistors, I was able to correct this because there was room, however the other one was flush with the side, so it had to be corrected in the wiring (which makes some of the pictures below misleading). This board is somewhat similar to what you can buy pre-made from DealExtrme, however their version is using two daisy chained shift registers, and as mentioned, this will waste time when cycling the groups of LEDs.

Finally, note again that although it is possible to drive all this from the Arduino, even when powered over USB, better performance will be achieved with a higher powered external brick. Also, note that it is possible to mess up the code, so that all LEDs are turned on at the same time, and potentially draws more than 1 A. I shall not be held liable for what ever damage that might cause to hardware or people. Please see the license in the code for details.

First, some "making of" pictures, soldering all the LED pins and headers.

And here's the final product, well at least for now.

Comments Off

Large bright 10 mm LED

1 comment

Earlier this year, I found some uncommon LEDs at Clas Ohlson: Large bright 10mm LEDs (note that the link takes you to selection of different kinds; I got the “ultra white”). As seen from the pictures below, it is unusually large, compared to the 5mm LEDs which are most common. At 5000 mcd it is also very bright, and can easily be used as a flash light. Its forward voltage is 3.2 V, so with three AA batteries and a ~68 Ohm resistor, you’re all set.

I used a macro lens to take some of the pictures, and I found this one particularly interesting (make sure you zoom in 1:1). You can very clearly see the wire bond between the anvil and post. With most LEDs, this is usually too small to get a glimpse of. In this picture, you can get an idea of its cone. Like most LEDs, it is very directional, and thus flash-light idea.

How to make an LED Cube

2 comments

User “fruitkid101″ has a short and to-the-point video up on YouTube on how to construct a 3x3x3 LED cube, and drive it with an Arudino. What distinguishes his video from the hundreds of other LED cube videos out there, is that he explains step by step how to arrange the LEDs and route the wiring. Secondly, as it’s only three layers and nine columns, he needs no other components (save for resistors and three transistors), and all twelve wires are hooked directly up to the Arudino.

Another interesting and instructive video is Kevin Darrah’s on a 8x8x8 RGB cube. (Unfortunately, it wont show in HTML5 yet, but can be downloaded with the Video DownloadHelper Firefox plugin). He explains how to construct the towers which make up the cube, and also goes into detail on testing the LED and connections along the way. Much more work, of course, but also looks very cool.

Interacting with the Button Pad Controller SPI

4 comments

By popular request, here’s an update to the first code for the Button Pad Controller SPI. This sketch now includes interactive feedback, in the form of changing colours based on key-presses.

Click view full size image.

Click to view full size image.

Click to play video.

Click to play video.

Download the sketch here.

// The wires
#define CS 10
#define MISO 11
#define MOSI 12
#define SCK  13

// Frame buffer for lights
int lights = 16;
int depth = 3;
byte buf[16][3];

boolean wait = false;
boolean ready = true;

void setup() {
  pinMode(CS, OUTPUT);
  pinMode(MISO, OUTPUT);
  pinMode(MOSI, INPUT);
  pinMode(SCK, OUTPUT);

  digitalWrite(CS, LOW);
  delay(100);

  blank();

  Serial.begin(9600);
  Serial.println("Starting");
}

void blank() {
  // init frame buffer
  for(int l = 0; l < lights; l++) {
    for(int d = 0; d < depth; d++) {
      buf[l][d] = 0;
    }
  }
}

void loop() {
  digitalWrite(SCK, HIGH);
  digitalWrite(CS, HIGH);
  delayMicroseconds(15);

  // Set lights: 3 x 16 bytes
  for(int d = 0; d < depth; d++) {
      for(int l = 0; l < lights; l++) {
        writeByte(buf[l][d]);
      }
  }

  if(!ready) {
    wait = true;
    ready = true;
  }

  // Read the buttons: 16 bytes.
  for(int l = 0; l < lights; l++) {
    // readByte returns a non-zero value when a button is pressed.
    byte b = readByte();

    // If a button is pressed, change the colour.
    if (b > 0 && ready && !wait) {
      if      (buf[l][2] > 0) { buf[l][2] = 0; }
      else if (buf[l][1] > 0) { buf[l][1] = 0; buf[l][2] = 255; }
      else if (buf[l][0] > 0) { buf[l][0] = 0; buf[l][1] = 255; }
      else {                                   buf[l][0] = 255; }

      Serial.print("Button ");
      Serial.print(l);
      Serial.print(": ");
      Serial.print((int)b);
      Serial.println();

      // Change colours slowly.
      ready = false;
    }
  }

  digitalWrite(CS, LOW);

  // After the second cycle, get ready to read new button clicks.
  if(wait) {
    delay(500);
    wait = false;
    ready = true;
  }

  delayMicroseconds(400);
}

// Write out a byte.
// This is for a single colour channel, for a single button.
void writeByte(byte data) {
  for(int i = 0; i < 8; i++) {
    digitalWrite(SCK, LOW);
    delayMicroseconds(5);

    digitalWrite(MISO, (data & (1 << i)) >> i);
    delayMicroseconds(5);

    digitalWrite(SCK, HIGH);
    delayMicroseconds(10);
  }
}

// Read a byte.
// Returns the value for a single button.
// Non-zero if it is pressed, or 0 otherwise.
byte readByte() {
  byte result = 0;
  for(int i = 0; i < 8; i++) {
    digitalWrite(SCK, LOW);
    delayMicroseconds(5);

    result += (!digitalRead(MOSI)) * i;
    delayMicroseconds(5);

    digitalWrite(SCK, HIGH);
    delayMicroseconds(10);
  }

  return result;
}

First code for the Button Pad Controller SPI

4 comments

I recently got the Button Pad Controller SPI from SparkFun. Today I had some time to play around with the code, and got it working. Currently, it is only blinking all the lights, however the code should demonstrate the basics. The user guide from SparkFun documented the details of the interface. Add a few delayMicroseconds() calls, and it’s up and running.

Download the sketch here.

// The wires
#define CS 10
#define MISO 11
#define SCK  13

void setup() {
  Serial.begin(9600);
  Serial.println("setup");
  
  pinMode(CS, OUTPUT);
  pinMode(MISO, OUTPUT);
  pinMode(SCK, OUTPUT);
  
  digitalWrite(CS, LOW);
  delay(1);
}

int lights = 0;

void loop() {
  Serial.println("loop");
  
  // "The Button Pad Controller SPI will begin listening
  //  for a data set once the CS input has been brought high."
  
  // "(HINT: It's best if the SCK signal is set high before
  //  setting the CS pin high)."
  digitalWrite(SCK, HIGH);
  digitalWrite(CS, HIGH);
  delayMicroseconds(15);

  // Blink the lights.  
  lights = !lights;
  
  // Four frames (3 for lights input, 1 for button output)
  // (Here, it is writing to output on the 4th as well, which
  // is wrong, but seems not to cause any issues).
  for(int f = 0; f < 4; f++) {
     
       // Each frame has 16 * 8 = 128 bits of data.
       for(int i = 0; i < 128; i++) {
         
           // "The data on the MISO line should be set while the
           //  clock is low. When the SCK signal goes high the
           //  Button Pad Controller locks in the data currently
           //  on the MISO line."
           digitalWrite(SCK, LOW);
           delayMicroseconds(5);
           
           digitalWrite(MISO, lights);
           delayMicroseconds(5);
           
           digitalWrite(SCK, HIGH);
           delayMicroseconds(10);
       }
   }
   
   digitalWrite(CS, LOW);
  
   Serial.println("end");
   
   // "The CS signal should remain high for the duration of the
   //  data set, at which point it should be brought low for a
   //  minimum of 400 μs before sending the next set of data."
   
   //delayMicroseconds(400);
   delay(500);
 }