About these ads

Posts Tagged Arduino

Makergear M2 vs. LinuxCNC: Project Overview

M2 - cushwa Owl - half scale

M2 – cushwa Owl – half scale

During the course of my Makerbot Thing-O-Matic experience, I concluded:

  • Enthusiasm may get a product out, but engineering makes it work
  • Plywood and plastic do not produce a stable 3D printer
  • Measurements matter
  • 8-bit microcontrollers belong in the dustbin of history

With that in mind, I’ve long thought that LinuxCNC (formerly EMC2) would provide a much better basis for the control software required for a 3D printer than the current crop of Arduino-based microcontrollers. LinuxCNC provides:

  • Hard real time motion control with proven performance
  • A robust, well-defined hardware interface layer
  • Ladder-logic machine control
  • Isolated userspace programming
  • Access to a complete Linux distro’s wealth of programs / utilities
  • Access to an x86 PC’s wealth of hardware gadgetry

Rather than (try to) force-fit new functions in an Arduino microcontroller, I decided it would be interesting to retrofit a DIY 3D printer with a LinuxCNC controller, improve the basic hardware control and sensing, instrument the extruder, then take measurements that might shed some light on DIY 3D printing’s current shortcomings.

The overall plan looks like this:

  • Start with a Makergear M2
  • See what the stock hardware can do
  • Replace the RAMBo controller with LinuxCNC
  • See what the hardware can do with better drivers
  • Adapt the G-Code / M-Code processing to use more-or-less stock Marlin G-Code
  • Add useful controllers along the lines of the Joggy Thing
  • Improve the platform height / level sensing
  • Rebuild the extruder with temperature and force sensors
  • Start taking measurements!

My reasons for choosing the Makergear M2 as the basis for this project should be obvious:

  • All metal: no plywood, no acrylic (albeit a plastic filament drive)
  • Decent stepper motors (with one notable exception)
  • Reasonable hot end design
  • Good reputation

The first step of the overall plan included a meticulously documented M2 build that I figured would take a month or two, what with the usual snafus and gotchas that accompany building any complex mechanism. Quite by coincidence, a huge box arrived on my birthday (the Thing-O-Matic arrived on Christmas Eve, so perhaps this is a tradition), the day when I learned that Mad Phil had entered his final weeks of life.

As the Yiddish proverb puts it: If you wish to hear G*d laugh, tell him of your plans.

So I converted a box of parts into a functional M2 3D printer over the course of four intense days, alternating between our living room floor and a card table in Phil’s home office, showing him how things worked, getting his advice & suggestions, and swapping “Do you remember when?” stories. Another few days sufficed for software installation, configuration, and basic tuneup; I managed to show him some shiny plastic doodads just before he departed consensus reality; as nearly as I can tell, we both benefited from the distractions.

Which means I don’t have many pictures or much documentation of the in-process tweakage that produced a functional printer. The next week or so of posts should cover the key points in enough detail to be useful.

Not to spoil the plot or anything: a stock M2 works wonderfully well.

Owl - half size - left

Owl – half size – left

For example, a half-scale cushwa owl printed in PLA at 165 °C with no bed cooling and these Slic3r parameters:

  • 500 mm/s move
  • 300 mm/s infill
  • 200 mm/s solid infill
  • 100 mm/s internal perimeter
  • 50 mm/s bottom layer
  • 30 mm/s external perimeter
  • 1 mm retract @ 300 mm/s

The beak came out slightly droopy and each downward-pointing feather dangles a glittery drop. There’s room for improvement, but that’s pretty good a week after opening a box o’ parts…

About these ads

, , ,

8 Comments

LED Forward Voltages vs. Color

Running a random set of colored LEDs from the Basement Laboratory Parts Warehouse Wing through the LED Curve Tracer produced this pleasant plot:

ROYGBUIW

The white LED doesn’t match up with either the blue or the UV LED. Perhaps the blue LED uses a completely different chemistry that shoves further to the right than seems proper? I suppose I should run a handful of white, blue, and UV LEDs through the thing just to see what’s going on…

The Bash / Gnuplot source code:

#!/bin/sh
numLEDs=8
#-- 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 "%4.1f"
set xrange [0.5:4.5]
#set xtics 0,5
set mxtics 2
#set logscale y
#set ytics nomirror autofreq
set ylabel "Current - mA"
set format y "%3.0f"
set yrange [0:35]
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 whitespace
set label 1 "IR" at 1.32,32 center
set label 2 "R"  at 1.79,32 center
set label 3 "O"  at 2.10,32 center
set label 4 "Y"  at 2.65,32 center
set label 5 "G"  at 2.42,32 center
set label 6 "B"  at 4.05,32 center
set label 7 "UV" at 3.90,32 center
set label 8 "W"  at 3.25,32 center
#set arrow from 2.100,32 to 2.125,31 lt 1 lw 2 lc 0
plot \
 "$1" index 0 using (\$5/1000):(\$2/1000) with linespoints pt 1 lw 2 lc rgb "red" ,\
 "$1" index 1 using (\$5/1000):(\$2/1000) with linespoints pt 1 lw 2 lc rgb "orange" ,\
 "$1" index 2 using (\$5/1000):(\$2/1000) with linespoints pt 1 lw 2 lc rgb "dark-yellow" ,\
 "$1" index 3 using (\$5/1000):(\$2/1000) with linespoints pt 1 lw 2 lc rgb "green" ,\
 "$1" index 4 using (\$5/1000):(\$2/1000) with linespoints pt 1 lw 2 lc rgb "blue" ,\
 "$1" index 5 using (\$5/1000):(\$2/1000) with linespoints pt 1 lw 2 lc rgb "purple" ,\
 "$1" index 6 using (\$5/1000):(\$2/1000) with linespoints pt 1 lw 2 lc rgb "magenta" ,\
 "$1" index 7 using (\$5/1000):(\$2/1000) with linespoints pt 1 lw 2 lc rgb "dark-gray"
EOF

And the raw data file:

# LED Curve Tracer
# Ed Nisley - KE4ZNU - March 2013
# VCC at LED: 4897 mV
# Bandgap reference voltage: 1041 mV

# Insert LED, press button 1 to start...
# INOM	ILED		VccLED	VD	VLED		VG	VS	VGS VDS <--- LED 1
0	0	4892		3889		1002		0	0	0	3889
5	4613		4892		3264		1627		1990		48	1942		3216
10	10148	4892		3216		1675		2092		106 1985		3109
15	15223	4892		3182		1709		2199		159 2039		3022
20	19836	4892		3148		1743		2271		208 2063		2940
25	24910	4897		3129		1767		2354		261 2092		2867
30	30446	4897		3104		1792		2431		319 2111		2785

# Insert LED, press button 1 to start...
# INOM	ILED		VccLED	VD	VLED		VG	VS	VGS VDS <--- LED 2
0	0	4892		3884		1007		0	0	0	3884
5	4613		4892		3124		1767		1985		48	1937		3075
10	9687		4897		3037		1860		2111		101 2010		2935
15	14761	4897		2964		1932		2189		155 2034		2809
20	19836	4897		2906		1990		2271		208 2063		2697
25	24910	4897		2848		2048		2349		261 2087		2586
30	30446	4892		2794		2097		2431		319 2111		2475

# Insert LED, press button 1 to start...
# INOM	ILED		VccLED	VD	VLED		VG	VS	VGS VDS <--- LED 3
0	0	4892		3826		1065		0	0	0	3826
5	4613		4897		2862		2034		1990		48	1942		2814
10	10148	4897		2688		2208		2097		106 1990		2581
15	15223	4897		2552		2344		2194		159 2034		2392
20	19836	4892		2436		2455		2276		208 2068		2228
25	24910	4897		2349		2547		2354		261 2092		2087
30	29985	4897		2257		2639		2426		314 2111		1942

# Insert LED, press button 1 to start...
# INOM	ILED		VccLED	VD	VLED		VG	VS	VGS VDS <--- LED 4
0	0	4892		3734		1157		0	0	0	3734
5	5074		4892		2935		1956		1976		53	1922		2882
10	10148	4897		2823		2073		2102		106 1995		2717
15	15223	4892		2722		2170		2199		159 2039		2562
20	20297	4897		2649		2247		2276		213 2063		2436
25	24910	4897		2567		2329		2349		261 2087		2305
30	29985	4897		2489		2407		2426		314 2111		2174

# Insert LED, press button 1 to start...
# INOM	ILED		VccLED	VD	VLED		VG	VS	VGS VDS <--- LED 5
0	0	4892		4485		406 0	0	0	4485
5	4613		4897		1724		3172		1990		48	1942		1675
10	10148	4892		1443		3448		2097		106 1990		1336
15	15223	4897		1249		3647		2199		159 2039		1089
20	19836	4892		1099		3792		2276		208 2068		891
25	24910	4897		983 3913		2354		261 2092		721
30	29985	4892		862 4030		2426		314 2111		547

# Insert LED, press button 1 to start...
# INOM	ILED		VccLED	VD	VLED		VG	VS	VGS VDS <--- LED 6
0	0	4892		4165		726 0	0	0	4165
5	5074		4892		1448		3443		1985		53	1932		1395
10	10148	4897		1322		3574		2102		106 1995		1215
15	15223	4892		1220		3671		2194		159 2034		1060
20	20297	4892		1147		3744		2276		213 2063		934
25	25372	4892		1075		3816		2354		266 2087		808
30	29985	4892		1002		3889		2426		314 2111		687

# Insert LED, press button 1 to start...
# INOM	ILED		VccLED	VD	VLED		VG	VS	VGS VDS <--- LED 7
0	0	4892		4247		644 0	0	0	4247
5	5074		4892		3647		1244		1981		53	1927		3594
10	10148	4892		3618		1273		2107		106 2000		3511
15	14761	4892		3603		1288		2170		155 2015		3448
20	20297	4892		3584		1307		2271		213 2058		3371
25	25372	4892		3574		1317		2354		266 2087		3308
30	29523	4892		3565		1327		2412		310 2102		3255

# Insert LED, press button 1 to start...
# INOM	ILED		VccLED	VD	VLED		VG	VS	VGS VDS <--- LED 8
0	0	4892		4945		-53 0	0	0	4945
5	5074		4892		2160		2731		1985		53	1932		2107
10	10148	4892		2034		2857		2097		106 1990		1927
15	15223	4897		1927		2969		2194		159 2034		1767
20	19836	4892		1826		3066		2271		208 2063		1617
25	25372	4897		1734		3162		2349		266 2082		1467
30	29523	4892		1666		3225		2412		310 2102		1356

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

4 Comments

6C21 Triode

Aitch bestowed this gem on me while cleaning out his collection:

6C21 Triode

6C21 Triode

It’s a 6C21 triode, originally used as a radar modulator, atop a letter-size sheet of graph paper. The plate terminal is on top, the grid sticks out to the side, and the filament is common with the cathode through the base pins.

It has impressive specs (datasheet and pictures):

  • 30 kV plate voltage
  • 15 A pulsed plate current, 100 ms max
  • 7.5 V filament at 15 A = 112 W (!)
  • Pulse duty cycle 0.2%

The gray film inside the bulb shows that it’s been used, but the filament still has continuity. Ordinarily, you could turn something like this into a night light by running the filament at a voltage somewhat under its rating, but my bench supply maxed out at @ 3 A without even warming it up; a dim orange night light that burns maybe 75 W is Not A Good Idea.

The base has some intriguing holes, originally used for forced-air cooling, that lead directly to the glass envelope:

6C21 Triode - base

6C21 Triode – base

One could mount discrete LEDs in those holes, maybe a slightly turned-down 10 mm cool-white LED in the middle flanked by red and blue, and run a low-power Arduino-based mood light; by some cosmic coincidence, the hole spacing matches up almost perfectly with those LED strips. Or one could go full analog with three red LEDs driven by the WWVB signal.

I’m thinking a plain black acrylic case, with the tube base sunk into the middle, would be about right. No readouts, no dials, no buttons, just a gently glowing tube.

Maybe a 3D printed socket holding everything in place?

6 Comments

5 mW Laser Module

A trio of 5 mW laser modules arrived with a bunch of other surplus gear after an end-of-year sale:

5 mW Laser Module

5 mW Laser Module

It runs on 5 V at 20 mA, determined by the 91 Ω SMD resistor soldered across the terminals at the back of the PCB. That suggests the laser diode itself runs at about 3.2 V: 5 V – 0.020 A * 91 Ω.

The brass case connects to the red (positive) wire, so you must insulate the laser module from the usual grounded metal chassis.

Two of the three lasers arrived badly defocused, but a twist of the brass barrel broke the sealing glue and a bit more twiddling found the sweet spot.

Running one of these from an Arduino would be just like the UV LED: redefine a bit in the shift register bitfield and drive the laser with a MOSFET switch.

I’d be tempted to bypass the SMD resistor and run it from an LM317-style current regulator hitched directly to the raw battery; I’m pretty sure I have some LM317 regulators in TO-92 packages. The sense resistor would be 62.5 Ω = 1.25 V / 0.02 A, dissipating 25 mW = 1.25 V * 20 mA. From a freshly charged 7.2 V Li-ion battery at 8.5 V, the regulator would dissipate something like 80 mW =(8.5 – 1.25 – 3.2 V) * 20 mA.

Or just add more series resistance and ignore the brightness variation?

7 Comments

Arduino Snippets: LED Stroboscopic Tachometer

A bit of fiddling with the Arduino PWM hardware can turn a white LED into a stroboscopic tachometer to chop smooth motion into chunks:

Strobe - Maze 1 - 50 Hz 100 us

Strobe – Maze 1 – 50 Hz 100 us

I was moving that pendant by hand and slight speed changes were easily visible:

Strobe - Maze 2 - 50 Hz 100 us

Strobe – Maze 2 – 50 Hz 100 us

IBMers of a certain era may recognize the test object; the rest of you can go there.

That’s a 10 mm warm-white LED with 5 parallel chips, running at about 100 mA from a 5 V supply, and driven from the same PWM channel and MOSFET that used to drive also drives the red channel of the RGB LED Mood Light:

White LED Strobe

White LED Strobe

The ZVNL110A MOSFET has a 3 Ω drain resistance, which becomes a significant part of the resistance; you’d want a bigger, better, lower resistance MOSFET to wring more light out of the LED. In fact, I ran the LED from 12 V with the same resistor at a few hundred mA.

The reason you need more light is to make up for the minuscule duty cycle. In order to “stop motion”, you want a very short pulse; I picked a 100 μs pulse. At 50 Hz, that works out to a 0.5% duty cycle: not much light at 100 mA, but OK for a demo.

You can’t do this with the standard Arduino PWM setup, because it produces a constant frequency (about 488 Hz) and varies the duty cycle; we need a variable frequency with a constant pulse length. Because a stroboscope needs fine-grained control over the frequency, in order to stop the motion of rotating objects, it should run from one of the 16 bit Timer1 PWM outputs, which means either PWM9 or PWM10. Note that simply changing the timer’s clock prescaler as described there won’t suffice, because that gives very coarse control of the PWM frequency.

It’s probably worth noting that trying to do precise timing purely in software with, say, the millis() and micros() functions, produces terrible results…

The Arduino timer hardware includes control over both the period and the duration of the output pulses. The Fine Manual describes all the timer configuration registers starting on page 109; see that post for a push-pull PWM driver that formed the basis of this one.

Fast PWM (Mode 14) has some useful characteristics:

  • Single-slope operation: timer counts only upward
  • Output PWM9 goes high when TCNT1 resets to 0
  • Output PWM9 goes low when TCNT1 = OCR1A
  • TCNT1 resets when TCNT1 = ICR1

The lowest possible output frequency occurs with ICR1 = 0xffff, so that Timer1 counts from 0×0000 to 0xffff before resetting (which, in that case, is indistinguishable from simply wrapping). The wrap period = ICR1 * tick period and the corresponding frequency = 1 / period.

The clock prescaler determines the overall range of Timer1 by setting the tick period. The Clock Select bit field can take on 6 useful, albeit widely separated, values (the other two select the external clock pin):

  • 0 – stop timer
  • 1 – prescale 1:1 = 62.5 ns tick → 244 Hz
  • 2 – prescale 1:8 = 500 ns tick → 30 Hz
  • 3 – prescale 1:64 = 4 μs tick → 3.8 Hz
  • 4 – prescale 1:256 = 16 μs tick → 0.95 Hz
  • 5 – prescale 1:1024 = 64 μs tick → 0.24 Hz

For my purposes, a lower limit around 4 Hz seemed about right. That means CS = 3, the prescaler runs at 1:64, and the timer ticks at 4 μs.

The frequency upper limit could be just under 1/(pulse width), which would produce a very high duty cycle. I arbitrarily set the limit to 1/(4 × pulse width), for a 25% duty cycle that works out to 1/(4 × 100 μs) = 2.5 kHz = 150 k flash/min. If you’re using very high current drive, then limit the duty cycle to prevent toasting the LED.

Because a strobe tach needs quick & easy adjustment, the encoder knob tweaks the pulse frequency in 1 Hz steps. Pushing the knob to close the shaft switch (if you have such a knob, of course, otherwise use another button; they all do the same thing here) reduces the step size to 0.01 Hz, which is more useful for fine tuning when you’re close to the goal. A real application requires better control over the numeric values (probably using integer values); I used floating point and simply ignored all the usual roundoff issues:

Stroboscope Tachometer
Ed Nisley - KE4ZNU - December 2012
Frequency: 10.00
Pulse duration: 100 us
Frequency: 11.00
Frequency: 12.00
Frequency: 13.00
Frequency: 14.00
Frequency: 14.01
Frequency: 14.02
Frequency: 14.02
Frequency: 14.02
Frequency: 14.01
Frequency: 14.00
Frequency: 13.98
Frequency: 13.97
Frequency: 13.97
Frequency: 13.96
Frequency: 13.94
Frequency: 13.93
Frequency: 14.93
Frequency: 15.93
Frequency: 16.94
Frequency: 17.94

Updating the counter period requires:

  • Shut off interrupts to prevent interference with the high byte storage register
  • Stop the timer: CS=0
  • Load the new upper limit in ICR1
  • Force TCNT1 to be just below IRC1 to terminate the current pulse
  • Start the timer: CS=3
  • Enable interrupts again

You’d probably plunk that into a separate function in a real program…

Printing the frequency becomes a hassle without floating point formatting in printf(). It should appear on the character LED display, too. Optionally / additionally showing the value in rev/min would be very nice.

You’d want to increment the frequency by some reasonable fraction of the current value, perhaps rounded to 1 / 2 / 5 / 10 percent steps. Larger steps by pushbutton? Truncate the current value to a multiple of the step size?

You would also want some way to adjust the flash duration, but that’s definitely in the nature of fine tuning.

As it stands, a 100 μs pulse really does stop motion:

Fan stopped at 2500 rpm

Fan stopped at 2500 rpm

That’s a fan running at about 2500 rpm, with the LED flashing at 41.86 Hz. The camera exposure is 1/2 sec @ f/3.5, handheld, which means the camera integrated about 20 flashes. Ambient light accounts for the background blur: I boosted the grossly underexposed image right out of darkness. The square on the hub is retroreflective tape for a laser tachometer that verified the speed.

Yes, half a second handheld. The morning tea wears off during the day…

In round numbers, 41.86 Hz = 23.9 ms / rev. The fan diameter is 86 mm, so the blade tips travel 1.1 mm = (270 mm / 23.9 ms) × 100 μs during each flash. The tips seem slightly blurred when you (well, I) look very closely in real life, but I think this lashup worked pretty well right off the sketchpad.

The Arduino source code:

// Stroboscopic Tachometer
// Ed Nisley - KE4ANU - December 2012

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

const byte PIN_KNOB_A = 2;			// knob A switch - must be on ext interrupt 2
const byte PIN_KNOB_B = 4;			//  .. B switch
const byte PIN_BUTTONS = A5;		//  .. push-close momentary switches

const byte PIN_STROBE = 9;			// LED drive, must be PWM9 = OCR1A using Timer1

const byte PIN_PWM10 = 10;			// drivers for LED strip, must turn these off...
const byte PIN_PWM11 = 11;

const byte PIN_SYNC = 13;			// scope sync

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

const int UPDATEMS = 10;				// update LEDs only this many ms apart

#define TCCRxB_CS 0x03					// Timer prescaler CS=3 -> 1:64 division

const float TICKPD = 64.0 * 62.5e-9;	// basic Timer1 tick rate: prescaler * clock

enum KNOB_STATES {KNOB_CLICK_0,KNOB_CLICK_1};

// ButtonThreshold must have N_BUTTONS elements, last = 1024

enum BUTTONS {SW_KNOB, B_1, B_2, B_3, B_4, N_BUTTONS};
const word ButtonThreshold[] = {265/2, (475+265)/2, (658+475)/2, (834+658)/2, (1023+834)/2, 1024};

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

float FlashLength = 0.1e-3;						// strobe flash duration in seconds
word FlashLengthCt = FlashLength / TICKPD;		//  ... in Timer1 ticks

float FlashFreq = 20.0;							// strobe flash frequency in Hz
float FlashPd = 1.0 / FlashFreq;				//  ... period in sec
word FlashPdCt = FlashPd / TICKPD;				//  ... period in Timer1 ticks

float FreqIncr = 1.0;							// default frequency increment
const float FreqMin = 4.0;
const float FreqMax = 1.0/(4.0*FlashLength);

volatile char KnobCounter = 0;
volatile char KnobState;

byte Button, PrevButton;

unsigned long MillisNow;
unsigned long MillisThen;

//-- Helper routine for printf()

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

//-- Knob interrupt handler

void KnobHandler(void)
{
	byte Inputs;
	Inputs = digitalRead(PIN_KNOB_B) << 1 | digitalRead(PIN_KNOB_A);  // align raw inputs
//	Inputs ^= 0x02;                             // fix direction

	switch (KnobState << 2 | Inputs) {
	case 0x00 : 				// 0 00 - glitch
        break;
	case 0x01 : 				 // 0 01 - UP to 1
        KnobCounter++;
		KnobState = KNOB_CLICK_1;
		break;
	case 0x03 : 				 // 0 11 - DOWN to 1
        KnobCounter--;
		KnobState = KNOB_CLICK_1;
		break;
	case 0x02 : 				 // 0 10 - glitch
        break;
	case 0x04 : 				 // 1 00 - DOWN to 0
        KnobCounter--;
		KnobState = KNOB_CLICK_0;
		break;
	case 0x05 : 				 // 1 01 - glitch
        break;
	case 0x07 : 				 // 1 11 - glitch
        break;
	case 0x06 : 				 // 1 10 - UP to 0
        KnobCounter++;
		KnobState = KNOB_CLICK_0;
		break;
	default :  					// something is broken!
        KnobCounter = 0;
		KnobState = KNOB_CLICK_0;
	}
}

//-- Read and decipher analog switch inputs
//		returns N_BUTTONS if no buttons pressed

byte ReadButtons(int PinNumber) {

word RawButton;
byte ButtonNum;

	RawButton = analogRead(PinNumber);

	for (ButtonNum = 0; ButtonNum <= N_BUTTONS; ButtonNum++){
		if (RawButton < ButtonThreshold[ButtonNum])
			break;
	}

	return ButtonNum;

}

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

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

	analogWrite(PIN_PWM10,0);			// turn off other PWM outputs
	analogWrite(PIN_PWM11,0);

	analogWrite(PIN_STROBE,1);			// let Arduino set up default Timer1 PWM
	TCCR1B = 0;							// turn off Timer1 for strobe setup
	TCCR1A = 0x82;						// clear OCR1A on match, Fast PWM, lower WGM1x = 14
	ICR1 = FlashPdCt;
	OCR1A = FlashLengthCt;
	TCNT1 = FlashLengthCt - 1;
	TCCR1B = 0x18 | TCCRxB_CS;			// upper WGM1x = 14, Prescale 1:64, start Timer1

	pinMode(PIN_KNOB_B,INPUT_PULLUP);
	pinMode(PIN_KNOB_A,INPUT_PULLUP);

	KnobState = digitalRead(PIN_KNOB_A);
	Button = PrevButton = ReadButtons(PIN_BUTTONS);

	attachInterrupt((PIN_KNOB_A - 2),KnobHandler,CHANGE);

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

	printf("Stroboscope Tachometer\r\nEd Nisley - KE4ZNU - December 2012\r\n");

	printf("Frequency: %d.%02d\nPulse duration: %d us\n",
		   (int)FlashFreq,(int)(100.0 * (FlashFreq - trunc(FlashFreq))),
		   (int)(1e6 * FlashLength));

	MillisThen = millis();

}

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

void loop() {

	MillisNow = millis();

	if ((MillisNow - MillisThen) > UPDATEMS) {

		digitalWrite(PIN_SYNC,HIGH);

		Button = ReadButtons(PIN_BUTTONS);
		if (PrevButton != Button) {
			if (Button == N_BUTTONS) {
//				printf("Button %d released\n",PrevButton);
				FreqIncr = 1.0;
			}
			else
//				printf("Button %d pressed\n",Button);
//				if (Button == SW_KNOB)
					FreqIncr = 0.01;
			PrevButton = Button;
		}

		if (KnobCounter) {
			FlashFreq += (float)KnobCounter * FreqIncr;
			KnobCounter = 0;

			FlashFreq = constrain(FlashFreq,FreqMin,FreqMax);
			FlashFreq = round(100.0 * FlashFreq) / 100.0;

			FlashPd = 1.0 / FlashFreq;
			FlashPdCt = FlashPd / TICKPD;

			noInterrupts();
			TCCR1B &= 0xf8;				// stop Timer1
			ICR1 = FlashPdCt;			// set new period
			TCNT1 = FlashPdCt - 1;		// force immediate update
			TCCR1B |= TCCRxB_CS;		// start Timer1
			interrupts();

			printf("Frequency: %d.%02d\n",
				   (int)FlashFreq,(int)(100.0 * (FlashFreq - trunc(FlashFreq))));
		}

		digitalWrite(PIN_SYNC,LOW);

		MillisThen = MillisNow;
	}

}

That’s a grandiose name for a blinking LED, if I ever saw one…

4 Comments

Amber LEDs: Current vs. Voltage

While wiring up the LED stress tester, I realized I should abuse a string of amber LEDs along with the three red strings. Herewith, four amber LEDs from the top of their bag, with LED 5 = LED 1 retested:

Amber LEDs - 100 mA

Amber LEDs – 100 mA

Apart from being an outlier, that red trace seems much prettier than the others, doesn’t it?

The data file:

# LED Curve Tracer
# Ed Nisley - KE4ZNU - December 2012
# VCC at LED: 4872 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    4872    3668    1203    0    0    0    3668
10    10087    4872    2951    1920    2079    105    1973    2845
20    19716    4872    2898    1973    2257    207    2050    2691
30    30262    4872    2864    2007    2416    317    2099    2546
40    39891    4872    2840    2031    2551    418    2132    2421
50    49520    4872    2821    2050    2686    519    2166    2301
60    59607    4872    2806    2065    2811    625    2185    2180
70    69694    4872    2792    2079    2927    731    2195    2060
80    79782    4872    2777    2094    3061    837    2224    1940
90    90328    4872    2768    2103    3206    948    2257    1819
100    99957    4867    2763    2103    3307    1049    2257    1713

# Insert LED, press button 1 to start...
# INOM    ILED    VccLED    VD    VLED    VG    VS    VGS    VDS    <--- LED 2
0    0    4872    3991    881    0    0    0    3991
10    9628    4872    2946    1925    2084    101    1983    2845
20    20174    4872    2888    1983    2257    211    2046    2676
30    30262    4872    2850    2022    2416    317    2099    2532
40    39891    4872    2826    2046    2551    418    2132    2407
50    49978    4872    2802    2070    2681    524    2156    2277
60    60066    4872    2782    2089    2811    630    2180    2152
70    69694    4872    2768    2103    2936    731    2205    2036
80    79782    4872    2753    2118    3076    837    2238    1916
90    89869    4872    2744    2127    3177    943    2233    1800
100    99957    4872    2739    2132    3297    1049    2248    1689

# Insert LED, press button 1 to start...
# INOM    ILED    VccLED    VD    VLED    VG    VS    VGS    VDS    <--- LED 3
0    0    4872    3788    1083    0    0    0    3788
10    9628    4872    2941    1930    2084    101    1983    2840
20    19716    4872    2888    1983    2262    207    2055    2681
30    29803    4872    2850    2022    2412    312    2099    2537
40    39891    4872    2826    2046    2551    418    2132    2407
50    49978    4872    2806    2065    2681    524    2156    2282
60    60066    4872    2787    2084    2811    630    2180    2156
70    70153    4872    2777    2094    2960    736    2224    2041
80    80240    4872    2768    2103    3061    842    2219    1925
90    90328    4872    2753    2118    3182    948    2233    1805
100    99957    4867    2753    2113    3302    1049    2253    1704

# Insert LED, press button 1 to start...
# INOM    ILED    VccLED    VD    VLED    VG    VS    VGS    VDS    <--- LED 4
0    0    4872    3899    972    0    0    0    3899
10    9628    4872    2936    1935    2084    101    1983    2835
20    19716    4872    2888    1983    2262    207    2055    2681
30    29803    4872    2854    2017    2412    312    2099    2542
40    39891    4872    2835    2036    2551    418    2132    2416
50    49978    4872    2816    2055    2681    524    2156    2291
60    60066    4872    2797    2075    2816    630    2185    2166
70    70153    4872    2787    2084    2927    736    2190    2050
80    80240    4872    2773    2099    3061    842    2219    1930
90    90328    4867    2768    2099    3196    948    2248    1819
100    99957    4872    2758    2113    3331    1049    2282    1709

# Insert LED, press button 1 to start...
# INOM    ILED    VccLED    VD    VLED    VG    VS    VGS    VDS    <--- LED 5
0    0    4872    3841    1030    0    0    0    3841
10    10087    4872    2951    1920    2079    105    1973    2845
20    20174    4872    2907    1964    2257    211    2046    2696
30    30262    4872    2869    2002    2412    317    2094    2551
40    39891    4872    2845    2026    2551    418    2132    2426
50    50437    4872    2826    2046    2686    529    2156    2296
60    60066    4872    2806    2065    2821    630    2190    2176
70    69694    4872    2797    2075    2941    731    2209    2065
80    80240    4872    2782    2089    3076    842    2233    1940
90    89869    4872    2773    2099    3177    943    2233    1829
100    99957    4872    2763    2108    3321    1049    2272    1713

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

The Bash / Gnuplot routine that produced the graph has a few tweaks:

#!/bin/sh
numLEDs=4
#-- 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 $((numLEDs + 1))" at 2.100,110 right font "arialbd,18"
set arrow from 2.100,110 to 2.105,103 lt 1 lw 2 lc 0
plot	\
    "$1" index 0:$((numLEDs - 1)) using (\$5/1000):(\$2/1000):(column(-2)) with linespoints lw 2 lc variable,\
    "$1" index $numLEDs using (\$5/1000):(\$2/1000) with linespoints lw 2 lc 0
EOF

,

1 Comment

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 Comments