Author Topic: help with rapid ADC data aquizition  (Read 8644 times)

0 Members and 1 Guest are viewing this topic.

Offline rstofer

  • Super Contributor
  • ***
  • Posts: 9893
  • Country: us
Re: help with rapid ADC data aquizition
« Reply #25 on: December 09, 2019, 04:23:16 pm »
An alternative that I think you mentioned at the end of your comment is to store maybe like 0.5Mb of data on the native RAM and whilst the other code is still reading data in, to dump that 0.5 Mb to a memory card, is that what you were saying?
More or less, reserving some space to buffer some of the filesystem metadata. Take care that you don't surpass your filesystem's limitations. You might find it easier on the whole to work in terms round wall-clock time, maybe do 2-second chunks instead, which is more than enough time to create or append a file on the flash.

how can you actually acheive this (code wise), how can I get the 10kHz aquistition rate not to slow down when dumping 512KB of data to an SD card?

That's the whole point of using DMA channels and multiple buffers.  Once an SD operation is set up, it happens by automagic and generates an interrupt or sets a semaphore when complete.  Maybe there is a counting semaphore that, when non-zero, causes more buffers to be transferred until the semaphore reaches 0.  Maybe there is a circular queue of 512 byte sector data.  This operation wants to have very low overhead but needs to be elastic in terms of timing.

On the other end, there is a timer or something generating ADC start signals and, just guessing, the ADC values will fill up 512 byte buffers in the circular queue.  Again, these samples can be moved into the processor by DMA.  Some DMA channels are pretty sophisticated in how they can use circular queues.

What is really necessary, as a first step, is to understand the DMA controller.  Read the datasheet while imaging how the data will flow through the system.  You have to assume, and prove, that you can write faster than you can read.  If this isn't true, you have the wrong chip.

ADC -> DMA -> Buffer -> DMA -> SD Card where 'buffer' means a circular queue of sector sized blocks.  You will want several such blocks and, given the 512K of RAM on the Teensy 4 (there are some strange memory limitations that I don't understand, yet), you might as well use it up.  Maybe 32 buffers is sufficient, maybe 64.  You need to study the write time of the SD card along with the incoming ADC rate to figure out how many buffers you need.  While a buffer (block) is being written to SD, it is unavailable to the ADC stream.  The ADC stream needs to be certain it isn't overrunning the SD write buffers.  Maybe the SD card will support multiple sector write commands - Compact Flash will.

All of this will likely work but the programming is far beyond what Arduino users are used to seeing.  I sort of doubt that moving the data in a superloop (void loop() is a 'superloop') will be satisfactory since there is no concept of multi-tasking.
 
The following users thanked this post: powerfly

Offline rstofer

  • Super Contributor
  • ***
  • Posts: 9893
  • Country: us
Re: help with rapid ADC data aquizition
« Reply #26 on: December 09, 2019, 04:35:54 pm »
Depending on how you want to post-process the data, you may not need an actual file system.  You can simply write contiguous sectors, starting at 0 (or wherever) and continuing on in the address space.

Then, you can use a utility like Linux's 'dd' command to read the raw sectors from the SD card into a named file in the Linux file system.  I suspect this can be done in Windows using one of the raw read utilities. 

Using the SD card for raw data would be faster and less complex but you would need to write a sentinal block at the end of the dataset to mark the end.  Given the speed of the Teensy 4 something like the ChaN FatFs might be suitable but if you wind up using FreeRTOS, there is a FS ready to go
https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_FAT/index.html

A filesystem would be nice as long as the system can keep up.  Given DMA input and DMA output, the only thing the CPU does is handle the filesystem.  It should work fine.
 

Offline jhpadjustable

  • Frequent Contributor
  • **
  • Posts: 295
  • Country: us
  • Salt 'n' pepper beard
Re: help with rapid ADC data aquizition
« Reply #27 on: December 09, 2019, 06:58:02 pm »
how can you actually acheive this (code wise), how can I get the 10kHz aquistition rate not to slow down when dumping 512KB of data to an SD card?
I would not advise using Arduino for this. Its general spirit is to be simple and very transparent, to the point you can virtually reach out and touch every bit of data that you're working with, in order to aid understanding of programming and to get simple things done easily and quickly. In this case, to meet your brief, you don't want to handle every piece of data. You need to work at arm's length, on the "control plane", and let engines outside of the code push data around on the "data plane" while you sit back and steer.

Speaking for STM32, ST's Cube tools offer a way to pointy-clicky set that up quickly, leaving you to fill in a few hundred lines of code at most, and it does a decent job of that. NXP Processor Expert seems to fill that role for Kinetis. You may be able to bend the sample code found here to your needs: https://community.nxp.com/docs/DOC-96507  NXP app notes AN4639 and AN4590 seem to be useful reading as well. (I'm not a Kinetis guy, but it does seem like the STM32 hardware is a bit more suited to handling just this sort of data acquisition task.)

Arduino’s raison d’être is to make it easy for beginners to, well, begin. It’s quite uncalled for to disparage beginners for not being experts in electronics and/or programming.
It's a fair cop. I've toned it down.

Depending on how you want to post-process the data, you may not need an actual file system.  You can simply write contiguous sectors, starting at 0 (or wherever) and continuing on in the address space.
For the best of both worlds, set up a Potemkin FAT file system with one giant file covering the entire data space, and treat it as a fixed header that you need not touch. You then use direct block access with an offset to address the contents of the file. The output size seems to be upper-bounded to a couple of GB (504 million bytes/hour, a few hours at a session) and the card size seems to be lower-bounded to at least double that. So simple, compact FAT16 would suffice.

I've crafted a FAT16 image in the attached 7zip archive. When written to the SD card using dd, Etcher or other similar tool, there will appear one 2GB FAT16 partition containing a single large file DATA.BIN with 4 hours (2,016,000,000 bytes) in consecutive blocks, and some blocks and directory entries left over for another device to add post-session notes or whatever you will. Measured from the very beginning of the card, the user content area begins at byte address 1937168 (decimal), absolute sector #3783 (decimal, 512-byte basis), and extends for (3937500) 512-byte sectors. That's one less foray into the weeds for powerfly to have to deal with. :)

(Ideas for a stretch goal: the FAT tables themselves are mostly just consecutive uint16_t numbers which could be easily resynthesized with a for loop or two. If they were stripped out and reconstructed at card build time, the few sectors remaining could be converted to C arrays to occupy a small amount of flash, and the recording device could "format" any card on the fly. If there were enough flash space available, it might (or might not) be easier and less in-the-weeds to bring along something like zlib and just decompress directly to the SD card's blocks.)

Quote
Using the SD card for raw data would be faster and less complex but you would need to write a sentinal block at the end of the dataset to mark the end.
A block "timestamp" or counter would maintain append-only semantics, which eliminates a little bit of uncertainty in timing which the card and any filesystem code may or may not introduce due to rewriting the same block twice. If one chooses a sufficiently unique starting number (the time from a real-time clock would be perfect, but a good randomness source such as user input would work too), and assuming no wrapping, you'll know you've reached the end of the recording when you see any out-of-sequence block serial number, fortunately including 0 or -1 from unused flash.
"There are more things in heaven and earth, Arduino, than are dreamt of in your philosophy."
 
The following users thanked this post: powerfly

Offline mikerj

  • Super Contributor
  • ***
  • Posts: 3244
  • Country: gb
Re: help with rapid ADC data aquizition
« Reply #28 on: December 09, 2019, 10:03:06 pm »
Your Arduino mate was doing it wrong. He would have had better luck if he batched up a whole sector of data before writing it out, and I bet he was also formatting the output as text instead of binary.  (But the Arduino fans don't usually think at this level...)
Arduino’s raison d’être is to make it easy for beginners to, well, begin. It’s quite uncalled for to disparage beginners for not being experts in electronics and/or programming.

Arduino makes it easy and fast to do things the wrong way.  OTOH replace the Arduino ecosystem with a conventional C compiler and decent editor, and the boards and shields are cheap and useful for all sorts of things.
« Last Edit: December 09, 2019, 10:04:37 pm by mikerj »
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6305
  • Country: fi
    • My home page and email address
Re: help with rapid ADC data aquizition
« Reply #29 on: December 10, 2019, 12:00:27 am »
how can you actually acheive this (code wise), how can I get the 10kHz aquistition rate not to slow down when dumping 512KB of data to an SD card?
That's the whole point of using DMA channels and multiple buffers.  Once an SD operation is set up, it happens by automagic and generates an interrupt or sets a semaphore when complete.  Maybe there is a counting semaphore that, when non-zero, causes more buffers to be transferred until the semaphore reaches 0.  Maybe there is a circular queue of 512 byte sector data.  This operation wants to have very low overhead but needs to be elastic in terms of timing.
If you do not use a filesystem (I wouldn't bother), I would have the acquisition only do say 500 samples per 512-byte sector, and at least a monotonic 32-bit counter (8 bytes) at the start of each block.  (A 32-bit counter suffices for roughly a terabyte.)

The data acquisition part generate all the 512 bytes per sector, just storing the incremented counter before the actual samples.  It does not "cost" more than a couple of variable assignments and increments in machine code (in C/C++ it is just an assignment), so it definitely should not affect the ADC sampling at all.

The trick here is that you "format" the SD storage by initializing the very first and last sectors, with the first sector having counter value 0, and the last sector counter value one less than the number of sectors. (This is one of the two special cases in this scheme, and means the very last sector was the most recently written one.  The other special case is when the counter rolls over; that's why a 32-bit counter suffices for only a terabyte or so, and not two (512×232=2,199,023,255,552).

Because the sectors are written in sequence, you get wear leveling for free.  A reader, and the microcontroller when starting up, reads the first and last sectors to determine where the most recently written sector is (often having to do a binary search -- but that's less than 32 additional sectors to read -- and only the counter parts of the sectors need to be read); and the sector following it is the next one to be replaced/written to.  (The method should be obvious, but if you don't see it, let me know and I or someone else here will describe the exact logic.  I'm trying to avoid writing too much of a wall of text here!)

In essence, this works very much like a super-lightweight, wear-leveling, continuous data stream storage system, with 508 bytes available per each 512-byte sector.

You will want several such blocks and, given the 512K of RAM on the Teensy 4 (there are some strange memory limitations that I don't understand, yet), you might as well use it up.
Very good point.  Teensy 4 has a slightly oddball architecture.  It has 1M of RAM, and 2M of Flash, but half the RAM is tightly coupled; so, essentially, you have two 512k RAM "banks" of sorts.  Usually, code is copied to the tightly coupled RAM to run faster.

This is such a new architecture, it is taking a bit of time for the Teensyduino support to stabilize and grow all the necessary features.  I see powerfly already started a thread at the PJRC forums, where KurtE and others point out the features still under development.

One crucial point I'd like to make, is to first define the terms and data rates precisely.
Rounding up the figures, let's say you need 10 channels, each sampling at 9-16 bits per sample, at a sample rate of 10,000 samples per second per channel, i.e. two bytes per sample, at a rate of 10×2×10,000 = 200,000 bytes per second.

As Defragster mentioned at the linked thread, the standard SdFat library (it's written by Paul Stoffregen, the developer of the Teensies) can do 5,000,000 - 15,000,000 bytes per second to an SD card (depending on the card).

So, in theory, you should be good for at least 20 channels, at 400,000 bytes per second, and not have issues with the data throughput.  (Note that even on a 3.2 the SD card interface is just the connector, there are no active components needed.  The SD Adaptor at PJRC.com is for the 8-bit Teensies, 2.0 and 2.0++, and has level shifting as they use 5V logic instead of the 3.3V logic like later Teensies and SD cards.)

Whether the internal ADC suffices for your case, or if you should use external ADCs, I can't really tell.  I've used both, but I don't really have the test equipment to say what kind of real-world results you can expect.  The common I2C ADC modules like ADS1x15, MCP342x, ADS1220 don't have the sample rate, but there are AD7606-based 8-channel 16-bit ADC modules capable of sampling at 200,000 samples/second; i.e. capable of sampling 8 channels at 25,000 samples/second.  The interfacing is not as easy as some other modules, but there are existing AD7606 Arduino projects, and you can get the modules from fleabay at 13-25 euros.  Since the sample rate is more than twice your required data rate, you should be able to use a single SPI bus (not the one connected to the SD card), to interleave samples from two separate boards, giving you 16 channels, 16 bits per sample, at 10,000 samples per second per channel.  Or, better yet, use two different SPI buses, and DMA to suck the interleaved samples from the AD7606 modules.

Personally, I would be tempted to go with the Teensy 4 and two AD7606 modules, because of the RAM and the speed available, but fact is, the software support isn't there yet.  For now, I'd say Teensy 3.5 or 3.6 is a better choice, and should have easily the oomph to do this (aside from whether the internal ADCs suffice or not).  I would probably also use 4096-byte sectors, just to reduce the (counter) overhead; you can probably use about 200k on the Teensy 3.5/3.6 for buffering sample data, or about 50 buffers, which should easily suffice even over SD card hiccups.
« Last Edit: December 10, 2019, 12:02:38 am by Nominal Animal »
 

Offline rstofer

  • Super Contributor
  • ***
  • Posts: 9893
  • Country: us
Re: help with rapid ADC data aquizition
« Reply #30 on: December 10, 2019, 01:34:56 am »
I first mentioned the external ADCs and now that it has come up again, it's important to figure out if the sampling and transfers can be automagic.  If there needs to be a major exchange of commands just to transfer a single sample, DMA isn't going to work.  DMA wants a stream of data, not chit-chat.

It will be worth the time to figure out whether the internal ADCs are adequate because they likely can be connected through DMA.  Nevertheless, this needs to be proven.

In a perfect world, the foreground task wouldn't be involved with transfer at any stage.  It would simply set up the DMA registers and sit back and watch (for the next DMA register update).

This whole project is going to be built around data flow, not some superloop of code.
 
The following users thanked this post: Nominal Animal

Offline tooki

  • Super Contributor
  • ***
  • Posts: 11627
  • Country: ch
Re: help with rapid ADC data aquizition
« Reply #31 on: December 10, 2019, 10:28:59 am »
Arduino’s raison d’être is to make it easy for beginners to, well, begin. It’s quite uncalled for to disparage beginners for not being experts in electronics and/or programming.
It's a fair cop. I've toned it down.
:-+ Thank you!


Your Arduino mate was doing it wrong. He would have had better luck if he batched up a whole sector of data before writing it out, and I bet he was also formatting the output as text instead of binary.  (But the Arduino fans don't usually think at this level...)
Arduino’s raison d’être is to make it easy for beginners to, well, begin. It’s quite uncalled for to disparage beginners for not being experts in electronics and/or programming.

Arduino makes it easy and fast to do things the wrong way.  OTOH replace the Arduino ecosystem with a conventional C compiler and decent editor, and the boards and shields are cheap and useful for all sorts of things.
No doubt. But nonetheless, getting it done at all is still hugely valuable as a learning tool. I can say confidently that without Arduino, I would never, ever have even begun dabbling with MCUs, because as a non-programmer, I would never have been able to successfully interface with all the chips and components for which ready-made Arduino libraries exist. My programming skills are too basic to be able to implement interface timing, etc. Also, the Arduino ecosystem has produced hundreds of little breakout boards with (usually) decent minimal viable circuits. I can’t really say how many n00b mistakes I would have made if I’d had to try and get just the bare chips and stuff working. And finally, Arduino really brought down the cost of entry, given that historically, dev boards, EPROM programmers, IDEs, etc cost a lot of money. Would eBay be chock full of $2 ISP programmers if Arduino hadn’t come along? Maybe, but I doubt it.

So Arduino is fantastic for getting people into MCUs. And then, little by little, one can transition to using fewer premade components and libraries. (For example, I’m now starting to use bare Atmega chips instead of Arduino nanos, etc.) But many of us never will move past Arduino, simply because we aren’t programmers and don’t really aspire to be. And that’s OK. Don’t deny and begrudge us our successes just because it’s not the “optimal” way to solve a problem.
« Last Edit: December 10, 2019, 10:31:48 am by tooki »
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6305
  • Country: fi
    • My home page and email address
Re: help with rapid ADC data aquizition
« Reply #32 on: December 10, 2019, 11:13:57 am »
Excellent points, rstofer.

It looks like Teensy 3.2, 3.5, and 3.6 (but not LC or 4.0) have sufficiently accurate SAR ADCs (two of them!) to get 200,000 samples/second.  One does need to reduce the number of samples averaged (from the default 32) to 16 or less, but that should still leave at least 12 meaningful bits in the result, I think; I haven't verified this.

I don't think there is a way to DMA data from different analog input pins using a single ADC.  I could be wrong, though.

I would try a periodic interrupt that triggers the next conversion on the current ADC, then reads the conversion result from the other ADC, and selects the input pin for the next-next sample (the current ADC).  It has no waits or anything like that, so even a high-sounding rate like 200,000 interrupts per second should work okay.

I do not know if the overhead of such an interrupt is too much for SD card transfers.  The interrupt can only occur between byte transfers, so it is a matter of if the interrupt causes stretches a SPI clock cycle between bytes too long.

20 channels is pushing Teensy 3.2 to its limits (and half of those analog input pins are on pads underneath, which are a bit tricky to connect to), but might work fine on a 3.5 or 3.6, as they have a much higher clock rate, and the analog pins are easier to access.  I don't have a 3.5 or 3.6 myself to test, though.



I took a looksee at the AD7606 datasheet.

It turns out AD7606 can do 200 ksamples/second per channel, and can support both ±10V and ±5V analog voltage ranges when powered from 5V (AVcc) while the logic voltage levels are 3.3V (Vdrive).  Darned interesting to my needs as well; I wonder why I haven't encountered it before!  The modules at fleabay are suspiciously cheap compared to the chips themselves, although they look a lot like the circuit note.

It is completely controlled by logic-state pins (i.e., no command input, and only output is sample data; mode is set by tying control pins high or low), and is rather simple to operate.  It has three main modes: serial output, byte output/8-bit parallel output, and 16-bit parallel output.

To trigger a new sample (all channels are sampled simultaneously), you pull CONVST(s) high.  Within 45ns, the AD7606s pull BUSY high.  It will stay high for up to 4.2us (longer if oversampling is enabled).  When an AD7606 pulls BUSY back low, the samples are available.

To read the 8 samples in serial mode:

SCLK is high. The microcontroller pulls CS low for the AD7606, and waits 35ns or longer.  Then, it starts pulsing SCLK (down) 128 times at a maximum frequency of 15 MHz (and duty cycle between 40% and 60%, so this must be a single noninterrupted SPI transaction).  On each rising edge of SCLK, the microcontroller receives the next bit on the DOUTA pin.  After the 128 bits, the microcontroller pulls CS high.

The byte mode is very similar:

RD and CS are high. The microcontroller pulls CS low, and RD low for at least 30ns, for the AD7606. On each rising edge of RD, the next 8 bits are available on DB7:0.  RD must be low for at least 30ns, and high for at least 15ns, so the max RD rate is about 20MHz at 3.3V VDRIVE logic levels.  After the 16 bytes (rising edges of RD), the microcontroller pulls CS high.

The 16-bit parallel mode supports a linked CS and RD mode:

RD and CS are is high. The microcontroller pulls both low, and waits for at least 30ns (at 3.3V VDRIVE), then reads the sample from DB15:0, and pulls both high again.  After at least 22ns, it can repeat this for the second channel; and repeat for all eight channels.

In the byte mode, an inline assembly routine on Teensy 3.2/3.5/3.6 can read the eight samples in about 1us per AD7606.

With three AD7606s/24 channels, and using an interrupt (the AD7606s can share the data lines, as long as only one has its CS low at any time), this is 30,000 interrupts per second, and 30ms or so of execution time, per second.  Not bad at all.  Whether the latencies caused by the 1us interrupts are too much jitter for the SD cards, I do not know.  (It is equivalent to about 25 cycles of the SD card SPI clock running at 25MHz, but only occurs between byte transfers, not mid-byte.)



So, it seems to me, trying out the built-in ADCs on a Teensy 3.5 or a 3.6 would be the way to go.
If they don't give good enough results, I'd go for the AD7606s, unless something better is found.

If the code to handle both the sampling and the SD card storage on a single Teensy is too complicated, then I'd use two, connecting the two using UART for commands, and SPI for sample data.  Then, the one storing the data to an SD card can definitely use DMA for the data (being SPI slave), and having enough RAM for buffering the data, can use the standard SdFat library to write the data to a standard FAT filesystem on the SD card, with a loop that just tries to keep up with the DMA.  (The DMA is connected to a completion interrupt, which sets up the next chunks, with at least a dozen or so such chunks in your buffer.)

Teensy 4.0 should be able to control three AD7606s in byte mode. The data pins would be 16-23 (DB0 pin 19, 18, 17, 16, 22, 23, 20, DB7 pin 21); pins 0 and 1 for UART; pins 10-13 for SPI; CONVST on pin 14 and RD on pin 15; and the three CSs, three BUSYs and RESET somewhere on pins 2-9.  There are an additional ten pins on the bottom of Teensy 4.0 on pads, but they're a bit tricky to solder to, but if used, would allow expanding to a fourth AD7606 for 32 channels. Teensy 4.0 would be the SPI master, although only providing CS, SCK, and MOSI/data out.  The CONVST pin would be a PWM or timer output, for optimal sampling stability while still allowing precise sample rate control.
The UART is used to set the sample rate and select the channels sampled (well, ALL are sampled, but only some are forwarded), with say a single-byte "OK" or "Error" acknowledgement (for simplicity and ease of use).

The nice thing is that if connected to USB, it could switch to USB mode, and since Teensy 4.0 has a 480Mbit/s USB interface, could provide up to the maximum 200,000 samples/second per channel for all 24 connected channels, making the same thing useful as a AD7606 data acquisition controller.

If my Christmas budget allows (I'm broke :-[), I'll get a couple of those AD7606 modules off fleabay, and try them with my Teensy 4.0.
 

Offline powerflyTopic starter

  • Contributor
  • Posts: 22
  • Country: gb
Re: help with rapid ADC data aquizition
« Reply #33 on: December 10, 2019, 03:55:13 pm »
Just wow, I am surprised by the sheer number of replies from so many different members. Thank you so much for all being willing to help me along with my little project. I think I'm going to read the thread again as I definitely did not understand all of the comments above and some of it is a bit too much for me to take in just from reading it once.

My coding is very much beginner level so I'm not even sure what all of the terms discussed mean. If you could elaborate more on technical terms or provide links that describe them that would be helpful.

I'm really hoping that some pre-existing code is freely available as I've seen similar things for an Arduino:

https://forum.arduino.cc/index.php?topic=228549.0&fbclid=IwAR3U5bL_cAvS-P9N1w4Eq5ZJuPOiywBqPmLKXwgcUpS0Ki7DqYYJMuFkP50

https://github.com/greiman/SdFat/blob/master/examples/AnalogBinLogger/AnalogBinLogger.ino

In the links I've posted it's apparently possible to get 40 kHz 10 bit data to an uno, and by looking at the code (github link), I think a much higher rate is possible. The best option at the moment for my needs seems to be the T3.6, which I now have available to me (as well as a T4.0).

Does anyone know if there is an equivalent code to the one above but for ARM based processors such as those on the teensy 3.6? I currently don't have anyway near the coding capability to construct code like the one above and this project isn't primarily about coding, and I have a deadline for it. As the T3.6 has 23 analog in ports which are at least 13 bit, and at least initially I'll be using 8 pins or less it should be able to handle the 12bit 10kHz rate. It's just getting it done in the timeframe with my lack of coding experience may prove challenging.

Edit: I've been reading throught the thread, and looking up the definition of some of the terms and think I have a better idea of what they mean.

There a still many things I'm unsure of because of my lack of experience. I don't know how to set up a buffer, I don't know whether I should use a file system and if I do use one which one I should use etc.

Does the T3.6 have DMA channels built in, are they a software thing or are they external pieces of hardware?

As this is my first major coding project, it would be really helpful if someone knows of any script similar to the arduino one above. I don't even know where to start in constructing code with buffer systems, DMAs or anything similar.
« Last Edit: December 10, 2019, 06:44:39 pm by powerfly »
 

Offline powerflyTopic starter

  • Contributor
  • Posts: 22
  • Country: gb
Re: help with rapid ADC data aquizition
« Reply #34 on: December 11, 2019, 09:26:54 am »
does anyone have experience with coding of that type? Or know of any existing available code of that type?

If that doesn't work I might just have to limit myself to maxing out the RAM and stopping and dumping that to a card and hope that that is good enough, as I'm not sure I have the time to learn how to make a code and then make a functioning code on how to make DMA buffers and stuff :(
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6305
  • Country: fi
    • My home page and email address
Re: help with rapid ADC data aquizition
« Reply #35 on: December 12, 2019, 04:29:01 am »
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.)
« Last Edit: December 12, 2019, 04:33:03 am by Nominal Animal »
 
The following users thanked this post: mycroft

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6305
  • Country: fi
    • My home page and email address
Re: help with rapid ADC data aquizition
« Reply #36 on: December 13, 2019, 04:21:56 am »
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.
 
The following users thanked this post: mycroft, powerfly

Offline powerflyTopic starter

  • Contributor
  • Posts: 22
  • Country: gb
Re: help with rapid ADC data aquizition
« Reply #37 on: December 16, 2019, 12:34:23 pm »
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.
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6305
  • Country: fi
    • My home page and email address
Re: help with rapid ADC data aquizition
« Reply #38 on: December 16, 2019, 02:48:41 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.
You really should have mentioned this in your initial message!  OpenLogger definitely looks like a good tool for your use case, in my opinion.

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.
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.

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).
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.)
 
The following users thanked this post: jhpadjustable


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf