View previous topic :: View next topic |
Author |
Message |
test153
Joined: 09 Feb 2009 Posts: 28
|
Multiplexing LEDs |
Posted: Thu Mar 05, 2009 2:41 pm |
|
|
How do I multiplex LEDs? I have been trying to find a solution on my own for quite some time, since I haven't come up with a good one i turn to you.
From what I understand one would have to use some kind of timer and update every LED 50 times a second to avoid flickering. Below is the circuit I'm using. The PIC I'm using is 12f629.
|
|
|
Sydney
Joined: 13 Feb 2009 Posts: 71
|
|
Posted: Thu Mar 05, 2009 2:57 pm |
|
|
It looks like that would have to be done in 4 cycles, with 3 leds controllable in each cycle.
GP0 HIGH
GP1 FLOAT //D2 D6 D10 controllable
GP0 LOW
GP1 FLOAT //D1 D5 D9 controllable
GP0 FLOAT
GP1 HIGH //D4 D8 D12 controllable
GP0 FLOAT
GP1 LOW //D3 D7 D11 controllable |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
test153
Joined: 09 Feb 2009 Posts: 28
|
|
Posted: Thu Mar 05, 2009 3:31 pm |
|
|
Floating outputs is a nice approach I haven't tried, will indeed simplify the code. Thanks.
This appnote is a good one but it doesn't provide a programing example, appnote TB029 is a good one too. I have read everything I could find on that topic unfortunately I hit a dead end mostly due to my programing skills in PIC C.
Correct me if I'm wrong but I think I need a timer. So I need to use "setup_timer_0( )" right? and if so how do I set it up? |
|
|
Sydney
Joined: 13 Feb 2009 Posts: 71
|
|
Posted: Thu Mar 05, 2009 3:48 pm |
|
|
Start simple, just get it working with a delay_ms(5); between each cycle.
You could use an int16, with bits 0-11 representing your leds, then use bit_test() to see if the led should be on or off.
Here I have started you off:
Code: | int16 leds = 0b000000000000; // all leds off
while(1){
output_float(PIN_A5);
output_float(PIN_A4);
output_float(PIN_A2); // clear leds
output_high(PIN_A0);
output_float(PIN_A1); // cycle 1
if(bit_test(leds, 1)) // D2
output_low(PIN_A5);
if(bit_test(leds, 5)) // D6
output_low(PIN_A4);
if(bit_test(leds, 9)) // D10
output_low(PIN_A2); // test leds and switch on
delay_ms(5);
} |
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Mar 05, 2009 3:50 pm |
|
|
There are a lot of project examples to be found with Google
and they have source code:
Here's one:
http://www.micro-examples.com/public/microex-navig/doc/091-dymoclock
He has a function called dymoLight(), in which he writes to TRISB
and PortB to turn on the specified LED.
I don't know what the purpose of your project is. I don't know if this
example is completely applicable. |
|
|
test153
Joined: 09 Feb 2009 Posts: 28
|
|
Posted: Fri Mar 06, 2009 2:41 am |
|
|
Thanks for the code. I have one question. Wouldn't it be bad to return so much current through one pin, unless there is a special reason we use " output_float" instead of setting it as an input. Aslo if we drive three LEDs at once will they not light less bright?
My initial thought was to controll every LED one by one (lightining only on at a time), with approach is the best. As far as I know a single pin can only handle 25mA. |
|
|
Sydney
Joined: 13 Feb 2009 Posts: 71
|
|
Posted: Fri Mar 06, 2009 3:52 am |
|
|
test153 wrote: | Wouldn't it be bad to return so much current through one pin, unless there is a special reason we use " output_float" instead of setting it as an input. |
output_float(PIN_A0); // set PIN_A0 as input ;)
test153 wrote: | Aslo if we drive three LEDs at once will they not light less bright?
My initial thought was to controll every LED one by one (lightining only on at a time), with approach is the best. As far as I know a single pin can only handle 25mA. |
No they will appear brighter, because each led will be on 4x longer, since there are only 4 cycles vs. 12 if you turn each led on in turn.
Depends what leds you use, most will be way under 25ma, so gp5, 4, and 2 will be fine but gp0, and 1 may need transistors. You could use a resistor in gp5, 4, and 2 to limit the current(putting a resistor in gp0 or 1 will affect the brightness depending on how many of the 3 leds are on in each cycle).
You could also adjust the duty cycle to limit the current, ie change the delay_ms(5) to say 1ms, then put the rest of the delay after my clear leds section. Then the leds are ony on for 1ms every 20ms instead of 5ms.
Code: | int16 leds = 0b000000000000; // all leds off
while(1){
output_float(PIN_A5);
output_float(PIN_A4);
output_float(PIN_A2); // clear leds
delay_ms(4);
output_high(PIN_A0);
output_float(PIN_A1); // cycle 1
if(bit_test(leds, 1)) // D2
output_low(PIN_A5);
if(bit_test(leds, 5)) // D6
output_low(PIN_A4);
if(bit_test(leds, 9)) // D10
output_low(PIN_A2); // test leds and switch on
delay_ms(1);
} |
|
|
|
Sydney
Joined: 13 Feb 2009 Posts: 71
|
|
Posted: Fri Mar 06, 2009 4:09 am |
|
|
Here is a test program that should(might) work
Code: | int16 leds = 0b000000000001; // D1 on
while(1){
output_float(PIN_A5);
output_float(PIN_A4);
output_float(PIN_A2); // clear leds
output_high(PIN_A0);
output_float(PIN_A1); // cycle 1
if(bit_test(leds, 1)) // D2
output_low(PIN_A5);
if(bit_test(leds, 5)) // D6
output_low(PIN_A4);
if(bit_test(leds, 9)) // D10
output_low(PIN_A2); // test leds and switch on
delay_ms(5);
output_float(PIN_A5);
output_float(PIN_A4);
output_float(PIN_A2); // clear leds
output_low(PIN_A0);
output_float(PIN_A1); // cycle 2
if(bit_test(leds, 0)) // D1
output_high(PIN_A5);
if(bit_test(leds, 4)) // D5
output_high(PIN_A4);
if(bit_test(leds, 8)) // D9
output_high(PIN_A2); // test leds and switch on
delay_ms(5);
output_float(PIN_A5);
output_float(PIN_A4);
output_float(PIN_A2); // clear leds
output_float(PIN_A0);
output_high(PIN_A1); // cycle 3
if(bit_test(leds, 3)) // D4
output_low(PIN_A5);
if(bit_test(leds, 7)) // D8
output_low(PIN_A4);
if(bit_test(leds, 11)) // D12
output_low(PIN_A2); // test leds and switch on
delay_ms(5);
output_float(PIN_A5);
output_float(PIN_A4);
output_float(PIN_A2); // clear leds
output_float(PIN_A0);
output_low(PIN_A1); // cycle 4
if(bit_test(leds, 2)) // D3
output_high(PIN_A5);
if(bit_test(leds, 6)) // D7
output_high(PIN_A4);
if(bit_test(leds, 10)) // D11
output_high(PIN_A2); // test leds and switch on
delay_ms(5);
if(leds << 1 == 4096)
leds = 1; // D12 is on, go back to D1
else
leds <<= 1; // turn current led off, and next one on
} |
|
|
|
test153
Joined: 09 Feb 2009 Posts: 28
|
|
Posted: Fri Mar 06, 2009 4:25 am |
|
|
Thanks for the code. Will try it out when I get home.
The LEDs I'm using are blue ones with a VF of 3.8 under a 20mA load. Somehow I missread the datasheet and did indeed not a any resistors to GP0 and 1. The pins GP0 and 1 are the only ones I can add restistors to due to the PCB layout. I can't add a transitor either.
If I have three LEDs on I will fry the output... (20*3=60mA going back to GP0 or 1).
What is the difference between output_float() and input()? |
|
|
Sydney
Joined: 13 Feb 2009 Posts: 71
|
|
Posted: Fri Mar 06, 2009 5:55 am |
|
|
output_float() makes the pin high impedance, by making it an input, all it does is set the corresponding tris bit high. input() reads the pin, and if you aren't using fast_io will also set the corresponding tris bit high. |
|
|
Sydney
Joined: 13 Feb 2009 Posts: 71
|
|
Posted: Fri Mar 06, 2009 5:59 am |
|
|
If resistors aren't an option I would limit the duty cycle of the leds as I described, and you will prolly get away with it, either way the leds will appear as bright. |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
Re: Multiplexing LEDs |
Posted: Fri Mar 06, 2009 7:15 am |
|
|
This is a very unusual circuit. Let's look at what it takes to turn on D1. If we understand that, we understand all the other LEDs because the diagram is symmetric.
To turn on D1, you must set GP5 HIGH and GP0 LOW. Just knowing those two facts has some important implications. Note that D1 is in parallel with the series combination of D3-D8-D5. Those three diodes point in the correct direction to conduct current whenever D1 is conducting current. The only thing that keeps D3-D8-D5 from lighting up is that it takes 3 forward diode drops of voltage to get current flowing through that series, and we only have one diode drop (the forward voltage of D1). But this does mean that the junctions of D3-D8 and D8-D5 must assume the voltages of .67Vcc and .33Vcc respectively. But that means GP1 must be .67Vcc and GP4 must be .33Vcc. In fact, by analyzing the series D3-D12-D9 in a similar way, you can see that GP2 must also be at .33Vcc.
So just starting from the minimal requirement to turn on D1, we find that all the other PIC pins (GP1, GP4, GP2) must be floating. If any of those three are forced either high or low, then some other diode besides D1 will be turned on. Therefore we can conclude that the only way to multiplex these 12 diodes in the circuit shown is to select only one row and one column (one high, one low) and make every other PIC pin floating.
As for adding resistors, yes, do so. Adding resistors in series with GP0 and GP1 is sufficient, as long as you don't try to turn on more than one LED at a time. Otherwise you have no real control over LED current, and are relying on the unspecified current-limiting properties of the PIC outputs.
Edit: I just realized that it is not strictly true that any non-participating output will turn on some other LED. In the example cited above for turning on D1, you could set GP1 high without turning on any other LEDs. That is because doing so would remove voltage from across D3 and divide the remaining voltage across the series of two diodes: D8-D5 and D12-D9. This would force GP2 and GP4 both to assume .50Vcc. So they definitely must be left floating. And the forward voltage drop of D1 is still not enough to light up a series of two diodes, so D8-D5 and D12-D9 will not light up, even though they have a forward voltage across them. The voltage is just not high enough.
But this does bring up another issue. Whenever you leave non-participating pins floating and they assume an intermediate voltage, you have to consider what the CMOS input structure does. For Schmitt trigger inputs, like GP2, it is no problem. They are made for tolerating intermediate voltages. But for all the TTL inputs (every other pin in the 12F629), putting an intermediate voltage on an input can cause the input stage to be biased into its linear region and cause excessive supply current to flow. It is not a good idea. _________________ Robert Scott
Real-Time Specialties
Embedded Systems Consulting |
|
|
Sydney
Joined: 13 Feb 2009 Posts: 71
|
Re: Multiplexing LEDs |
Posted: Fri Mar 06, 2009 10:03 am |
|
|
RLScott wrote: | Therefore we can conclude that the only way to multiplex these 12 diodes in the circuit shown is to select only one row and one column (one high, one low) and make every other PIC pin floating. |
That statement is not true, as I expained. |
|
|
test153
Joined: 09 Feb 2009 Posts: 28
|
|
Posted: Fri Mar 06, 2009 11:24 am |
|
|
Would something like this work in practice? I have chosen to only light 1 LED at a time because of the risk of overloading an "output low" port. It works in the simulator and I think it will work on the hardware too. Unfortunately I can't test it right now and will have to wait until Monday.
It doesn't matter how many LEDs are lit it still takes 48ms (4*12) to cycle through all the LEDs - effectively keeping the brightness at a constant level. (at least that's how it's suppose to work).
Code: |
char command;
const long delay=4; // How long each LED should be on
//===============================
void portReset() {
output_float(PIN_A0);
output_float(PIN_A1);
output_float(PIN_A2);
output_float(PIN_A4);
output_float(PIN_A5);
}
void led1() {
output_low(PIN_A0);
output_high(PIN_A5);
}
void led2() {
output_low(PIN_A5);
output_high(PIN_A0);
}
void led3() {
output_low(PIN_A1);
output_high(PIN_A5);
}
void led4() {
output_low(PIN_A5);
output_high(PIN_A1);
}
//Next matrix line
void led5() {
output_low(PIN_A0);
output_high(PIN_A4);
}
void led6() {
output_low(PIN_A4);
output_high(PIN_A0);
}
void led7() {
output_low(PIN_A1);
output_high(PIN_A4);
}
void led8() {
output_low(PIN_A4);
output_high(PIN_A1);
}
//Next matrix line
void led9() {
output_low(PIN_A0);
output_high(PIN_A2);
}
void led10() {
output_low(PIN_A2);
output_high(PIN_A0);
}
void led11() {
output_low(PIN_A1);
output_high(PIN_A2);
}
void led12() {
output_low(PIN_A2);
output_high(PIN_A1);
}
void main() {
command = 60; // The ASCII code for 12 is 60, we use ASCII for easy connection to a UART later on.
while(1){
if (command>=49) { led1(); }
delay_ms(delay); // How long the LEDs should be on
portReset();
if (command>=50) { led2(); }
delay_ms(delay); // How long the LEDs should be on
portReset();
if (command>=51) { led3(); }
delay_ms(delay); // How long the LEDs should be on
portReset();
if (command>=52) { led4(); }
delay_ms(delay); // How long the LEDs should be on
portReset();
if (command>=53) { led5(); }
delay_ms(delay); // How long the LEDs should be on
portReset();
if (command>=54) { led6(); }
delay_ms(delay); // How long the LEDs should be on
portReset();
if (command>=55) { led7(); }
delay_ms(delay); // How long the LEDs should be on
portReset();
if (command>=56) { led8(); }
delay_ms(delay); // How long the LEDs should be on
portReset();
if (command>=57) { led9(); }
delay_ms(delay); // How long the LEDs should be on
portReset();
if (command>=58) { led10(); }
delay_ms(delay); // How long the LEDs should be on
portReset();
if (command>=59) { led11(); }
delay_ms(delay); // How long the LEDs should be on
portReset();
if (command>=60) { led12(); }
delay_ms(delay); // How long the LEDs should be on
portReset();
}
}
|
|
|
|
|