Wednesday, June 6, 2018
Fast 1-wire shift register control
One-wire shift register control systems are an old idea, with the benefit of saving an IO pin at the cost of usually much slower speed than standard SPI. I'm a bit of a speed nut, so I decided to see how fast I could make a 1-wire shift system.
The maximum speed of 1-wire shift control systems is limited by the charge time of the resistor-capacitor network used. The well-known RC constant is the resistance in Ohms times the capacitance in Farads, giving the time in seconds to reach 63.2% charge or discharge. To determine the discharge of a capacitor at an arbitrary time, look at a graph for (1/e)^x:
In a 1-wire shift system, the RC network must discharge less than 50% in order to transmit a 1, and it must discharge more than 50% in order to transmit a 0. That 50% threshold is 0.7*R*C. The hysteresis for the shift register and the system error margin will determine how far from 50% those thresholds must be, and therefore the difference between the low times for transmitting a 1 or 0 bit. The SN74HC595 datasheet indicates a typical margin of about 0.05*Vcc, so an input high must be more than 0.55*Vcc, and input low must be less than 0.45*Vcc. After writing some prototype code, and a bunch of math, I settled on an order of magnitude difference between the two. That means in an ideal setup, a transmitted one discharges the RC network by 16.5%, and a zero discharges the network by 83.5%. That gives a rather comfortable margin of error, and it does not entail significant speed compromises.
Half of the timing considerations in previous 1-wire shift systems is the discharge time, and the other half is the charge time. That is because after transmitting a bit, the RC network needs to charge back up close to the high value. In order to eliminate the charge delay, I simply added a diode to the RC network as shown in the schematic. If you are thinking I forgot the "C" of the RC network, you are mistaken. Using the knowledge I gained in Parasitic capacitance of AVR MCU pins and Using a 74HC595 as a 74HC164 shift register, I saved a component in my design by using parasitic capacitance in the circuit.
I'm using a silicon diode that has a capacitance of 4pF. The total capacitance including the 74HC595, the diode, and the resistor on a breadboard is about 13pF. A permanent circuit with the components soldered on a PCB would likely be around 10pF. The circuit is designed for AVRs running at 8-16Mhz, so the shortest discharge period for a transmitted zero would be 10 cycles at 16Mhz, or 625ns. With R*C = 330ns, a 625ns discharge would be 1.9*RC, and the discharge fraction would be 1/e^1.9, or 0.1496. The discharge time for a transmitted one would be 62.5ns, and the fraction would be 1/e^0.189, or 0.8278. Considering the diode forward voltage drop keeps the circuit from instantly charging to 100%, the optimal resistor value when running at 16Mhz would be close to 47K Ohm. In my testing with a tiny13 running at 9.3Mhz on 3.3V, the circuit worked with as little as 12K Ohm and as much as 110K Ohm. The "sweet spot" was around 36K Ohm, hence my use of 33K in the schematic above.
For debugging with my scope, I needed to count the probe capacitance of ~12pF, which gives a total capacitance of 25pF. Here's a screen shot using a 22K Ohm resistor, transmitting 0xAA:
I've posted example code on github. It uses the shiftOne function for software PWM, creating a LED fade effect on all 8 outputs of the shift register. I tested it with MicroCore, and the shiftOne function can be copied verbatim and used with avr-gcc. Since it uses direct port access instead of Arduino's slow digitalWrite, the references to PORTB & PINB will need to be changed in order to use a pin on a different port.