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.

Author: Ed

  • Arduino: Bit Fields Start Low

    The first bit in an Arduino bit field is the lowest-order bit.

    Set up a bit field to hold, oh, say, the data bits from the WWVB radio time code:

    struct WWVB_bits_ {
     unsigned char Minute_10:3;
     unsigned char Minute_1:4;
     unsigned char Hour_10:2;
     unsigned char Hour_1:4;
     unsigned char DOY_100:2;
     unsigned char DOY_10:4;
     unsigned char DOY_1:4;
    // unsigned char UT1_SIGN:3;
    // unsigned char UT1_CORR:4;
    // unsigned char Year_10:4;
    // unsigned char Year_1:4;
     unsigned char LY:1;
     unsigned char LS:1;
     unsigned char DST57:1;
     unsigned char DST58:1;
    };
    

    [Update: Remember, that struct is bass-ackwards. You want the most-significant fields on the bottom, rather than the top, so the bits fill them in properly. This is how I found out that’s true…]

    Coerce the bits into an unnatural union with an unsigned long int and create a variable:

    union WWVB_code_ {
     uint32_t WWVB_ul;
     struct WWVB_bits_ WWVB_bits;
    };
    
    union WWVB_code_ ReceivedBits;
    

    Then set a few bits to find out how the compiler arranges things:

     ReceivedBits.WWVB_bits.DST57 = 1;
     ReceivedBits.WWVB_bits.DST58 = 1;
     ReceivedBits.WWVB_bits.Hour_1 = 9;
     ReceivedBits.WWVB_bits.Minute_1 = 4;
    
     sprintf(PrintBuffer,"Bit field: %08lX",ReceivedBits.WWVB_ul);
     Serial.println(PrintBuffer);
    

    Which produces this informative line:

    Bit field: 03001220
    
    

    Soooo, rats, the DST bits are on the left and the Minute bits are on the right. That doesn’t tell you how they’re actually laid out in memory, but if you’re doing the interrupt handler in C, then you just stuff the incoming MSB-first bits from the radio directly into the right side of the int and let them slide leftward.

    If you count ’em up, you’ll find that the commented-out bits allow the remainder to fit into an unsigned long int, which is all of 32 bits on the Arduino. You can actually use an unsigned long long int to get 64 bits, but it seems Arduino bit fields can’t extend beyond 32 bits.

    There are ways around that, particularly seeing as how I’m using a simpleminded interpreter to parse the incoming bits, but I’ll doodle about that later.

    Insert the usual caveats about portability and interoperability and maintainability and … well, you know why bits fields are such a bad idea.

  • C-Max CMMR-6P-60 Receiver Polarity

    It goes without saying: the positive receiver output (TCO: Time Code Output) on the CMMR-6P-60 WWVB receiver board tracks the received RF amplitude. In fact, it does go without saying: nowhere does the (rather skimpy) board doc mention that fact.

    Receiver Delay
    Receiver Delay

    Here’s the output, driven from my WWVB simulator in the Basement Lab. Note that reduced RF corresponds to the active part of the bit, so the output goes low when the bit starts. Conversely, the inverted output (TCON: Time Code Output Negative) goes high, which may be more useful for some purposes.

    Of interest: there’s maybe 12 ms of delay on the leading edge and 5 ms on the trailing edge. The received pulse duration is therefore different than the transmitted pulse by some amount. The doc says less than ±35 ms, so it can be longer than what you expect.

    I can’t measure the actual RF amplitude at the receiver, but it’s barely above the rather high ambient noise level near all the test equipment. The receiver gives up a few feet away from the simulator’s bar antenna, which means I’m (probably) not corrupting WWVB receivers for miles around.

  • Calculating the Current Setting Resistor for a TLC5916 LED Driver

    The Official TI TLC5916 Datasheet is far more confusing on the subject of calculating the external current-setting resistor than it really needs to be.

    The basic relations appear on page 17, but the detailed discussion on page 22 will make your head explode. Here’s how I see it …

    VRext, the voltage across Rext, the reference resistor, is controlled by a 1.26 V band gap reference multiplied by VG, the voltage gain of a 7-bit DAC.

    VRext = 1.26 V * VG
    

    The DAC produces VG from the HC (High Current) bit and the six CC0..CC5 (Configuration Code register) bits. HC selects a range and CC0..CC5 are a binary multiplier.

    VG = ((1 + HC) * (1 + CC/64) ) / 4

    The minimum VG value for HC = 0 is 1/4 and the max is 1/4 + 63/256 = 127/256:

    VG = (1 + CC/64)/4 = 1/4 + CC/256

    The minimum VG value for HC = 1 is 1/2 and the max is 1/2 + 63/128 = 127/128:

    VG = (1 + CC/64)/2 = 1/2 + CC/128

    The  absolute minimum VG = 0.25 happens when HG and CC0..5 are all zero. Conversely, absolute maximum VG = 0.992 happens when they’re all one.

    That means VRext varies from

    VRext (min) = 1.26 * 0.25 = 0.315 V

    to

    VRext (max) = 1.26 * 0.992 = 1.25 V

    There’s a kink in the middle of that range, as shown in the graph on page 23, where HC shifts gears to the high range.

    Rext then converts that voltage into Iref, the LED reference current:

    Iref = VRext / Rext
    

    The CG (Current Gain) multiplies Iref to set the actual LED current. The CM bit controls CG. You can either use the weird exponential formula in the datasheet or just remember:

    • CM = 0 selects CG = 5
    • CM = 1 selects CG = 15

    Sooo, finally, Iout (LED current) is just:

    Iout = CG * Iref

    Knowing all that, you can choose Rext so that the absolute maximum current doesn’t exceed the LED rating:

    Iout (max) = 20 mA = 15 * (1.25 V / Rext)

    Which tells you that Rext must be no less than

    Rext = (15 * 1.25 V) / (0.02 A) = 938 Ω

    Then you can find the minimum current through the LEDs:

    Iout (min) = 5 * (0.315 V / 938 Ω) = 1.68 mA

    Now, given that the chip’s specs seem to indicate that the minimum regulated current is 5 mA, I suspect that all bets are off as regards linearity and matching and all that. Given that my friend wants a really dim clock, I think this will work out just fine…

    Memo to Self: For whatever reason (and it must be a real stinker), TI numbers the CC bits backwards. CC0 is the Most Significant Bit, CC5 is the Least Significant Bit.

    And because CC5 is the first bit shifted into the TLC5916’s shift register, you must run the Arduino hardware-assisted SPI in LSB-first mode.

  • Arduino: Remember the Ampersand with PROGMEM Variables!

    Got burned by this one again…

    The pgm_read_whatever() functions/macros require the address of the variable stored in PROGMEM (the program Flash ROM), not the variable’s value. More on that in the Arduino doc there.

    So, for example, this does not work, no matter how easy it might be to type:

    #include <avr/pgmspace.h>
    #define TZ_INDEX     3     // hardcoded for simplicity
    PROGMEM prog_char TimeZone[8] = {0,-3,-4,-5,-6,-7,-9,-10};
    char TZOffset;
    TZOffset = pgm_read_byte(TimeZone[TZ_INDEX]);
    

    What you really want looks like this:

    TZOffset = pgm_read_byte(&TimeZone[TZ_INDEX]);
    

    See the difference?

    Maybe next time I’ll remember that…

  • Crystal Properties: Quick-and-Dirty

    Grab-bag 12 MHz crystals
    Grab-bag 12 MHz crystals

    Having cooked up a simpleminded 12.000 MHz crystal oscillator for the WWVB simulator and gotten something close-to-but-not-spot-on the right frequency, I thought it’d be interesting to measure some of my 12 MHz crystal grab-bag collection.

    This is in the nature of exploratory surgery to see if anything more complex is warranted.

    The general procedure is covered in excellent detail by K8IQY there. His method includes building a precision oscillator and a very nice test fixture, plus a bit of straightforward commercial instrumentation. Well worth perusing.

    I have the good fortune to own an HP8591E spectrum analyzer (hereinafter, the SA) with a tracking generator (ditto, TG) that can calibrate itself to a fare-thee-well. I had to let it warm up for a few hours, as the Basement Laboratory is a wee bit colder than it really likes. Heck, it’s a lot colder than I really like, for whatever that’s worth.

    Alligator-clip crystal connections
    Alligator-clip crystal connections

    I bodged up some random coax and a few clip leads into a fur-ball circuit in front of the SA. This is not the right way to do it; a fixture with mechanical stability is the first step toward solid numbers.

    The tracking generator & analyzer present 50 Ω impedances to the outside world, which is much too high for the expected crystal series resistance, but it’ll do for a start. You want to measure the crystal in an environment that’s pretty close to what it’ll be built into, so as to get meaningful numbers.

    The crystal sits in series between the TG and the SA, looking a lot like a simpleminded (and badly terminated) crystal filter. Write down the sum of the crystal’s source (TG) and load (SA) termination resistances: 50+50 = 100.

    12 MHz Crystal - Fs and Fp
    12 MHz Crystal – Fs and Fp

    Center frequency to 12 MHz (or whatever the crystal’s nominal frequency might be), span to 200 kHz, TG at -10 dBm, get a display of the series resonance peak and the parallel resonance dip, poke the Auto Tracking Adjust button to get the TG lined up with the SA. Span to 100 kHz, tune center frequency for best picture.

    That shows the series resonant peak and parallel resonance notch, but it’s way too broad for any decent resolution in the measurements.

    Soooo…

    Poke marker peak search to find the series peak, poke marker to center frequency to slap the peak to the middle of the screen.

    12 MHz Crystal - Fs Bandwidth
    12 MHz Crystal – Fs Bandwidth

    Span down to 5 kHz, which sets the resolution bandwidth to 100 Hz. Poke auto tracking adjust again, because it’ll be way off. Manual adjust moves in too-large steps.

    Marker peak search, marker to center frequency. Tick the reference level down enough to get the peak near the top of the graticule, set 3 dB/div to get enough vertical resolution. Another peak search, to center, then write down the peak frequency Fs. Single sweep to freeze the display.

    Marker delta, dial up the marker frequency, poke marker amplitude, set -3 dB, read off the marker delta frequency. Dial the marker frequency down to the other side of the peak, set amplitude -3 dB again, read off the frequency again. Compute the crystal’s 3-dB bandwidth BW as the sum of those two values.

    Marker normal, auto-sweep to get a live trace again, auto tracking adjust again as needed. Display line on, set to peak for amplitude reference.

    Replace the crystal with a 50-Ω (or so) non-inductive twiddlepot, twiddle to set level to the display line. Measure twiddlepot resistance, which will be equal to the crystal’s series resistance. Write down Rs (a.k.a, the ESR).

    Measure crystal capacitance: short both leads, measure to case. Write down C0. I used an Autek RF-1 with a homebrew fixture, which has 1 pF resolution at RF frequencies; obviously, you pick a frequency well below Fs.

    With all those numbers in hand, compute the crystal’s motional parameters…

    Lm = (100 + Rs) / (2π BW)

    Cm = 1 / ( (2π Fs)^2 Lm)

    Q = (2π Fs Lm) / Rs

    The three crystals from the grab bag are all reasonably close to what you’d expect.

    12 MHz Crystal Parameters 50 Ohm Term
    Marking Fs -3 dB Lo -3 dB Hi Fp-Fs Rs C0 BW Lm Cm Q

    MHz Hz Hz kHz Ω pF Hz mH fF
    TEW 5C 11.996962 1030 920 24.8 8.9 3 1950 8.16 21.56 69133
    HCI 1200 11.997500 1087 1025 26.8 5.0 2 2112 7.54 23.35 113618
    ECS 12.00 11.999975 1162 1125 26.3 6.2 2 2287 6.96 25.28 84635

    Memo to Self:

    The Lm equation shows why you really need lower termination resistances. The K8IQY fixture involves 4:1 matching transformers on each side of the crystal to get the generator down to 12.5-ish Ω and the output back up to 50 Ω. Time to rummage through my pile of ferrite toroids.

    An accurate BW with excellent (1 Hz) resolution feeds directly into better Lm values. I’m not convinced I have the SA set up for that much resolution.

    Absolute Fs accuracy isn’t needed, but high resolution is. With that many digits, thermal drift is a real issue, hence the repeated TG tweakage.

    I also need better resolution for Rs and C0. The former needs a smaller twiddlepot. Both could use a better meter with more resolution and zero-offset for low values. Measuring pF caps requires a good fixture.

  • Buttonless Panic Button

    Buttonless Panic Button
    Buttonless Panic Button

    I can understand why it no longer has a button, but in which parallel universe does it make sense to put a clearly labeled Panic Button inside a high-school cafeteria, right next to the door?

    Come on, now, really?

    I managed to squelch the temptation to poke the remains of the switch stem with a pencil…

  • ICOM IC-Z1A Tone Squelch: Fixed?

    ICOM IC-Z1A HT with UT-93 Tone Board
    ICOM IC-Z1A HT with UT-93 Tone Board

    A few days ago I rode off to an eye doctor appointment and my ladies rode off later to meet me at the grocery store after they stopped in the garden to harvest root crops. This sort of thing is easy enough to synchronize with amateur radio, but this morning I didn’t hear a thing until they rolled up beside me in the store parking lot.

    It seemed they could hear each other and me, but I couldn’t hear either of them. We’re all on 144.39 MHz, the APRS data frequency, with 100 Hz tone squelch to keep the robots out of our ears. Our daughter has the GPS APRS tracker feeding data into the mic input, which is why we’re using a data channel for tactical comm.

    This has happened once or twice before, but it’s very intermittent. I now had sufficient motivation to disconnect the radio, an ancient ICOM IC-Z1A, from the bike and pith it on the Electronics Workbench for examination. The UT-93 Tone Squelch board is unplugged & flipped over, resting on the front half of the radio body at the lower-left of the photo.

    Turns out that there’s nothing visibly wrong in there. I suspect it’s a molecule or two of oxidation on the (gold-plated!) connector between the UT-93 and the main board, because the UT-93’s held firmly in position by the black foam square you can see in the lower-left of the photo. The small white plug near the top of the UT-93 mates with the equally small socket on the main board, just to the left of the lithium secondary cell in the middle.

    It’s all CMOS logic, of course, and there’s no actual load current involved. That’s the worst condition for contacts, as a dry connection simply doesn’t produce enough energy to burn through the least hint of oxidation. That’s why they use gold plating on connectors, but it’s been a long time since that board has moved at all; the foam square is deeply indented.

    So I wiggled & jiggled all the ribbon-cable connectors while I was in there, buttoned everything back up, and the tone decoding works again. I hope this will continue…

    Memo to Self: remove only the four black corner screws on the upper case, plus the two silver screws near the very bottom inside the battery compartment, and the two halves pop apart. No need to remove the mic and earphone plugs, whew!