ModDAQ: USB to SPI Controller and Modular DAQ
Control the universe from your PC
Blog for this project
To access the ExpressPCB .PCB and .SCH files for this page, use this
Here are the Labview files
for the VI (Voltage / Current) source running on the USB DAQ
Serial and parallel ports have disappeared from most PCs. PCI bus
design is only for serious pros, and modern operating systems have
made it harder for the average programmer to access PC hardware. USB
is, well, universal, but also complex and hard to program. To build
a custom device from scratch, prepare to write a hairy PC driver,
install a bunch of tools, develop embedded code and only then can
you interface to your PC's USB port. For years, FTDI came to the
rescue with their easy to use USB to Serial and parallel chips. But
serial to what? There are very few hardware-only serial devices on
earth. Most serial devices are based on a microprocessor, so if you
want to build one, again, expect to develop embedded code.
I have always been a fan of SPI. It is fast and fairly simple, and
looks kind of like a bus since multiple devices can supported. There
are lots of SPI things to control, from ADCs, DACs, GPIO
ports, temperature, memories, accelerometers, and lots of other
stuff. Even the ubiquitous SD card is basically an SPI device.
FTDI supports SPI as well as other serial protocols like I2C and
JTAG with their MPSSE (Multi-Protocol Serial Engine). Its software
support isn't great, and until recently supported only full speed
USB. But with the new H parts such as the FT232H, all that has
changed. This part is a single USB to serial port, but can not only
do RS232 at very fast speeds, but SPI, I2C, JTAG and a few parallel
busses. Basically it can talk to just about any device out there.
So what is the catch? Generally USB implies slow speed, not
real-time. True, you don't have micro-second control of hardware,
but I am very impressed with the FT232H's ability to drive devices
at high speed, to handle both writing and reading data, and to
minimize the time between operations. I am able to do thousands of
ADC conversions from a simple 100KHz ADC with no interruptions
between cycles. You can even mix SPI devices and maintain pretty
fast data There are tricks which I will share with you. FTDI doesn't
really talk about time much so I made lots of measurements. It is
quite fast. FTDI is modest about it's capabilities.
I liked the looks of the chip so bought a module: the UM232H. Wired
it up on a Radio Shack breadboard to a few SPI devices and voila!
The two 8-pin chips on the board are a Microchip dual 12 bit
DAC (MCP4822) and an aging TI, 12 bit ADC (ADS7816). I wrote up some
LabView code to control them.
Here is the simple schematic. The pull-up resistors are to make sure
the SPI chip selects (CSn/) are all disabled high before the
FT232 is initialized.
This simple test showed that the FT232H is quite capable. It was
able to keep up with the ADC at its full speed of 100K samples per
second, sampling up to about 100K samples in a single command.
The trick to making an FT232H go fast is to send multiple commands
in a single write packet. When you send multiple packets, each with
a separate SPI command, the FTDI driver introduces about 300-500 uS
of delay between SPI operations. But when multiple commands are sent
in one packet, there is only about 300nS between commands. Nice! So
if you want to collect 1000 samples at the fastest rate, send a
packet with 1000 SPI commands. So as long as you can pre-calculate
all your SPI commands, full speed can be achieved.
But for how many samples? There is no spec for this, but after doing
some empirical testing (increase the amount until it crashes, then
back it off a bit), I found that the limit is the number of bytes
sent to the device in a single FT_Write(). The limit is apparently
about 1 MB, probably due to a buffer size in the DLL. When measuring
ADC values, it takes 3 bytes to assert CS, 2 or 3 bytes to get the
ADC data, and 3 more to de-assert CS for a total of 9 or 10 bytes.
The limit is 1MB so about 100K samples can be collected with no
interruptions. This is pretty good. A non-real-time operating system
communicating with a non-real-time bus and achieving full speed
hardware control. Nice! In fact I didn't quite understand how, since
the FT232 chip only has about 4KB of buffer for transmit and for
receive data. The ADS7816 is a 12 bit part that sends 16 bits of
data. But it does. My explanation is that the FTDI driver must
continue to unload data from the buffer while SPI reads are still
occurring. I am guessing since FTDI doesn't provide any details at
that level. The concern is that if they choose to reduce driver
buffer sizes, applications will break.
The FTDI MPSSE protocol performs low level serial commands such as
setting the clock, sending SPI bytes, receiving them, setting GPIO
pins direction and state, and a few other niceties. GPIO
commands only take about 300nS, so setting SPI chip selects with
them is quite fast. Get your digital scope ready because you'll want
to see the fast timing. I use AD[3:7] for the first 5 chip
selects. AC[7:0] can also be used as additional chip selects or
simply as fast GPIO pins.
Another consideration is the delay after a write and before a read.
The example code from FTDI uses a delay( ) of about 10ms, followed
by a call to determine how many bytes are in the read buffer. Then a
read of that many bytes. I found that if you know how many bytes are
in the read buffer, you can skip the delay and the status read, and
just read that many bytes. The challenge with this approach is that
if you are off in your calculation, the FTDI DLL will hang.
Here is the Labview screen of 1000 measurements of a DC level and
showing its stats in order to quantify the ADC noise. I haven't yet
figured out where the larger + and - noise spikes are from.
I decided to design a PC board with the FT232H that provides modular
control hardware. it supports up to 4 modules, but the 3 most common
features I need are already on the main board: 8 channel AtoD (ADC),
DACs and GPIO. Here is the feature set for the base board and
for some of the planned modules.
- High Speed USB 2.0 based on FT232H
- Powered from USB up to 100mA
- Powered from external +5V for > 100mA
- Electrically isolated or non-isolated (optional)
- Isolated SPI and up to 4 device Select signals
- +/- 5V, 1Watt DC-DC converter
- 500V isolation
- Isolation eliminates PC ground noise and DC errors, allowing
precise DC measurements
- Non-isolated SPI and GPIO on a 20 pin header
- Optional mix of isolated and non-isolated devices via select
- ADC options
- 12 bit 8 channel SE / 4 DIFF based on ADS7844 (TBD)
- 16 bit 8 channel SE / 4 DIFF based on ADS8344
- Or one or more modules can be used
- Or none
- DAC options
- 2, 4 or 8, 10 bit or 12 bit DACs on board
- 2 channel DACs use MCP48x2
- 4 and 8 channel DACs use AD562x
- 16 bit GPIO on board, 20 pin header
- 8 bit high-speed GPIO, non-isolated, on 20 pin header
- 8 bit 100mA relay driver module
- Modules use simple 10 pin SIP connector for SPI bus
- Each Module 1.2" square, ruggedly mounted via two #2 screws
- DAC1: 2, 4 or 8 , 10 bit or 12 bit DACs on board
- GPIO8: eight GPIO pins on a 10 pin SIP header. Uses MCP32S08
- RLY1: 8 relay outputs, 24V, 100ma each, external power
- SOIC20: Prototype module for narrow or wide SOIC up to 24
- SSOP20: Prototype module for SSOP .65mm pitch 8 to 20 pins.
- SOIC20: Prototype module for narrow or wide SOIC 8 to 24
- DIP24: Prototype module for DIP up to 24 pins.
- SMT prototype modules have provisions for SOT23, 0805, 0603
- All modules have ground plane for low noise, high frequency.
I have the board controlling my VI
system. Compared the the NI USB6009 USB DAQ that I used to
use, it adds isolation, 4 DAC channels vs 2, higher speed, much
lower cost, improved accuracy and improved noise. The VI is a 2
quadrant device that provides 0 to +20V at up to +2.5A and can sink
up to -5A. It is ideal for testing many devices, particularly
batteries and high current LEDs. More on this project....
A few lessons learned:
The ADI AD5327 quad 12 bit DAC turned out to be a poor choice
due to its INL (Integral Non-Linearity) spec of 16LSBs, and 60mV
offset. 16 LSBs basically makes this a good 8 bit DAC, but it is
sold and priced as a 12 bit device. 60mV of offset is nearly 100
codes at 610uV/LSB! I had better results with the Microchip
MCP4822, but it's only a dual. Microchip doesn't make a quad 12 bit
SPI DAC. A better choice is the AD5624 quad or AD5628 octal with
10mV offset and 1 LSB INL at 12 bits. INL is a critical spec. The
DAC can be no more accurate at converting digital to analog than
it's INL. You can often compensate for gain and offset errors by
calibrating those errors out. But unless you want to measure and
build a table for every DAC value. INL errors cannot be corrected.
Stay away from the AD5327 if you really want 12 bits.
Another source of errors to watch for is offset error. Low cost
DACs, sometimes called "String DACs", use a resistor ladder and CMOS
switches to generate voltages. This approach has advantages:
inherent monotonicity: each code is larger than the one below it.
Good offset and gain errors too. But the INL, or overall linearity
isn't so good since the little resistors in the ladder do not all
match. The ladder is a high impedance structure, so a voltage buffer
is required to get a low impedance voltage output. Unfortunately the
buffer is a source of offset error, usually dominating the DAC
ladder offset errors. With a positive offset, the output doesn't go
to ground when you apply code 0. With a negative offset error,
several of the lowest codes cause no change in output. So don't
depend on the DAC output to go all the way to ground, and don't even
expect the lowest 16 or so codes (on a 12 bit DAC) to do anything.
The fix for this is to add in a fixed analog offset downstream, then
add 16 or so codes to the desired code. If you do this you can
get to 0, and you can also calibrate out the offset error either in
hardware (trim) or in software. This is a problem with unipolar
circuits, but not with bipolar circuits that typically have a 1/2 of
full scale offset downstream.
The 12 bit TI ADS7844 was another problem part. It has quite bad
channel-to-channel crosstalk, around 6-8 LSBs. Crosstalk is a
particularly ugly error because you cannot calibrate it out. This
makes the ADS7844 a 9 bit ADC for any application where you switch
channels. I curse TI for not specifying crosstalk on the ADS7844.
Every other multi-channel ADC does. It's bas enough that you need to
read and understand a 30 page data sheet, but TI forces you to read
between the lines and anticipate missing specs that they are
apparently ashamed to publish. A better choice for 12 bit 8 channel
ADC is the AD7927.
In evaluating 3 different ADCs, I found ADC dependent errors in
each. Most, including the ADS7844 crosstalk, were dependent on the
driving circuit. Modern ADCs use a are capacitive charge balancing
approach. Each conversion is begun by the input charging a sample
capacitor to capture the input voltage. This capacitor is
usually 20-30pF. This doesn't sound like much, but most source
circuits will be disturbed when asked to charge a capacitor
abruptly. The ADCs allow less than 1 us Acquisition Time to charge
the cap, then whatever voltage the cap has, gets measured by the ADC
circuitry. If the driver circuitry cannot charge the cap accurately
in the alloted time, errors result. It takes 9 RC time constants to
charge a cap to 1/2 LSB at 12 bits -ln (1/8192). If the acquisition
time is 300nS and the input cap is 30pF, the maximum
resistance is 1K. R*C * 9 = 300nS. I use LM358A's as my go-to
op-amp. Driving an ADC directly with an LM358 induces large (100s of
mV) glitches on the op-amp output (the ADC input). a 500 ohm series
resistor helps a lot. If the op-amp doesn't settle in response to
this disturbance fast enough, within the ADC's acquisition time,
errors result. To quiet the glitch you can ad a resistor in the
200-50 ohm range in series. This limits the disturbance to the
op-amp but doesn't cause ADC errors. Another trick I found was to
bias the op-amp output either high or low to a power supply, via a
resistor or current source that draws a few milliamps. When a
bipolar op-amp output drives a DC current, the output impedance of
its emitter-follower output transistors is much reduced and the ADC
error is reduced. I didn't try using a faster op-amp but I suspect
it would help also, maybe because of the extra output stage
quiescent current. You could use a capacitor to GND to absorb the
glitch, but the math is ugly: to get 1/2LSB charge error on a 12 bit
ADC with Cin of 30pF requires 2^12 * 2 * 30pF or .25uF on every
input. When you evaluate ADCs, always observe at the input glitch
during acquisition and be careful about your driving source.
|ADC Input Mitigation
|Big capacitor (.2uF) to GND
|Simple, low cost
|Slow response, hard to drive
Dielectric absorption may be an issue
|Op-amp and series resistor, 2-500 ohms
|Small DC errors
|Source resistance >= 1K ohms
|Simple, low cost
||May not be enough to minimize op-amp
|Good AC performance
|Introduces DC errors
Expensive, especially multi-channel
|Op-amp plus pull-down or pull-up (2-5ma)
|Extra power draw. Can cause power-up issues
Virtually every modern CMOS ADC uses this un-buffered capacitor
input approach and so most ADCs have peculiar and sometimes unstated
requirements on source impedance. This includes the ones on your
favorite microprocessor. The manufacturer's don't discuss this much,
but there are some papers out there on it. I am talking about a slow
ADC measuring slowly changing or DC voltages. When you measure low
distortion audio or RF and the problems get much bigger. Fortunately
there are ADC buffer amplifiers specifically designed for those
Watch the op-amp DC errors too. 12 bits at 2.5V full scale is 610uV
per LSB. A 5mV offset error op-amp causes 8LSBs of error. And op-amp
gain also multiplies op-amp offset errors.
Some combination of these approaches can be used. Say some ADC
inputs are measuring a 10K thermistor with a 10K pull-up resistor. A
large cap might be the best solution since it doesn't add much cost,
doesn't introduce DC errors, and response time probably isn't an
issue. 0.2uF x 10K = 2mS RC time constant. 9 time constants (1/2 LSB
at 12 bits) is only 20mS.
With LM358 op-amps, a 470 ohm resistor does a decent job with most
ADCs. For even better performance, add a 1-3 mA pull-up resistor
from the op-amp output to V+.
Differences in SPI interfaces
One downside of most Analog Devices SPI parts, is that they require
the serial clock to be HI by default, pulsing low. This is
unfortunate since just about everyone else uses LO clock, pulsing
high. The FT232H can deal with this, but it adds a bit of extra
complexity to SPI control.
Revision to USB DAQ
I decides that the original USB DAQ had too much modularity.
This capability was somewhat useful while I was evaluating ADCs, but
even there I tended to use only one module at a time. I still like
the modules, but a typical system would use the baseline ADC, one of
2 DAC options, and 16 GPIOs. Even as parts on the main board, these
can be installed or deleted, plenty of modularity for me. Also I
wanted the DAQ to be capable to be used as a module, like a large
DIP, capable of being plugged into a carrier.
So the new USB DAQ MOD meets these needs, and still supports one
plug-in module. I increased the module pinout to 12 pins.
There was nothing magic about 10 pins, and the 12 pins fit fine on
the 1.2" square modules and allowed one more control pin plus a
The module size is 1.6" x 3". It can be built as a DIP plug-in
(connector pins facing down) or as a stand-alone board
(connectors facing up). It has four corner mounting holes for solid
mounting either way. Here is the board layout. Boards will be
the Blog for this project
Back to Dave's Home Page
This page was last updated 12/1/12