I've started blogging at Embeddedrelated.com, so go over there and check out my post on Heartbeat LEDs. It's a bit basic but you'd be surprised how often they come in handy and how rarely I see people actually use them.
Enjoy!
Monday, April 1, 2013
Thursday, March 14, 2013
Oops
I messed up.
Oooh it hurts to say that but it's true. I should say it again.
I messed up.
Ah, still stings a bit. Maybe it will get better if I explain just how I messed up.
My last post linked to my overview of design considerations in voltage regulator circuits. In that writeup I staunchly defended the circuit I had designed to power my costume. Here's the block diagram:
The general idea is that a 9V battery powers two separate voltage rails (5V and 3.3V) which are generated from linear voltage regulators. The 5V rail powers the microcontroller and LEDs and the 3.3V rail powers the accelerometer. Hopefully some of you will have raised your eyebrows at this point regarding my design and noticed a few things:
Oooh it hurts to say that but it's true. I should say it again.
I messed up.
Ah, still stings a bit. Maybe it will get better if I explain just how I messed up.
My last post linked to my overview of design considerations in voltage regulator circuits. In that writeup I staunchly defended the circuit I had designed to power my costume. Here's the block diagram:
The general idea is that a 9V battery powers two separate voltage rails (5V and 3.3V) which are generated from linear voltage regulators. The 5V rail powers the microcontroller and LEDs and the 3.3V rail powers the accelerometer. Hopefully some of you will have raised your eyebrows at this point regarding my design and noticed a few things:
- Two voltage rails? That seems like needless complexity. Why not drive the microcontroller from 3.3V?
- The microcontroller and accelerometer have different voltage sources. Doesn't that make it difficult to interface them?
- You're using linear voltage regulators in a battery-powered application? Don't they waste a lot of power? Isn't that bad for battery life?
- Why drive the LEDs from a regulator? Aren't they high current devices? Won't that force you to choose a more capable linear regulator and dissipate a lot more heat through the regulator?
- What's that cool graph paper you use? It looks all retro engineering-y!
I pretended I had good answers to these questions. There are two rails because I need the microcontroller to run on 5V because then it can use a 20MHz clock rate. Don't you want that extra performance in case you need it? You know... for the complex maths and large amounts of data this costume will be processing? And no, it isn't a problem to interface the microcontroller and accelerometer because it's an analog accelerometer, so the 3.3V levels it produces can be easily read by the A/D on the microcontroller no problem! Yes, I know I don't get to use the full range of the A/D (0-5V) to read the accelerometer because they're using different operating voltages. I could always connect the A/D reference to 3.3V and get that extra precision back. No, I didn't actually do that and no I can't directly manipulate any of the control lines on the accelerometer to change the measurement range or put it to sleep. I'm sure I'll never need to put it to sleep - this is a battery-powered application! How important could that be?
Linear regulators? They're fine! I've been using them forever! Why change now? My calculations indicate they're 80% efficient in this application! Why add all of those extra components like inductors and zener diodes for a mere 16% more efficiency? Oh, oops. I guess I did my math wrong and the linear regulator is only 55% efficient. But hey, 40% more efficiency that... that can't be... worthwhile, right? In a battery-powered application?
But the LEDs - those need to be power from 5V! The green LEDs have a forward voltage of 3V - way too close to the voltage rail to allow me to properly bias them. I think. I didn't really check. Wait, power them directly from the battery and avoid the voltage regulators? I cant... I can't do that... because... because the battery voltage will decay as the battery drains! They LEDs will get less bright as that happens if I drive them directly from the battery! Of course, I am already using PWM to dim them, so as the battery dies I could increase the duty cycle to compensate. But I can't do that because I didn't monitor the battery voltage on the microcontroller via the A/D. Because... because...
Because I'm an idiot. There I said it. It hurts less now. Supposedly, scientists say that sometimes your brain subconsciously makes a decision and then only afterwards you consciously justify it. (I ate that pie. All of it. Because I had low blood sugar. Yeah I did kinda feel grouchy...) That's exactly what happened here. I wanted to start making circuits, soldering things and etching boards so I decided first and thought later. I pulled up an old circuit from my past, used it without thinking and paid the price for it. While testing the board I was always checking the 5V regulator to make sure it wasn't getting hot. I was tearing through 9V batteries during testing too (20 minutes of run-time? That will be an impressive Halloween costume!). I saw all the signs of a poor design but was able to justify them until I actually sat down and started writing an article about the decisions real engineers are supposed to make when designing circuits like this. The article just wasn't coming together - I couldn't make all of the pieces fit. Nothing seemed to make sense. Because it didn't - I hadn't followed my own advice.
At big companies with complex, involved processes for running projects they love to collect Lessons Learned. What did we do wrong on this project? What can we do better in the future? What worked? What didn't? While most processes and 'learning experiences' like this at big companies seem useless and painful, Lessons Learned is one tool that you should utilize everywhere you work. We're all imperfect and we all have a lot to learn. We all need to own up to our mistakes so we can correct them and become better engineers.
This means sharing - a perhaps uncomfortable amount of sharing. Honestly I can't say I'm comfortable admitting my mistake like this. I knew switching regulators existed, I knew microcontrollers could run from 3.3V. But I wasn't basing my design decisions on engineering - I was pretty much basing them on nostalgia (Ah, the LM7805! Reminds me of my childhood!) and inertia. This is a trap that all of us will run in to when we work on projects alone. With no one to watch our back and call us out when we make terrible decisions we will create less awesome stuff. Some of it will be downright embarrassing. All of it we will want to hide.
If we want to be better engineers and make more awesome stuff we're going to have to venture way out of our comfort zone and share our mistakes. Trust me, you haven't seen the last of mine.
Sunday, March 10, 2013
Power Supply Design
I've started writing up the design of power supplies in my costume project. When I say 'power supply' I don't mean anything as exotic as designing a linear regulator out of parts or designing a 300KV supply for a Tesla coil but instead a more pedestrian 'which chip do I pick and why?' sort of writeup. There are an absurd number of potential voltage sources out there - more than enough to get confused. The writeup focuses on the criteria you would use to select a voltage regulator and discusses the differences between linear and switching regulators before discussing the reasons for choosing regulator I did. You can read it here.
Friday, February 1, 2013
Schematic Capture
I said that the next post I made would be about the LED driver circuit - right? I honestly don't remember and refuse to read my old post. It's like hearing your voice on a tape recorder - or voicemail I guess. Tape recorders don't exist anymore. Anyhow that's not going to happen. You'd think I would have learned by now that every time I say the next post is going to be about something I'm usually wrong. I only do it to build suspense and try to get people to come back and read the next post. Of course, this isn't worth much unless people read my blog to begin with - and unless I write a next post...
Instead I'm going to briefly discuss schematic capture and the schematic capture tool that I use. Briefly put, schematic capture is the process where you generate a schematic diagram for an electrical circuit. A schematic can serve several functions:
TinyCAD is a Windows-based open-source schematic capture tool. While it doesn't come as part of a complete EDA toolset it is able to integrate with a wide variety of other EDA tools. It doesn't, for example, allow you to simulate circuits but instead allows you to export a SPICE netlist that any SPICE compatible simulation tool (which is the vast majority of them) can accept and work with. It doesn't have its own layout tool but it can easily export netlists compatible with a wide variety of available layout tools: Protel, Eagle, gEDA and PADS. It can also generate PNG graphics of its schematics for sharing. These options give me the basic functionality and compatibility that I need.
As far as available libraries go it doesn't have quite the number of libraries and parts that Eagle has and is certainly nowhere close to professional-level tools like OrCAD. I don't view this as a large issue because I've found that no matter how much library support your schematic capture tool claims to have it never has the one part you really need. If you don't get familiar and comfortable with making your own parts and libraries you'll never go very far making things. TinyCAD has a good interface for making new parts and since everything is XML-based I could probably even whip up a script to generate rudimentary schematic symbols from information in the datasheet to save me time. There are also efforts out there to develop better libraries that incorporate more commonly-used parts and integrate with SPICE and layout tools.
TinyCAD's interface is not fancy and modern but is straightforward and easy to use. The only feature I've never quite gotten the hang of is the busses but I'll learn someday. I've never felt confused or angry (well, any more angry than usual) when using TinyCAD. The workflow lets me quickly capture schematics without issue. You can't give a schematic capture tool a better compliment than that.
TinyCAD has many neat features that keep me coming back. The open nature of the libraries, parts and schematics means I can create scripts to manipulate all of my design files to automate my work flow. It can export a parts list/bill of materials for a design. It offers a design rule check to make sure you don't connect two outputs and unlike other design rule checks this one has proved useful to me. It allows you to create hierarchical parts - you can create a 5V regulator circuit, save it as a hierarchical part and drop it as a block into any new design you want. TinyCAD is currently under development and not abandoned like some open source proejcts. Despite the fact that development has slowed down there is a bug list and feature request list on Sourceforge that is steadily being worked at. And of course since it is open source you can add in your own efforts at any time. There is also a large busy users' group on Yahoo which can help to answer all of your questions.
Instead I'm going to briefly discuss schematic capture and the schematic capture tool that I use. Briefly put, schematic capture is the process where you generate a schematic diagram for an electrical circuit. A schematic can serve several functions:
- Informational only - A schematic is informational only if you just need to draw a circuit to show someone. You could do this with a 50K schematic capture tool, Visio or with a pen and paper.
- Layout - You may want to generate a PCB with your circuit on it at some point. To do this you need a schematic capture tool that can integrate with a layout tool. I'll discuss layout in a later blog post.
- Simulation - If you should need to determine how much power your amplifier is dissipating by simulating the circuit you'll need a schematic capture tool that interfaces with a circuit simulator like SPICE.
As you might image your choice of a schematic capture tool depends on which of these purposes you want to fulfill. Generally the very expensive Electronic Design Automation (EDA) packages contain all of the above: schematic capture, simulation and layout. You can do anything with them if you can afford them. In case you can't there are many open-source alternatives which also might suit your needs depending on what they are. If you want to start comparing schematic capture tools or even complete EDA suites some of the things you'll want to consider are these:
- Basic Functionality - Your schematic capture tool must be able to support the functionality necessary to do what you want with it. For example, almost every schematic capture tool can be used to generate schematics for informational purposes but even this is not strictly true - some online circuit tools may not offer an export function. To support layout and simulation functionality it must be able to generate a netlist appropriate to the purpose you intend.
- Compatibility - Following from the above, your schematic capture tool must be able to generate outputs that are compatible with other applications. This may be something as simple as generating a graphic or PDF version of the schematic for publishing and complex as being able to generate a SPICE netlist and stimulus in the proper format for your simulation tool. Ensure you know what types of outputs the other tools in your EDA toolset will accept.
- Availability of parts/libraries - The more pre-made parts that are made available with the schematic capture tool the quicker your design process will be. If a wide variety of parts are not available you'll have to generate them yourself - this can be tedious to say the least. However take note: not all parts are the same. Some part libraries are only for specific purposes such as simulation. You can't use those parts to generate a netlist for a PCB program. Vice-versa, parts that can be used to generate layouts may have no simulation information associated with them. It is also generally helpful if the parts are popular and freely available from a variety of sources - parts you can't find or are too expensive aren't very worthwhile.
- Interface/Ease of Use - There are some very awful schematic capture tools out there. I won't name names but when you use one of the bad ones it feels like the keyboard is made of needles. Don't tolerate poor software - there are very likely alternatives.
- Neat features - There are lots of features which are desirable to make your life easier: Bill of Materials generation is good, Design Rule Checks can help out a lot, integration with source control systems may be worthwhile, etc. While these features are fun and neat this bullet is at the bottom of the list for a reason: compatibility, library support and ease of use are definitely more important.
- Cadence OrCad/PSPICE - Known by many names, this professional suite of EDA programs that let you design electronics from step 0 to product. It has everything: schematic capture, simulation, layout, a well-stocked component library and a huge price tag. Seriously, it's tens of thousands of dollars. This is only for colleges and businesses. While I used it in college and at some places I've worked I wouldn't use it for my personal projects.
- Multisim - National Instruments' answer to OrCad. I have very little to say about it. My university tried switching to it in my junior year and it really failed to impress anyone. I assume it's also expensive but I've never bothered to check.
- gEDA - An open source suite of EDA tools that aims to be the free software EDA toolset. I haven't used this one mostly because it's geared towards Unix/Linux and I use Windows.
- KiCad - Another open source suite of EDA tools. I just haven't used this one.
- Eagle - An EDA suite that is something of a standard in Maker circles. Everyone tends to use it or release libraries for it. Do you have a part from Sparkfun or LadyAda? They've released an Eagle library with that part. I also do not use this - mostly because the free version is restricted and it's not open source (although they have opened up their file formats).
If you want to peruse even more open source/free options take a look at this page for a listing of many open source EDA tools of all kinds (schematic capture, simulation, layout, VHDL authoring, etc.). I've settled on a suite of tools that works well for me. Eventually I will document all of them but for today I'll focus on my schematic capture software: TinyCAD.
TinyCAD is a Windows-based open-source schematic capture tool. While it doesn't come as part of a complete EDA toolset it is able to integrate with a wide variety of other EDA tools. It doesn't, for example, allow you to simulate circuits but instead allows you to export a SPICE netlist that any SPICE compatible simulation tool (which is the vast majority of them) can accept and work with. It doesn't have its own layout tool but it can easily export netlists compatible with a wide variety of available layout tools: Protel, Eagle, gEDA and PADS. It can also generate PNG graphics of its schematics for sharing. These options give me the basic functionality and compatibility that I need.
As far as available libraries go it doesn't have quite the number of libraries and parts that Eagle has and is certainly nowhere close to professional-level tools like OrCAD. I don't view this as a large issue because I've found that no matter how much library support your schematic capture tool claims to have it never has the one part you really need. If you don't get familiar and comfortable with making your own parts and libraries you'll never go very far making things. TinyCAD has a good interface for making new parts and since everything is XML-based I could probably even whip up a script to generate rudimentary schematic symbols from information in the datasheet to save me time. There are also efforts out there to develop better libraries that incorporate more commonly-used parts and integrate with SPICE and layout tools.
TinyCAD's interface is not fancy and modern but is straightforward and easy to use. The only feature I've never quite gotten the hang of is the busses but I'll learn someday. I've never felt confused or angry (well, any more angry than usual) when using TinyCAD. The workflow lets me quickly capture schematics without issue. You can't give a schematic capture tool a better compliment than that.
TinyCAD has many neat features that keep me coming back. The open nature of the libraries, parts and schematics means I can create scripts to manipulate all of my design files to automate my work flow. It can export a parts list/bill of materials for a design. It offers a design rule check to make sure you don't connect two outputs and unlike other design rule checks this one has proved useful to me. It allows you to create hierarchical parts - you can create a 5V regulator circuit, save it as a hierarchical part and drop it as a block into any new design you want. TinyCAD is currently under development and not abandoned like some open source proejcts. Despite the fact that development has slowed down there is a bug list and feature request list on Sourceforge that is steadily being worked at. And of course since it is open source you can add in your own efforts at any time. There is also a large busy users' group on Yahoo which can help to answer all of your questions.
If you want to be like me and use TinyCAD I've documented the process of installing, downloading libraries, creating libraries, creating your first part and your first circuit. Find it here. The next tool I'll document is the layout tool I use: FreePCB. But before that I'll discuss a few of the circuits I've created for the costume and some of the lessons I've learned in the process.
Until next time!
Until next time!
Tuesday, January 8, 2013
Github Repo Up!
I put all of the costume project files up on Github here: https://github.com/angryee/light-costume
There's not much explanation there yet but there is a wiki article on Github detailing what most of the files/directories are - see it here: https://github.com/angryee/light-costume/wiki/Directory-Structure
Take note everyone: open source or open design files by themselves are useless. There's so much information in any given project that at first glance to anyone it's all useless gibberish. Unless you take the time to thoroughly document the files, tools, directory structure, design decisions and other minutiae it really doesn't matter if you release your code into the wild and promise not to sue - if it's not well documented no one can use it or learn from it. It just makes you sick trying to figure it out.
It makes me sick just looking at it and thinking how much documentation work I have to do. I'm going to bed.
There's not much explanation there yet but there is a wiki article on Github detailing what most of the files/directories are - see it here: https://github.com/angryee/light-costume/wiki/Directory-Structure
Take note everyone: open source or open design files by themselves are useless. There's so much information in any given project that at first glance to anyone it's all useless gibberish. Unless you take the time to thoroughly document the files, tools, directory structure, design decisions and other minutiae it really doesn't matter if you release your code into the wild and promise not to sue - if it's not well documented no one can use it or learn from it. It just makes you sick trying to figure it out.
It makes me sick just looking at it and thinking how much documentation work I have to do. I'm going to bed.
Sunday, December 23, 2012
System Design and Parts Selection
A while back I said I was going to create a series of posts detailing how I go about building an electronics project from start to finish. That hasn't happened - until now!
You see I'm usually pretty lazy on my off hours. And as lazy as I am about actually working on an electronics project in my spare time I'm lazier about writing about it. It turns out that I actually started working on a project months ago and haven't written anything about it despite having designed the electronics, fabricated the board and started developing the firmware. But since I have a good head start on actually making this project I figured I could do some backfill on the writing and show off some of my process.
Here's the idea (on paper):
So I have a shirt with a traffic light on it. The lights are several sets of red, yellow and green LEDs soldered onto a PCB and attached to the inside of the shirt with velcro or sewn on. The traffic light on the front is probably an iron-on applique or a custom shirt if I decide to get expensive. The lights are controlled by an accelerometer: if I sit still the light is red, when I move forward the light turns green and when I stop it turns yellow for three seconds then red again until the process starts over. It's a simple clear idea that makes for an interesting electronics project.
The first steps in a project like this are to generate a block diagram and general system description so you can start finding suitable parts. That's what I'll discuss today. Here's the system block diagram I came up with:
Usually the transistor I would select would be an old standby: the 2N2222. I know as a rule of thumb that you get a current gain of 10 with the 2N2222: 10mA base current gives you 100mA collector current when the transistor is saturated. However, if I have about 30 LEDs all drawing 20mA I need to deliver 600mA. That's getting close to the current limit of the 2N2222 and it also means that I need to deliver 60mA base current to switch it. That will be more than a typical microcontroller can source from its pins. For this application I need something with more current gain - in this case a Darlington Array is probably best. A Darlington Array is basically two transistors connected together such that their current gain is multiplied - so instead of a current gain of 10 you have a current gain of 100. Also Darlington Arrays can usually handle more current than a single transistor.
All of the other parts are just miscellaneous: tactile switches, rectangular headers, resistors, capacitors, LEDs, jumpers, etc. All of that I have on-hand and don't have to order. With any luck I don't have to make any other supplemental orders to get more of anything or replace something I broke. When ordering parts make sure to note whether you're close to a price break. I needed about 30-35 LEDs (the number I thought I needed plus extras) and was able to get a slight price break by ordering a few more than I thought I needed. And of course, always always order extras - you WILL break something. I broke plenty of LEDs and perhaps a few microcontrollers during this process.
Next time I'll talk about capturing/generating the schematics. For now I'll leave you with a bit of a teaser: pictures of the board after assembly.
You see I'm usually pretty lazy on my off hours. And as lazy as I am about actually working on an electronics project in my spare time I'm lazier about writing about it. It turns out that I actually started working on a project months ago and haven't written anything about it despite having designed the electronics, fabricated the board and started developing the firmware. But since I have a good head start on actually making this project I figured I could do some backfill on the writing and show off some of my process.
Here's the idea (on paper):
So I have a shirt with a traffic light on it. The lights are several sets of red, yellow and green LEDs soldered onto a PCB and attached to the inside of the shirt with velcro or sewn on. The traffic light on the front is probably an iron-on applique or a custom shirt if I decide to get expensive. The lights are controlled by an accelerometer: if I sit still the light is red, when I move forward the light turns green and when I stop it turns yellow for three seconds then red again until the process starts over. It's a simple clear idea that makes for an interesting electronics project.
The first steps in a project like this are to generate a block diagram and general system description so you can start finding suitable parts. That's what I'll discuss today. Here's the system block diagram I came up with:
Let's go over this briefly block by block. The most obvious are the LEDs - I need one set of LEDs in each color of the traffic light. There aren't many requirements for the LEDs. I don't think they'll need to be super bright or require heat sinking. I'm not dealing with any esoteric colors or optical properties - I just need colored light. The only requirement I can think of is that since these LEDs are going to be velcro'd to the inside of a shirt that I will be wearing I'd prefer that there not be metal pins sticking into me. In that spirit they should probably be surface mount LEDs. Since I can't keep my hand steady due to excessive coffee intake I'd like them to be the largest surface mount package I can find. Also I imagine I'll need a fair number of them - 30 or so. Thus, they should be cheap as well. This isn't a tall order for LEDs.
I usually order my electronics on Digi-Key. Digi-Key is rather... old school. For example, they love their paper catalog. They love it so much that they created an app for their website that actually shows you the pages from their catalog. If you love the paradigm of paper please use this. If you love the Google paradigm then read on for a less-confusing way to use their website.
When finding anything on Digi-Key you have to know what you're looking for and you have to be very patient. This is how I found the LEDs I would use for this project:
- Go to Digi-Key.com, type 'LED' in search in upper right corner
- Browse through the categories until I find one that looks right - Opto-Electronics/Discrete LEDs. If you're looking for something common it's best to select the category that has the most items in it. In this case these should be common LEDs and the Discrete LED category has about 15K items in it. It's a good bet.
- Now you have several columns you can use to filter all of the items in this category. Always always filter on 'In-Stock' before you do anything else. 95% of the time if something is not in stock you don't want it anyhow.
- Now start filtering on what you know you need. Most of the columns you'll never touch. Dominant wavelength? Lens style? Size? No idea. Surface mount? Yes! Color? Green, yellow or red! Pick the things you know for sure first but select filters one at a time. If you don't you might end up with 0 results and have to work backwards to figure out why.
- Guess with the rest. Look at the results - specifically the pictures - and determine roughly what you want, then try to apply filters to remove the other stuff.
- Finally, look at price with an eye towards minimum quantities. Typically you won't be buying in such bulk that you get price breaks so look for minimum quantities of 1 and (generally) select the lowest price.
The filters I applied to find my LEDs were (in this order):
- Mounting Type: Surface Mount - For the reasons I discussed earlier
- Colors: Red, Green and Yellow - This is obvious
- Package/Case: 1206 - From experience I know this is the largest and most readily-available surface mount LED package.
- Lens Type: Clear - I definitely don't want tinted and I've never liked the look of diffused (think frosted) lenses. Personal choice.
- Test Current: 20mA - More current means more brightness. I'd like as much brightness as I can manage without too much work.
- Lens Style: Rectangular - Honestly I have little or no idea about this one but the domed top lenses didn't sound like what I want and I didn't like the look of them from the picture, so I stuck with rectangular and flat top. It's worth noting that at this point I have 118 options remaining.
- Price: Sort by lowest price with a minimum of 1 piece - If you click the up arrow in the Unit Price column it offers to sort by price with a specified minimum. Always use a minimum of 1 piece.
Now at this point everything's pretty much the same except for color. As long as there's nothing suspicious happening with price (ie, lowest price is $.01/unit but every other manufacturer is at least $.25/unit) you might as well select the lowest priced option. In the end I selected LEDs with a bit more brightness than the cheapest ones. Here are my selections for LEDs:
- Green LED: 754-1440-1-ND
- Yellow LED: 754-1144-1-ND
- Red LED: 754-1143-1-ND
The LEDs need something to drive them and there are two main options for driving LEDs: voltage or current. Ultimately LEDs are current-mode devices: the more milliamps you push through them the brighter they get. There are all sorts of methods to get this current through the LEDs in a controlled fashion - here are a few general ideas:
- Use an LM317 as a constant current source
- Use a dedicated current-mode LED driver IC
- Build a switch-mode current driver from discrete components
- Bias the LEDs with a resistor and turn them on and off with a transistor
In the end biasing the LEDs with a resistor and controlling them with a transistor is the best choice for this application because it's the simplest. While it won't give me the most bang for the buck in terms of power expended vs. light produced it has the benefit of being simple and easily controllable compared to the others. I'm not out to prove how clever I am with this project and even if I were I'm not that clever when it comes to analog circuits. You can be as complicated as you want when it comes to controlling LEDs and have control over ever aspect of the system but realistically all this application calls for is to produce some light and wow a few people at Halloween. In that spirit I'm going with the simplest driver I can - a transistor switch. However, this requires a sanity check.
Usually the transistor I would select would be an old standby: the 2N2222. I know as a rule of thumb that you get a current gain of 10 with the 2N2222: 10mA base current gives you 100mA collector current when the transistor is saturated. However, if I have about 30 LEDs all drawing 20mA I need to deliver 600mA. That's getting close to the current limit of the 2N2222 and it also means that I need to deliver 60mA base current to switch it. That will be more than a typical microcontroller can source from its pins. For this application I need something with more current gain - in this case a Darlington Array is probably best. A Darlington Array is basically two transistors connected together such that their current gain is multiplied - so instead of a current gain of 10 you have a current gain of 100. Also Darlington Arrays can usually handle more current than a single transistor.
And in fact there's a part that was made for this application: the ULN2803. It's eight separate Darlington transistors in one chip. The only rub is that each Darlington channel is rated for a maximum of 500mA. However, you can double up the channels and get a maximum of 1A. So if each LED color gets two channels that still only uses six of the eight channels. Also, the inputs are biased to allow TTL signals (such as those generated by microcontroller output pins) to be used so I don't have to worry about how much current they're drawing. The ULN2803 can switch the LEDs and I can bias them with resistors to control the brightness. In the end I selected a through-hole DIP version of the ULN2803: Part #497-2356-5-ND
As far as the microcontroller is concerned there are many options. The computation and timing requirements for this application aren't excessive so I won't need anything too esoteric. That still doesn't really narrow it down - there are plenty of good microcontroller options in the hobbyist market. To name a few that I've used: AVR, PSoC, MSP430, PIC and the ARM Cortex-M3. Of course, there's many potential chips in each of those families as well.
Since I don't intend to do anything fancy with the microcontroller so I'm going to use an ATMega328 - the microcontroller that powers the Arduino. The microcontroller is part of Atmel's AVR line of microcontrollers which are a good all-around choice for hobbyists for several reasons:
- The development tools are free - AVR Studio can be downloaded from Atmel and has everything you need to write C firmware for both Atmel's 8-bit microcontroller families and the AVR32 family as well.
- The programmer is cheap - the Dragon is a full-featured In-System Programmer (and a less-capable debugger) that will program nearly all of Atmel's 8-bit for a cost of $50 (and I already own one)
- The chips themselves are cheap and I already have several on hand.
- Arduino-compatible so that others can easily reproduce the project if they want.
- It has a wide variety of peripherals ready to use.
You can either find them on Digi-Key with this part #: ATMEGA328P-PU-ND or you can get it from Sparkfun with or without the Arduino bootloader pre-programmed. The prices are comparable at either place.
The accelerometer is a trickier question. I know I'm going to buy it from Sparkfun because they have a good selection of accelerometers with breakout boards. You can't really buy accelerometers in hobbyist-friendly packages like DIP so the breakout board is a necessity. I only really need one axis but I might as well get three so I can use this as a platform for other projects. I could use a serial interface but the ATMega328 has only one serial peripheral, so if I use it for the accelerometer I can't have a serial port for debugging. Given that, I'm going to try to find a three-axis accelerometer with voltage outputs. If you go to their site on the left they have their categories: choose Sensors/Accelerometers/3-Axis. There are about a dozen options. We can immediately ignore the ones that aren't breakout boards. Next, we can ignore the ones that have serial interfaces - that is, either I2C or SPI. That pretty much leaves one option: the MMA7361.
Power is the last issue, but there's a problem. The accelerometer is 3.3V. The ULN2803 prefers 5V. The green LED has a forwad drop of about 3V. The microcontroller could be either 5V or 3.3V. From this, it looks like the LEDs will have to be driven with 5V and the microcontroller will also have to be 5V so it can control the ULN2803. But we need 3.3V for the accelerometer as well. There isn't an easy solution to this: we need both a 5V and a 3.3V source. The 5V source has to be pretty hefty as well - each set of LEDs will draw about 600mA. Only one set should be on at a time - by design and because otherwise it will draw so much power that the battery will die quickly. For that sort of current draw I need another old standby - the LM7805. It can supply up to 1A at 5V and is more than enough to power the LEDs and all of the 5V components. If you search for LM7805 on Digi-Key you get the option of either a Texas Instruments version at $1.66/unit or a Fairchild version at $.66/unit. The difference seems to be that the TI part has no lead in it. I'm going to choose the cheaper version: LM7805CT-ND.
For the 3.3V regulator I just typed '3.3V regulator' into the Digi-Key search and selected the 'Linear-LDO' category. I know this from experience - the 7805, LM317 and other similar voltage regulators I've used are all linear so I stuck with it. After selecting 'In-Stock' I drilled down into the columns this way:
- Regulator Topology: Positive Fixed - I don't need adjustable or negative voltages
- Voltage Output: 3.3V - That's all I need
- Mounting Type: Through-hole - I tend to prefer through-hole
- Output Current: 250mA - I need so little current, but in case I want to use this part again in the future I prefer to select a part that can accommodate more applications
That leaves part #497-7246-1-ND as the 3.3V regulator.
The last major thing I need is some sort of battery. I'd like to not screw around with some combination of AA batteries - they only go up to 1.5V (1.2V for rechargeables) so If I have to generate 5V that means at least 4, possibly more batteries. A 9V battery seems ideal - plenty of voltage overhead. Keep in mind though, 9V batteries tend to have about 500mAH of capacity. That's not a lot of time to run this costume, but it's the tradeoff I have to make to accommodate everything else. It's mostly for novelty anyhow. So I decided I wanted a nice 9V battery holder and not just a couple of wires and a connector. If you search Digi-Key for '9V battery holder' you're presented with the 'Battery holders, clips and contacts' category in which you can drill down the columns thusly:
- Battery Cell Size: 9V - Obviously
- Number of Cells: 1 - Also obvious
All of the other parts are just miscellaneous: tactile switches, rectangular headers, resistors, capacitors, LEDs, jumpers, etc. All of that I have on-hand and don't have to order. With any luck I don't have to make any other supplemental orders to get more of anything or replace something I broke. When ordering parts make sure to note whether you're close to a price break. I needed about 30-35 LEDs (the number I thought I needed plus extras) and was able to get a slight price break by ordering a few more than I thought I needed. And of course, always always order extras - you WILL break something. I broke plenty of LEDs and perhaps a few microcontrollers during this process.
Next time I'll talk about capturing/generating the schematics. For now I'll leave you with a bit of a teaser: pictures of the board after assembly.
Saturday, September 15, 2012
Choosing data structures
I try to write tight, efficient code. This is very important when you're working on a small microcontroller with limitations on speed and memory. However, I also try to write code that is well-organized and safe (ie, preventing buffer overflows, recognizing and handling error conditions, etc.). Let me tell you that these two goals are often at odds: code suddenly becomes much less efficient when you have to constantly check for errors ("Is this passed pointer NULL? Am I outside the bounds of my array? Did I leave the oven on?"). Trying to write well-organized code just makes it worse - to achieve this I usually separate all functionality out into independent functions. Pretty soon you have a class with 18 functions and the call stack for any given publicly-visible function might be five or six calls deep. Say hello to stack overflows!
It's an eternal struggle with no good solution. And since I don't have a solution, I'll just shut up and go to bed. Good night!
Just kidding - you can't get me to be quiet so easily. While there is no universal answer to the problems posed above there are application-specific solutions that a good engineer can utilize to improve efficiency, readability and robustness. Choosing the correct data structure for your task gives you improvements on all fronts with no downsides. Let's discuss choosing between a circular buffer and a linked-list based on the application.
(As always you can look on Wikipedia to find a good basic description of Linked Lists and Circular Buffers to supplement my descriptions).
A circular buffer is a way to turn a linear array into a circular one. Say you have an array of bytes and it's 32 bytes long. These arrays are linear in that people think about them having a beginning and an end: the beginning is always 0 and the end is always size-1. Typically programmers will use a for loop to iterate over the whole range of the array from the first element to the last or perhaps use an enumeration to access specific elements in the array. Circular buffers are conceptually different in that they have no 'beginning' or 'end' but instead a 'head' and 'tail'. The head and tail can be any position within the array. When the head or tail reaches the last element they wrap around back to the beginning. Elements can be added to the bufer and removed during the course of program execution and the head and tail values change to match. In this way the buffer keeps track of which parts of the array are unread and which can be overwritten. Here's an example:
I instantiate a circular buffer with room for 32 elements. At the beginning the buffer is empty, so the head and tail pointers both set to element zero. At some point I want to add data to the buffer. Data is always written to the tail and read from the head, so when I add data to the buffer it's written to the current position of the tail (element zero) and then the tail is incremented to now point to element one. I can keep adding data to the buffer and the tail will continue to increment until it's equal to 31. At this point I've added 31 elements to the buffer and read none, so the head pointer is still set to zero but the tail pointer is set to 31. If I incremented the tail pointer again it would be 32 and my array doesn't have 32 elements and that would be a buffer overflow. So instead of just incrementing the tail pointer I have to increment it then wrap it back around to point to the beginning of the array. So (31+1)mod 32 = 0. Doing that would cause the head and the tail to point to the same location. By convention I've decided that if the head pointer and the tail pointer are equal that means the buffer is empty, not full. So I've added 31 elements and I can add no more without disturbing my convention. In order to add more I have to remove some elements. As I said before elements are always read from the head pointer. So in order to remove an element I read the element pointed to by the head pointer and then increment the head pointer. Now the head points to position 1 and I can add another element at which point the tail will be equal to 0. As long as you maintain the state of the head and tail pointers you can reuse the same memory over and over again and hopefully not get too confused.
Circular buffers are very useful for serial ports. Imagine for example you have a microcontroller and you need to monitor a serial port. You have an RX interrupt that tells you when a byte comes in. You have two options: you can handle the byte in the interrupt (which is not always a good idea depending on the amount of 'handling' you have to do) or you can just add that byte to a circular buffer and return. Then in your main loop you just check to see if there's any data in the circular buffer and handle it there outside of the interrupt context.
I coded a nice circular buffer library with a focus on robustness and code clarity over efficiency. This meant that I had a function to add a byte, remove a byte, increment pointers, copy multiple bytes, etc. It ended up being a lot of functions and a lot of overhead: every time you wanted to add a byte it would check to see if the buffer was empty (one function call) then add the byte and call another function to increment pointers (two function calls, AH AH AH!). This was probably more function calls than I should have tolerated but the performance was acceptable for its intended application of handling serial traffic. It was so useful that when I needed a packet-based serial scheme I coded it utilizing my circular buffer as a basis: serial data would be pushed into the buffer and the packet communication functions would read the buffer looking for packets. Stacking these two libraries on top of each other caused more function calls and more overhead but it still wasn't unbearable because this arrangement was a pretty good fit for this application.
But one fateful day I needed to handle TCP and UDP traffic instead of serial traffic. TCP and UDP communication doesn't piddle around with single bytes but instead can transfer hundreds of bytes at a time inside a packet. At the time I foolishly thought that code reuse was a higher priority than efficiency so I implemented the same scheme for TCP and UDP data as I did for serial. That meant that for every packet that came in called a function to copy one byte at a time into a circular buffer. And of course, that function called other functions which called other functions and suddenly my overhead just for copying data to the buffer was rather large. But wait, it gets worse. Even more foolish than copying bytes singly is copying them at all. You see, the TCP/IP stack didn't intend for me to copy that data to another buffer - it passed a pointer to the data and the length of the data and assumed I would work with it in-place without copying. But my circular buffer didn't work that way so instead I had to instantiate a huge circular buffer capable of holding multiple packets (we're talking a thousand bytes or so) and copy data into it one byte at a time at the cost of multiple function calls per byte when it was entirely unnecessary to do any of that!
As you can see I was a fool. This system was not efficient in any way. It doubled the amount of memory I needed and made many useless function calls. There's no way this system could handle large amounts of data and sure enough my first attempt to handle TCP traffic produced an astounding data rate of 8kbps at the application layer. This was over Ethernet by the way.
I failed to produce efficient code because the data structure I chose was totally unsuited for the task. When I used my circular buffer for the serial port I gained the advantages of robust and safe code with a minimum loss in efficiency. When I tried to apply the same code to a different system I lost a significant amount of efficiency with no increase in robustness or safety. Different situations call for different solutions. What should I have implemented to better handle TCP/IP traffic?
In this situation a linked list would have been preferable. Linked lists can perform many of the same functions as circular buffers but they arrange the data differently. Imagine a linked list as a bunch of Easter eggs connected by pieces of string. Each Easter egg has yummy candy inside of it. There's a string on one end of the egg that's secured so you can't remove it. On the other end of the egg is a place to tie another piece of string. In this example you're blind so you're doing everything by touch. You need to keep track of all of your eggs and not lose any. The first thing you do is secure one egg to the table and call it your 'head' egg. Unless you misplace the table it's unlikely you'll misplace the head egg. So someone (we'll call him Mr. TCP/IP Stack) hands you another egg and in order not to lose it you tie it to the ball that's secured to the table. When he hands you more, you just follow the string of eggs until you find the last one and tie the new egg there. You can keep adding to the string of eggs this way and be sure you'll never lose any because they're all secured to a single immovable point. At some point another person (Mr. Packet Parser) will ask for the first egg (called egg #1 hereafter) Mr. Stack gave you. Here's how you do it without losing all of your egg:
As you can see this approach allows you to keep an unlimited number of eggs and give any particular egg away without being worried that you'll lose the others. This is the idea behind a linked list except that the strings holding the eggs together are pointers and the goodies inside the eggs are the payload. The TCP/IP stack would hand me the payload it received, but instead of putting the goodies into an egg I picked them apart one by one and put them in one of those pill boxes where there's a compartment for every day of the month. Thus, Mr. Parser had to open each compartment individually one eat one piece of candy at a time rather than opening the egg and dumping it all into his mouth.
The difference between the two approaches is striking. With the linked list approach all I would have had to do when I received new data from the TCP/IP stack is to add the payload to the linked list. The performance of the circular buffer approach was dependent on the amount of data received where a linked list doesn't have that restriction. The linked list approach also reduces memory usage: there's no reason to copy all of the data into one large buffer to search through it. With TCP and UDP unless something goes horribly horribly wrong you won't receive half of your data in one packet and half in another - it's all or nothing. This means that you don't have to maintain two large buffers - just one. In addition with the linked list there's no overhead in maintaining the state of the circular buffer. This means fewer function calls compared to the circular buffer. For this application, using a linked list is superior to the circular buffer because it reduces memory usage, scales better and has lower overhead.
But wait! I don't want your takeaway from this post to be that the linked list is superior to the circular buffer in an absolute sense. That's not true at all - the takeaway from this post should be that you have to choose your data structure to suit your application. Let's take a backwards look at this and see what would happen if we used the linked list in place of the circular buffer for the application of receiving serial data.
You see, the circular buffer was fine for the serial application because it was designed to handle data that came in singly. All it had to do to add data was stick the new byte into the array at the location of the tail pointer and then increment that pointer. Incrementing the pointer is the hardest part and even that was fairly simple. The linked list is different. To add something to the end of the linked list first you have to find the end. This means starting at the head and working your way down until you find an egg that doesn't have another one tied to it. And you have to do this every time you want to find the end. In this serial application data comes in one byte at a time. That means each egg only contains one jelly bean. The efficiency of the linked list was due to the fact that you could put a lot of jelly beans in each egg. But if you're artificially limited as to the number of jelly beans you can put in each egg suddenly a linked list isn't very efficient. As the list gets larger you have to search longer and longer to find the end of the chain. And that list gets larger and larger with every byte that comes in. Not every ten bytes or every 100 bytes - every single byte. That gets very inefficient very quick.
You can see that simply by choosing the correct data structure for your application you can improve performance without sacrificing robustness, stability or readability. By choosing your data structures wisely you can have your cake and eat it too!
It's an eternal struggle with no good solution. And since I don't have a solution, I'll just shut up and go to bed. Good night!
Just kidding - you can't get me to be quiet so easily. While there is no universal answer to the problems posed above there are application-specific solutions that a good engineer can utilize to improve efficiency, readability and robustness. Choosing the correct data structure for your task gives you improvements on all fronts with no downsides. Let's discuss choosing between a circular buffer and a linked-list based on the application.
(As always you can look on Wikipedia to find a good basic description of Linked Lists and Circular Buffers to supplement my descriptions).
A circular buffer is a way to turn a linear array into a circular one. Say you have an array of bytes and it's 32 bytes long. These arrays are linear in that people think about them having a beginning and an end: the beginning is always 0 and the end is always size-1. Typically programmers will use a for loop to iterate over the whole range of the array from the first element to the last or perhaps use an enumeration to access specific elements in the array. Circular buffers are conceptually different in that they have no 'beginning' or 'end' but instead a 'head' and 'tail'. The head and tail can be any position within the array. When the head or tail reaches the last element they wrap around back to the beginning. Elements can be added to the bufer and removed during the course of program execution and the head and tail values change to match. In this way the buffer keeps track of which parts of the array are unread and which can be overwritten. Here's an example:
I instantiate a circular buffer with room for 32 elements. At the beginning the buffer is empty, so the head and tail pointers both set to element zero. At some point I want to add data to the buffer. Data is always written to the tail and read from the head, so when I add data to the buffer it's written to the current position of the tail (element zero) and then the tail is incremented to now point to element one. I can keep adding data to the buffer and the tail will continue to increment until it's equal to 31. At this point I've added 31 elements to the buffer and read none, so the head pointer is still set to zero but the tail pointer is set to 31. If I incremented the tail pointer again it would be 32 and my array doesn't have 32 elements and that would be a buffer overflow. So instead of just incrementing the tail pointer I have to increment it then wrap it back around to point to the beginning of the array. So (31+1)mod 32 = 0. Doing that would cause the head and the tail to point to the same location. By convention I've decided that if the head pointer and the tail pointer are equal that means the buffer is empty, not full. So I've added 31 elements and I can add no more without disturbing my convention. In order to add more I have to remove some elements. As I said before elements are always read from the head pointer. So in order to remove an element I read the element pointed to by the head pointer and then increment the head pointer. Now the head points to position 1 and I can add another element at which point the tail will be equal to 0. As long as you maintain the state of the head and tail pointers you can reuse the same memory over and over again and hopefully not get too confused.
Circular buffers are very useful for serial ports. Imagine for example you have a microcontroller and you need to monitor a serial port. You have an RX interrupt that tells you when a byte comes in. You have two options: you can handle the byte in the interrupt (which is not always a good idea depending on the amount of 'handling' you have to do) or you can just add that byte to a circular buffer and return. Then in your main loop you just check to see if there's any data in the circular buffer and handle it there outside of the interrupt context.
I coded a nice circular buffer library with a focus on robustness and code clarity over efficiency. This meant that I had a function to add a byte, remove a byte, increment pointers, copy multiple bytes, etc. It ended up being a lot of functions and a lot of overhead: every time you wanted to add a byte it would check to see if the buffer was empty (one function call) then add the byte and call another function to increment pointers (two function calls, AH AH AH!). This was probably more function calls than I should have tolerated but the performance was acceptable for its intended application of handling serial traffic. It was so useful that when I needed a packet-based serial scheme I coded it utilizing my circular buffer as a basis: serial data would be pushed into the buffer and the packet communication functions would read the buffer looking for packets. Stacking these two libraries on top of each other caused more function calls and more overhead but it still wasn't unbearable because this arrangement was a pretty good fit for this application.
But one fateful day I needed to handle TCP and UDP traffic instead of serial traffic. TCP and UDP communication doesn't piddle around with single bytes but instead can transfer hundreds of bytes at a time inside a packet. At the time I foolishly thought that code reuse was a higher priority than efficiency so I implemented the same scheme for TCP and UDP data as I did for serial. That meant that for every packet that came in called a function to copy one byte at a time into a circular buffer. And of course, that function called other functions which called other functions and suddenly my overhead just for copying data to the buffer was rather large. But wait, it gets worse. Even more foolish than copying bytes singly is copying them at all. You see, the TCP/IP stack didn't intend for me to copy that data to another buffer - it passed a pointer to the data and the length of the data and assumed I would work with it in-place without copying. But my circular buffer didn't work that way so instead I had to instantiate a huge circular buffer capable of holding multiple packets (we're talking a thousand bytes or so) and copy data into it one byte at a time at the cost of multiple function calls per byte when it was entirely unnecessary to do any of that!
As you can see I was a fool. This system was not efficient in any way. It doubled the amount of memory I needed and made many useless function calls. There's no way this system could handle large amounts of data and sure enough my first attempt to handle TCP traffic produced an astounding data rate of 8kbps at the application layer. This was over Ethernet by the way.
I failed to produce efficient code because the data structure I chose was totally unsuited for the task. When I used my circular buffer for the serial port I gained the advantages of robust and safe code with a minimum loss in efficiency. When I tried to apply the same code to a different system I lost a significant amount of efficiency with no increase in robustness or safety. Different situations call for different solutions. What should I have implemented to better handle TCP/IP traffic?
In this situation a linked list would have been preferable. Linked lists can perform many of the same functions as circular buffers but they arrange the data differently. Imagine a linked list as a bunch of Easter eggs connected by pieces of string. Each Easter egg has yummy candy inside of it. There's a string on one end of the egg that's secured so you can't remove it. On the other end of the egg is a place to tie another piece of string. In this example you're blind so you're doing everything by touch. You need to keep track of all of your eggs and not lose any. The first thing you do is secure one egg to the table and call it your 'head' egg. Unless you misplace the table it's unlikely you'll misplace the head egg. So someone (we'll call him Mr. TCP/IP Stack) hands you another egg and in order not to lose it you tie it to the ball that's secured to the table. When he hands you more, you just follow the string of eggs until you find the last one and tie the new egg there. You can keep adding to the string of eggs this way and be sure you'll never lose any because they're all secured to a single immovable point. At some point another person (Mr. Packet Parser) will ask for the first egg (called egg #1 hereafter) Mr. Stack gave you. Here's how you do it without losing all of your egg:
- Find the head egg
- Move down the string of eggs until you find the egg after egg #1(call this egg #2)
- Untie egg #2's string from egg #1 and keep ahold of it
- Untie egg #1's string from the head egg
- Tie egg #2's string to the head egg
- Hand off egg #1 to Mr. Parser
As you can see this approach allows you to keep an unlimited number of eggs and give any particular egg away without being worried that you'll lose the others. This is the idea behind a linked list except that the strings holding the eggs together are pointers and the goodies inside the eggs are the payload. The TCP/IP stack would hand me the payload it received, but instead of putting the goodies into an egg I picked them apart one by one and put them in one of those pill boxes where there's a compartment for every day of the month. Thus, Mr. Parser had to open each compartment individually one eat one piece of candy at a time rather than opening the egg and dumping it all into his mouth.
The difference between the two approaches is striking. With the linked list approach all I would have had to do when I received new data from the TCP/IP stack is to add the payload to the linked list. The performance of the circular buffer approach was dependent on the amount of data received where a linked list doesn't have that restriction. The linked list approach also reduces memory usage: there's no reason to copy all of the data into one large buffer to search through it. With TCP and UDP unless something goes horribly horribly wrong you won't receive half of your data in one packet and half in another - it's all or nothing. This means that you don't have to maintain two large buffers - just one. In addition with the linked list there's no overhead in maintaining the state of the circular buffer. This means fewer function calls compared to the circular buffer. For this application, using a linked list is superior to the circular buffer because it reduces memory usage, scales better and has lower overhead.
But wait! I don't want your takeaway from this post to be that the linked list is superior to the circular buffer in an absolute sense. That's not true at all - the takeaway from this post should be that you have to choose your data structure to suit your application. Let's take a backwards look at this and see what would happen if we used the linked list in place of the circular buffer for the application of receiving serial data.
You see, the circular buffer was fine for the serial application because it was designed to handle data that came in singly. All it had to do to add data was stick the new byte into the array at the location of the tail pointer and then increment that pointer. Incrementing the pointer is the hardest part and even that was fairly simple. The linked list is different. To add something to the end of the linked list first you have to find the end. This means starting at the head and working your way down until you find an egg that doesn't have another one tied to it. And you have to do this every time you want to find the end. In this serial application data comes in one byte at a time. That means each egg only contains one jelly bean. The efficiency of the linked list was due to the fact that you could put a lot of jelly beans in each egg. But if you're artificially limited as to the number of jelly beans you can put in each egg suddenly a linked list isn't very efficient. As the list gets larger you have to search longer and longer to find the end of the chain. And that list gets larger and larger with every byte that comes in. Not every ten bytes or every 100 bytes - every single byte. That gets very inefficient very quick.
You can see that simply by choosing the correct data structure for your application you can improve performance without sacrificing robustness, stability or readability. By choosing your data structures wisely you can have your cake and eat it too!
Subscribe to:
Posts (Atom)



