The Smell of Molten Projects in the Morning

Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.

Category: Electronics Workbench

Electrical & Electronic gadgets

  • LF Crystal Tester: DDS Buffer Amp

    The Big Ideas: the DDS output, being more-or-less constant, needs a variable-gain amp to set the crystal drive level. The amp also fixes the impedance mismatch between the DDS output and the crystal, which may not be much of a problem for the (very) high ESR quartz tuning fork resonators in play.

    The AD9850 DDS output feeds a 70 MHz (-ish) elliptical reconstruction filter chopping off image frequencies descending from the 125 MHz sampling clock, with a 100 Ω (-ish) output impedance that’s just about purely resistive at 60 kHz. An on-board 3.9 kΩ resistor (labeled with 392 on their schematic) sets the full-scale output current to 10 mA for a peak voltage of 1 V. The module uses only the + output of the differential pair, which means the sine wave runs from 0 V to 1 V: 1 Vpp = 500 mVpeak = 353 mVrms (ignoring the 500 mV offset).

    Pin header J3 normally sports a jumper to connect the 3.9 kΩ RSET resistor, but you can insert an external resistor to increase the resistance and decrease the output current:

    IOUT = 32 × 1.248 V / RSET

    A little hot-melt glue action produced a suitable lashup from a 5 kΩ trimpot:

    AD9850 DDS Module - 5 k external RSET trimpot
    AD9850 DDS Module – 5 k external RSET trimpot

    The pillars of green wire insulation forestall screwdriver shorts to the bare pin headers, although that’s less of risk with the upper insulating foam sheet in place:

    Crystal Tester - First Light
    Crystal Tester – First Light

    A 5 kΩ trimpot can vary the output voltage downward by a factor of 2 = -6 dB, more or less.

    All the quartz tuning fork resonator specs I’ve found, none of which may apply to the units on hand, seem to require no more than 1 µW drive. Given a resonator’s equivalent series resistance of around 20 kΩ (for real!), the drive voltage will be 150 mV (-ish):

    1 µW = V² / 20 kΩ, so V = sqrt(20×10³) = 141 mV

    The nominal version of the crystal tester had a 50 Ω input impedance, so I picked a MAX4165 op amp with mojo sufficient for anything over 25 Ω; in retrospect, a lighter load than 48 Ω would be fine.

    In any event, the amp looks like this:

    MAX4165 Buffer Amp
    MAX4165 Buffer Amp

    What looks like a DIP switch is really the 3×2 jumper header just to the right of the foam insulation, in front of the SOT23 space transformer PCB carrying the MAX4165. No jumper = 0 dB gain, then 6 dB steps upward from there. The -6 dB trimpot range gives more-or-less continuous output tweakage across 24 dB, -6 dB to +18 dB, which is certainly excessive. The 24 Ω terminating resistors provide 6 dB loss into the crystal, so the effective range is -12 to +12 dB, with 0 dB = 350 mVrms and -6 dB = 150 mVrms (-ish) at the crystal.

    It’s a non-inverting amplifier, which (also in retrospect) probably isn’t a win:

    • Yet Another Bypass Cap on the cold end of the gain-setting resistors
    • Overly elaborate VCC/2 biasing to maintain sufficiently high input impedance

    I’m reasonably sure all those big caps contribute to some low-level motorboating, but haven’t tracked it down.

  • LF Crystal Tester: Pretty Plots

    A slight modification spits out the (actual) frequency and dBV response (without subtracting the 108 dB intercept to avoid negative numbers for now) to the serial port in CSV format, wherein a quick copypasta into a LibreOffice Calc spreadsheet produces this:

    Spectrum-32
    Spectrum-32

    Changing the center frequency and swapping in a 60 kHz resonator:

    Spectrum-60
    Spectrum-60

    Much prettier than the raw scope shot with the same data, there can be no denyin’:

    Log V vs F - 32766 4 Hz - CX overlay
    Log V vs F – 32766 4 Hz – CX overlay

    I think the wobbulations around the parallel resonant dip come from the excessively hugely too large 10 µF caps in the signal path, particularly right before the log amp input, although the video bandwidth hack on the AD8310 module may contribute to the problem. In any event, I can see the log amp output wobbling for about a second, which is way too long.

    Anyhow, the series-resonant peaks look about 1 Hz wide at the -3 dBV points, more or less agreeing with what I found with the HP 8591 spectrum analyzer. The series cap is a bit smaller, producing a slightly larger frequency change in the series resonant frequency: a bit under 2 Hz, rather than the 1 Hz estimated with the function generator and spectrum analyzer.

    I still don’t understand why the parallel resonant dip changes, although I haven’t actually done the pencil pushing required for true understanding.

    Ain’t they lovely, though?

  • Amazon Packaging

    The ample padding around this bag of fragile pecans leaves nothing to be desired:

    Amazon - well-packed pecans
    Amazon – well-packed pecans

    They’re firmly held in place on all sides, well protected from injury, and survived their shipping ordeal unscathed: not a bruise or break to be found. Well done!

    That’s not always the case. A padded envelope recently arrived with an obvious wound:

    Amazon - envelope perforations
    Amazon – envelope perforations

    Which came from its completely unprotected contents:

    Amazon - unprotected PCB pins
    Amazon – unprotected PCB pins

    Fortunately, the fragile glass front plate of that OLED managed to put itself flat against a small box inside the otherwise empty bag. it wasn’t broken, but due only to good fortune.

    “Static sensitive parts enclosed”, indeed …

  • LF Crystal Tester: First Light!

    After adding a MAX4165 buffer amp to drive the crystal test fixture at 1 µW and a MAX4255 to amplify the 1 mV crystal output by 40 dB, then removing the AD8310 log amp module’s 50 Ω terminator to better match the MAX4255’s output drive ability, this happened:

    Log V vs F - 32766 4 Hz - CX overlay
    Log V vs F – 32766 4 Hz – CX overlay

    That’s:

    • A 32.768 kHz quartz resonator
    • A ±2 Hz span centered on 32.766 kHz
    • 0.10 Hz frequency steps
    • The 22 pF cap out / in circuit (left & right peaks, respectively)
    • Log amp output at 24 mV/dBV, with a nominal -108 dBV intercept at 0 V

    With a 4 Hz span and 0.1 Hz steps, you get only 41 samples along the X axis: it’s supposed to look spotty.

    The 2.2 V response at the top of the left peak corresponds to 2.2 / 24 mV/dBV = 91.7 dBV, then you knock off the -108 dBV intercept to get -16.3 dBV. The valley at 1.88 V is 78.3 – 108 = -29.7 dBV, down about 13 dBV from the corresponding peak. The peak-to-baseline over on the right looks like 200 mV = 8 dBV.

    The AD8310 datasheet uses “intercept” in a manner I had not previously encountered. They plot the AD8310 output in volts against the input signal level in dBV, with the “intercept” marking the extrapolated point where the straight line with slope 24 mV/dBV crosses the X axis: the equation is volts = slope*(input dBV – intercept dBV). Back in the day, I learned the intercept was where the line crossed the Y axis at X=0, so the straight-line equation was simply y = slope*x + intercept. Took me a while to figure that out.

    Then subtract the 40 dB gain from the crystal output to the log amp to get -56 dbV = 1.6 mV. That’s close enough to the 1 mV before adding the MAX4255. All those numbers seem slightly squishy, but they’re close enough.

    The peaks are 13-ish spots apart, which corresponds to 1.3 Hz, which is roughly the 1 Hz I measured with the HP8591 spectrum analyzer. The baseline is down 8 dBV, not quite as much as the analyzer’s 13 dB at 1 Hz offset from the peaks.

    What’s not right: the parallel-resonant dip to the right of each peak should be at the same frequency for both traces, because it doesn’t vary with added series capacitance, but it’s pretty much tracking the series-resonant peak frequency.

    The amount of noise on the log amp output looks like 50 mV = 2 dBV. That’s a lot, compared to the 13 dBV response, but some judicious averaging may save the day.

    The 22 MHz GBW of the MAX4255 rolls off the high end at 220 kHz. I AC coupled the signal chain with 10 µF dipped tantalum caps from my lifetime supply, which may pass entirely too much of the low end; the settling time is way too long. This probably requires smaller caps and maybe an actual bandpass filter.

    The 50 mV-ish noise on the DAC output driving the X axis suggests my proto board layout isn’t up to the demands of this circuit: there shouldn’t be any noise in that direction.

    Some poking around suggests the OLED display is way noisier than you’d (well, I’d) expect. The faded-out lower section in the picture below suggests it’s refreshing one line = 128 pixels at a time. More study is indicated.

    But, if you squint hard enough, this lashup produces numbers in the right ballpark. Given that it’s a collection of cheap-as-dirt eBay modules flying in formation, that’s nothing to sniff at:

    Crystal Tester - First Light
    Crystal Tester – First Light

    Those “gold tone” SMA connectors really make it look like serious RF hardware, don’t they? [grin]

    The round twiddlepot floating on the white pillow trims the DDS output voltage by a factor of two = 6 dB. Combined with the 0-6-12-18 dB gain steps provided by the header in front of the MAX4165 (to the right of the pillow), you can set the drive voltage so the crystal gets (roughly) its rated 1 µW maximum drive power.

  • Proto Board Holder: Revised Screw Mounts

    Improving the crystal tester’s (nonexistent) grounding requires a band of copper tape around the inside of the proto board holder. Rather than cut the tape lengthwise to fit the holder, a new one will be just tall enough:

    Proto Board - 80x120 - revised inserts - Slic3r
    Proto Board – 80×120 – revised inserts – Slic3r

    While I was at it, I deleted the washer recesses, because those didn’t work out well, and fiddled the screw holes to put the inserts in from the bottom:

    Proto Board - 80x120 - revised inserts - detail - Slic3r
    Proto Board – 80×120 – revised inserts – detail – Slic3r

    Although the overhang inside the holes will be ugly, I’ll epoxy the inserts flush with the bottom and nobody will ever know.

    The copper tape now makes a tidy ground strap:

    Crystal Tester - ground strap - rear
    Crystal Tester – ground strap – rear

    With a gap in the front to eliminate the obvious loop:

    Crystal Tester - ground strap - front gap
    Crystal Tester – ground strap – front gap

    The OpenSCAD source code as a GitHub Gist:

    // Test support frame for proto boards
    // Ed Nisley KE4ZNU – Jan 2017
    // June 2017 – Add side-mount bracket, inserts into bottom
    Layout = "Frame";
    ClampFlange = true;
    Channel = false;
    //- Extrusion parameters – must match reality!
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.1;
    HoleWindage = 0.2;
    //- Screw sizes
    inch = 25.4;
    Tap4_40 = 0.089 * inch;
    Clear4_40 = 0.110 * inch;
    Head4_40 = 0.211 * inch;
    Head4_40Thick = 0.065 * inch;
    Nut4_40Dia = 0.228 * inch;
    Nut4_40Thick = 0.086 * inch;
    Washer4_40OD = 0.270 * inch;
    Washer4_40ID = 0.123 * inch;
    ID = 0;
    OD = 1;
    LENGTH = 2;
    Insert = [3.9,4.6,5.8];
    //- PCB sizes
    PCBSize = [80.0,120.0,1.6];
    PCBShelf = 1.0; // support rim under PCB
    Clearance = 2*[ThreadWidth,ThreadWidth,0];
    WallThick = 4.0;
    FrameHeight = IntegerMultiple(3/8 * inch,1.0);
    echo(str("Inner height: ",FrameHeight));
    ScrewOffset = 0.0 + Clear4_40/2;
    ScrewSites = [[-1,1],[-1,1]]; // -1/0/+1 = left/mid/right and bottom/mid/top
    OAHeight = FrameHeight + Clearance[2] + PCBSize[2];
    echo(str("OAH: ",OAHeight));
    FlangeExtension = 3.0;
    FlangeThick = IntegerMultiple(2.0,ThreadThick);
    Flange = PCBSize
    + 2*[ScrewOffset,ScrewOffset,0]
    + 2*[Washer4_40OD,Washer4_40OD,0]
    + [2*FlangeExtension,2*FlangeExtension,(FlangeThick – PCBSize[2])]
    ;
    echo(str("Flange: ",Flange));
    NumSides = 4*5;
    WireChannel = [Flange[0],15.0,3.0 + PCBSize[2]];
    WireChannelOffset = [Flange[0]/2,25.0,(FrameHeight + PCBSize[2] – WireChannel[2]/2)];
    //- Adjust hole diameter to make the size come out right
    module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes
    Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2);
    FixDia = Dia / cos(180/Sides);
    cylinder(r=(FixDia + HoleWindage)/2,h=Height,$fn=Sides);
    }
    //- Build things
    if (Layout == "Frame")
    difference() {
    union() { // body block
    translate([0,0,OAHeight/2])
    cube(PCBSize + Clearance + [2*WallThick,2*WallThick,FrameHeight],center=true);
    for (x=[-1,1], y=[-1,1]) { // screw bosses
    translate([x*(PCBSize[0]/2 + ScrewOffset),
    y*(PCBSize[1]/2 + ScrewOffset),
    0])
    cylinder(r=Washer4_40OD,h=OAHeight,$fn=NumSides);
    }
    if (ClampFlange) // flange for work holder
    linear_extrude(height=Flange[2])
    hull()
    for (i=[-1,1], j=[-1,1]) {
    translate([i*(Flange[0]/2 – Washer4_40OD/2),j*(Flange[1]/2 – Washer4_40OD/2)])
    circle(d=Washer4_40OD,$fn=NumSides);
    }
    }
    for (x=[-1,1], y=[-1,1]) { // screw position indexes
    translate([x*(PCBSize[0]/2 + ScrewOffset),
    y*(PCBSize[1]/2 + ScrewOffset),
    -Protrusion])
    rotate(x*y*180/(2*6))
    PolyCyl(Clear4_40,(OAHeight + 2*Protrusion),6); // screw clearance holes
    translate([x*(PCBSize[0]/2 + ScrewOffset),
    y*(PCBSize[1]/2 + ScrewOffset),
    -Protrusion])
    rotate(x*y*180/(2*6))
    PolyCyl(Insert[OD],OAHeight – PCBSize[2] – 3*ThreadThick + Protrusion,6); // inserts
    if (false)
    translate([x*(PCBSize[0]/2 + ScrewOffset),
    y*(PCBSize[1]/2 + ScrewOffset),
    OAHeight – PCBSize[2]])
    PolyCyl(1.2*Washer4_40OD,(PCBSize[2] + Protrusion),NumSides); // washer recess
    }
    translate([0,0,OAHeight/2]) // through hole below PCB
    cube(PCBSize – 2*[PCBShelf,PCBShelf,0] + [0,0,2*OAHeight],center=true);
    translate([0,0,(OAHeight – (PCBSize[2] + Clearance[2])/2 + Protrusion/2)]) // PCB pocket on top
    cube(PCBSize + Clearance + [0,0,Protrusion],center=true);
    if (Channel)
    translate(WireChannelOffset) // opening for wires from bottom side
    cube(WireChannel + [0,0,Protrusion],center=true);
    }
    // Add-on bracket to hold smaller PCB upright at edge
    PCB2Insert = [3.0,4.9,4.1];
    PCB2OC = 45.0;
    if (Layout == "Bracket")
    difference() {
    hull() // frame body block
    for (x=[-1,1]) // bosses around screws
    translate([x*(PCBSize[0]/2 + ScrewOffset),0,0])
    cylinder(r=Washer4_40OD,h=OAHeight,$fn=NumSides);
    for (x=[-1,1]) // frame screw holes
    translate([x*(PCBSize[0]/2 + ScrewOffset),0,-Protrusion])
    rotate(x*180/(2*6))
    PolyCyl(Clear4_40,(OAHeight + 2*Protrusion),6);
    for (x=[-1,1]) // PCB insert holes
    translate([x*PCB2OC/2,(Washer4_40OD + Protrusion),OAHeight/2])
    rotate([90,0,0])
    cylinder(d=PCB2Insert[OD],h=2*(Washer4_40OD + Protrusion),$fn=6);
    }

  • Generic I²C 128×64 OLED Displays: Beware Swapped VCC and GND

    A batch of 1.3 inch white I²C OLED displays arrived from halfway around the planet, so I figured I could run a quick acceptance test by popping them into the socket on the crystal tester proto board:

    White 1.3 inch OLED on crystal tester
    White 1.3 inch OLED on crystal tester

    The first one flat-out didn’t work, as in not at all. The original display continued to work fine, so I compared the old & new displays:

    OLED Modules - pinout difference
    OLED Modules – pinout difference

    Yup, swapped VCC and GND pins. I should be used to that by now.

    I rewired the socket, tried the new displays, undid the change, popped the original display in place, and all is right with the world. Somewhat to my surprise, all five new displays worked, including the one I’d insulted with reversed power.

  • Teledyne 732TN-5 Relay: Zowie!

    The first pass at the crystal tester used a manual jumper to switch the 33 pF series capacitor in / out of the circuit:

    Quartz crystal resonance test fixture
    Quartz crystal resonance test fixture

    With an Arduino close at hand, however, a relay makes somewhat more sense. For long-forgotten reasons, I have a small fortune in Teledyne 732TN-5 relays intended for RF switching:

    Teledyne 732TN-5 Relay
    Teledyne 732TN-5 Relay

    The 7820 date code on the side suggests they’ve been in the heap basically forever, although some fractions of Teledyne still exist and you can apparently buy the same relay today at 50 bucks a pop. It’s definitely overqualified for this job and you can surely get away with an ordinary DIP DPDT (or, heck, even SPST) relay.

    It seems I picked a hyper-bright white LED: the red ink tones it down a bit. Black might be more effective. A diffused LED may be in order.

    The “TN” suffix indicates a built-in transistor driver with a catch diode on the relay coil, so the relay needs power, ground, and a current drive into the transistor’s base terminal:

    Teledyne 732TN relay - drive schematic
    Teledyne 732TN relay – drive schematic

    Even with the internal catch diode, I ran the +5 V power through a 12 Ω resistor to a 10 µF cap in hopes of isolating the inevitable switching transients from the DDS and log amp. As a result, the turn-on transient isn’t much of a transient at all:

    Teledyne 732TN Relay - turn-on transient
    Teledyne 732TN Relay – turn-on transient

    The 560 mV drop suggests a 47 mA coil current through the 12 Ω resistor, just about spot on for a 100 Ω coil.

    The energy stored in the coil makes the turn-off transient much steeper:

    Teledyne 732TN Relay - turn-off transient
    Teledyne 732TN Relay – turn-off transient

    Note the 1.5 µs delay from the falling control input to the relay opening. Granted, it’s running at 4.7 V, not the rated 5 V, but that’s still rather peppy. The turn-on delay seems to be about the same, making the datasheet’s “6 ms nominal” operating time look rather conservative.

    Dang, that’s a nice gadget!