Charlieplexing led's on microcontroller/ Refresh rate

35 posts / 0 new
Author
Message

1. i have a micrcontroller whose all pins are used only 7 left & have to use 35 led's on it. SO i htought of using Charlieplexing.

2. Attached is the circuit. Its using 35 leds on 7 pins (X1-X7). Charlieplexing allows n(n-1), so 7*6=42 leds. Since I am using only 35 leds so haven't connected rest of them.  Is it ok?

3. I have reading on wikipedia for refersh rate. It says tyical rate for 1 led is 50Hz, so for 35 leds it would be 35*50=1750hz i.e around 571us.
So does that mean I have to make a timer interrupt of around 571us. On each interrupt one led will be brought to high or low depending on its state.
So consider if at any point all leds need to be high then in that one led seuence would be:
571us on & 19414us off  (517*34=19414)
So this will cycle 50 times in a second.

4. Is there any limit to maximum leds that can be used in Charlieplexing.
Since as led grows, on time decreases (and so off time), but I think there would be minimum on time required for led to glow?
So lets suppose I am using 100 pins (this is extreme example just for theory) & at some point all leds need to be on
Then no of led's = 9900
Refresh rate = 9900*50 = 495000Hz
That for single led: on time-2us & off time 20ms. Is 2us pulse enough to turn on led?

Attachment(s):

Last Edited: Fri. Jul 22, 2016 - 06:40 AM

Of course there is a limit to the number of LEDs whether you multiplex or charlieplex.

If an LED is bright enough with an average 1mA then with a 10% duty cycle you would have 10mA for 10% of the time.

Since an AVR pin has a maximum current limit you can see that you can only multiplex about 40 LEDs @ 2.5% duty cycle (1mA average).
And you have to check your LED datasheet to see if it allows 40mA peak current.

Quite honestly your wiring is going to be a nightmare with 35 LEDs. Why not just use a bigger AVR e.g. TQFP-64 ?

Multiplexing 7-segment LEDs is a common practice. Anything more than 8 digits is impractical due to currents and brightness. And you need a frequent ISR(). However the timer interrupt code is very short and simple.

David.

Last Edited: Fri. Jul 22, 2016 - 07:05 AM

I don't know your LED's but I would guess that your resistor need to be way smaller, (perhaps 0 ohm).

Try with just one LED on in 1/42 of the time to see if it's good enough.

Last Edited: Fri. Jul 22, 2016 - 07:38 AM

As sparrow2 has said,   your 1k0 is going to limit the peak current to about 3mA if you have VCC=5V and Vf=2V for the LED.

By the time you have reduced the duty cycle to 3% this gives you an average of 0.09mA.   Probably only visible to owls.

I certainly do NOT recommend 0R for your series resistors.   This will burn out your LED and AVR if the multiplexing ever "stops".   Especially if your VCC=5V.

If you are running your AVR at VCC=3.3V the resistor values need to be recalculated.

David.

3. I have reading on wikipedia for refersh rate. It says tyical rate for 1 led is 50Hz, so for 35 leds it would be 35*50=1750hz i.e around 571us.

You don't need to limit yourself to driving one LED at a time.  You can drive N-1 at a time, where N is the number of lines you have (or in your case, since you only need 35 instead of 42, you can drive N-2 at a time).  In effect, you only need to drive at N * refresh rate = 7 * 50 = 350 Hz, not 1,750 Hz.

Driving more than one LED at once means that each I/O pin will sink source more current when it's its turn to drive.  However, so long as you stay within the limit of the pin's maximum current, there won't be a problem.  The average current will stay the same, so the apparent brightness will be the same.  In your case, to keep sink current at 20 mA, you can select resistors to drive each LED at 4 mA.  The average current for each will then be 4/7 = 0.57 mA.

If you're running at 3.3V, the pin drive strength will limit this approach.

However, you can drive at higher current by using transistors.  There are a few ways to do that, but this should give you the idea:

http://tomscircuits.blogspot.ca/2009/04/musings-on-charliplexing.html

However I'd say that 70 Hz is a more suitable minimum refresh rate for LEDs.  Personally I like higher still, at least 100 Hz.

 "Experience is what enables you to recognise a mistake the second time you make it." "Good judgement comes from experience.  Experience comes from bad judgement." "Wisdom is always wont to arrive late, and to be a little approximate on first possession." "When you hear hoofbeats, think horses, not unicorns." "Fast.  Cheap.  Good.  Pick two." "We see a lot of arses on handlebars around here." - [J Ekdahl]

Last Edited: Sat. Jul 23, 2016 - 11:52 PM

joeymorin wrote:
Personally I like higher still, at least 100 Hz.
Same here, much below this and you can detect flicker if the display is moving relative to the viewer (as when turning your eyes to glance at the display).

David

I will like to see the calculation of how drive IO's to only turn those LED's on (more than one) , an not some of the other ones as well,

I will like to see the calculation of how drive IO's to only turn those LED's on (more than one) , an not some of the other ones as well,

Huh?  You mean how to drive more than one LED at a time?  Simple.  Pick an I/O line (call this the 'row').  Drive it low high.  For each of the remaining I/O lines (call them 'columns'), to energise its LED, drive that I/O line high low.  To leave its LED dark, tristate it.  Alternately, you can drive the chosen 'row' high low, and drive the remaining energised 'columns' low high, but for devices where the internal pullup is enabled with DDRxn=0/PORTxn=1, you'd need to disable the pullups with PUD.

If you're asking how to calculate resistor values, simply choose the total drive current you want for a given row.  Imagine a 3-line Charlieplex arrangement.  Each I/O line will be sinking sourcing current for at most 2 LEDs at once.  With a 20 mA total sink source current, you want the current through each LED to be no more than 20/2 = 10 mA.  With VCC = 5V and VF = 2V, that demands a resistor of (5 - 2) / 0.010 = 300R.  Mind you, VOL VOH will ride high low as IOL IOH increases, so each LED's IF will vary slightly depending on how many LEDs are being driven in a column, but not very noticeably.  If you're concerned about perfectly uniform LED brightness, or want a higher average current, use a transistor driver on each 'row'.

 "Experience is what enables you to recognise a mistake the second time you make it." "Good judgement comes from experience.  Experience comes from bad judgement." "Wisdom is always wont to arrive late, and to be a little approximate on first possession." "When you hear hoofbeats, think horses, not unicorns." "Fast.  Cheap.  Good.  Pick two." "We see a lot of arses on handlebars around here." - [J Ekdahl]

Last Edited: Sat. Jul 23, 2016 - 11:56 PM

Sorry but it's the mapping between each LED and "row" format I have a hard time picturing,

with only one led at the time it's easy you have a state for each LED, and an IO setting for ON and one for OFF.(and one bit that is set or reset for the LED status).

But I guess I have to use pen and paper, I have only used 3 IO's to make 6 LED's and there your code don't help, but perhaps when you start to make bigger "matrix's" it do.

david.prentice wrote:
Why not just use a bigger AVR

Or an external IO expander - devices specifically for driving LEDs are available ...

Top Tips:

1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...

Charlieplexing is pretty easy, though you need to remember you're turning each led on by setting one pin high and another low. I made a table in flash where I could look up which pin to set high and which to set low for each led. Remember, each led is on for a brief period. I just made an ISR running on a timer that cycled through the leds deciding to turn them on or not. A volatile uint variable had a bit for each. To turn leds on or off, the main program just set or cleared bits in the variable and left the ISR to do the work.

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut.

I have only used 3 IO's to make 6 LED's and there your code don't help

Sure it does.  With one-at-a-time drive, the duty cycle of each LED is 1/(N2-N) = 1/(9-3) = 1/6.  With row-at-a-time drive, the duty cycle of each LED is 1/N = 1/3.

 "Experience is what enables you to recognise a mistake the second time you make it." "Good judgement comes from experience.  Experience comes from bad judgement." "Wisdom is always wont to arrive late, and to be a little approximate on first possession." "When you hear hoofbeats, think horses, not unicorns." "Fast.  Cheap.  Good.  Pick two." "We see a lot of arses on handlebars around here." - [J Ekdahl]

Last Edited: Sat. Jul 23, 2016 - 04:02 PM

joeymorin wrote:
With row-at-a-time drive
Joey, are you thinking row/column multiplexing (12-pins for 36 LEDs) or the OP's charlieplexing (7-pins for up to 42 LEDs)?

David

Joey, are you thinking row/column multiplexing (12-pins for 36 LEDs) or the OP's charlieplexing (7-pins for up to 42 LEDs)?

Charlieplexing.  Each pin behaves both as a row and as a column, depending on which part of the scan cycle you're in.  More than one functionally equivalent topology is possible, but let's use the convention that 'row' is anode, and 'column' is cathode.  Each scan cycle has N phases, where N = number of I/O lines, and there are therefore N2 - N LEDs.  For each phase, the associated row line (anode) is driven high, while all of the other lines, which could be cathodes, are driven low.  The DDRx register is written such that the LEDs which are to be lit in this phase have DDRxn = 1, while those which are to remain unlit in this phase have DDRxn = 0.  The DDRxn bit corresponding to the row (anode) is always 1.  You just need to maintain a row (anode) mask, which the Charlieplex engine (usually in an ISR) walks phase-by-phase, and an array of column (cathode) masks corresponding to the DDRx value for that phase.  The column masks are set by user code, and read by the engine during each phase.

The OP's schematic makes a common mistake of having 1 resistor for each LED.  In fact, only one resistor per I/O line is needed.  So 7 resistors, not 42.

 "Experience is what enables you to recognise a mistake the second time you make it." "Good judgement comes from experience.  Experience comes from bad judgement." "Wisdom is always wont to arrive late, and to be a little approximate on first possession." "When you hear hoofbeats, think horses, not unicorns." "Fast.  Cheap.  Good.  Pick two." "We see a lot of arses on handlebars around here." - [J Ekdahl]

Last Edited: Sat. Jul 23, 2016 - 04:03 PM

something don't match, 3 IO with 6 LED will in your terms be :

Row 1 LOW

ROW1 HIGH

Row 2 LOW

ROW2 HIGH

Row 3 LOW

ROW3 HIGH

How do you get that to 1/3 of the time on ?

and :The OP's schematic makes a common mistake of having 1 resistor for each LED.  In fact, only one resistor per I/O line is needed.  So 7 resistors, not 42.

If you want more than one LED on at the time you will need too have it that way!? (or you need a time factor for each different current draw on each IO line).

Last Edited: Fri. Jul 22, 2016 - 10:55 PM

Say three lines:
PB0
PB1
PB2

So six LEDS:

LED A: PB0 -|>|- PB1
LED B: PB0 -|>|- PB2

LED C: PB1 -|>|- PB0
LED D: PB1 -|>|- PB2

LED E: PB2 -|>|- PB0
LED F: PB2 -|>|- PB1

When PB0 is the anode:
PB1 can be the cathode to LED A
PB2 can be the cathode to LED B

LED A: PB0 -|>|- PB1
LED B: PB0 -|>|- PB2
LED C: PB1 -|>|- PB0

LED D: PB1 -|>|- PB2
LED E: PB2 -|>|- PB0
LED F: PB2 -|>|- PB1

When PB1 is the anode:
PB0 can be the cathode to LED C
PB2 can be the cathode to LED D

LED A: PB0 -|>|- PB1
LED B: PB0 -|>|- PB2

LED C: PB1 -|>|- PB0
LED D: PB1 -|>|- PB2

LED E: PB2 -|>|- PB0
LED F: PB2 -|>|- PB1

When PB2 is the anode:
PB0 can be the cathode to LED E
PB1 can be the cathode to LED F

LED A: PB0 -|>|- PB1
LED B: PB0 -|>|- PB2

LED C: PB1 -|>|- PB0
LED D: PB1 -|>|- PB2
LED E: PB2 -|>|- PB0
LED F: PB2 -|>|- PB1

Now let's light LEDs D, E, and F, while LED A, B, and C are off.

Here's the colour legend used in the above tables, and the below code fragments:

LED A

LED B

LED C

LED D

LED E

LED F

In addition, the uncoloured but underlined bit in each DDR assignment represents the phase's anode.

// Phase 1: PB0 as anode, to drive LEDs A and B

//   With: A on PB1 off

//         B on PB2 off

// Blanking
PORTB = 0b000;
// Configure cathodes

DDRB  = 0b001;

// Drive anode

PORTB = 0b001;

// Phase 2: PB1 as anode, to drive LEDs C and D

//   With: C on PB0 off

//         D on PB2 lit

// Blanking
PORTB = 0b000;
// Configure cathodes

DDRB  = 0b110;

// Drive anode

PORTB = 0b010;

// Phase 3: PB2 as anode, to drive LEDs E and F

//   With: E on PB0 lit

//         F on PB1 lit

// Blanking
PORTB = 0b000;
// Configure cathodes

DDRB  = 0b111;

// Drive anode

PORTB = 0b100;

Of course this fixes the states of the 6 LEDs.  A proper system would use volatile masks shared between the Charlieplex engine's ISR and the main loop.

sparrow2 wrote:

If you want more than one LED on at the time you will need too have it that way!?

Nope.

Note that the I/O lines are directly connected to the LED anodes, but their cathodes pass through a resistor before connecting to another I/O line:

EDIT:  changed naming, colour scheme, and imagery to be more consistent, and a bit easier to follow.

 "Experience is what enables you to recognise a mistake the second time you make it." "Good judgement comes from experience.  Experience comes from bad judgement." "Wisdom is always wont to arrive late, and to be a little approximate on first possession." "When you hear hoofbeats, think horses, not unicorns." "Fast.  Cheap.  Good.  Pick two." "We see a lot of arses on handlebars around here." - [J Ekdahl]

Last Edited: Sun. Jul 24, 2016 - 02:42 AM

joeymorin wrote:
Note that the I/O lines are directly connected to the LED anodes, but their cathodes pass through a resistor before connecting to another I/O line

I think you could, instead, connect both anodes & cathodes to the one resistor per IO pin.

That would mean that each LED always has two resistors in its path - one to anode, and one to cathode - so you'd halve the calculated value for a single resistor.

Dunno if this makes things any simpler - other than the scematic being a bit simpler to draw?

Top Tips:

1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...

Hi Andy,

The problem then would be that the LED current will change depending on the number of active LEDs.

David

But, surely, that happens anyhow?

In the cases where you want to simultaneously light 2 LEDs that share a resistor, at least ...

Top Tips:

1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...

Thanks Joey,  that is an excellent animation / explanation.

All the same,  my head starts hurting when it comes to programming.  Especially for 42 LEDs.

1.  Charlieplex is (N-1) * N e.g. 2 Anodes * 3 GPIO pins

2.  ISR() would have N phases (states).

3.  LED pins  would be organised in N tables.

4.  LED DDRs  would be organised in N tables.

5.  Each state would enable one GPIO pin high. (N-1) GPIO pins low (i.e. from LED tables)

6.  Each LED would be on/off according to the DDR tables.

On this basis,  the duty-cycle is 1/N.   The active anode pin would be sourcing (N-1) LEDs (if they are all on).

Our particular LEDs require 1mA average current.  Your 2x3 example would have 33% duty-cycle i.e. 3mA * 2 = 6mA peak.

The OP's 6x7 example would have 14% duty-cycle i.e. 7mA * 6 = 42mA peak.

The N cathode resistors would be calculated according to the duty-cycle.  e.g. 1k0 for 33% (3mA).  or 430R for 14% (7mA)

The ISR() repetition rate would be 50Hz * N.   e.g. 150Hz for 2x3 or 350Hz for 6x7.

High brightness (1mA) LEDs look quite possible.

The ISR() looks simple.

Turning an LED on or off looks quite easy.

I suspect that 100Hz frame rate might look better.   Since the ISR() is so simple,  cpu cycles are not a problem.

My head still hurts when it comes to wiring everything up.

David.

Edit.  Following Joey's advice,  I have changed the terminology to (N-1) x N from my original N * (N+1)

Edit.  Yes,  my 42mA peak current is high.    Absolute Maximum current per IO pin is 40mA.   20mA might be a better design choice.

Last Edited: Sat. Jul 23, 2016 - 02:20 PM

If you look at Joey's excellent animation you'll see the source (orange) has no resistor and each sink (blue) has it's own resistor so no shared current flow through a resistor.

David

But the animation doesn't show all cases - there are cases in that schematic where turning on 2 LEDs would involve a shared resistor

Top Tips:

1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...

I don't think that I have said otherwise. 7 phases with 6 LEDs selected per phase. 7 cathode resistors 430R.
.
I have no desire to wire up 42 LEDs. I might try a 3x4 with 12 LEDs and 4 cathode resistors.
I am a bit of a wimp. It seems easier to buy a TFT or OLED or even an 8x8 LED matrix (with MAX7219).
.
(Bald) David.

Yes, the animation looks absolutely fine. 3 phases with 2 LEDs selected per phase. (you don't select the bad combinations)
.
Meanwhile, I suppose that I should run my SPI Slave tests to see if I can get my own back on Joey.
.
David.

david.prentice wrote:
you don't select the bad combinations

I wrote:
there are cases in that schematic where turning on 2 LEDs would involve a shared resistor

So those would be the "bad" combinations?

I guess you could still use them - and adjust the timing accordingly ... ?

It seems easier to buy a TFT or OLED or even an 8x8 LED matrix

Indeed!

Or an IO expander or proper LED controller

Top Tips:

1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...

Why would you want to use them?
2x3 = 6 LEDs
3x4 = 12
4x5 = 20
5x6 = 30
6x7 = 42
.
The whole point is to have complete control over a large number of LEDs with few GPIO pins. 6x7 seems to be limit i.e. 42mA peak current unless you can get super bright low current LEDs.

awneil wrote:
there are cases in that schematic where turning on 2 LEDs would involve a shared resistor
Not when you have only one signal at a time driving the anodes high, as Joey is doing.

david.prentice wrote:
The OP's 6x7 example would have 14% duty-cycle i.e. 7mA * 6 = 42mA peak.
That is a lot of current from a 20mA I/O...even at 14% duty.

David

awneil wrote:

But the animation doesn't show all cases - there are cases in that schematic where turning on 2 LEDs would involve a shared resistor

While it's true that each resistor is physically shared by N-1 LEDs, there is never more than one LED being fed by each resistor because no phase of the scan cycle ever has more than one anode.

david.prentice wrote:

N * (N+1)

Your assessment in #20 is more or less correct.  I would point out only a couple of things.

First is that N usually refers to the number of I/O lines, not the number of cathodes in a phase.  So the number of LEDs becomes N * (N - 1) or N2 - N, and the duty cycle is 1/N.

Secondly is the calculated value for the resistors.  Your suggestion of a 430R resistor will result (as you have shown) in about 7 mA per LED, with each anode sourcing a maximum of 42 mA per phase.  That's a bit higher than the absolute maximum for an AVR I/O pin.  Now, the OP will only be using 35 of the 42 possible nodes, so only 5 LEDs per phase, or 35 mA per phase.

However I would sooner stay under 20 mA for longevity of the device, and to limit the effects of dwindling I/O pin driver strength at elevated currents.  Your choice of a 1K resistor value would result in 3 mA per LED (as you've mentioned), and 18 mA max source current per anode driver line.  For the OP, it would be 15 mA max.  With a standard 750 ohm resistor, LED current rises to 4 mA, with the OP's 5-per anode driver max current equalling a comfortable 20 mA.  Average current is then 4/7 = 0.57 mA.  Not especially bright, but the use of high brightness LEDs will still yield acceptable intensity in most circumstances.

If higher currents are desired, we can add transistors for higher drive strength.

This technique can be used to great effect when driving 7-segment displays.  Whereas a conventional matrix arrangement would require a number of I/O lines equal to 8 (7 + DP) times the number of digits, or 17 lines to control 9 digits, Charlieplexing could control up to 9 digits with 9 lines.

Here's a schematic I dug up with Google showing 6 of the digits (omitting any of the required resistors), but the concept is the same:

My head still hurts when it comes to wiring everything up.

Yes, it can get a bit hairy.  I've wired up an N=5 (20 LEDs, aranged in a 4x5 grid) demo board using an ATtiny85, powered from a single CR2032.  As a consequence, I was able to omit the resistors entirely, since the battery voltage drop, combined with the associated reduction in I/O pin drive strength, was enough to keep the t85 and all the LEDs happy.  The consequence, of course, is that LED brightness varied depending upon how many LEDs were lit per phase, and overall.  No matter, this was just a quick demo board on a 3x7cm prototyping board.  Just a bit of fun.  Managed to get scrolling text, battery voltage display, temperature display, and 8-bit software PWM across the array.

Incidentally, with 8-bit PWM and N=5, the ISR rate is 5*256 = 1280 times the refresh rate.  With a refresh rate of 100 Hz, the ISR rate is 128 kHz.  At 8 Mhz, that's about 62 cpu cycles per ISR.  The worst case execution time for the ISR turned out to be 59 cycles, with an average of 54 cycles.  That was in C, using the Arduino IDE and GCC 4.3.4 and AVR Libc 1.6.7.  Written in assembler (without twisting myself into knots in the name of speed), the worst case is 45 cycles, with an average of about 40 cycles.  So even with the heavy cpu load imposed by 100 Hz 8-bit PWM, an 8 MHz t85 still has 2.8 MIPS available (out of 8 MIPS total), or about 35% cpu capacity for other tasks.

Incidentally, with 8-bit PWM and N=5, the ISR rate is 5*256 = 1280 times the refresh rate.  With a refresh rate of 70 Hz, the ISR rate is 89.6 kHz.  At 8 Mhz, that's 89 cpu cycles per ISR.  The worst case execution time for the ISR turned out to be 87 cycles, with an average of 81 cycles.  That was in C, using the Arduino IDE and GCC 4.3.4 and AVR Libc 1.6.7.  Written in assembler I expect I could get that down near 60 cycles.  So even written in C, even with the heavy cpu load imposed by 70 Hz 8-bit PWM, an 8 MHz t85 still has 0.7 MIPS available (out of 8 MIPS total), or about 9% cpu capacity for other tasks.  A lower refresh rate and/or a reduced PWM resolution (I expect 4 would be plenty for most uses), and/or a higher clock speed would increase that margin dramatically.  For example, 100 Hz, 6-bit PWM on a 16 MHz t85 would still leave %84 cpu available for other tasks.

Is this the best approach for most applications?  Arguably not.  But it >>is<< fun, and at the very least an interesting exercise ;-)

EDIT: took me a while to compose this post, others already addressed most of its content :)

ANOTHER:  It occurs to me that the EEs among us would raise a red flag concerning my casual use of the words 'anode' and 'cathode'.  In truth, my use is technically incorrect in some places.  To clarify, when I refer to anode such as in:

because no phase of the scan cycle ever has more than one anode.

... I intend to mean:

because no phase of the scan cycle ever has more than one line driving the anodes of a group of LEDs.

Technically, that line would be considered the cathode, since it is a source of positive charge.

ANOTHER-STILL: corrected cycle math for PWM (had mistakenly quoted the non-PWM case earlier)

 "Experience is what enables you to recognise a mistake the second time you make it." "Good judgement comes from experience.  Experience comes from bad judgement." "Wisdom is always wont to arrive late, and to be a little approximate on first possession." "When you hear hoofbeats, think horses, not unicorns." "Fast.  Cheap.  Good.  Pick two." "We see a lot of arses on handlebars around here." - [J Ekdahl]

Last Edited: Sat. Jul 23, 2016 - 07:45 PM

Thank you for the nice illustration, I stay corrected, and have learned something today :)

Thank you for the nice illustration, I stay corrected, and have learned something today :)

Well it was about time I returned the favour :)

 "Experience is what enables you to recognise a mistake the second time you make it." "Good judgement comes from experience.  Experience comes from bad judgement." "Wisdom is always wont to arrive late, and to be a little approximate on first possession." "When you hear hoofbeats, think horses, not unicorns." "Fast.  Cheap.  Good.  Pick two." "We see a lot of arses on handlebars around here." - [J Ekdahl]

Well,  I summoned up courage to get out the soldering iron.

Only with a 3x4 arrangement using some rectangular red wire-ended LEDs.   I used 390R series resistors in the cathode leg and a 5V Uno clone.   i.e. 3V / 390R peak * 25% duty-cycle = 1.9mA average.    Any lower current would have been too dim.    Modern SMD leds would be brighter.

```uint8_t phtable[4] = { 0x10, 0x20, 0x40, 0x80 };  //port values
uint8_t ontable[4] = { 0x10, 0x20, 0x40, 0x80 };  //ddr values

void set_LED(uint8_t n, bool on)
{
uint8_t phase = n / 3;
n %= 3;
if (n >= phase) n++;  //anode is on "phase" bit.
if (on) ontable[phase] |= (0x10 << n);
else ontable[phase] &= ~(0x10 << n);
Serial.print("table[" + String(phase) + "] = 0x");
Serial.println(ontable[phase], HEX);
}

ISR(TIMER2_COMPA_vect)
{
static uint8_t phase;
if (++phase >= 4) phase = 0;
PORTD &= 0x0F;   //all off
DDRD &= 0x0F;    //all off
PORTD |= phtable[phase]; //enable anodes
DDRD |= ontable[phase];  //display ON leds
}

void setup()
{
Serial.begin(9600);
TCCR2A = (2 << WGM20);
TCCR2B = (6 << CS20); //div256
OCR2A = F_CPU / 256 / 250 - 1; //250Hz (4ms = 16ms Frame)
TIMSK2 |= (1 << OCIE2A);
//sei();              //interrupts always run on Arduino
}

void loop()
{
uint8_t i;
for (i = 0; i < 12; i++) {
set_LED(i, true);
delay(100);
}
for (i = 0; i < 12; i++) {
set_LED(i, false);
delay(100);
}
}```

Extending to 6x7 is easy.  Just increase the two tables.

The ISR() is pretty simple.   You could avoid the DIV and MOD by a further table.

I would never consider hand-soldering a 6x7 or even the 5x7 proposed by the OP.    It would be fine with a pcb.

David.

I have dropped idea of using Charlieplexing, now I am planning to use MCP23S17.

I have a SPI port pending. so I will use two of these expanders for 32 led's

The Charlieplexing software is pretty simple.  You just increase the number of phases for the larger numbers.

Once you have to start adding extra driver chips / transistors it takes the shine off the technique.

Seriously,  if you are going to add external chips,  choose MAX7219 or similar.    These look after the multiplexing and average current.

David.

Used Max7301 for 28 led's & 7 led using direct MCU pins.

MAX7301 is 28 io port exapnder using SPI.