Red LEDs: Current vs. Voltage Sorting

Running ten random red LEDs (taken from the bag of 100 sent halfway around the planet) through the LED Curver Tracer produces this plot:

Red LEDs - 80 mA
Red LEDs – 80 mA

The two gray traces both come from LED 1 to verify that the process produces the same answer for the same LED. It does, pretty much.

Repeating that with the same LEDs in the same order, but stepping 10 mA up to 100 mA produces a similar plot:

Red LEDs - 100 mA
Red LEDs – 100 mA

The voltage quantization comes from the Arduino’s 5 mV ADC resolution (the readings are averaged, but there’s actually not much noise) and the current quantization comes from the step value in the measurement loop (5 mA in the first plot, 10 mA in the second). Seeing the LEDs line up mostly the same way at 80 mA in both graphs is comforting, as it suggests the measurement results aren’t completely random numbers.

Apply this bit of Bash-fu to the dataset file:

seq 1 11 > /tmp/seq.txt ; grep -E "^100" Red\ LEDs\ -\ 100\ mA.csv | cut -f 2,5 | paste /tmp/seq.txt - > "Red LED Vf at 100 mA.csv"

Produces a numbered listing of the LED current (in μA) and voltage (in mV) at a nominal 100 mA for each LED:

1	100415	2108
2	100415	2185
3	99957	2152
4	100415	2132
5	99957	2137
6	99957	2103
7	99957	2161
8	99957	2137
9	100415	2171
10	100415	2132
11	100415	2113

Putting three red LEDs in series could produce a total forward drop anywhere between 6.309 V (3*2.103) and 6.555 V (3*2.185), a difference of nigh onto a quarter volt, if you assume this group spans the entire range of voltages and the whole collection has many duplicate values and you’re remarkably unlucky while picking LEDs. For this particular set, however, summing three successive groups of three produces 6.445, 6.372, and 6.469 V, for a spread of just under 100 mV. That suggests it’s probably not worthwhile to select LEDs for forward voltage within each series group of three, although matching parallel LEDs makes a lot of sense. I have no confidence the values will remain stable over power-on hours / thermal cycling / current stress.

The capacity plot for the Wouxun KG-UV3D lithium battery packs shows that there’s not a lot of capacity left after 7.0 V, so shutting down or scaling back to lower current wouldn’t be a major loss. However, it’s not clear a fixed resistor will do a sufficient job of current limiting with 6.5 V forward voltage across the LED string:

  • At 7.5 V, 100 mA calls for 10 Ω (drop 1 V at 100 mA)
  • At 8.2 V, 10 Ω produces 170 mA (1.7 V across 10 Ω)
  • At 7.0 V, 10 Ω produces 50 mA (0.5 V across 10 Ω)

Obviously, 170 mA is way too much, even by my lax standards.

A 100 mV variation in forward voltage between stacks, each with a 10 Ω resistor, translates into about 10 mA difference in current. This may actually call for current sensors and direct current control, although using a sensor per string, seems excessive. Low dropout regulators in current-source mode might suffice, but that still seems messy.

The test rig will run from a hard 7.5 V supply, which means I can use fixed resistors and be done with it.

The raw data behind those graphs, with LED 1 and LED 11 being the same LED:

# LED Curve Tracer
# Ed Nisley - KE4ZNU - December 2012
# VCC at LED: 4877 mV
# Bandgap reference voltage: 1039 mV

# Insert LED, press button 1 to start...
# INOM	ILED	VccLED	VD	VLED	VG	VS	VGS	VDS	<--- LED 1
0	0	4877	3707	1169	0	0	0	3707
10	10087	4877	2970	1906	2084	105	1978	2864
20	20174	4872	2907	1964	2262	211	2050	2696
30	29803	4877	2869	2007	2412	312	2099	2556
40	39891	4877	2840	2036	2546	418	2127	2421
50	49978	4872	2821	2050	2681	524	2156	2296
60	60066	4877	2806	2070	2816	630	2185	2176
70	69694	4872	2792	2079	2927	731	2195	2060
80	80240	4877	2777	2099	3071	842	2229	1935
90	89869	4872	2768	2103	3196	943	2253	1824
100	100415	4872	2763	2108	3312	1054	2257	1709

# Insert LED, press button 1 to start...
# INOM	ILED	VccLED	VD	VLED	VG	VS	VGS	VDS	<--- LED 2
0	0	4877	3803	1073	0	0	0	3803
10	9628	4872	2960	1911	2084	101	1983	2859
20	19716	4877	2898	1978	2257	207	2050	2691
30	30262	4877	2850	2026	2421	317	2103	2532
40	39891	4877	2816	2060	2551	418	2132	2397
50	49978	4872	2787	2084	2686	524	2161	2262
60	60066	4872	2763	2108	2816	630	2185	2132
70	69694	4872	2744	2127	2927	731	2195	2012
80	79782	4872	2729	2142	3052	837	2214	1892
90	90328	4872	2700	2171	3191	948	2243	1752
100	100415	4872	2686	2185	3331	1054	2277	1632

# Insert LED, press button 1 to start...
# INOM	ILED	VccLED	VD	VLED	VG	VS	VGS	VDS	<--- LED 3
0	0	4877	3716	1160	0	0	0	3716
10	10087	4877	2960	1916	2094	105	1988	2854
20	19716	4877	2893	1983	2257	207	2050	2686
30	30262	4877	2850	2026	2416	317	2099	2532
40	39891	4872	2821	2050	2546	418	2127	2402
50	49520	4872	2797	2075	2681	519	2161	2277
60	59607	4872	2782	2089	2802	625	2176	2156
70	70153	4877	2763	2113	2932	736	2195	2026
80	79782	4872	2749	2123	3076	837	2238	1911
90	90328	4872	2734	2137	3182	948	2233	1786
100	99957	4872	2720	2152	3321	1049	2272	1670

# Insert LED, press button 1 to start...
# INOM	ILED	VccLED	VD	VLED	VG	VS	VGS	VDS	<--- LED 4
0	0	4877	3716	1160	0	0	0	3716
10	10087	4877	2965	1911	2079	105	1973	2859
20	19716	4872	2903	1969	2253	207	2046	2696
30	30262	4877	2859	2017	2407	317	2089	2542
40	39891	4877	2830	2046	2546	418	2127	2412
50	49520	4877	2806	2070	2686	519	2166	2286
60	60066	4872	2787	2084	2821	630	2190	2156
70	69694	4872	2773	2099	2927	731	2195	2041
80	79782	4872	2763	2108	3052	837	2214	1925
90	90328	4872	2749	2123	3196	948	2248	1800
100	100415	4872	2739	2132	3331	1054	2277	1685

# Insert LED, press button 1 to start...
# INOM	ILED	VccLED	VD	VLED	VG	VS	VGS	VDS	<--- LED 5
0	0	4877	3697	1179	0	0	0	3697
10	10087	4877	2965	1911	2079	105	1973	2859
20	20174	4877	2898	1978	2257	211	2046	2686
30	30262	4877	2854	2022	2412	317	2094	2537
40	39891	4872	2830	2041	2551	418	2132	2412
50	49520	4872	2802	2070	2681	519	2161	2282
60	60066	4877	2787	2089	2816	630	2185	2156
70	70153	4872	2768	2103	2932	736	2195	2031
80	79782	4872	2758	2113	3071	837	2233	1920
90	89869	4872	2744	2127	3177	943	2233	1800
100	99957	4872	2734	2137	3293	1049	2243	1685

# Insert LED, press button 1 to start...
# INOM	ILED	VccLED	VD	VLED	VG	VS	VGS	VDS	<--- LED 6
0	0	4877	3764	1112	0	0	0	3764
10	9628	4877	2980	1896	2079	101	1978	2879
20	20174	4877	2922	1954	2262	211	2050	2710
30	30262	4877	2883	1993	2412	317	2094	2566
40	39891	4872	2859	2012	2551	418	2132	2440
50	50437	4872	2835	2036	2686	529	2156	2306
60	60066	4872	2821	2050	2816	630	2185	2190
70	69694	4872	2802	2070	2941	731	2209	2070
80	79782	4872	2787	2084	3081	837	2243	1949
90	90328	4872	2773	2099	3191	948	2243	1824
100	99957	4872	2768	2103	3307	1049	2257	1718

# Insert LED, press button 1 to start...
# INOM	ILED	VccLED	VD	VLED	VG	VS	VGS	VDS	<--- LED 7
0	0	4877	3870	1006	0	0	0	3870
10	10087	4877	2970	1906	2089	105	1983	2864
20	20174	4877	2907	1969	2262	211	2050	2696
30	30262	4872	2859	2012	2412	317	2094	2542
40	39891	4872	2830	2041	2551	418	2132	2412
50	49978	4872	2802	2070	2686	524	2161	2277
60	60066	4872	2777	2094	2821	630	2190	2147
70	69694	4872	2758	2113	2927	731	2195	2026
80	79782	4872	2744	2127	3052	837	2214	1906
90	90328	4872	2724	2147	3196	948	2248	1776
100	99957	4872	2710	2161	3302	1049	2253	1660

# Insert LED, press button 1 to start...
# INOM	ILED	VccLED	VD	VLED	VG	VS	VGS	VDS	<--- LED 8
0	0	4877	3702	1174	0	0	0	3702
10	10087	4877	2970	1906	2084	105	1978	2864
20	20174	4872	2903	1969	2262	211	2050	2691
30	30262	4877	2859	2017	2412	317	2094	2542
40	39891	4877	2830	2046	2546	418	2127	2412
50	49978	4872	2806	2065	2676	524	2152	2282
60	59607	4872	2792	2079	2802	625	2176	2166
70	70153	4872	2777	2094	2932	736	2195	2041
80	79782	4872	2763	2108	3076	837	2238	1925
90	90328	4872	2749	2123	3196	948	2248	1800
100	99957	4872	2734	2137	3302	1049	2253	1685

# Insert LED, press button 1 to start...
# INOM	ILED	VccLED	VD	VLED	VG	VS	VGS	VDS	<--- LED 9
0	0	4872	3721	1150	0	0	0	3721
10	9628	4877	2975	1901	2084	101	1983	2874
20	19716	4877	2898	1978	2257	207	2050	2691
30	30262	4877	2854	2022	2407	317	2089	2537
40	39891	4877	2821	2055	2546	418	2127	2402
50	49978	4872	2787	2084	2686	524	2161	2262
60	60066	4872	2763	2108	2821	630	2190	2132
70	69694	4872	2744	2127	2927	731	2195	2012
80	79782	4872	2724	2147	3052	837	2214	1887
90	90328	4872	2705	2166	3196	948	2248	1757
100	100415	4872	2700	2171	3297	1054	2243	1646

# Insert LED, press button 1 to start...
# INOM	ILED	VccLED	VD	VLED	VG	VS	VGS	VDS	<--- LED 10
0	0	4872	3702	1169	0	0	0	3702
10	9628	4872	2980	1892	2070	101	1969	2879
20	20174	4872	2912	1959	2253	211	2041	2700
30	30262	4872	2874	1997	2412	317	2094	2556
40	39891	4877	2840	2036	2546	418	2127	2421
50	50437	4877	2821	2055	2691	529	2161	2291
60	60066	4877	2802	2075	2816	630	2185	2171
70	69694	4872	2782	2089	2927	731	2195	2050
80	79782	4872	2773	2099	3052	837	2214	1935
90	90328	4872	2753	2118	3182	948	2233	1805
100	100415	4872	2739	2132	3331	1054	2277	1685

# Insert LED, press button 1 to start...
# INOM	ILED	VccLED	VD	VLED	VG	VS	VGS	VDS	<--- LED 11
0	0	4877	3707	1169	0	0	0	3707
10	10087	4877	2970	1906	2084	105	1978	2864
20	20174	4877	2907	1969	2257	211	2046	2696
30	30262	4872	2869	2002	2412	317	2094	2551
40	39891	4872	2845	2026	2546	418	2127	2426
50	50437	4872	2821	2050	2686	529	2156	2291
60	60066	4872	2806	2065	2821	630	2190	2176
70	70153	4872	2792	2079	2941	736	2205	2055
80	80240	4872	2777	2094	3061	842	2219	1935
90	90328	4872	2773	2099	3187	948	2238	1824
100	100415	4872	2758	2113	3317	1054	2262	1704

# Insert LED, press button 1 to start...

The Bash / Gnuplot script that produces them:

#!/bin/sh
#-- overhead
export GDFONTPATH="/usr/share/fonts/truetype/"
base="${1%.*}"
echo Base name: ${base}
ofile=${base}.png
echo Input file: $1
echo Output file: ${ofile}
#-- do it
gnuplot << EOF
#set term x11
set term png font "arialbd.ttf" 18 size 950,600
set output "${ofile}"
set title "${base}"
set key noautotitles
unset mouse
set bmargin 4
set grid xtics ytics
set xlabel "Forward Voltage - V"
set format x "%6.3f"
set xrange [1.8:2.2]
#set xtics 0,5
set mxtics 2
#set logscale y
#set ytics nomirror autofreq
set ylabel "Current - mA"
set format y "%4.0f"
set yrange [0:120]
set mytics 2
#set y2label "right side variable"
#set y2tics nomirror autofreq 2
#set format y2 "%3.0f"
#set y2range [0:200]
#set y2tics 32
#set rmargin 9
set datafile separator "\t"
set label 1 "LED 1 = LED 11" at 2.100,110 right font "arialbd,18"
set arrow from 2.100,110 to 2.110,103 lt 1 lw 2 lc 0
plot    \
"$1" index 0:9 using (\$5/1000):(\$2/1000):(column(-2)) with linespoints lw 2 lc variable,\
"$1" index 10 using (\$5/1000):(\$2/1000) with linespoints lw 2 lc 0
EOF

And the Arduino source code, which bears a remarkable resemblance to the original firmware:

// LED Curve Tracer
// Ed Nisley - KE4ANU - December 2012

#include <stdio.h>

//----------
// Pin assignments

const byte PIN_READ_LEDSUPPLY = 0;    // AI - LED supply voltage        blue
const byte PIN_READ_VDRAIN = 1;        // AI - drain voltage            red
const byte PIN_READ_VSOURCE = 2;    // AI - source voltage            orange
const byte PIN_READ_VGATE = 3;        // AI - VGS after filtering        violet

const byte PIN_SET_VGATE = 11;        // PWM - gate voltage            brown

const byte PIN_BUTTON1 = 8;            // DI - button to start tests    green
const byte PIN_BUTTON2 = 7;            // DI - button for options        yellow

const byte PIN_HEARTBEAT = 13;        // DO - Arduino LED
const byte PIN_SYNC = 2;            // DO - scope sync output

//----------
// Constants

const int MaxCurrent = 100;                // maximum LED current - mA
const int ISTEP = 10;                    // LED current increment

const float Vcc = 4.930;                // Arduino supply -- must be measured!

const float RSense = 10.500;            // current sense resistor

const float ITolerance = 0.0005;        // current setpoint tolerance

const float VGStep = 0.019;                // increment/decrement VGate = 5 V / 256

const byte PWM_Settle = 5;                // PWM settling time ms

#define TCCRxB 0x01                        // Timer prescaler = 1:1 for 32 kHz PWM

#define MK_UL(fl,sc) ((unsigned long)((fl)*(sc)))
#define MK_U(fl,sc) ((unsigned int)((fl)*(sc)))

//----------
// Globals

float AVRef1V1;                    // 1.1 V bandgap reference - calculated from Vcc

float VccLED;                    // LED high-side supply

float VDrain;                    // MOSFET terminal voltages
float VSource;
float VGate;

unsigned int TestNum = 1;

long unsigned long MillisNow;

//-- Read AI channel
//      averages several readings to improve noise performance
//        returns value in mV assuming VCC ref voltage

#define NUM_T_SAMPLES    10

float ReadAI(byte PinNum) {

word RawAverage;

digitalWrite(PIN_SYNC,HIGH);                // scope sync

RawAverage = analogRead(PinNum);            // prime the averaging pump

for (int i=2; i <= NUM_T_SAMPLES; i++) {
RawAverage += (word)analogRead(PinNum);
}

digitalWrite(PIN_SYNC,LOW);

RawAverage /= NUM_T_SAMPLES;

return Vcc * (float)RawAverage / 1024.0;

}

//-- Set PWM output

void SetPWMVoltage(byte PinNum,float PWMVolt) {

byte PWM;

PWM = (byte)(PWMVolt / Vcc * 255.0);

analogWrite(PinNum,PWM);
delay(PWM_Settle);

}

//-- Set VGS to produce desired LED current
//        bails out if VDS drops below a sensible value

void SetLEDCurrent(float ITarget) {

float ISense;                // measured current
float VGateSet;            // output voltage setpoint
float IError;                // (actual - desired) current

VGate = ReadAI(PIN_READ_VGATE);                    // get gate voltage
VGateSet = VGate;                                    //  because input may not match output

do {

VSource = ReadAI(PIN_READ_VSOURCE);
ISense = VSource / RSense;                        // get LED current

//    printf("\r\nITarget: %lu mA",MK_UL(ITarget,1000.0));
IError = ISense - ITarget;

//    printf("\r\nISense: %d mA VGateSet: %d mV VGate %d IError %d mA",
//           MK_U(ISense,1000.0),
//           MK_U(VGateSet,1000.0),
//           MK_U(VGate,1000.0),
//           MK_U(IError,1000.0));

if (IError < -ITolerance) {
VGateSet += VGStep;
//      Serial.print('+');
}
else if (IError > ITolerance) {
VGateSet -= VGStep;
//      Serial.print('-');
}

VGateSet = constrain(VGateSet,0.0,Vcc);
SetPWMVoltage(PIN_SET_VGATE,VGateSet);

VDrain = ReadAI(PIN_READ_VDRAIN);        // sample these for the main loop
VGate = ReadAI(PIN_READ_VGATE);
VccLED = ReadAI(PIN_READ_LEDSUPPLY);

if ((VDrain - VSource) < 0.020) {            // bail if VDS gets too low
printf("# VDS=%d too low, bailing\r\n",MK_U(VDrain - VSource,1000.0));
break;
}

} while (abs(IError) > ITolerance);

//    Serial.println(" Done");
}

//-- compute actual 1.1 V bandgap reference based on known VCC = AVcc (more or less)
//        adapted from http://code.google.com/p/tinkerit/wiki/SecretVoltmeter

float ReadBandGap(void) {

word ADCBits;
float VBandGap;

ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);    // select 1.1 V input
delay(2); // Wait for Vref to settle

ADCSRA |= _BV(ADSC);                                        // Convert
while (bit_is_set(ADCSRA,ADSC));

ADCBits = ADCL;
ADCBits |= ADCH<<8;

VBandGap = Vcc * (float)ADCBits / 1024.0;
return VBandGap;
}

//-- Print message, wait for a given button press

void WaitButton(int Button,char *pMsg) {
printf("# %s",pMsg);
while(HIGH == digitalRead(Button)) {
delay(100);
digitalWrite(PIN_HEARTBEAT,!digitalRead(PIN_HEARTBEAT));
}

delay(50);                // wait for bounce to settle
digitalWrite(PIN_HEARTBEAT,LOW);
}

//-- Helper routine for printf()

int s_putc(char c, FILE *t) {
Serial.write(c);
}

//------------------
// Set things up

void setup() {
pinMode(PIN_HEARTBEAT,OUTPUT);
digitalWrite(PIN_HEARTBEAT,LOW);    // show we arrived

pinMode(PIN_SYNC,OUTPUT);
digitalWrite(PIN_SYNC,LOW);        // show we arrived

TCCR1B = TCCRxB;                    // set frequency for PWM 9 & 10
TCCR2B = TCCRxB;                    // set frequency for PWM 3 & 11

pinMode(PIN_SET_VGATE,OUTPUT);
analogWrite(PIN_SET_VGATE,0);        // force gate voltage = 0

pinMode(PIN_BUTTON1,INPUT_PULLUP);    // use internal pullup for buttons
pinMode(PIN_BUTTON2,INPUT_PULLUP);

Serial.begin(9600);
fdevopen(&s_putc,0);                // set up serial output for printf()

printf("# LED Curve Tracer\r\n# Ed Nisley - KE4ZNU - December 2012\r\n");

VccLED = ReadAI(PIN_READ_LEDSUPPLY);
printf("# VCC at LED: %d mV\r\n",MK_U(VccLED,1000.0));

AVRef1V1 = ReadBandGap();            // compute actual bandgap reference voltage
printf("# Bandgap reference voltage: %lu mV\r\n",MK_UL(AVRef1V1,1000.0));

}

//------------------
// Run the test loop

void loop() {

Serial.println('\n');                        // blank line for Gnuplot indexing

WaitButton(PIN_BUTTON1,"Insert LED, press button 1 to start...\r\n");
printf("# INOM\tILED\tVccLED\tVD\tVLED\tVG\tVS\tVGS\tVDS\t<--- LED %d\r\n",TestNum++);
digitalWrite(PIN_HEARTBEAT,LOW);

for (int ILED=0; ILED <= MaxCurrent; ILED+=ISTEP) {
SetLEDCurrent(((float)ILED)/1000.0);
printf("%d\t%lu\t%d\t%d\t%d\t%d\t%d\t%d\t%d\r\n",
ILED,
MK_UL(VSource / RSense,1.0e6),
MK_U(VccLED,1000.0),
MK_U(VDrain,1000.0),
MK_U(VccLED - VDrain,1000.0),
MK_U(VGate,1000.0),
MK_U(VSource,1000.0),
MK_U(VGate - VSource,1000),
MK_U(VDrain - VSource,1000.0)
);
}

SetPWMVoltage(PIN_SET_VGATE,0.0);

}

4 thoughts on “Red LEDs: Current vs. Voltage Sorting

  1. Why not use a constant current switcher made for LEDs? Or boost the battery voltage up to where all the LEDs can be in a single series string, combined with a current limiting resistor?

    1. a constant current switcher made for LEDs

      It may come down to that… [grin]

      I want several strings, which would require a current-controlled switcher for each string. Because I only need two or three of these lights, I’m thinking I can mix-and-match some LEDs so the strings share the current equitably. Might not work, but it’ll be interesting to find out why or why not.

      combined with a current limiting resistor

      The great thing about a current-control driver is that you don’t need a ballast resistor. It generally does, however, require a current-sense resistor, which I’d like to avoid. Eliminating the sense resistor would allow operation with less margin between the battery voltage and the LED forward drop; ideally, the MOSFET doing the switching could also serve as the current limiter, right down to the point where the MOSFET on-resistance becomes the limiting issue.

      That’s why I doodled up those notes on a Hall effect current sensor… it might come in handy some day!

Comments are closed.