-
Integrating the ADC
01/23/2023 at 07:12 • 0 commentsIn the previous project log I settled on a MUX, an ADC with 8-bit parallel output, and a shift register. Something like this:
For the ADC I've selected the ADC1175 by Texas Instruments, an 8-bit, 20 MSPS ADC with parallel output. It's available in a prototyping-friendly, 0.65 mm pitch, 24-pin TSSOP package and costs less than $4 in singles.
For the parallel-to-serial shift register I will go with a 74x165, for example the SN74HC165. HC because that's the family I already use in the original NeoPixel Punk Console. The important feature is that this shift-register loads the parallel data asynchronously, i.e. the first bit is available at the output without an extra clock-cycle, which simplifies things down the line.
For the MUX, I picked the 4052, a dual 4:1 analog switch. For example the CD74HC4052 to stick with 74HC series chips. We will only use one of the two channels, but these are cheap and widely available.
I will also need a few counters to control the MUX and operate the shift register. No specific selection for those yet. I may be able to just use some logic based off the existing LED bit counter.
The basic plan is as follows:
- Pulse ADC clock to capture sample
- Load shift register
- Clock out 8 bits to LED string
- Switch MUX to next color
- Repeat until end of LED string is reached
- Reset all counters and restart
One problem is, that the sample of the ADC is only showing up at the output after 3 clock cycles. So it would take three rounds of the above before the bits of the first sample come out of the shift register. With an LED string, this would mean that the first LED is dark or undefined.
A workaround is to switch the MUX at the beginning of a color and trigger the ADC with the bit-clock. That way the sample pipeline is already filled with the new color by the time the next load of the shift register occurs. Hard to explain in words, but hopefully clearer with the diagram below.
After a lot of fiddling (and further tweaking while I wrote this log), here's the timing diagram I came up with:
Colorization and bit numbering in the diagram reflect the order in which NeoPixels expect RGB data, which is 8 bits each, MSB first, for green, red and blue.
RST_PULSE, /RST_PULSE, CLK, LED_CNT and LED_FF are signals that already exist in the NeoPixel Punk Console circuit.
CLK is the master clock at which bits are sent to the LED string, running at about 700 kHz. LED_CNT is the counter for the bits in the LED string. Here it counts up to 12288 for 512 RGB LEDs, but it could be any multiple of 24. When the counter reaches the end of the string, it returns to 0 and triggers the RST_PULSE which resets the whole system. LED_FF is a flip-flop that holds the value of the bit currently sent to the LED string. LED_CNT and LED_FF are clocked by the positive edge of CLK.
Working backwards from the flip-flop, output SR_Q of the shift register must be valid on the positive edge of CLK. That means the shift register, which shifts on positive clock edges, needs to be clocked before the flip-flop. To achieve this, I created /CLK which is the inverted CLK, except during RST_PULSE when /CLK stays low. As CLK is low for a few 100 ns after reset, /CLK starts with a positive edge before the first positive edge of CLK.
Next we need to load the shift-register with the output of the ADC at the start of each 8-bit cycle. The shift register is loaded when SR_LD (SH/LD or SH/PL pin in datasheets) is low. As the load operation is asynchronous, bit 7 (MSB) will immediately show up at output SR_Q. To load the flip-flop with the correct bit, SR_Q must update between the negative and the following positive edge of CLK. With that, the SR_LD pulse should happen when SR_CNT is 0 and /CLK is 1.
SR_CNT counts from zero to seven, clocked by CLK. Instead of a dedicated counter, the lowest three bits of LED_CNT can be used.
Next up is the ADC. It captures new samples shortly (3ns) after a negative clock edge, and data output is valid on the negative edge of its clock:
To load a valid sample into the shift-register, ADC_OUT must be valid when SR_LD transitions to high. As the MSB is immediately available on the output of the shift register, ADC_OUT must also be valid when LED_FF loads the bit on the positive edge of CLK. Given those requirements, the ADC must be clocked with /CLK.
The input of the ADC is coming from the MUX, controlled by MUX_SEL. MUX_SEL must be updated once per color, i.e. every 8 bits. The switching should be settled by the time the ADC samples, i.e. it must switch on the positive edge of /CLK. The negative edge of the SR_LD pulse seems to be a good source for that signal.
I need some kind of counter to generate the two-bit MUX_SEL signal. The counter will be clocked by the inverted SR_LD, and reset to 0 by the RST_PULSE or when reaching 3. Not sure yet whether that's best implemented with a counter IC or two flip-flops and some logic.
As mentioned above, it takes three clock ticks for the ADC to output a sample. Therefore, despite the MUX already switching to a new color, the shift register will still load sample data of the previous color.
This causes an issue immediately after reset, i.e. the first LED of a string. As /CLK is suspended during reset, sample N-3 will be from before the longish reset pulse (~0.3ms). Add some more complexity at the end of the string, and we may even end up with outputting an old red sample. But starting with the red bits of the first LED the data is up-to-date and in sync with the requirements, and with the second LED we're fully on track.
I will probably put an extra WS2812 onto the PCB, wired in series before the LED string, and just treat that pixel as a colorful activity indicator.
I think that's it. Next up will be the schematic, or at least a diagram with chips and logic gates.
-
Finding an ADC
01/16/2023 at 06:25 • 0 commentsFirst I will need an 8-bit ADC, with a simple enough interface to be driven without an MCU. Ideally, I'd like to have a pulse to initiate the analog-to-digital conversion, and then 8 pulses to clock out the result - preferably MSB first as that's the bit order the LEDs expect.
Most ADCs that I've found use a SPI-like protocol like this:
CS transitions low to initiate a conversion, then a few dummy bits need to be clocked out before the MSB of the result shows up. So for each 8-bit color value we'd need to pause sending data pulses to the NeoPixel for a few clock cycles.
Another, less common serial protocol looks like this: (AD7823)
While not requiring extra clock pulses, this ADC does require a pause for the conversion to complete. This isn't an issue if the pause is much shorter than our clock (~1MHz). Unfortunately the AD7823 requires a 5us pause, and I didn't find any faster ones.
One approach to avoid pausing the bit stream to the LEDs would be to place the ADC with the signal source and interleave results with the MUX.
The downside is cost (3x $10 for the AD7823, ouch!), and for the cheaper and more common SPI ADCs the complexity of orchestrating the extra clock cycles.
There are also ADCs with parallel output. The TI ADC1175 looks like a good candidate:
Similar to the SPI ADCs, the conversion result is only available after 3 extra clock cycles. But after filling the pipeline, a new sample is available with every clock cycle. So worst case, we may have an issue with the first LED on a string.
A parallel-to-serial shift register will be required to convert the 8-bit data to a serial stream. The logic would be something like this:
- switch mux
- start ADC conversion
- parallel load shift register (with sample N-3)
- clock out 8 bits
- repeat
This seems doable with very little extra logic.