Welcome! Thank you HackADay for publishing my project, and thanks for coming and reading :-)
For those of you who are used to reading about my usual exploits in cooking, fixing things, and family, be forewarned this post gets a bit long, technical, and geeky! :-)
I've been interested in electronics since I was a kid, and have always enjoyed experimenting, building, and learning something new along the way. With the growing popularity of microcontrollers in the hobbyist community, it's become easier to get started in what used to seem too complicated to approach in my spare time.
Since microcontrollers are essentially tiny computers in a chip, you can do much more than you would be able to do with a handful of passive electrical components, and making changes to your device usually only requires making changes to the software that runs on the chip. While I can write software on the computer to work with data, there's something more satisfying about being able to write software to control objects in the physical world like sensors, motors, lights, etc.
I've actually been picking away at this project for several months, in the limited time I have available to me as a parent of a 1-year-old. I'm intrigued by how much can be accomplished by some of the tiny chips available. I decided to start learning with something small, the ATTiny85, with a mere 8 pins to work with. I was also intrigued by what I had read about Charlieplexing LEDs. Basically controlling many LEDs with only a few pins. For simplicity, I wanted to avoid any extra hardware/chips for my first project. To see how much I could do with only a few pins, I decided to take the 5 output pins available to me and make a charlieplexed LED display. Technically I could have used 6 pins, but that would mean losing my ability to reprogram the chip easily. By wiring up my LEDs just right, I could control up to 20 LEDs with only 5 pins.
So I have a tiny chip driving 20 LED lights, but I have no source of input since I used up all my pins. I might as well try doing something interesting... random blinking isn't very intellectually stimulating. I decided I would make a small 4 by 5 grid which could run Conways Game of Life, a simple simulation of cellular life invented by a mathematician in 1970, and a simple programming exercise for me to learn to program my microcontroller in C. By turning on a set of random LEDs, you can let the "colony" evolve based on a set of rules, and see how long it lasts. Sometimes the colonies die off quickly, sometimes the right configuration will get into a loop and survive forever. It's a bit abstract, but interesting to watch.
Setting up the wiring was a bit tricky. I wanted 20 LEDs in a 4x5 grid, all aligned the same direction on the board, and had to run 5 wires to the 40 different connection points. I ended up running bus-lines for each wire on the top and bottom of the board. One side of the board would run to the positive side of each LED, and the other side to the negative leads. After some sketching I ended up with my basic concept for wiring the array and it ended up conceptually simpler than I had expected.
Next step was prototyping. I did some simple experiments with 2 or 3 pins and a breadboard to prove out the charlieplexing concept. Only one LED can be lit at a time. To do this, you have to set one side of the LED high on an output, set the other side to ground on an output, and set all other pins on the array to an input mode. Setting the others to inputs prevents current from flowing and thus keeps all the other LEDs dark. This was perhaps one of the more complicated prototypes I've built, since it involved so many jumper wires on the topside of the board, and such complex wiring in a tight space. I could handle it for 20 LEDs, but probably wouldn't have the patience for much more.
Programming was a new challenge in itself. I've written software in a variety of languages and knew enough to start playing around with some low-level C language with the help of other people's sample code. Getting the programming on the chip required some extra hardware. Awhile back I picked up the USBTinyISP AVR Programmer from Adafruit. The kit was easy to build and in the end I had a top-notch programmer for getting software into my chips. I downloaded the WinAVR development environment and got familiar with it, and after much reviewing of datasheets and other people's software posted online, I started to get simple programs running on my ATTiny85. One big drawback was in-system programming. The AVR chips allow you to program the chip while it's plugged into the rest of your circuit. However, due to the wiring of my board, where basically every output pin is connected to every other with little in between... this interfered with the programming data. I could only get programming to work if I pulled the chip and put it in my dedicated programmer. Not a major issue, but a bit of a nuisance. This also meant I wouldn't be able to include a programming header on my final board design.
I actually started developing my Game of Life software while on vacation in northern Minnesota. I had the WinAVR software working on my laptop, had my prototype hardware built and working, and with this vacation I had plenty of pockets of free time where I wasn't responsible for doing anything in particular. I developed some routines for lighting specific LEDs, storing the current grid, calculating the next generation, and did a little "software PWM" to make it look like the LEDs were fading in and out. I'm sure there are dozens of ways my code could be improved/optimized, but as a first project I'm pretty proud of what came out of it.
As I am always learning, and willing to learn from others, I've posted my code & schematic as an open-source project on GitHub:
I just installed Git and just signed up for GitHub, so hopefully I did it right! But my files are there, so that's a good sign.
Detecting when to reset the simulation was an interesting programming exercise as well. The simple ways didn't take much effort. If the next generation has all lights out (dead), then reset. If the next generation matches the current one (steady state) then reset. Detecting a loop was a bit more complex. I didn't want my device to get into a loop and stay there until the power was reset. I didn't have the memory to store an endless record of previous grids to compare to. So, at a certain interval, I saved the grid, and subsequent generations would be compared to that past one. Once the software detected that it's next grid matched one we had seen before, it would know a loop had occurred, and would reset. But how often should I record the grid? How many generations back should I look? I wanted to know how long of a loop I could expect. Since I was only dealing with 20 LEDs, my grid could only display 1,048,575 (2^20) possible combinations of lights, relatively small for a computer to deal with. I wrote some software in VB.NET to run through every possible combination and see how each would play out in the Game of Life simulation. What I found was that about 8.13% of the possible combinations would loop in one way or another, and the longest loop I could expect was 18 generations. Based on this, I knew that if I looked at every 20th generation, I should be guaranteed to catch every possible loop eventually. If my grid were larger I'm sure it would get much harder to analyze every possible configuration, but this worked well for my small setup.
Here's how it looks running on the breadboard:
Now I could have stopped here, but there was more to learn, and after all, I like to make things. I wanted to turn this into a little trinket I could have running on my desk that looked nice & clean. It was time to tighten up the design and put it on a circuit board. Moving my project off the breadboard into a finished product is the perfect way to wrap it up. I haven't done any circuit board designing in about 7 or 8 years since I created my VU-Meter Kit back in 2003. I designed that in ExpressPCB, a proprietary software package, so this was the perfect excuse to teach myself Eagle CAD, with some help from the excellent tutorials at Sparkfun. Once I got rolling, Eagle turned out to be a nice piece of software, and was easy enough to use. Bringing my schematic to a board design was a smooth intuitive process. I arranged my LEDs into a tighter grid, and added some extra circuitry to regulate a 9v power supply to an even 5 volts, and added a reset button. The whole thing fit into a board just 1.95 inches square. Since my board was a charlieplexed LED display running off 5 pins of an ATTiny, I named the board design "TinyChuck5". In the future I can modify my software to do other things besides the Game of Life simulation.
Before committing my design to a board, I ran through some checks to make sure it would work, besides double-checking my traces in Eagle. I printed out my design on paper and taped to a piece of styrofoam, and proceeded to insert the components. This allowed me to make sure everything would physically fit, and that everything lined up properly. I then generated my gerber files for having the board manufactured. As a last check, I viewed the gerber files with ViewPlot, and discovered that my voltage regulator included a drill-hole that I wasn't aware of, which put a hole right through one of my traces. I didn't see this in Eagle, so was thankful I could catch it by reviewing the gerber files. I moved my trace to avoid this hole and the design was finished!
At the time of this writing I have a few boards on order. Once they arrive I can solder some together and make it look nice. I'd like to put my LEDs behind some semi-transparent white plastic. In the end I may have a useless blinking gadget, but at least I can say I created it from scratch, and I have the ability to make it do whatever I want. With the knowledge I've gained in this experience, I can move on to another more elaborate, more interesting project.
Excellent write-up and project! I was a bit miffed as to getting started with the attiny85. Looking forward to seeing your future projects.ReplyDelete
You're supposed to use transistors or driver ICs like (ULN2803) to do this. I/O pins of this microcontroller are only supposed to drive about 40mA current! I'm saying this because bigger layouts may burn the chip :/.ReplyDelete
Thanks JazzD, I haven't looked into driver ICs before. However, I don't know if components like this would be able to switch between high-output to ground-output to input like the IC does for charlieplexing. I assumed the current load would be low since technically only one LED is being lit at a time, but I haven't really analyzed/tested current flow.ReplyDelete
Very nice board layout. I think you should make a high density SMD version.ReplyDelete
Thanks! Never tried SMD by hand, but a little grid of SMD LEDs could be fun.ReplyDelete
You never cease to amaze me!!!!!!!!ReplyDelete
I did come up with an idea a while back to extend a basic 2*4 grid to 6*6 by using capacitors and diodes to (briefly) power each diode string for just long enough to light up when powered from the existing pins on the 12F683.
Never got around to trying it though but it should work.
Great project! Thanks for sharing!ReplyDelete
Thanks for the inspiration! I used your schematic to help me figure out how to make a 4x5 breadboard charlieplex, and did some light conversion on your AVR code to get it running on my Arduino. The LINE_x defines and the DDRB and PORTB calls needed to be changed, more or less. It still needs some tweaking (it runs ridiculously fast and my resistors are unequal, so the display is uneven), but since it was built from parts I had on my workbench already, I'm happy with the results.ReplyDelete
Awesome, glad to hear you're having some fun with the design. Sounds like a fun project.Delete
Thanks for the sharing the schematic and code. Nice project. I used it to wire up a 20 led heart and animated the leds around it. Turned out pretty nice. 20 leds with only 5 i/o pins, sweet...ReplyDelete
Sounds cool, would enjoy seeing some photos of the finished product.Delete
Bug report: It's subtle and might only happen in my modified code, but there's no guarantee that the last LED is off at the end of the draw_frame function. If it isn't off, it remains lit while the inter-draw_frame code is executed, making it slightly brighter than the rest of the array. I had always been hacking around it by iterating over a slightly longer array (x<=20 instead of x<=19, say) but that's the wrong answer. Dropping a "leds_off()" after the for loop ensures that all the LEDs are off when draw_frame exits, regardless of the brightness of the last element of the array.ReplyDelete
(one of those forehead slapping moments that I only discovered while tracking down another bug in my frame generation code.)