One aspect of the philosophy of GPL is to contribute back to the community. However, as we all know in the GPL license the only way to contribute is by submitting code.
This creates several problems; the primary being a chasm between it and all other economic activity. This has resulted in lots of people coming up with very creative but ultimately ancillary ways to fund GPL activity, the primary being support. There is probably a mountain of literature written about this and so I won't go into them more deeply here other than to summarize:
These methods are not satisfactory because the effort/reward relationship is obscure at best and at worst rewards the wrong activities.
So let us modify the GPL with a economic clause. This would essentially state, "If you want to use this software without licensing your derivative software via GPL, then simply pay us money.
In this way a company or individual can contribute back to the project in the traditional ($) manner (as an aside, does it also allow code contributions to be valued monetarily and therefore taxed?). But of course what should the project do with that cash? While many projects could definitely use the money to pay for server space and bandwidth; we can imagine an extremely successful piece of OSS that is actually generating a surplus of money. It would obviously be inappropriate for the project founder to keep it since in theory the software has benefited from submissions from many individuals.
That observation cannot sit in anyone's mind without producing the obvious answer within a modern meritocracy:
Project surplus should be distributed to individuals according to level of contribution
If the project is successful, one might imagine that this could become key contributors primary source of income. Essentially they would be "employees" of the project's "company", but without some of the pain associated with corporate involvement (and perhaps also without some of the benefits).
Of course, this brushes all kinds of issues under the rug such as how to measure "level of contribution". Let me throw out an initial breakdown formula:
contribution = "Trust Network" opinion * (alpha * sum(cool feature values) + beta * lines of code + delta * sum(bug fix value) - epsilon*(sum bugs you created))
"Trust Network opinion" = how other people rate you; used to catch people who are padding out their contributions by writing wordy code, etc.
Alpha, beta, and delta = arbitrary weighting factors depending on what's most important.
"cool feature values" = How much people like the feature you added (or maybe real-time feedback reporting what features the users are using)
"lines of code" should be obvious
"bug fixes" = Fixing bugs is a boring nasty job but someone has to do it.
And of course your contribution should be penalized by the # of bugs found; after all, if someone has to essentially rewrite your stuff; I'm not sure if you deserve too much credit.
But this only focuses on the code. How about the community? We can imagine similar metrics created for support forums; # of posts, response time, quality of posts, etc, and combined into a monster "value" equation. And documentation...
Even the necessary evil of "hierarchy and planning" can perhaps be captured by a pyramid scheme whereby section owners "contribution value" includes a tiny % of all contribution values to that section.
All of this begs the question: "Is it possible to capture all business activities?" To create an "Open Company?"
All of the above are my own ideas except for the word "Open Company". I was inspired to blog about them today because I think we are about to take the initial steps in regard to feasibility; there is a company out there who is going to try to do just that.
You can read a bit about it at: http://e-texteditor.com/blog/2009/opencompany
Popular Posts
- TLC5940, TLC5941 and Arduino
- Arduino L298 stepper motor driver
- Arduino PWM on all pins.
- DIY PCB fabrication; Breakout Board Toner Transfer Experiments
- Arduino and M5451 -- Control 35 LEDs, motors, etc!
- DIY PCB SMT Breakout board fabrication: Results
- Lightuino Design Thoughts
- Arduino/AVR vs Cypress/PSOC
Tuesday, March 24, 2009
Wednesday, March 11, 2009
Arduino L298 stepper motor driver
Here is an example Arduino sketch to drive a stepper motor using the L298 chip. The actual driver is the "StepperL298N" class, and then there is some code to test it. When you instantiate the class, pass in the 4 Arduino pins that you have connected to the stepper motor. For example, "StepperL298N motor(4,5,6,7)" will drive a motor connected to Arduino pins 4,5,6,7. If you need to drive 2 motors, just create 2 instances of the class with different pins!
To move the motor 1 tick, call the step(bool direction) member function. Call it with 1 to go forward, and 0 to go backwards. The faster you call it, the faster the motor will go. Note that the Arduino can easily call step() much faster than the motor can possibly be driven, so you must insert a delay by calling the delay function. For my test motor (and old AMP 2.3 volt 60oz*in, which I got from taking apart a printer) you have to delay at least 5ms for the motor to work.
Michael Margolis ('mem' on the Arduino.cc forum) was kind enough to let me cross-post a schematic:
To move the motor 1 tick, call the step(bool direction) member function. Call it with 1 to go forward, and 0 to go backwards. The faster you call it, the faster the motor will go. Note that the Arduino can easily call step() much faster than the motor can possibly be driven, so you must insert a delay by calling the delay function. For my test motor (and old AMP 2.3 volt 60oz*in, which I got from taking apart a printer) you have to delay at least 5ms for the motor to work.
/*
* stepperL298N
* Copyright 2008 G. Andrew Stone
* Licensed under the Common Public License V1.0
* For the full text of this license see: http://www.opensource.org/licenses/cpl1.0.php
*
*/
int ledPin = 13;
void setup() // run once, when the sketch starts
{
pinMode(ledPin, OUTPUT); // sets the digital pin as output
}
void mydelay(int clk)
{
int i;
for (i=0;i< clk;i++);
//delay(clk);
}
class StepperL298N
{
public:
byte pins[4];
byte curState;
byte* lut;
StepperL298N(byte pin1,byte pin2,byte pin3,byte pin4);
void step(boolean dir);
void halfStep(boolean yes) { if (yes) lut = halfStepLut; else lut = fullStepLut;}
void start() { curState = 0xa;}
void free() { curState = 0; }
void brake() { curState = 0; }
byte fullStepLut[16];
byte halfStepLut[16];
};
StepperL298N::StepperL298N(byte pin1,byte pin2,byte pin3,byte pin4)
{
pins[0] = pin1; pins[1] = pin2; pins[2]=pin3; pins[3] = pin4;
lut = fullStepLut;
for (int i=0;i<4;i++)
{
pinMode(pins[i], OUTPUT); // sets the digital pin as output
}
memset(&fullStepLut,sizeof(byte)*16,0);
memset(&halfStepLut,sizeof(byte)*16,0);
// High nibble goes one way, low the other
fullStepLut[0] = 0;
// 1010 -> 1001 -> 0101 -> 0110
fullStepLut[0xa] = 0x9 | 0x60;
fullStepLut[0x9] = 0x5 | 0xa0;
fullStepLut[0x5] = 0x6 | 0x90;
fullStepLut[0x6] = 0xa | 0x50;
halfStepLut[0] = 0;
// 1010 -> 1000 -> 1001 -> 0001 -> 0101 -> 0100 -> 0110 -> 0010
halfStepLut[0xa] = 0x8 | 0x20;
halfStepLut[0x8] = 0x9 | 0xa0;
halfStepLut[0x9] = 0x1 | 0x80;
halfStepLut[0x1] = 0x5 | 0x90;
halfStepLut[0x5] = 0x4 | 0x10;
halfStepLut[0x4] = 0x6 | 0x50;
halfStepLut[0x6] = 0x2 | 0x40;
halfStepLut[0x2] = 0xa | 0x60;
}
void StepperL298N::step(boolean dir)
{
curState = lut[curState];
if (dir) curState &= 0xf;
else curState >>= 4;
for (int i=1,j=0;i< 16;i*= 2,j++)
{
byte lvl = LOW;
if (i&curState) lvl=HIGH;
digitalWrite(pins[j],lvl);
}
}
int serDataVal = LOW;
void loop() // run over and over again
{
unsigned long int j;
StepperL298N motor(3,4,5,6);
motor.halfStep(true);
motor.start();
for (j=0;j< 1000;j++)
{
motor.step(1);
digitalWrite(ledPin, j&1); // sets the LED on
delay(5+j/10); // Change the delay to change the rate the motor spins
}
motor.halfStep(false);
motor.start();
for (j=0;j< 1000;j++)
{
motor.step(0);
digitalWrite(ledPin, j&1); // sets the LED on
delay(5+j/10);
}
}
Michael Margolis ('mem' on the Arduino.cc forum) was kind enough to let me cross-post a schematic:

Saturday, February 28, 2009
GUI Design and click-distance
I am not a GUI designer by trade, but I have done some GUI design both of web applications and normal GUIs. And I'd like to both rant a little and describe some of my GUI design thoughts.
First I think that we can all agree that many GUIs are poorly designed and therefore frustrating to use. And a lot has been written about how to clearly lay out information on the page, etc. A good layout is alway good, but especially for new users to the system. But what about the other 99% of the time the user uses the system? Once the user understands your GUI, how long it takes to physically move the mouse across the screen to use each widget required to get the job done becomes the determining factor as to whether your GUI is useable or not.
A lot less (if anything -- I can't find anything but I certainly haven't done a lit review on this!) has been written this -- about the kinematics of web design.
Wikipedia defines kinematics as studying motion without considering the circumstances leading to that motion. That is very similar to what I want to talk about -- which is the motion of the user's hands as he moves the mouse, moves from the mouse to the keyboard and so on. It doesn't really matter what the purpose of this motion is, other than to observe that good GUI design should attempt to minimize or eliminate the motion for common tasks.
Let me suggest a new metric called "click-distance". The purpose of click-distance is to create a quantitive measurement of how "far" the user has to move from his current GUI-state to get to his desired GUI state. So the metric will contain a lot more components then just clicking and moving the mouse, but I think that the term "click-distance" implies a good first order approximation.
One think that I don't like about the term is that perhaps even more important as "click-distance" is "click-time". That is, how long did it take to traverse the click-distance? But I think that "click-time" itself is not as important as "click-frustration" or how PISSED the user gets because he has to do something like individually select 500 files in 500 upload file dialog boxes instead of shift-clicking them all at once. But the further we go down this path, the less measurable the metric becomes.
Clearly by recording the sequence of input the user makes, we can infer something about the distance the user's hands travelled given a particular physical setup (desktop, laptop, or PDA/phone). And don't forget about left-handed people!
Click-distance should capture the following concepts.
Literal distance metrics:
1. Total distance the mouse cursor moved. Obvious!
2. Number of mouse-repositions. A person can move halfway across the screen in almost the time it takes to move just a little way. From experimenting on myself, it seems to take around a second for each individual mouse motion.
3. Number of uninterrupted mouse direction changes. A person generally moves the mouse in a straight line from its current position to the desired destination. If the mouse moves back and forth or around and around, that implies difficulty positioning -- that is, the GUI element is too small. From your high school calculus we know that the direction is changing along a particular axis when the velocity is zero...
Button press metrics:
4. Number of clicks and keys pressed.
5. Number of alternations between mouse press and key press. This metric is a rough estimate of how many times the user had to move his hand from the mouse to the keyboard, and is therefore a major contribution to click distance
6. ctrl-alt-shift-click Combinations.
Hard to measure metrics:
6. How often a certain sequence is repeated. Repetition is a huge source of user frustration (and is the source of carpel-tunnel syndrome) but conveniently is often the easiest to remove through a bit of GUI redesign. I think that it is so important that click-distance should be the square of the repetition, but only proportional to the rest of the metrics. However, it is difficult to programatically identify repetitive movements...
To make a quantitive formula, each of the terms described above should be multiplied by a constant adjustment factor that reflects the distance the hand must move. These constants might differ based on the physical layout being considered:
click-distance = A*mouseDist + B*mouseRepos + C*mouseVelZero + D*presses + E*mouseKeyAlternations + F*keyClickCombinations + (G*repetitions)^2
The idea is of course to mininize this click-distance for common tasks. Of course, you wouldn't really have to DO this math, just start thinking about your GUI in terms of how long it takes to do an operation and you'll be well along the path to good, useable design.
Here are some practical ideas that have emerged from my consideration of click-distance in GUI designs:
1. No modal dialog boxes!
They force the user to move the hand to the mouse, reposition, and click. For all "alerts", "errors", etc, use a "message bar" (preferably NOT hidden on the bottom of the screen) and graphics (like red text and animation) to draw attention.
They also position themselves "creatively" -- aka NOT where your mouse is!
Using a modal dialog box makes it hard to see the information behind it.
Does not scale: doing 200 operations that contain a modal dialog requires 200 reposition-click-reposition operations.
The only time a modal dialog should be used is in TRUELY irrevocable operations. Like formatting your hard drive.
Of course, the corrolary to this is that your program should not HAVE any truely irrevocable operations.
2. Keep it all in one window.
Use tabs, etc. They much shorter in mouse distance to switch between than windows that have to be sized and positioned.
3. Design with REAL data, not your unit-test toys.
You really ought to try the GUI with real-world examples, that contain lots of data. Doing so is the best way to discover repetition.
4. Allow multi-select absolutely whenever possible.
Creatively apply what this might mean in cases where you are not sure... implementing one of 3 possibilities at least fixed 1/3 of the problem...
5. Keep values populated in fields, and don't force a history selection from the selection box.
This will dramatically reduce click-distance for unanticipated repetitive tasks. Look, this is about click-frustration. If a person is entering 1000 people's addresses over the whole USA, it is not so bad to type the city and state. But if the person is the tax accountant for a particular town and so the city and state is almost always the same it can be very tiresome.
Its best to have an easy way to delete the field in one go like most boxes have. Or even better try a tiny "pin value" icon button near the field. If the "pin value" button is depressed, the last value entered is remembered for the next time.
6. Selection boxes suck!
Clearly the problem is that the user was probably typing, so now he must move from the keyboard to the mouse. Click to open the selection, all kinds of tricky clicking (especially since the selection box sometimes closes itself if the mouse wanders too far) to scroll down, and finally click to select. It is major mouse distance.
Here's one example that would save millions of people 10 seconds per day and massively reduce office rage:
Its much harder to find "New York" in a list of states 52 states then to just type "NY"!
Especially when some of you only show us 3 states at a time!
7. For the prime function, click distance should be <= 1.
Word processors allow you to add lots of letters to a document with 1 keypress per letter. They are not so good at saving 10 different copies of the document. You have to pop up 10 modal dialog boxes. File managers let you copy 100 documents to 10 different places with a mouse distance of about 20 (1 selection operation + 10*(mouse position + key:ctrl-v)). And so on...
Ok. I think that's enough examples. There are lots more. As you think of examples, please add them as comments!
First I think that we can all agree that many GUIs are poorly designed and therefore frustrating to use. And a lot has been written about how to clearly lay out information on the page, etc. A good layout is alway good, but especially for new users to the system. But what about the other 99% of the time the user uses the system? Once the user understands your GUI, how long it takes to physically move the mouse across the screen to use each widget required to get the job done becomes the determining factor as to whether your GUI is useable or not.
A lot less (if anything -- I can't find anything but I certainly haven't done a lit review on this!) has been written this -- about the kinematics of web design.
Wikipedia defines kinematics as studying motion without considering the circumstances leading to that motion. That is very similar to what I want to talk about -- which is the motion of the user's hands as he moves the mouse, moves from the mouse to the keyboard and so on. It doesn't really matter what the purpose of this motion is, other than to observe that good GUI design should attempt to minimize or eliminate the motion for common tasks.
Let me suggest a new metric called "click-distance". The purpose of click-distance is to create a quantitive measurement of how "far" the user has to move from his current GUI-state to get to his desired GUI state. So the metric will contain a lot more components then just clicking and moving the mouse, but I think that the term "click-distance" implies a good first order approximation.
One think that I don't like about the term is that perhaps even more important as "click-distance" is "click-time". That is, how long did it take to traverse the click-distance? But I think that "click-time" itself is not as important as "click-frustration" or how PISSED the user gets because he has to do something like individually select 500 files in 500 upload file dialog boxes instead of shift-clicking them all at once. But the further we go down this path, the less measurable the metric becomes.
Clearly by recording the sequence of input the user makes, we can infer something about the distance the user's hands travelled given a particular physical setup (desktop, laptop, or PDA/phone). And don't forget about left-handed people!
Click-distance should capture the following concepts.
Literal distance metrics:
1. Total distance the mouse cursor moved. Obvious!
2. Number of mouse-repositions. A person can move halfway across the screen in almost the time it takes to move just a little way. From experimenting on myself, it seems to take around a second for each individual mouse motion.
3. Number of uninterrupted mouse direction changes. A person generally moves the mouse in a straight line from its current position to the desired destination. If the mouse moves back and forth or around and around, that implies difficulty positioning -- that is, the GUI element is too small. From your high school calculus we know that the direction is changing along a particular axis when the velocity is zero...
Button press metrics:
4. Number of clicks and keys pressed.
5. Number of alternations between mouse press and key press. This metric is a rough estimate of how many times the user had to move his hand from the mouse to the keyboard, and is therefore a major contribution to click distance
6. ctrl-alt-shift-click Combinations.
Hard to measure metrics:
6. How often a certain sequence is repeated. Repetition is a huge source of user frustration (and is the source of carpel-tunnel syndrome) but conveniently is often the easiest to remove through a bit of GUI redesign. I think that it is so important that click-distance should be the square of the repetition, but only proportional to the rest of the metrics. However, it is difficult to programatically identify repetitive movements...
To make a quantitive formula, each of the terms described above should be multiplied by a constant adjustment factor that reflects the distance the hand must move. These constants might differ based on the physical layout being considered:
click-distance = A*mouseDist + B*mouseRepos + C*mouseVelZero + D*presses + E*mouseKeyAlternations + F*keyClickCombinations + (G*repetitions)^2
The idea is of course to mininize this click-distance for common tasks. Of course, you wouldn't really have to DO this math, just start thinking about your GUI in terms of how long it takes to do an operation and you'll be well along the path to good, useable design.
Here are some practical ideas that have emerged from my consideration of click-distance in GUI designs:
1. No modal dialog boxes!
They force the user to move the hand to the mouse, reposition, and click. For all "alerts", "errors", etc, use a "message bar" (preferably NOT hidden on the bottom of the screen) and graphics (like red text and animation) to draw attention.
They also position themselves "creatively" -- aka NOT where your mouse is!
Using a modal dialog box makes it hard to see the information behind it.
Does not scale: doing 200 operations that contain a modal dialog requires 200 reposition-click-reposition operations.
The only time a modal dialog should be used is in TRUELY irrevocable operations. Like formatting your hard drive.
Of course, the corrolary to this is that your program should not HAVE any truely irrevocable operations.
2. Keep it all in one window.
Use tabs, etc. They much shorter in mouse distance to switch between than windows that have to be sized and positioned.
3. Design with REAL data, not your unit-test toys.
You really ought to try the GUI with real-world examples, that contain lots of data. Doing so is the best way to discover repetition.
4. Allow multi-select absolutely whenever possible.
Creatively apply what this might mean in cases where you are not sure... implementing one of 3 possibilities at least fixed 1/3 of the problem...
5. Keep values populated in fields, and don't force a history selection from the selection box.
This will dramatically reduce click-distance for unanticipated repetitive tasks. Look, this is about click-frustration. If a person is entering 1000 people's addresses over the whole USA, it is not so bad to type the city and state. But if the person is the tax accountant for a particular town and so the city and state is almost always the same it can be very tiresome.
Its best to have an easy way to delete the field in one go like most boxes have. Or even better try a tiny "pin value" icon button near the field. If the "pin value" button is depressed, the last value entered is remembered for the next time.
6. Selection boxes suck!
Clearly the problem is that the user was probably typing, so now he must move from the keyboard to the mouse. Click to open the selection, all kinds of tricky clicking (especially since the selection box sometimes closes itself if the mouse wanders too far) to scroll down, and finally click to select. It is major mouse distance.
Here's one example that would save millions of people 10 seconds per day and massively reduce office rage:
Its much harder to find "New York" in a list of states 52 states then to just type "NY"!
Especially when some of you only show us 3 states at a time!
7. For the prime function, click distance should be <= 1.
Word processors allow you to add lots of letters to a document with 1 keypress per letter. They are not so good at saving 10 different copies of the document. You have to pop up 10 modal dialog boxes. File managers let you copy 100 documents to 10 different places with a mouse distance of about 20 (1 selection operation + 10*(mouse position + key:ctrl-v)). And so on...
Ok. I think that's enough examples. There are lots more. As you think of examples, please add them as comments!
Friday, February 27, 2009
First Arduino shield boards arrived!
I designed an Arduino shield to provide a lot of outputs using very few pins on the Arduino... it uses 2 M5451 35 segment LED display driver chips.

Here's the board with all the parts soldered on.

Here is a angled view. On the front you can see a lot of pads. The idea was to allow the user to choose which Arduino pins should control the board by only bridging some of the gaps. However it turns out to be very difficult to solder tiny wires across these gaps.
Any ideas on how to do this better?

Here it is shown lighting up all the LEDs I happened to have lying around.

Here is the bottom view showing the Arduino connected.
A youtube video about it:

Here's the board with all the parts soldered on.

Here is a angled view. On the front you can see a lot of pads. The idea was to allow the user to choose which Arduino pins should control the board by only bridging some of the gaps. However it turns out to be very difficult to solder tiny wires across these gaps.
Any ideas on how to do this better?
Here it is shown lighting up all the LEDs I happened to have lying around.
Here is the bottom view showing the Arduino connected.
A youtube video about it:
Tuesday, February 24, 2009
constant current LED driver thoughts (LM334)

I was experimenting on how to mount surface mount LEDs in wood and made this little arch to test out ideas.
All the LEDs are in series. But the question was how to drive them? I could connect them to a wall wart transformer and put in a resistor, but that would mean that the resistance of the resistor would have to be tuned to the wall wart transformer. Since I was just going to grab a transformer from my junk pile of dead electronics I didn't want to do that. Instead I settled on this constant current circuit shown in discover circuits:
http://www.discovercircuits.com/DJ-Circuits/constantcurretled1.htm
This circuit is really strange because the current from the LED goes through R1 along with the current from the LM334's current selection. Was it a typo? Should the LED current really just go straight to ground?
Also, I popped up the LM334 spec. The LM334 basically has 3 pins: Positive, ground, and a third "current selection" pin. You just connect a resistor across the current selection pin and the ground pin to choose what current you want. So it turned out that the LM334 is a constant current driver itself, so why have the extra transistor?
And In fact, the naked LM334 circuit what I ended up using. But the issue kept bothering me and so I finally decided to think about it.
I decided to try 3 simple circuits. Let's call the simple one "A":

So basically the current from the LEDs runs through the LM334, and so, regardless of the input voltage, is regulated by the size of the resistor connected between pins 2 and 3.
I guess the problem is that all the current is passing through the LM334...
And then a "simplified" discovercircuits, call it "B":

The LM334 is used as suggested by the spec. But the LM334 only controls the current that flows out of the base of the transistor, so current through the transistor and LED will be about 100 times (or whatever the amplification of your transistor is) what is going through the LM334.
This turns out to be very useful if you want to use the relatively low current LM334 chip to control a much larger current. Or if you only have larger value transistors lying around.
This one from discovercircuits "C":

As I previously said, "C" is really strange because the current from the LED goes through R1 along with the current from the LM334's current selection.
The LM334 holds is middle pin at 68mV above the bottom. So without doing any math, you can see that current going through the LED wil have to "share space" through the resistor. As the current through the LED rises, the current sensed by the LM334 goes down. This therefore is equivalent (to the LM334) like putting a bigger resistor across its pins. End result: the LM334 lets a lower current pass through itself (i.e. out of the transistor's base). You actually end up with a stable situation.
As with the second circuit (B), the advantage is that load is going through the transistor, not the LM334. An additional advantage is that the resistor values to make it all work are similar to that in the LM334 spec, and you don't have a 100x multiplication of the current (which may or may not be an advantage)
Another disadvantage of B is that it is very sensitive to the exact amplification (beta) of the transistor. But I guess transistor betas used to vary pretty widely within the same part due to small fabrication differences (I am not sure how accurate they are nowadays)... So while B is great for home use where you can test the exact beta of the transistor and choose a resistor to match, this circuit might be a bad idea to use in a production circuit board.
However, a big disadvantage of C is that the load (from the LED) is going through the resistor, so you do waste more power than in B and most importantly will have to use a high-watt (bulky, expensive, don't have it in your basement) resistor. Or of course a bunch of normal resistors in parallel...but that is also pretty ugly.
Sunday, February 22, 2009
Arduino and M5451 -- Control 35 LEDs, motors, etc!
I've been fooling around with an Ardunio, which is a small embedded microprocessor and discovered that its a great way for software engineers to learn some hardware engineering. It is fairly limited in its outputs though, so I bought a couple of M5451 chips (futureelectronics.com MM5451YN for small quantities). This is nice because it is a 40 pin DIP device that plugs right into a breadboard, and is intended to drive LED displays.
But it does not HAVE to be used for LED displays -- I'm thinking of it as 35 constant-current switches that can be conveniently controlled (on/off) by 2 Arduino digital pins (clock and serial data). And if you want to change the current (of all pins) you can use a third pin to connect a "brightness" pin on the 5451 to a PWM on the Arduino.
So this is a *LOT* cooler than the chips that drive 8x8 arrays of LEDs by strobing them (MAX7219, search for "Arduino 8x8 matrix") because those chips flash the LEDs. So they really can't be used for other applications (like driving motors, or turning on a big-current transistor switch), and the LEDs are in theory not as bright because they are not on the entire time. In theory, I should be able to drive 2 of these 8x8 matrixes with one M5451 with 3 outputs left over, or one 16x16 (256 LEDS!) display (if I can get my hands on one).
Also, the cool thing about the M5451 being a constant current driver is that you don't need a current-limiting resistor inline with each LED. And you can connect several LEDs in series without worrying about V=IR math.
But let me emphasize for the beginning hardware DIYer, its important to NOT think of the outputs as logic, just think of them as switches. Basically you connect the M5451 to ground, and connect your load (let's just use LEDs, for example) between the + and it. So the M5451 must be on the cathode, minus, ground side -- whatever you call it -- of your LED. This is IMPORTANT, since if you buy something like RGB LEDs you want to get common ANODE. Remember, relative to the LED, the anode side is where conventional current (+) goes IN.
Of course, I bought the common cathode RGB LEDs (*sigh*).
So here's a picture of a M5451 on a breadboard with a boarduino and with a bunch of LEDs:
Ignore the upper part of the breadboard. all the activity is on the bottom. Also ignore the square silver chip and transistors on the far right. That's just stuff hanging around from another project!
The boarduino is on the bottom left. The 2 pins used are the green and yellow wires on the top side (the blue and short green wires are just power and ground).
You can see all the LEDs bridging from the + rail to the pins on the M5451 (middle)
I wrote a bit of code to drive the M5451 (see below). You can turn on each M5451 switch independently, control the brightness via one of the Arduino's PWM ports (PWM means pulse-width-modulation -- the Arduino toggles the line between 5 and 0 volts rapidly and so that it seems like the line actually has a voltage which is the weighted average of the time the line is 5 vs. 0 -- i.e. a simulated analog voltage).
On top of that, I made a class that is similar to PWM; it toggles the M5451 lines rapidly so the LEDs seem to change brightness.
Here is a video. To make it come out, I have a halogen desk lamp shining directly on the board from about a foot away. About halfway through I turned off the light so you could see the LED brightness in normal room light! Check out the video here:
Here's the code:
But it does not HAVE to be used for LED displays -- I'm thinking of it as 35 constant-current switches that can be conveniently controlled (on/off) by 2 Arduino digital pins (clock and serial data). And if you want to change the current (of all pins) you can use a third pin to connect a "brightness" pin on the 5451 to a PWM on the Arduino.
So this is a *LOT* cooler than the chips that drive 8x8 arrays of LEDs by strobing them (MAX7219, search for "Arduino 8x8 matrix") because those chips flash the LEDs. So they really can't be used for other applications (like driving motors, or turning on a big-current transistor switch), and the LEDs are in theory not as bright because they are not on the entire time. In theory, I should be able to drive 2 of these 8x8 matrixes with one M5451 with 3 outputs left over, or one 16x16 (256 LEDS!) display (if I can get my hands on one).
Also, the cool thing about the M5451 being a constant current driver is that you don't need a current-limiting resistor inline with each LED. And you can connect several LEDs in series without worrying about V=IR math.
But let me emphasize for the beginning hardware DIYer, its important to NOT think of the outputs as logic, just think of them as switches. Basically you connect the M5451 to ground, and connect your load (let's just use LEDs, for example) between the + and it. So the M5451 must be on the cathode, minus, ground side -- whatever you call it -- of your LED. This is IMPORTANT, since if you buy something like RGB LEDs you want to get common ANODE. Remember, relative to the LED, the anode side is where conventional current (+) goes IN.
Of course, I bought the common cathode RGB LEDs (*sigh*).
So here's a picture of a M5451 on a breadboard with a boarduino and with a bunch of LEDs:
The boarduino is on the bottom left. The 2 pins used are the green and yellow wires on the top side (the blue and short green wires are just power and ground).
You can see all the LEDs bridging from the + rail to the pins on the M5451 (middle)
I wrote a bit of code to drive the M5451 (see below). You can turn on each M5451 switch independently, control the brightness via one of the Arduino's PWM ports (PWM means pulse-width-modulation -- the Arduino toggles the line between 5 and 0 volts rapidly and so that it seems like the line actually has a voltage which is the weighted average of the time the line is 5 vs. 0 -- i.e. a simulated analog voltage).
On top of that, I made a class that is similar to PWM; it toggles the M5451 lines rapidly so the LEDs seem to change brightness.
Here is a video. To make it come out, I have a halogen desk lamp shining directly on the board from about a foot away. About halfway through I turned off the light so you could see the LED brightness in normal room light! Check out the video here:
Here's the code:
/*
* M5451 LED driver chip
* Author: G. Andrew Stone
* Public Domain
*/
int myClockPin = 2; // Arduino pin that goes to M5451 clock
int mySerDataPin = 3; // Arduino pin that goes to M5451 data
void setup() // run once, when the sketch starts
{
}
#define M5451_NUMOUTS 35
#define M5451_CLK 0
class M5451
{
public:
byte clockPin;
byte brightPin;
byte serDataPin;
M5451(byte clockPin,byte serDataPin,byte brightPin);
void set(unsigned long int a, byte b=0);
void setBrightness(byte b);
private:
void mydelay(int clk);
};
void M5451::setBrightness(byte b)
{
if (brightPin < 0xff)
analogWrite(brightPin,b);
}
#define MaxBrightness 4096 //256
class FlickerBrightness:public M5451
{
public:
FlickerBrightness(byte clkPin, byte dataPin, byte brightnessPin);
void shift(int amt=1)
{
offset+=amt;
if (offset>=M5451_NUMOUTS) offset -=M5451_NUMOUTS;
else if (offset< 0) offset +=M5451_NUMOUTS;
}
void loop(void);
int brightness[M5451_NUMOUTS];
int bresenham[M5451_NUMOUTS];
int iteration;
int offset;
};
FlickerBrightness::FlickerBrightness(byte clkPin, byte dataPin,byte brightnessPin):M5451(clkPin,dataPin,brightnessPin)
{
for (int i=0;i < M5451_NUMOUTS;i++)
{
brightness[i] = 0;
bresenham[i] = 0;
}
iteration = 0;
offset = 0;
}
void FlickerBrightness::loop(void)
{
int i;
byte pos;
unsigned long int a=0;
byte b=0;
boolean lvl=false;
for (i=0,pos=offset;i < M5451_NUMOUTS;i++,pos++)
{
if (pos>=M5451_NUMOUTS) pos=0;
bresenham[i] += brightness[pos];
if (bresenham[i]>=MaxBrightness)
{
bresenham[i] -= MaxBrightness;
lvl = true;
}
else lvl = false;
if (i<32) a = (a<<1)|lvl;
else b = (b<<1)|lvl;
}
iteration++;
if (iteration > MaxBrightness) iteration = 0;
set(a,b);
}
M5451::M5451(byte clkPin, byte dataPin, byte brightnessPin)
{
int i;
clockPin = clkPin;
serDataPin = dataPin;
brightPin = brightnessPin;
pinMode(clkPin, OUTPUT); // sets the digital pin as output
pinMode(serDataPin, OUTPUT); // sets the digital pin as output
pinMode(brightPin,OUTPUT);
// Clear out the device so we can clock in items
digitalWrite(serDataPin,LOW);
for (i=0;i< M5451_NUMOUTS+2;i++)
{
mydelay(M5451_CLK);
digitalWrite(clockPin,HIGH);
mydelay(M5451_CLK);
digitalWrite(clockPin,LOW);
}
}
void M5451::mydelay(int clk)
{
int i;
for (i=0;i< clk;i++);
//delay(clk);
}
void M5451::set(unsigned long int a, byte b)
{
int i;
// Write the initial "start" signal
digitalWrite(clockPin,LOW);
digitalWrite(serDataPin,LOW);
mydelay(M5451_CLK);
digitalWrite(clockPin,HIGH);
mydelay(M5451_CLK);
digitalWrite(clockPin,LOW);
mydelay(M5451_CLK/2);
digitalWrite(serDataPin,HIGH);
mydelay(M5451_CLK/2);
digitalWrite(clockPin,HIGH);
mydelay(M5451_CLK);
digitalWrite(clockPin,LOW);
// Write the bits
for (i=0;i< M5451_NUMOUTS;i++)
{
int serDataVal;
if (i<32) serdataval =" (a&1);">>=1;}
else { serDataVal = (b&1); b>>=1;}
mydelay(M5451_CLK/2);
digitalWrite(serDataPin,serDataVal);
mydelay(M5451_CLK/2);
digitalWrite(clockPin,HIGH);
mydelay(M5451_CLK);
digitalWrite(clockPin,LOW);
}
}
void loop() // run over and over again
{
unsigned long int j;
int i;
FlickerBrightness leds(myClockPin,mySerDataPin,9);
leds.setBrightness(255);
for (i=3;i>=0;i--)
{
for (j=0;j<35;j++)
{
leds.set(1L<< j,(j>=32) ? 1L<<(j-32):0);
delay(10*i);
}
}
// Proportional fading
if (1) for (j=0;j<200;j++)
{
for (i=0;i< M5451_NUMOUTS;i++)
{
int k = 1<<(j%13);
if ((i&3)<2)
{
if (leds.brightness[i] < 35) leds.brightness[i] = 35;
else leds.brightness[i] += leds.brightness[i]>>2;
}
else
{
if (leds.brightness[i] < 35) leds.brightness[i] = MaxBrightness;
else leds.brightness[i] -= leds.brightness[i]>>2;
}
}
for (i=0;i<100;i++) leds.loop();
}
leds.set(0xffffffff,0xff); /* ALL ON */
delay(250);
leds.set(0,0); /* ALL OFF */
delay(250);
// Linear per-LED brightness method
if (1) for (j=0;j<4096;j++)
{
for (i=0;i< M5451_NUMOUTS;i++)
{
int k = j*10;
if (i&1)
{
leds.brightness[i] = abs((k&(MaxBrightness*2-1))-MaxBrightness);
}
else
leds.brightness[i] = MaxBrightness - abs((k&(MaxBrightness*2-1))-MaxBrightness);
}
for (i=0;i<10;i++) leds.loop();
}
// ALL FADE using M5451 Brightness feature
leds.set(0xffffffff,0xff); /* ALL ON */
for (j=1;j<5;j++)
{
for (i=0;i<256;i++)
{
leds.setBrightness(i&255);
delay(j);
}
for (i=255;i>=0;i--)
{
leds.setBrightness(i&255);
delay(j);
}
}
leds.setBrightness(255);
leds.set(0xffffffff,0xff); /* ALL ON */
delay(250);
leds.set(0,0); /* ALL OFF */
delay(250);
// MARQUEE
for (i=0;i< M5451_NUMOUTS;i++) // Clear all LEDs to black
{
leds.brightness[i]=0;
}
// Turn on a couple to make a "comet" with dimming tail
leds.brightness[0] = MaxBrightness-1;
leds.brightness[1] = MaxBrightness/2;
leds.brightness[2] = MaxBrightness/4;
leds.brightness[3] = MaxBrightness/8;
leds.brightness[4] = MaxBrightness/16;
leds.brightness[5] = MaxBrightness/64;
leds.brightness[6] = MaxBrightness/100;
for (j=0;j<100;j++)
{
leds.shift(1);
for (i=0;i<150;i++) leds.loop();
}
for (j=0;j<100;j++)
{
leds.shift(-1);
for (i=0;i<100;i++) leds.loop();
}
if (1)
{
leds.set(0xffffffff,0x7);
delay(1000);
//leds.set(0xf0f0f0f0,0x7);
//delay(10000);
//leds.set(0x11111111,0x1);
//delay(10000);
}
}
Subscribe to:
Posts (Atom)