Electronics > Beginners

help with rapid ADC data aquizition

<< < (6/8) > >>

rstofer:

--- Quote from: powerfly on December 09, 2019, 10:40:14 am ---
--- Quote from: jhpadjustable on December 07, 2019, 03:40:44 pm ---
--- Quote from: powerfly on December 07, 2019, 01:41:35 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?
--- End quote ---
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.

--- End quote ---

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?

--- End quote ---

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.

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

jhpadjustable:

--- Quote from: powerfly on December 09, 2019, 10:40:14 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?

--- End quote ---
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.)


--- Quote from: tooki on December 09, 2019, 12:43:34 pm ---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.

--- End quote ---
It's a fair cop. I've toned it down.


--- Quote from: rstofer 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.
--- End quote ---
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.
--- End quote ---
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.

mikerj:

--- Quote from: tooki on December 09, 2019, 12:43:34 pm ---
--- Quote from: jhpadjustable on December 07, 2019, 03:40:44 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...)

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

--- End quote ---

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.

Nominal Animal:

--- Quote from: rstofer on December 09, 2019, 04:23:16 pm ---
--- Quote from: powerfly on December 09, 2019, 10:40:14 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?

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

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


--- Quote from: rstofer on December 09, 2019, 04:23:16 pm ---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.
--- End quote ---
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.

Navigation

[0] Message Index

[#] Next page

[*] Previous page

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