From Saurabh Gupta and Dhananjay V Gadre, Netaji Subhas Institute of Technology, Dwarka, New Delhi, India
This method allows 2×N×(N1) LEDs using only N I/O lines and a few additional discrete components. The technique exploits the fact that each I/O line has three states: one, zero, and high impedance. Thus, with two I/O lines, states 00, 01, 10, and 11 of eight possible states control the LEDs.
Here is an example with two I/O lines controlling four LEDs. P1 and P2 are the inputs. The circuit requires that the LED turn-on voltage should be slightly more than VCC/2. For red LEDs with a turn-on voltage of approximately 1.8V, a suitable supply voltage is 2.4V. For blue or white LEDs, you can use a 5V supply voltage. For N I/O lines, this technique requires N1 transistor pairs.
Here are the states of P1 and P2 as they relate to which LED lights up:
| P1 | P2 | TP1 | LED |
| 0 | 0 | Vcc | D3 |
| 0 | 1 | Vcc | D2 |
| 1 | 0 | 0 | D1 |
| 1 | 1 | 0 | D4 |
| Z | Z | Vcc/2 | None |
A second example shows 24 white LEDs with 4 IO pins and 6 transistors controlled by an ATMEL AT Tiny 13 Microcontroller.
And here is the C source code for a bargraph display with the analog input comming into PB2 (pin 7, Ain).
/*Saurabh Gupta and Dhananjay V. Gadre*/
/*Tiny13 Processor*/
/* January 2008*/
#include<avr/io.h>
#include<avr/interrupt.h>
#include<avr/pgmspace.h>
typedef unsigned char u08;
void delay(u08 a)
{
u08 i,j;
for(i=0;i<a;i++)
{
for(j=0;j<250;j++)
{
asm("nop");
asm("nop");
asm("nop");
asm("nop");
}
}
}
const unsigned char portb_value[] PROGMEM = {
0b00000000
, 0b00011000
, 0b00000000
, 0b00010010
, 0b00000000
, 0b00010001
, 0b00010000
, 0b00001000
, 0b00010000
, 0b00000010
, 0b00010000
, 0b00000001
, 0b00000000
, 0b00001010
, 0b00000000
, 0b00001001
, 0b00001000
, 0b00000010
, 0b00001000
, 0b00000001
, 0b00000010
, 0b00000001
, 0b00000000
, 0b00000011
};
//port_value[0]-->for L0 (1st led)
//port_value[23]-->for L23 (24th led)
const u08 ddrb_value[] PROGMEM = {
0b00011000
, 0b00011000
, 0b00010010
, 0b00010010
, 0b00010001
, 0b00010001
, 0b00011000
, 0b00011000
, 0b00010010
, 0b00010010
, 0b00010001
, 0b00010001
, 0b00001010
, 0b00001010
, 0b00001001
, 0b00001001
, 0b00001010
, 0b00001010
, 0b00001001
, 0b00001001
, 0b00000011
, 0b00000011
, 0b00000011
, 0b00000011
};
u08 value=0,count=0;
ISR(TIM0_OVF_vect) //1200 Hz
{
//delay(100);
TCNT0=-32;
ADCSRA|=(1<<ADSC);
// if(value!=0)
count=(count<value)?(count+1):0;
// else
// count=0;
//count=(count<24)?count:0;
DDRB=pgm_read_byte(ddrb_value + count);
PORTB=pgm_read_byte(portb_value + count);
}
ISR(ADC_vect)
{
int temp;
u08 temp2;
temp2=ADCH;
temp=temp2*15;
temp=temp>>7;
value=temp;
value=(value<24)?value:24;
}
int main()
{
DDRB=0x00;
PORTB=0x00;
TCCR0A=0X00;
// TCCR0A - Timer/Counter Control Register A
// ---------------------------------------------------------
// |COM0A1|COM0A0|COM0B1|COM0B0| | | WGM01| WGM00|
// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
// ---------------------------------------------------------
TCCR0B=(1<<CS02); //9.6 MHz divided by 256
// TCCR0B - Timer/Counter Control Register B
// -------------------------------------------------
// |FOC0A|FOC0B| | |WGM02| CS02| CS01| CS00|
// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
// -------------------------------------------------
TIMSK0=(1<<TOIE0);
// TIMSK - Timer/Counter Interrupt Mask Register
// ---------------------------------------------------------
// | - | - | - | - |OCIE0A|OCIE0B|TOIE0 | - |
// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
// ---------------------------------------------------------
TCNT0=-32; //1200 Hz
ADMUX=(1<<REFS0)|(1<<ADLAR)|(1<<MUX0);
// ADMUX - ADC Multiplexer Selection Register
// -------------------------------------------------
// | - |REFS0|ADLAR| - | - | - | MUX1| MUX0|
// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
// -------------------------------------------------
ADCSRA=(1<<ADEN)|(1<<ADIE)|(1<<ADPS2);
// ADCSRA - ADC Control and Status Register A
// -------------------------------------------------
// |ADEN |ADSC |ADATE| ADIF| ADIE|ADPS2|ADPS1|ADPS0|
// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
// -------------------------------------------------
ADCSRB=0x00;
DIDR0 = (1<<ADC1D); //Disable Digital input buffer on PB5
// DIDR0 - Digital Input Disable Register 0
// -------------------------------------------------
// | | |ADC0D|ADC2D|ADC3D|ADC1D|AIN1D|AIN0D|
// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
// -------------------------------------------------
sei();
while(1);
return 0;
}