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

  • Arduino Mega: Showstopper Workaround

    The discussion following that post gave me enough impetus to figure this out. What I have here is not a complete solution, but it seems to solve the immediate problem.

    Downside: this will not survive the next regular system update that touches the gcc-avr package (yes, it’s the avr-gcc compiler and the gcc-avr package). Hence, I must write down the details so I can do it all over again…

    To review:

    The problem is that the avr-gcc cross-compiler produces incorrect code for Atmega1280-class chips with more than 64 KB of Flash space: a register isn’t saved-and-restored around a runtime routine that alters it. Simple sketches (seem to) run without problems, but sketches that instantiate objects crash unpredictably. Because Arduino sketches depend heavily on various objects (like, oh, the Serial routines), nontrivial sketches don’t work.

    The workaround is to patch the library routine that invokes the constructors, as detailed in that gcc bug report, to push / pop r20 around the offending constructors. The patch tweaks two spots in the libgcc.S source file, which then gets built into an assortment of chip-specific libgcc.a files during the compile.

    I was highly reluctant to do that, simply I’ve already installed the various gcc packages using pacman (the Arch Linux package manager) and really didn’t want to screw anything up by recompiling & reinstalling gcc from source. It’s certainly possible to update just the avr portion, but I don’t know exactly how to do that and doubt that I could get it right the first time… and the consequences of that catastrophe I don’t have time to deal with.

    So I elected to build the avr cross-compiler from source, verify that the as-built libgcc.a file was identical to the failing one, apply the patch, recompile, then manually insert the modified file in the right spot(s) in my existing installation. This is less manly than doing everything automagically, but has a very, very limited downside: I can easily back out the changes.

    Here’s how that went down…

    The instructions there (see the GCC for the AVR target section) give the overview of what to do. The introduction says:

    The default behaviour for most of these tools is to install every thing under the /usr/local directory. In order to keep the AVR tools separate from the base system, it is usually better to install everything into /usr/local/avr.

    Arch Linux has the tools installed directly in /usr, not /usr/local or /usr/local/avr, so $PREFIX=/usr. Currently, they’re at version 4.5.1, which is typical for Arch: you always get the most recent upstream packages, warts and all.

    Download the gcc-g++ (not gcc-c++ as in the directions) and gcc-core tarballs (from there or, better, the gnu mirrors) into, say, /tmp and unpack them. They’ll both unpack into /tmp/gcc-4.5.1, wherein you create and cd into obj-avr per the directions.

    I opted to feed in the same parameters as the Arch Build System used while installing the original package, rather than what’s suggested in the directions. That’s found in this file:

    /var/abs/community/gcc-avr/PKGBUILD
    

    Which contains, among other useful things, this lump of command-line invocation:

    ../configure --disable-libssp \
                   --disable-nls \
                   --enable-languages=c,c++ \
                   --infodir=/usr/share/info \
                   --libdir=/usr/lib \
                   --libexecdir=/usr/lib \
                   --mandir=/usr/share/man \
                   --prefix=/usr \
                   --target=avr \
                   --with-gnu-as \
                   --with-gnu-ld \
                   --with-as=/usr/bin/avr-as \
                   --with-ld=/usr/bin/avr-ld
    

    Yes, indeed, $PREFIX will wind up as /usr

    Feeding that into ./configure produces the usual torrent of output, ending in success after a minute or two. Firing off the make step is good for 15+ minutes of diversion, even on an 11-BogoMIPS dual-core box. I didn’t attempt to fire up threads for both cores, although I believe that’s a simple option.

    The existing compiler installation has several libgcc.a files, each apparently set for a specific avr chip:

    [ed@shiitake tmp]$ find /usr/lib/gcc/avr/4.5.1/ -name libgcc.a
    /usr/lib/gcc/avr/4.5.1/libgcc.a
    /usr/lib/gcc/avr/4.5.1/avr35/libgcc.a
    /usr/lib/gcc/avr/4.5.1/avr3/libgcc.a
    /usr/lib/gcc/avr/4.5.1/avr51/libgcc.a
    /usr/lib/gcc/avr/4.5.1/avr4/libgcc.a
    /usr/lib/gcc/avr/4.5.1/avr6/libgcc.a
    /usr/lib/gcc/avr/4.5.1/avr5/libgcc.a
    /usr/lib/gcc/avr/4.5.1/avr31/libgcc.a
    /usr/lib/gcc/avr/4.5.1/avr25/libgcc.a
    

    The key to figuring out which of those files need tweaking lies there, which says (I think) that the Atmega1280 is an avr5 or avr51. Because I have an Arduino Mega that’s affected by this bug, I planned to tweak only these files:

    /usr/lib/gcc/avr/4.5.1/libgcc.a
    /usr/lib/gcc/avr/4.5.1/avr51/libgcc.a
    /usr/lib/gcc/avr/4.5.1/avr5/libgcc.a
    

    I have no idea what the top-level file is used for, but … it seemed like a good idea.

    Now, I innocently expected that the libgcc.a files for a 4.5.1 installation would match the freshly compiled files for a 4.5.1-from-source build, but that wasn’t the case. I don’t know what the difference might be; perhaps there’s an embedded path or timestamp or whatever that makes a difference?

    The Arch Linux standard installation of gcc 4.5.1 has these files:

    $ find /usr/lib/gcc/avr/4.5.1/ -iname libgcc.a -print0 | xargs -0 ls -l
    -rw-r--r-- 1 root root 2251078 Sep  4 16:26 /usr/lib/gcc/avr/4.5.1/avr25/libgcc.a
    -rw-r--r-- 1 root root 2256618 Sep  4 16:26 /usr/lib/gcc/avr/4.5.1/avr31/libgcc.a
    -rw-r--r-- 1 root root 2252506 Sep  4 16:26 /usr/lib/gcc/avr/4.5.1/avr35/libgcc.a
    -rw-r--r-- 1 root root 2256310 Sep  4 16:26 /usr/lib/gcc/avr/4.5.1/avr3/libgcc.a
    -rw-r--r-- 1 root root 2250930 Sep  4 16:26 /usr/lib/gcc/avr/4.5.1/avr4/libgcc.a
    -rw-r--r-- 1 root root 2251846 Sep 27 12:58 /usr/lib/gcc/avr/4.5.1/avr51/libgcc.a
    -rw-r--r-- 1 root root 2251550 Sep 27 12:58 /usr/lib/gcc/avr/4.5.1/avr5/libgcc.a
    -rw-r--r-- 1 root root 2252458 Sep  4 16:27 /usr/lib/gcc/avr/4.5.1/avr6/libgcc.a
    -rw-r--r-- 1 root root 2251474 Sep 27 12:57 /usr/lib/gcc/avr/4.5.1/libgcc.a
    

    The compilation-from-source using the gcc 4.5.1 tarballs has these files:

    $ pwd
    /tmp/gcc-4.5.1/obj-avr
    $ find -iname libgcc.a -print0 | xargs -0 ls -l
    -rw-r--r-- 1 ed ed 2250258 Sep 27 15:51 ./avr/avr25/libgcc/libgcc.a
    -rw-r--r-- 1 ed ed 2255798 Sep 27 15:51 ./avr/avr31/libgcc/libgcc.a
    -rw-r--r-- 1 ed ed 2251686 Sep 27 15:51 ./avr/avr35/libgcc/libgcc.a
    -rw-r--r-- 1 ed ed 2255490 Sep 27 15:51 ./avr/avr3/libgcc/libgcc.a
    -rw-r--r-- 1 ed ed 2250110 Sep 27 15:51 ./avr/avr4/libgcc/libgcc.a
    -rw-r--r-- 1 ed ed 2251838 Sep 27 15:51 ./avr/avr51/libgcc/libgcc.a
    -rw-r--r-- 1 ed ed 2251550 Sep 27 15:51 ./avr/avr5/libgcc/libgcc.a
    -rw-r--r-- 1 ed ed 2251638 Sep 27 15:52 ./avr/avr6/libgcc/libgcc.a
    -rw-r--r-- 1 ed ed 2251474 Sep 27 15:52 ./avr/libgcc/libgcc.a
    -rw-r--r-- 1 ed ed 2250258 Sep 27 15:51 ./gcc/avr25/libgcc.a
    -rw-r--r-- 1 ed ed 2255798 Sep 27 15:51 ./gcc/avr31/libgcc.a
    -rw-r--r-- 1 ed ed 2251686 Sep 27 15:51 ./gcc/avr35/libgcc.a
    -rw-r--r-- 1 ed ed 2255490 Sep 27 15:51 ./gcc/avr3/libgcc.a
    -rw-r--r-- 1 ed ed 2250110 Sep 27 15:51 ./gcc/avr4/libgcc.a
    -rw-r--r-- 1 ed ed 2251838 Sep 27 15:51 ./gcc/avr51/libgcc.a
    -rw-r--r-- 1 ed ed 2251550 Sep 27 15:51 ./gcc/avr5/libgcc.a
    -rw-r--r-- 1 ed ed 2251638 Sep 27 15:52 ./gcc/avr6/libgcc.a
    -rw-r--r-- 1 ed ed 2251474 Sep 27 15:52 ./gcc/libgcc.a
    

    The top-level files have the same size, but are not identical:

    $ diff ./avr/libgcc/libgcc.a ./gcc/libgcc.a
    Binary files ./avr/libgcc/libgcc.a and ./gcc/libgcc.a differ
    

    Haven’t a clue what’s going on with different files in different spots, but I saved the existing files in the installed tree as *.base and copied the new ones from ./gcc/avr* into place. While there are many ways to crash a program, the AnalogInOutSerial demo program ran correctly on a Duemilanova (presumably with the existing libgcc.a) and failed on the Mega (with the recompiled libgcc.a). Save those files as *.rebuild just in case they come in handy.

    Manually change the libgcc.S source file (it’s only four lines, I can do this), recompile, and the build process recompiles only the affected files; that’s comforting. Copy those into the installed tree and, lo and behold, the demo program now runs on both the Duemilanova and the Mega.

    While it’s too soon to declare victory, the hardware bringup program I’m writing also works, so the initial signs are good.

    Thanks to Mark Stanley for blasting me off dead center on this. I didn’t do a complete install, but he got me thinking how to make the least disruptive change…

    And a tip o’ the cycling helmet to the whole Free Software collective for making a mid-flight patch like this both feasible and possible: Use The Source!

  • Tour Easy: Underseat Pack Repair Finished

    So, after a bit more than a year, I replaced the cracked backing plate in the other ERRC underseat pack on my Tour Easy. The first plate held up much better than I expected: hasn’t cracked or poked through the pack fabric.

    This repair followed the same outline, including cutting off the ripped netting on the outside of the pack and marching the pack into the clothes washer for a spin with a few shop rags. Reassembled everything, put it back on the bike, and … the new aluminum extrusion across top  of the plate smacked firmly into the water bottle holder clamped to the rear of the seat frame for the amateur radio.

    Underseat pack vs radio holder
    Underseat pack vs radio holder

    The extrusion is the lump running horizontally, just under the seat cushion. The corner of the pack extended rearward (left) of the water bottle holder’s black plastic body.

    The original flexy plastic pack plate simply bent out of the way, but that’s not going to work now.

    So I loosened the clamp, moved it a bit more to the right, and tightened it up again. I’d originally located it at the far right end of the straight part of the seat frame, so it’s now edging into the curved part that eventually forms the right side of the frame, but it’s good enough.

    My shop assistant says she wants another water bottle holder for an actual water bottle on her bike. I say she should just go to the shop and make whatever she wants, then install it. Negotiations continue…

  • Copper Tape for PCB Edge Binding

    I’m laying out a PCB with ampere load currents, millivolt sense voltages, and PWM drive, all connected to an Arduino’s strictly digital ground layout through the usual headers. While I’ve laid the board out with the high-current stuff over there, the sense inputs here, and the PWM as far off in its own corner as possible, I fear this will get ugly.

    One step to reducing the noise involves a decent ground system. The Arduino pretty much eliminates the whole single-point ground concept, so I’m using a double-sided ground plane with plenty of Z-wire stitching , plus copper tape around the edge binding the top and bottom planes.

    Copper tape on PCB edge
    Copper tape on PCB edge

    The PCB is 60 mils thick, so I cut four copper foil strips about 3/16-inch wide, folded them around the board edges, then burnished the surfaces flat.

    Although the tape has adhesive on one side which is allegedly conductive, I figured running a solder bead along the edges couldn’t possibly hurt. That worked out reasonably well, if you don’t mind blobular solder along the edge of your board.

    Copper tape solder joint
    Copper tape solder joint

    The joint along the bottom edge shows one problem: some adhesive oozed out while soldering and formed a barrier. I think that happened along the tape edges from the outside of the roll, because it’s most prominent along two board edges.

    Memo to Self: Slice off and discard the outer few millimeters. Mask the outer board edge for a solid pour, not a hatch.

  • Sharpie as PCB Etch Resist

    Because my hombrew circuit boards don’t have plated-through holes, I solder Z-wires from top to bottom. This entails little more than a solder blob around the wire on each side, but this time I wondered if having a slightly larger solid-copper area on each surface would be an improvement. Regrettably, I wondered this after masking the board.

    Because I use an Ultra-fine-point Sharpie to touch up pinholes & suchlike, I decided to try it on larger areas by simply coloring in a few of the openings in the ground-plane grid.

    Sharpie etch mask - Results 1
    Sharpie etch mask – Results 1

    Short answer: doesn’t work so well.

    However, I’m using direct etching: rubbing ferric chloride on the masked PCB with a sponge. The abrasion probably wears the Sharpie ink off the surface and then the copper begins etching as usual. If I were doing this with normal agitation / aeration, perhaps a Sharpie mask would work better.

    This is also 1-ounce copper, so there’s twice as much etching going on. Perhaps half-ounce copper would vanish fast enough that the Sharpie mask would remain effective.

    A bit more detail, with another Z-wire pointed right at you.

    Sharpie etch mask - Results 2
    Sharpie etch mask – Results 2

    The grid is 20-mil wide on 50-mil centers, with 25-mil isolation to other signals. The “via” holes use a 24-mil drill.

    The row of dents just below the wire came from tiny openings in the mask that happen when Eagle poured the ground plane against the isolation surrounding the trace at the bottom. The toner-transfer resolution isn’t quite good enough to leave a clean opening and the etchant can’t quite reach the bottom to dig out the copper.

    Memo to Self: Next time, try a 100-mil square pad around the via, centered on a grid intersection to fill in four adjacent openings.

  • Arduino Mega: Showstopper!

    I planned to use an Arduino Mega for an upcoming Circuit Cellar project, but … it doesn’t work. Well, it works, but under very limited circumstances.

    The problem manifests itself as a complete crash / lockup under very straightforward conditions: attempting to use the serial output will suffice. This unmodified example sketch fails: AnalogInOutSerial.

    After considerable Googling, there’s the showstopper: the gcc-avr compiler fails to save-and-restore a register that gets clobbered by the object constructors. Simple code doesn’t instantiate any objects, so it works fine. The serial failure is just a symptom, which means the various workarounds suggested in the forums don’t fix the general case.

    The patch offered for gcc-avr is basically four lines (a pair of save / restores on R20), but requires recompiling what seems to be the entire AVR toolchain from source. That, alas, lies far beyond my capabilities… I could probably figure out enough to recompile it, but I’m very uncertain I could accomplish that without screwing up the main gcc compiler or the setup thereof.

    It is not clear to me that the many claims of “it works on this version” are correct. From the nature of the problem, the failures depend critically on addresses occupied, final layout of the program / data in Flash, and (most likely) the execution path. The “working” configurations / systems may simply not fail using the sample programs.

    This is on Arch Linux, for what it’s worth, with gcc-avr 4.5.1.

    If anybody can walk me through the process of rebuilding whatever must be rebuilt, preferably in a safe place, perhaps I can manually stuff the new file(s) into the proper spots(s) to replace the incorrect ones…

  • CPU Heatsink: Flattening Thereof

    I suppose I should have known better: the bottom of that heatsink wasn’t anywhere near flat. I think it mated directly with the top of the CPU through thermal grease, not a compliant pad.

    Curved copper heatsink surface
    Curved copper heatsink surface

    The obvious solution is to flycut the thing, which is where the Sherline’s limited Y-axis travel and teeny table put a cramp on your style. Normally, you’d put the length of the heatsink parallel to the X axis so the flycutter would clear on both ends, but there’s no obvious (read: quick and easy) way to clamp the thing that way.

    So I mounted it parallel to the Y axis, which meant I couldn’t get the flycutter completely off the near end. The first pass at Z=-0.1 mm, however, showed that not only was the surface curved, but it wasn’t parallel to the top of the fins (which were flat on the tooling plate). I suppose I should have expected that.

    This cut is has Z=-0.1 mm referred to the front end. It completely missed the other end:

    First flycut pass
    First flycut pass

    I flipped the heatsink around, measured the front-to-back tilt (about 0.16 mm), stuck a couple of brass shims under the front, and the second pass at Z=-0.05 mm from the new low point did the trick. Copper is nasty stuff and I did these cuts dry: the chips visible near the front are stuck firmly to the surface.

    Final flycut pass
    Final flycut pass

    I scrubbed both the heatsink and the spreader plate on some fine sandpaper atop the sacrificial side of my surface plate until they were all good. I can see the remaining flycutter marks, but I can’t feel them, and the plates slap solidly together with a pffff of escaping air:

    Flattened heatsink and spreader
    Flattened heatsink and spreader

    A dab of heatsink compound should work wonders; the maximum dissipation will be under 20 W, roughly comparable to that old K6 CPU, but now the heatsink will be contacting the entire hot surface.

  • Erasing a Hole

    Turning the plug OD
    Turning the plug OD

    The scrap pile disgorged a chunk of aluminum plate exactly the correct size for a heat spreader that will mate eight power FETS to that heatsink. The catch: a 1-1/4-inch deep hole tapped 1/4-20 for about 3/4 inch at almost the right spot along one end. Rather than sawing off Yet Another Chunk from the original plate, I figured it’d be more useful to just plug the hole.

    Note that this is somewhat different than the situation described there, where I screwed up by putting a hole in the wrong place. Here, I’m just being a cheapskate by making a piece of junk good enough to use in a project, rather than having it kick around in the scrap pile for another decade.

    Anyway.

    I turned a 3/8-inch diameter aluminum rod down to 1/4 inch for the threaded part and a bit under 0.200 inch to fit into the partially threaded end.

    A real machinist would single-point the thread, but I just screwed a die over it. The narrow end is slightly larger than the minor thread diameter, which helped get things started. Then a trial fit, saw off the excess on the skinny end, and apply a touch of the file to shape the end to mate with the hole’s drill-point bottom:

    Threaded hole plug
    Threaded hole plug
    Plug epoxied in place
    Plug epoxied in place

    I buttered up the plug with a generous helping of JB Weld epoxy and screwed it in. Toward the end of that process, the air trapped in the end became exceedingly compressed, to the extent I had to stop after each quarter-turn to let it ooze outward; eventually the hole gave off a great pffft as the remaining air pooted out. Unscrewed slightly to suck some epoxy back in, screwed it tight, and let it cure overnight.

    Squared-up block with plugged hole
    Squared-up block with plugged hole

    Sawed off the plug, filed the rubble more-or-less smooth, then squared it in the Sherline mill. The heatsink prefers to sit on a nice, smooth metal surface, so I flycut the other side of the block to get rid of a few dings and the entire anodized layer while I was at it.

    The epoxy ring doesn’t have a uniform width, because you’re looking at a cross section of the thread. The skinny part is the crest of the plug thread, the wide part is along one flank. Barely a Class 1A fit, methinks.

    New hole
    New hole

    Locate the midpoint of the block’s end, center-drill, then poke a new #29 hole 20 mm deep (I really do prefer metric!) for an 8-32 screw. The plug didn’t move at all during this process, pretty much as you’d expect. The chips came out of this hole in little crumbles, rather than the long stringy swarf from the solid aluminum on the other end.

    Using a simple peck drill canned cycle is just downright wonderful:

    G83 Z-20 R1 Q3 F100
    

    The rule of thumb is 3000 RPM with a feed 100 times the drill diameter. In this case, the drill is about 3 mm and calls for 300 mm/min, but the Sherline is happier with slower feeds. Maybe if I was doing production work, I’d push it harder.

    A real machinist would have a milling machine with a servo-driven spindle for rigid tapping, but I just screwed an ordinary hand tap into the holes.

    A bit of futzing converted a pair of solderless connectors into clips that capture the hooks on the ends of the heatsink’s springy wiffletree to secure the spreader to the heatsink. You can see the flycut surface peeking out from below the end of the heatsink. I should hit it with some fine abrasive to polish it out, but I think heatsink compound alone will do the trick.

    Heat spreader on heatsink
    Heat spreader on heatsink

    The next step: drilling-and-tapping eight more blind holes along the sides for the FETs. It’d be really neat to have a servo spindle…