Do you get a lot of use out of the scroll wheel on your mouse? How about that Griffin PowerMate you got for your birthday? If so, chances are that rotary encoders are responsible for some of the joy in your life.
After being unpleasantly startled that our jaw-style rat trap had, uh, done its job on a much smaller rodent, I decided that a less shocking audible warning might be useful. [Edit: See revision later in this post.]
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
I have a self-clocking line/signaling code in mind. I don’t know what it’s called, and I’m having a rough time finding out.
Coincidentally to my recent discussion of thermostats, I got a request from a co-worker today. He asked for a refrigerator that doesn’t care about the temperature of anything in the fridge except the beer. I’m not really into beer, but I know where he’s coming from.
First of all, I got the demo USB peripheral running. The thing has a button and a light; software on a Windows PC can switch the light and read the button. Also, in a surprisingly unrelated fashion, I had a lovely experience at Fry’s yesterday that somehow made me optimistic on behalf of the casual hobbyist. I shall describe both forthwith.
Somewhat out of my current character, I lead with a lyric from They Might Be Giants:
Turn it up, turn it down
Turn it up when the cold brings you down
When the heat bothers you turn it down
Turn it up, turn it down
— They Might Be Giants, “Thermostat”
This is a song about a thermostat, a brilliant device contrived in 1883 to either turn off or turn on a heating/cooling system in order to favor a certain temperature. The traditional version of the thermostat is among the simplest examples of a negative-feedback control system, a very important class of device.
An “on-off” control system basically repeats the following ad infinitum. The following example is for a thermostat controlling a heater (with a generalized version in parentheses).
- Is the current temperature the same as the temperature we want? (Is the current state the same as—or similar enough to—the desired state?)
- If so, good—stop here and check again after a certain period of time or as soon as there is an indication that this may no longer be the case.
- If not, continue.
- If so, then we’re already doing what we need to be doing. (Our effort is already in the right direction.)
- If not, switch off (if on) or on (if off). (Switch our effort to the right direction.)
Note that the typical thermostat is an on-off control, meaning that there are exactly two modes to its operation—0% and 100%. In the middle of winter, when it’s 68°F in your house and you decide to crank it to 78°F, well, first of all, you must like it hot and enjoy high energy bills; 68’s always fine in my house. Anyway, you as a human know that you’ve turned up the set point by 10 degrees, which is a lot to do at once. What you may or may not have realized is that your furnace isn’t making any special effort to work faster than if you’d only turned it up 2 degrees. It’s just going to stay on as long as it takes to do its job and then kick off.
This is because an on-off system asks “if I’m too far in either direction, which direction?” but not “and by how much?” This is the domain of a linear control system.
One might liken a linear control system with a human being with his foot on the accelerator pedal of a car. (For the time being, we’ll ignore that the brake is also pretty handy for slowing down a car.) The new process is as follows:
- Is my current speed the speed I want? (Same as 1 above.)
- If so, good—stop here and check again after a certain period of time or as soon as there is an indication that this may no longer be the case.
- If not, continue.
- If faster, push the pedal harder (if possible).
- If slower, ease off the pedal (if possible).
- If the desired speed is a little faster, push the pedal a little harder.
- If the desired speed is a lot faster, push the pedal a lot harder.
- If the desired speed is a little slower, ease off the pedal a little.
- If the desired speed is a lot slowed, ease off the pedal a lot.
What’s remarkable about a system like this is that it’s inexact by design. For example, if I’m driving 60mph and need to 70mph, I’m going to push the pedal harder. But how much harder? Can I state it with an exact measurement of radians per mile per hour? No. (It could possibly be done, but I hope nobody is that bored.) So I just push the pedal (control) and see what happens (feedback). If I end up too slow, I push harder. If I undershoot, I let up on the pedal. This trial-and-error is called oscillation and a system generally benefits from it being kept to a minimum.
The system seems to work in situations where
- the directions of feedback and control have a consistent relationship; e.g. pushing harder always means increase in speed and, conversely, an increase in speed requires pushing harder
- the magnitude of change at the control is at least somehow proportional to the magnitude of the resulting change in the feedback; e.g. pushing a little or a lot harder means going a little or a lot faster, respectively, with the ratio of change at the control to the change at the feedback being adjusted on the fly as necessary; if we push the pedal a little and the car bolts, we adjust our definitions so that pushing the pedal a little means a much smaller push than before
One application of this that I consider fun is a phase-locked loop (PLL), which is able to arbitrarily multiply a clock signal from e.g. a crystal. I’ve never messed with one directly, but the ones in some of the Microchip PIC devices are able to bump the 20MHz CPU clock to a whopping 96MHz, which is then divided (a much simpler operation) to 48MHz in order to implement the onboard USB peripheral.
If the driver analogy above made any sense, then PLLs are a piece of cake: For every 20 million pulses that are generated by the crystal, it has to generate 96 million pulses. Or, if you’re clever, for every 5 pulses from the crystal, generate 24—probably easier to count that way. It’s by no means trivial, but it’s straightforward.
Control: The PLL train is produced by a voltage-controlled oscillator (VCO), which is able to generate really, really fast pulse trains but must be tuned on the fly. (This can be replaced with some other form of controlled variable-frequency oscillator.) When it’s first kicked, it delivers a pulse train that is most likely not the right frequency.
Pre-processing: To correct it, the first thing we do is have available a frequency divider for each of the crystal (divide by 5) and the VCO output (divide by 24). A frequency divider is dead simple; for example, to divide a pulse train by 5, just do a transition on output for every 5 you count on input. Simple! Our goal is to make the pulse train of the crystal/5 match the VCO/24 as precisely as possible. When they match, the VCO output is exactly 24/5 of the crystal’s input. Then, we just pass the VCO output directly to wherever it’s needed.
Error: A phase detector sets about finding the difference between the VCO/24 and the crystal/5 trains, noting where the trains are not the same width as well as where they don’t line up even if they are the same frequency. A low-pass filter cleans up places where the difference is so slight that it should be ignored (i.e., where the differences are so insignificant that acknowledging them would hurt rather than help).
Feedback: The error value from the phase detector is fed back into the VCO.
And, as they say, viola…96MHz from 20MHz. Nifty, right?
If I get my way this weekend, I’ll have turned a PIC18F4550 into a bona fide peripheral. Just figured that a note or two about how this is possible might be in order…
A device you can keep pointed at your TV to prevent others from using a remote to change the channel, the volume, and so forth. (When you want to use the remote, turn it off. Turn it back on when you’re done.)
In the previous post, I believe I dismissed Manchester coding too quickly as a possibility, thinking that some unwieldy analog circuitry would be necessary for its implementation. Today, I realized that it would actually be fairly easy to create a decoder using microcontroller code that would fairly easily handle arbitrary bitrates and even possibly do a decent job accounting for drift.
To explain, Manchester coding is a type of self-clocking signal (i.e., it combines data and clock lines) that guarantees either one or two edges (transitions from low to high or high to low) per bit. A logical 0 starts high and then switches to low mid-bit; a logical 1 goes low to high instead.(Described above is the IEEE version. As the image shows, it’s possible to flip the two by convention.)
What the microcontroller must do to make sense of such a signal is pretty simple, much to my delight. It’s simple because there are only two possibilities for the duration between transitions; we’ll call them D/2 for the narrow pulses and D for the wider ones. Assuming we have a decent guess at what D is (we’ll get to that), and that any two consecutive D widths are reasonably similar, we can decode the pulse train and even compensate for gradual drift of the D value. It goes something like this:
- Say we have 3 initial guesses for D, which are D0, D1, and D2. At any given moment, our estimated value of D = (D0 + D1 + D2)/3.
- At each transition, find X, the distance between this and the previous transitions.
- If X < (3/4)D, consider X to be approximately D/2. Ignore this transition and start waiting for the next one.
- If 4X < 3D then X < (3/4)D. 4X = X shifted left 2. 3D = D0 + D1 + D2. No actual multiplication or division required!
- If (3/4)D ≤ X < (3/2)D, consider X to be approximately D. Accept the current input as the next bit. Let D0 = D1, D1 = D2, D2 = X.
- If 2X < 3D then X < (3/2)D. 2X = X shifted left 1. 3D is as above.
- If X ≥ (3/2), the transmission has ended or there is an error; handle accordingly.
Using this mechanism, the average of the previous three bit widths is used as the expected width for the next bit, but the next bit can be nearly 25% narrower or 50% wider and still be valid. Since this sliding window approach is used, the bitrate could generally vary by quite a lot as long as the change is gradual. Oversampling is key here—I imagine that the receiver should try to sample at least 6 times (wild guess) the expected bitrate.
How do we set up initial guesses for D? Here’s one proposition on how to make an explicit initial sync:
- The initial guess for D would be very short, possibly shorter than any actual expected D.
- When the line is idle, it is held low, and should be held low longer than the max nominal 2D specified by the system. Consequently, if the line is low for over twice the expected 2D, the receiver should recognize an idle state.
- When a frame is about to be sent, the line goes high for exactly X = 2D as decided by the sender. At the next high-low transition, the receiver assigns D0 and D1 based on this value: D0 = X shift right 1; D1 = X – D0.
- The transition following this transition is the start bit, which is a 1 (low-to-high). The width Y of the low before the transition is approximately D/2. Assign D2 based on this value: D2 = Y shift left 1.
- Start sampling as specified from the start bit transition.
- After some fixed number of bits, the frame ends with a stop bit, which is a 0 (high-to-low). After the bit, the line is held low to indicate idle as mentioned before.
This one’s got some true promise, I think. I’m guessing it may even be possible to get it working using a high-tolerance clock on the slave micro (such as an RC network) making it potentially feasible to use even a super-cheap one, like the PIC16F54 of which I happen to have plenty lying around, without an excessive amount of tuning. I could be wrong, though. Who knows?
Way back in 2007, I developed a big PC-controlled LED interface operated via serial port. The basic principle was simple: a PIC micro with a serial receiver pushed bits out to some shift registers which turned LEDs on and off.
The implementation was a bit more complicated:
- A computer sends commands to the RS-232 port (nominally at 19200bps as I recall).
- The serial line runs to a level converter (MAX232) that changes RS-232 signal voltages into TTL levels.
- The converted signal runs into the serial port on a microcontroller (here the USART on a PIC16F88).
- Code on the micro converts the received data into clock and data lines.
- The signals from the micro are carried to the breakout board. (If the run is short enough, this part can be omitted entirely.)
- The clock and data lines each run into an input of a differential line driver (MC3487), doubling the number of lines but adding noise immunity for potentially long runs.
- The driven lines are received by a differential line receiver (MC3486), changing them back to clock and data at logic levels.
- The clock and data signals are run into a chain of (nominally 4) latching shift registers (74HC595), converting the serialized data into logical outputs.
- The outputs of the shift registers feed the inputs of a transistor array (ULN2803A) to increase the current capacity of the output.
- Each output of the array sinks a high-brightness LED via a current-limiting resistor.
This week, I’ve been considering how I might cut the number of signal lines from two (clock and data) to one. This could possibly simplify the hardware at the expense of some increased code complexity.
One approach might be to combine the data and clock signals into an isochronous self-clocking signal. This would allow the breakout to receive data over one line while still not having to have previously agreed upon a data rate, keeping things flexible. Unfortunately, from what I can tell, there aren’t any microcontrollers to speak of that facilitate this behavior, and separate chips designed to implement this are expensive enough to make me think that this sort of thing is reserved for faster circuits than I can muster in my garage.
A more straightforward approach would be to do more or less what a UART does, which is to agree on a data rate in advance, signal start and end of frame, and have the receiver sample frequently enough to be reasonably dependable. Even though this is less flexible than self-clocking, it’s very easy to implement. Many micros have dedicated serial hardware for this sort of thing, and it’s also pretty easy to implement in code on (cheaper) micros that don’t.
Here’s some of the math: Say we want to use the ubiquitous 8-N-1 scheme (for each frame, 8 data bits, no parity bits, and 1 stop bit, plus the implicit start bit = 10 bits). To update one of the chains, we’ll send out 4 frames (32 data bits, but 40 bits overall). We’d like smooth animation, so let’s try for a full refresh at least 60 times per second. Our minimum bitrate is thus 40 bits × 60 times per second = 2400 bits per second.
The sender’s job is easy: Every (1/2400)s ≈ 416.67μs, send out a bit, making the first bit out for each frame a start bit (traditionally 0), the last a stop bit (traditionally 1), and the ones in between the data. This would be the job of the master board, which will hopefully be a micro with a timer-based interrupt peripheral. The clock source for the chip would either be chosen to be helpfully precise (for a PIC, which usually takes 4 clocks per cycle, an oscillator that is some large multiple of 4 × 2400 = 9600Hz, e.g. 19.2MHz) or quick enough to be precise enough (a PIC with a 20MHz clock would have a 0.2μs precision, making for a bit width of 416.6μs [−0.016%] or 416.8μs [+0.032%]).
Alternatively, you could just pick a higher framerate that makes for more convenient component selection. For example, the internal oscillator on many PICs is tuned to 8MHz divided by any of several powers of two. Say, for the sake of implementing potentially slower slave devices, we go for the 125kHz clock, meaning instructions cycle at 8μs each. We could round our maximum bit width down to 416μs (the closest, around 2404bps), 408μs (around 2451bps and is divisible by three), or 400μs (2500bps and is divisible by 100).
The receiver’s job isn’t quite as easy since there’s more busywork. However, this is done on the slave board, where hopefully there isn’t too much else to do. A timer-based interrupt makes this job easier, but can also be done through polling if necessary.
Polling: In this case, what we want to do is sample the input pin at 3 times the expected data rate, repeating the following, where clock, data, and latch signals are output to a ‘595-style shift register, where D = 1/3 expected bit width:
- First idle check: Sample line. Wait D. If sample was high, continue. Otherwise, redo.
- Second idle check: Sample line. Wait D. If sample was high, continue. Otherwise, go to first idle check.
- First start check: Sample line. Wait D. If sample was low, continue. Otherwise, redo.
- Second start check: Sample line. Wait D. If sample was low, continue. Otherwise, go to first idle check.
- Begin frame: Set latch low. Set remaining = 8 (data bits left this frame).
- Skip: Set clock low. Wait D. Set data low (optional). If remaining = 0, go to end frame.
- Accept: Sample line. Set data to sample. Decrement remaining. Wait D. Set clock high. Wait D. Go to skip.
- End frame: Set latch to high. Go to first idle check.
This is a state machine that generally takes a sample every D (except at the “skip” part). First, it waits for two high samples in a row to indicate that the line is idle. Then, it awaits (but doesn’t immediately require) two low samples in a row to indicate that a frame is starting. Once two samples have been taken to mean a start bit, the third is skipped (since it’s assumed to be near a transition) and the next sample is read as the first data bit. A new bit is then read every 3D (skip waits D and accept waits 2D) until the end of the frame. During the reads, the clock and data pins are set to output the data being read, turning the micro into a sort of async-to-sync serial converter. At the end of the frame, the latch pin is raised to signify to the shift register that the readout is complete. (If we were using a chain of 4 shift registers, we’d modify the code to only raise the latch every fourth frame.)
With timer interrupts: The state machine is similar, but there are no explicit delays; we simply defer to the timer to call us back when it’s ticked.
- At the start of the code, let state = 0.
- For the ISR, if the timer crossed over, dispatch to one of the following as indicated by state:
- 0 or 1 (Idle checks): Sample line. If line is high, increment state. Else, state = 0. Exit ISR.
- 2 or 3 (Start checks): Sample line. If line is low, increment state. Else, state = 0. Exit ISR.
- 4 (Begin frame): Set latch, clock, data low. Set remaining = 8, state = 6. Exit ISR.
- 5 (Skip): Set data low (optional). If remaining = 0, state = 8. Else, state = 6. Exit ISR.
- 6 (Accept): Sample line. Set clock low. Set data to sample. Decrement remaining. Increment state. Exit ISR.
- 7 (Clock): Set clock high. Set state = 5. Exit ISR.
- 8 (End frame): Set clock low. Set latch high. Set state = 0. Exit ISR.
And of course there’s always the option of just using the micro’s own UART peripheral where available, but I’m thinking of implementing this with some extremely cheap leftover chips I already have. Not only that, but I’m considering blocking the whole thing up into 32-data-bit frames instead of the 8- explored here, and for that a built-in UART just won’t do.
With any luck, I’ll be able to hammer some of this out in the next couple of weeks.