Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.
Category: Software
General-purpose computers doing something specific
I’m using hardware-assisted SPI for a project, copied in my own boilerplate code, assigned the bits, and… it didn’t work.
Jammed hard with mysterious symptoms. Looked like a stack crash, looked like the hardware was broken, looked like a lot of things.
The final hint, found by stuffing Serial.print() statements in all the usual spots, was that the SPCR register mysteriously changed from the desired 0x71 to 0x61, without any of my code doing the writing.
Turns out that the Fine Manual has this to say:
Bit 4 – MSTR: Master/Slave Select
[snippage] If -SS is configured as an input and is driven low while MSTR is set, MSTR will be cleared, and SPIF in SPSR will become set. The user will then have to set MSTR to re-enable SPI Master mode.
I planned to use Arduino Pin 10 (PWM10) as the signal to latch the output shift registers, but because I’m developing the code on an Arduino Pro before making the circuit board, I hadn’t gotten around to initializing that pin… which, as you might expect, is also the -SS pin.
With the pin not set up as an output, it defaults to an input. My cut-n-paste code blipped the pin high and left it low to simulate latching the ‘595 shift registers… but, for input pins, writing a value simulates what would happen when an external signal drives the pin.
Soooo, I had inadvertently set -SS low, which turned off Master mode, which meant the hardware wasn’t going to send the next byte, which means SPIF wasn’t going to automatically go high when I dropped a byte in SPDR. The code, of course, waited until SPIF was clear before loading SPDR, then hung waiting for it to go high again.
As always, stupid errors are easy to fix after figuring things out, but ouch did it take a while…
Moral of the story: always initialize all the I/O pins! (But you knew that, right?)
The general idea is a simple light sensor that can cope with typical indoor illumination, brightening and dimming the digits on a clock so that it’s both visible in the daylight and doesn’t light up the room at night.
This circuit produces a voltage that varies more-or-less inversely with the photoresistance R. The “decade resistor” DR acts as a range selector: the output voltage will be 2.5 V when DR = R.
Yesterday I doodled about the voltage-vs-resistance curves and how DR works, showing the equations that spit out V when you know R, which is handy from a circuit-analysis standpoint (if you can say that with a straight face for a two-resistor circuit).
What you want is the equation that spits out R when you know V, because you can actually measure V. Rearranging the equation in the doodle above produces that equation
R = DR * (5 – V) / V
Actually, what you really want is log R, because your sensation of brightness varies logarithmically with the illumination intensity: each doubling of intensity makes the scenery twice as bright. So you could find R, apply a floating-point math package to it, and come up with log R.
There’s a better way.
CdS Resistance vs Voltage
Stand yesterday’s graph on its ear and flip it side-to-side to get this view of the same data points. It’s plotted with a log scale on the Y axis, because the resistance varies over such a huge range.
The dots represent the values of R produced by the equation above with DR = 1 kΩ. Those are the actual resistance values, at least according to the circuit model.
The midsection of those dots is pretty straight, so the diagonal line second from the bottom is a straight line “curve fit” with intercepts at
(0 V, log 10000)
and
(5 V, log 100)
The y = mx + b equation fitting that line is
log R = (-2/5) * V + 4
where the (-2/5) comes from the slope of the line:
(log 10000 – log 100) / (0 – 5)
and the (+ 4) comes from the intercept at (log 10000), as set by the value of DR. In fact, the intercept is 1+ (log DR), because it’s always a factor of 10 higher than the value of DR.
Now, what’s nice about that is the equation spits out log R directly, with just some multiply-divide-add action.
Changing DR by factors of 10 produces the other lines, so (as before) switching DR gives you a low-budget, wide-dynamic-range output.
DR need not be a power of 10, of course. The dashed line near the middle is DR = 3 kΩ, which puts the more-or-less linear region between about 20 kΩ (fairly dim) and 500 Ω (rather bright). That’s a useful range in my house and it might be close enough for my friend.
The dashed line approximating those points has an intercept at 1 + log 3000 = 4.5, so the overall equation is
log R = (-2/5) * V + 4.5
The approximation gets progressively worse below, say, V = 0.5 and above V = 4.5, so the outline of the algorithm is:
V < 0.5 = pretty dark: lowest digit intensity
V between 0.5 and 4.5: puzzle over log R
V > 4.5 = lots o’ light: max digit intensity
The Arduino ADC produces a 10-bit integer: 0 through 1023. Call that Vb (“V binary”), which you can scale to V like this
V = (5/1024) * Vb
Plugging that into the equation produces
log R = (-2/5) * (5/1024) * Vb + 4.5
log R = (-2/1024) * Vb + 4.5
The useful limits on Vb for the linear approximation are
V = 0.5 -> Vb = 1024 * 0.5/5 = 102
V = 4.5 -> Vb = 1024 * 4.5/5 = 922
Checking those limits against the actual formula for R
Vb = 102 -> log R = 4.3 (instead of 4.43)
Vb = 922 -> log R = 2.7 (instead of 2.52)
That’s about what you’d expect from the graph: the line is lower than the dots on the dim end (left) and higher on the bright end (right). On the other paw, getting log R without the floating-point math package makes up for that.
Now, given that you’re going to use a table lookup anyway, you don’t need any arithmetic on Vb at all. Shove all the stuff surrounding Vb to the other side of the equation
(log R – 4.5) * (-1024 / 2) = Vb
(4.5 – log R) * 512 = Vb
Precompute the left side for useful values of R, fill in the corresponding bit pattern to get the desired brightness, then index into the table with the measured Vb: shazam, log R -> brightness bits in one step!
If you’re exceedingly lucky, the brightness bits will be more-or-less in a binary sequence, in which case you can just right-shift Vb to get that number of bits and send ’em directly to the LED drivers. No table needed: one shift and you’re done!
So, for example, suppose you want eight brightness levels controlled by three Brightness Bits BB
BB = Vb >> 7
What’s not to like?
Maybe you already knew that and were wondering why it took me so long to get there…
[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:
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.
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:
I just figured out how to use the WordPress “sourcecode” formatting and applied it to my software-related posts. It produces much nicer results than the manual formatting I was using, mostly by preventing long lines from jamming into the right column.
The catch: WordPress imposes a round trip from my original text to the screen encoding and back, which sometimes randomly mangles special symbols. Angle brackets and double-quotes, in particular, take serious damage.
If you happen to remember a favorite chunk of code in a previous post, please take a look at it and see if I missed any of the obvious text-replacement errors. Trawling through the Software category should turn up most of the posts.
As is always the case with program listings, the errors will be really obvious to everyone except me.
Linear Technology’s LTSpice generic capacitor model has all the parts you need to synthesize a crystal, which is pointed out in the help file and various spots around the web. What’s missing is the relation between all the parts and the values you have in hand for an arbitrary crystal.
SPICE Capacitor Model
The crystal capacitor model looks like this…
Cpar (usually C0) along the right edge is the inter-electrode capacitance, on the order of a few pF.
Rpar (usually R0) along the left edge is the parasitic resistance across the case, on the order of hundreds of MΩ.
The RCL string in the middle is the “motional” part of the crystal model, generally found with a subscript “m” in the specs.
Rser (Rm or ESR) is on the order of 100 Ω
Capacitance (Cm) is the motional capacitance, on the order of fF (that’s femtofarad: 10-15)
Lser (Lm) is tens to thousands of mH
RLshunt is something I haven’t seen in any other model and, in fact, it doesn’t appear in the properties panel.
Crystal Properties
Now, the part I screwed up is that the capacitor’s value (the number appearing on the schematic) is Capacitance (in the angle brackets that royally screw up WordPress HTML), not Cpar. So the crystal capacitor properties panel looks like this…
That models a 10 MHz crystal, taken directly from a sidebar in Refinements in Crystal Ladder Filter Design by the legendary Wes Hayward W7ZOI, in the June 1995 issue of QEX.
Guess what? Plug it into a model of his crystal-measuring circuit and it works exactly like he says it should. No surprise there…
SPICE has a bit of trouble simulating high-Q oscillators; they tend to not start up properly. If nothing seems to be happening, wait for a few tens-to-hundreds of milliseconds before despairing. Try chopping Rser down by a factor of two or four to see if that improves its disposition.
You could try injecting a few (hundred thousand) cycles of a kickstart signal, but that’s fraught with peril: you’re simulating something even further from reality than usual.
Memo to Self: You can rename the cap from C2 (or whatever) to X1 (or whatever) and everything still works fine.
OK, this is shooting the low-hanging fish right off the barrel (or some such mixed metaphor), but why does anybody still use Internet Explorer and Windows for embedded systems?
The proximate cause is a dead Internet link, but somebody obviously didn’t take that problem into account during the design phase. I’m sure there’s a keyboard hidden inside the box, wherever the box might be, but the rest of us are left to snicker at a jammed display.
The problem resolved itself (or somebody plugged in the cable) by the time we walked past the display again.