Posts Tagged Arduino
Makergear M2 vs. LinuxCNC: Project Overview
Posted by Ed in Electronics Workbench, Machine Shop, Science, Software on 4-April-2013
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.
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…
LED Forward Voltages vs. Color
Posted by Ed in Electronics Workbench, Science on 2-April-2013
Running a random set of colored LEDs from the Basement Laboratory Parts Warehouse Wing through the LED Curve Tracer produced this pleasant plot:
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...
6C21 Triode
Posted by Ed in Electronics Workbench, Machine Shop on 21-March-2013
Aitch bestowed this gem on me while cleaning out his collection:
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:
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?
5 mW Laser Module
Posted by Ed in Electronics Workbench on 10-January-2013
A trio of 5 mW laser modules arrived with a bunch of other surplus gear after an end-of-year sale:
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?
Arduino Snippets: LED Stroboscopic Tachometer
Posted by Ed in Electronics Workbench, Machine Shop, Software on 3-January-2013
A bit of fiddling with the Arduino PWM hardware can turn a white LED into a stroboscopic tachometer to chop smooth motion into chunks:
I was moving that pendant by hand and slight speed changes were easily visible:
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:
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
PWM9goes high whenTCNT1resets to 0 - Output
PWM9goes low whenTCNT1=OCR1A TCNT1resets whenTCNT1=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
TCNT1to be just belowIRC1to 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:
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…
Amber LEDs: Current vs. Voltage
Posted by Ed in Electronics Workbench, Software on 27-December-2012
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:
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
Red LEDs: Current vs. Voltage Sorting
Posted by Ed in Electronics Workbench, Software on 26-December-2012
Running ten random red LEDs (taken from the bag of 100 sent halfway around the planet) through the LED Curver Tracer produces this plot:
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:
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);
}














Blowback