Electronics > Beginners

help with rapid ADC data aquizition

<< < (8/8)

Nominal Animal:
powerfly, the reason you are getting such scattered responses is because you haven't specified the root problem.  Here are the things you should disclose FIRST:

* The input voltage range
* Reference type: absolute or ratiometric (to a reference or supply voltage)
* Required/desired precision, acceptable noise level
* Bandwidth (1 kHz?) or desired sample rate (10 kHz?) per channel
* Differential or single-ended measurement - common ground across all channels or not
* Number of channels
* Estimated duration of continued measurement
As revealed piecewise in this and the other thread, I too believe you need either a specialized ADC or at least an instrumentation amplifier per channel.  (Specialized ADCs like HX711 for strain gauges don't seem to have the sample rate needed, though.)

It seems to me you need a custom board to implement this, with at least an instrumentation amplifier (say, INA217) for each channel, connected to one or more ADCs (with single-ended inputs; the REF pins of the INA217s are connected to the ADC ground).  The INA217's are not difficult to layout, as each one needs just two 0.1uF/100nF bypass capacitors and a gain resistor.  The resistor precision and value is important, as it sets the gain, but the values are described in the datasheet.  The desired gain depends on the measured voltage range and the optimum range for the ADCs.

This is such a specialized thing that you have very little hope of finding an existing project and just tweak it to suit your purposes.

It is very likely, but not guaranteed, that you can use a Teensy 3.5 or 3.6's ADCs with a carrier board with the instrumentation amplifiers, and perhaps a couple of buttons to start and stop the measurements, and a small cheap I2C OLED display off fleabay to display the status (and maybe the file name pattern the data is being stored to).  The 128x32 and 128x64 modules are easy to use, nicely readable even in normal light conditions, but need to be protected from water; I especially like the white ones.  There are a lot of fonts etc. to choose from, too.

(The OLEDs are electrically noisy, so I'd have it well away from the amplifiers, and have extra filtering on its +VCC supply rail.  If the module has a 3.3V regulator, use the +5V VUSB power rail, as Teensies have pretty good filtering from that, including a ferrite bead.  Otherwise, use the 3.3V output from the Teensy, but with 100nF and 1uF decoupling capacitors, maybe even a ferrite bead.)


It looks like I was wrong, earlier!  It is possible to use DMA to sample from different analog input pins.

It involves using a Timer/PWM modules (TPM) or a Programmable Delay Block (PDB) to trigger the conversion at a stable rate with minimal sampling jitter, plus two DMA channels per ADC unit used.  The TPM or PDB triggers each ADC conversion at the total sample rate, with very little timing jitter.  The completion of the conversion triggers an 16-bit DMA transfer to a circular buffer, so the samples from one ADC unit are interleaved.  The second DMA channel is slaved to the first, so that it triggers immediately after the first one, and uses a small circular 32-bit buffer to transfer the sample channel (ADCx_SC1A, see pin2sc1a array for pin to sample mapping) to the ADC SC1A register.  You don't even need a DMA completion interrupt, because the offset in the buffer to the next sample to be stored is available as the TCD->DOFF member of the DMAChannel objects; your main loop simply examines those using a volatile access to see where the DMA will write the next sample.

Teensy 3.2, 3.5, and 3.6 have two ADC units.  Teensy 3.2 and 3.5 have 16 DMA channels, and 3.6 has 32 DMA channels, so this is not a problem at all.
I like the idea of using the two TPM modules, even though it restricts PWM outputs a lot, but then the total sample rate can be very precisely controlled.  (At 10 channels per ADC unit, and 10,000 samples per channel per second, the trigger interval is 1/200,000 seconds, or 5us; and thus the rate 200,000 Hz or 200 kHz.

Because the ADC and DMA handle the sampling, and you don't even need an ISR at all, you can safely use the standard SD card libraries to write the data to one or more files; the samples simply "appear" into SRAM completely without processor intervention, without using any processor time at all.  (There may be additional clock cycles inserted into instructions when both the processor and the DMA access the same memory, but I haven't checked.  It will not affect anything, including the SD card write routines, though, because they use the SPI subsystem and a couple of clock cycles per byte send via SPI in those routines should not affect anything at all.  For best results, let the DMA run at least a few samples ahead all the time.)

Pedro Pascal's (pedvide) ADC library implements some of these concepts.  The Teensyduino DMAChannel.h and DMAChannel.cpp describes how the DMA channels are manipulated, and provides a Teensy-portable abstraction for using them.

This looks like a very interesting, and tricky programming problem; just up my alley.  I'd be happy to help, but since I do not have a Teensy 3.5 or 3.6, my local vendor (Mandu) does not have 3.6's in stock, and none of my friends have a 3.6 they can lend me, I can only write the example code for Teensy 3.2; the same may or may not work for Teensy 3.5 or 3.6. (Looking at the above source projects, I believe it should because Teensyduino abstractions would be used, but I'd be no help in debugging it on Teensy 3.5 or 3.6 if any issues do occur.)

Nominal Animal:
Indeed, I tested DMA-based ADC on Teensy 3.2.  (Didn't bother to wire the conversion trigger yet, though, only the DMA part.)

The only really annoying thing was to map the pins to the ADC channels: you start from the pin name/number on the board pinout, then look up its datasheet pin name from the simplified schematics.  Then, you look up the datasheet pin name in the Signal multiplexing and Pin Assignment chapter in the relevant manual, and see if the default mapping has an analog input signal name beginning with ADC0 or ADC1.  If it does, that ADC can sample that input pin; just look up the analog input signal name in the datasheet ADC0 and ADC1 channel assignment sections (in the same manual), but note that the channel number is given in binary.  Also, note that Teensy 3.x and LC all have an additional ADC MUXSEL bit that cannot be modified during DMA.  Input signal names ending with 'a' are MUXSEL=0, input signal names ending with 'b' are MUXSEL=1, but all others are the same for both MUXSEL values.

I did the above for only Teensy 3.2:

* ADC0 can sample pins 14=A0, 15=A1, 18=A4, 19=A5, 20=A6, 21=A7, 22=A8, 23=A9, A10, A11, and A14.
* ADC1 can sample pins 27=A16, 28=A17, 29=A18, and 30=A19.
* Both can sample pins 16=A2, 17=A3, A12, and A13
* Both can also sample the internal temperature sensor, and the internal 1.2V reference voltage.
* To get the same sample rate for all pins, both should sample the same number of pins (can "pad" by sampling the reference voltage)
* Using DMA, neither can sample pin 26=A15 or pin 31=A20, since they use a different MUX selector ('a', the others are all 'b' or MUXSEL=1).
For 16-bit samples, the ADC clock must not exceed 12 MHz.  It is normally derived from the CPU clock by dividing by 2n, with certain limits on n.  For maximum sample rate for 16-bit samples, you'll want to run Teensy 3.2 at 48 MHz, Teensy 3.5 at 96 MHz, and Teensy 3.6 at 96 MHz (or 6.7% overclocked at 192 MHz).

For 12-bit samples, the ADC clock must not exceed 18 MHz.  For maximum sample rate, you'll want to run Teensy 3.2 and Teensy 3.5 at 72 MHz, and Teensy 3.6 at 144 MHz.

There is a separate 16 MHz clock not dependent on the CPU clock, which can be used for the ADC if using 12-bit samples.  Then, you can run Teensy 3.5 at 120 MHz, 96 MHz, or 72 MHz; Teensy 3.6 at 180/168/144 MHz.

If the analog voltage is from an instrumentation amplifier, the sampling time does not need to be stretched.  If the total sample rate allows, the ADC units can automatically average 4, 8, 16, or 32 samples before storing, to reduce noise (for n = 2, 3, 4 and 5, respectively; n = 0 without averaging).

A single circular buffer per ADC suffices, as there is a hardware register that tells where the DMA is filling the data.  To avoid interfering with normal operations, the TPM2 timer/pwm module looks like a good choice.

By default, at high sample rates, the analog signal is sampled 2+6×2n ADC clock cycles, and converted in 5+22×2n (12-bit) or 5+27×2n (16-bit) ADC clock cycles and 5 bus clock cycles.  DMA takes probably a couple of bus clock cycles too.

So, for 12-bit samples, one can estimate 17+28×2n ADC clock cycles, and for 16-bit samples, 17+33×2n ADC clock cycles, including the bus cycle estimates and the DMA overhead. Without averaging this means 400,000 total 12-bit samples per second per ADC, or 240,000 total 16-bit samples per second per ADC.

If one were to oversample (at, say, 40,000 samples/second per input), it would be rather trivial to average them when saving or passing over USB.

The sampling pattern can be freely chosen, too; it does not need to do round-robin over the chosen analog input pins.  This means that using a pattern like 'ABACABAC' A has 3x the sample rate of B or C, half the total sample rate.

powerfly:
So after much deliberation, I think I'm going to go with the openlogger ( https://store.digilentinc.com/openlogger-high-resolution-portable-data-logger/ ). Thanks Rstoffer for positing the link to it eariler in this thread

I'm up against time here and the project (it's part of my education) isn't about coding/electronics, this is supposed to be a device to use in experiments to record data, it's the data that I'm supposed to write about in the project, not how the thing that records it works. I've had a look at trying to code the teensy to use it as a data logger but so far I haven't got it to work at the required rate, and also the openlogger seems to be able to process the data in a more readable way which likely will save a lot of time in the post processing stage, and as previously mentioned time is a priority here.

The openlogger seems to be able to do what I need, 8 channels, all can go over 10kHz and it can read the data straight to an SD card. the only issue is converting the dlog file to a .csv file or something, which some users of this device have complained is complicated (look at the ratings). If any of you have experience with using this device, or converting dlog files, that would be helpful. I'm really hoping this device will work as doing it with a teensy seems daunting for someone with basically no coding experience or experience using microcontrollers.

Nominal Animal:

--- Quote from: powerfly on December 16, 2019, 12:34:23 pm ---I'm up against time here and the project (it's part of my education) isn't about coding/electronics, this is supposed to be a device to use in experiments to record data, it's the data that I'm supposed to write about in the project, not how the thing that records it works.
--- End quote ---
You really should have mentioned this in your initial message!  OpenLogger definitely looks like a good tool for your use case, in my opinion.


--- Quote from: powerfly on December 16, 2019, 12:34:23 pm ---I've had a look at trying to code the teensy to use it as a data logger but so far I haven't got it to work at the required rate, and also the openlogger seems to be able to process the data in a more readable way which likely will save a lot of time in the post processing stage, and as previously mentioned time is a priority here.
--- End quote ---
It isn't trivial, and selecting the optimum settings (sampling duration, averaging) depends a lot on the input circuits.  It is definitely a project on its own.


--- Quote from: powerfly on December 16, 2019, 12:34:23 pm ---The openlogger seems to be able to do what I need, 8 channels, all can go over 10kHz and it can read the data straight to an SD card. the only issue is converting the dlog file to a .csv file or something, which some users of this device have complained is complicated (look at the ratings).
--- End quote ---
It looks like you might need to download and compile the dlog-utils yourself, as the Agent software doesn't have it implemented yet (as of April 2019).

If you have Python 3, reading the binary format and emitting whatever you want is very straightforward and simple: you open the binary file in binary format (i.e., you read bytes, not strs).  (The same code will work on all OSes.) The first 512 bytes of the file contain the header.  You can convert these to integer values using unpack from the built-in struct library.

OpenScope uses 16 bits per sample, cChannels channels, sampled at xSPS/sampleFreqUnits samples per second, actualCount samples (per channel) in the file.  Each sample is a linear function of sampled voltage, with 1 V = voltageUnits (if you divide each sample by voltageUnits, you get the sampled voltage.)

Likely the most common format you could use is the pure text tabulated format, with one set of samples per line.  First column is typically time (in seconds relative to the start of the measurement is common), followed by the actual physical quantities measured via the voltage levels, optionally followed by the actual voltage levels.  This way, when you attach the dataset to your report, anyone can read it into whatever application they want, from Matlab/Octave to Excel/Calc to Gnuplot/xfig/etc. to custom programs.  (CSV is not as common for purely numeric data.)

Navigation

[0] Message Index

[*] Previous page

There was an error while thanking
Thanking...
Go to full version
Powered by SMFPacks Advanced Attachments Uploader Mod