SDcard Logger with FIFO and FIR Filter (FreeRtos)

Pito
Fri Oct 07, 2016 5:45 pm
SDcard Logger with FIFO and FIR Filter (FreeRtos) for Maple Mini or Blue Pill
====
As it has been discussed in this forum, writing a continuous stream of data to an SDcard is difficult.
Not because of the SDcard’s writing speeds, but because of Write Latencies (up to 250ms).
During an WL an SDcard does not respond to the incoming data, it means when the data are not buffered, the data are lost.
You have to overcome the WL outages (random occurence, frequent, random duration) somehow, here is an example with a “FIFO buffering”.
More on this WL issue you may find here:

http://www.stm32duino.com/viewtopic.php … 288#p18207

Below you may find a complete Demo I wrote for MapleMini and an SDcard connected to SPI1:
1. we sample 8ADCs, 200x per second,
2. we apply 30Hz low pass filter on each ADC channel measurement,
3. we write the data into the FIFO buffer,
4. in parallel we read the data from the FIFO and write the data onto an SDcard as fast as we can,
5. the format we write the data on the Sdcard is CSV, so you may analyze what is going on in Excel,
6. we also log information on FIFO statuses (the values of counting semaphores).

Note:
1. In this demo we read 8ADC channels as an example, but some ADC channels read SPI1 signals,
as you may see on the results in the CSV file,
2. You may play with FIFO size in Records (here default 150), the actual content of a Sample Record, and with
Sample period/rate (default 5ms =200Hz) such it all fits into MapleMini memory (we assume you got 20kB ram)
and you do not get FIFO overruns when an WL hits,
3. I’ve tried to comment the code as the time permitted,
4. Provided As-Is, no warranties of any kind,
5. Use at your own risk.

Enjoy logging,
Pito


Sandrine
Sun Oct 23, 2016 5:12 pm
Awesome work! I mean great that the .ino is laid out in a way that is understandable.
As you seem to know the system, I have a 16 mega-word buffer that could be treated as a FIFO. Would this work or is the SD Card going to hiccup before then? You mention 20K, have you tried dumping that much? Just trying to understand the buffer sizes ;)

Pito
Fri Oct 28, 2016 12:43 pm
The FIFO buffer size:
====
FIFO_size = data_rate * max_expected_sdcard_write_latency

Example:
====
You sample 100bytes 500x per second.
data_rate = 100 x 500 = 50kB/sec (this is the speed you want write data to the sdcard).
max_expected_sdcard_write_latency = 250msec

FIFO_size = 50kB/sec * 0.25sec = 12.5kB

Would this work or is the SD Card going to hiccup before then? You mention 20K, have you tried dumping that much? Just trying to understand the buffer sizes
Mind the writing the data to the FIFO AND to the Sdcard is done “in parallel” – thus it does not matter how the FIFO is large (provided it has been designed large enough for the given purpose) :)
You do not “dump” the FIFO in a single shot – it is a continuous process done in parallel – you pushing the data into FIFO AND data from FIFO into the Sdcard at the “SAME” time.. Look at FifoFree and FifoData in the resulting .csv file – when an WL occurs the FifoFree starts to decrease and FifoData increases (sample after sample) – after the WL is over, the process reverses itself..
Try the example/sketch source above – it uses almost all ram available. Also mind in the sketch it uses FIFO size of 150 “records of data” where the record consist of many items, and it writes the items as a READABLE_TEXT into the .CSV file – thus the datarate is the amount of TEXT CHARS (inclusive commas, spaces, \r\n) plus other additional info written to the Sdcard per second..
Also read this topic: http://www.stm32duino.com/viewtopic.php … 288#p18207


Sandrine
Tue Nov 01, 2016 2:21 am
Thankyou for your reply,

What I really meant was how much can one stuff into the SD Card buffers before that WL must happen?
I can re-write for the large external FIFO but need to know if it’s useful or not. I need to dump about 2M/second

-edit- OK I just saw in the other thread you had mentioned >2Megs / WL so I guess it’s worth to pursue
So with a very large RAM / FIFO buffer, continuous streams at 500K/S could easily be realized by streaming to the
SD card at, say 750K/S so the take-up max time of 250mS won’t ever be an issue.

Thanks!!

..But there must be a limit on the amount of data the SD Card can hold before transferring it in the flash cells…


Pito
Tue Nov 01, 2016 2:51 pm
..1) ..how much can one stuff into the SD Card buffers before that WL must happen?
-edit- OK I just saw in the other thread you had mentioned >2Megs / WL so I guess it’s worth to pursue
2) So with a very large RAM / FIFO buffer, continuous streams at 500K/S could easily be realized by streaming to the
SD card at, say 750K/S so the take-up max time of 250mS won’t ever be an issue.
3)..But there must be a limit on the amount of data the SD Card can hold before transferring it in the flash cells…

1) No idea, it depends on the actual SD card, the WL is a “random” effect..
2) The max data streaming rate depends on the MCU performance, SDcard type, SDcard interface parameters – ie. SPI clock freq., type of interface – SPI or SDIO, etc.
3) It depends on the actual SDcard – the card’s HW differ, firmware differ as well, so hard to answer..
The best way is to experiment and measure.. :)

Sandrine
Tue Nov 01, 2016 4:23 pm
Thankyou so much Pito, you’ve been a great help!
WL is random makes sense now, guess it’s off to the scientific experiments

Cheers,

Sandy


michael_l
Wed Nov 09, 2016 10:30 am
Pito, can you suggest some good but not too expensive SD Card adapter from ebay for use with BluePill ? I have some SPI ILI9341 TFT displays with SD CArd holder. Are they OK ?

EDIT: Found one with buffered chip:

http://www.ebay.co.uk/itm/Micro-SD-Stor … 1033157413


Pito
Wed Nov 09, 2016 1:38 pm
When using an SDcard with MapleMini or BluePill avoid the SdDcard’s shields/boards with level shifters, voltage translators, buffers/drivers, resistors in the signal paths.
The whole setup has to be 3.3V straight.. The only thing which is important is the SDcards are 3.3V, so a voltage regulator is needed when the SDcard is powered from 5V, and good decoupling (between VCC3V3 and GND) close to the SDcard (ie 2x 10-22uF ceramic multilayer).

michael_l
Mon Feb 13, 2017 2:29 pm
This is the closest I could find based on your recommendation. Quite many seem to have LDO regulator. This one does not but it still has some resistors. Can you recommed this ? Thanks.

http://www.ebay.co.uk/itm/LC-Technology … OSwARZXoem~


BennehBoy
Mon Feb 13, 2017 2:47 pm
One of the future requirements of the project I’m working on will be to dump data out to SD on a continual basis, the sampling frequency will be much lower than this, roughly once every 250ms, so in real terms I could write 4 times as much data once per second if the write/SD overheads were high.

There will be a maximum of 20 readings to be output.

So, question, am I likely yo be impacted by this or is my output volume ‘under the threshold’?


victor_pv
Mon Feb 13, 2017 3:20 pm
michael_l wrote:This is the closest I could find based on your recommendation. Quite many seem to have LDO regulator. This one does not but it still has some resistors. Can you recommed this ? Thanks.

http://www.ebay.co.uk/itm/LC-Technology … OSwARZXoem~


stevestrong
Mon Feb 13, 2017 3:23 pm
michael_l wrote: Can you recommed this ? http://www.ebay.co.uk/itm/LC-Technology … OSwARZXoem~

Pito
Tue Feb 14, 2017 12:08 pm
victor_pv wrote:michael_l wrote:This is the closest I could find based on your recommendation. Quite many seem to have LDO regulator. This one does not but it still has some resistors. Can you recommed this ? Thanks.

http://www.ebay.co.uk/itm/LC-Technology … OSwARZXoem~


Pito
Tue Feb 14, 2017 12:21 pm
BennehBoy wrote:So, question, am I likely yo be impacted by this or is my output volume ‘under the threshold’?

michael_l
Sat Mar 11, 2017 5:21 pm
Thanks guys, been too busy with other tasks but now got a change to try this. When I insert the cars blue board resets – is this normal and what causes this ?

Pito
Sun Mar 12, 2017 8:42 am
It is not normal when you insert the sdcard and the BP resets.
Double check wiring. The sdcard module needs 3.3V.

zmemw16
Sun Mar 12, 2017 2:27 pm
card detect switch crossed with the reset line ?
minor edit

Pito
Sun Mar 12, 2017 6:07 pm
Also check with an ohmmeter the possible shorts between the sdcard’s socket pins (all combinations). The socket disconnected from BP during the measurement..

sherinkapotein
Sat Apr 08, 2017 12:16 pm
A quick question on the fundamental functioning of RTOS version of SD logging.
I have been working on arduinos for a few months now and how I understand the SD library works is by creating a 512B buffer and every request to Write on SD is pushed on to the Buffer. The buffer is finally written on SD card when its nearing full. So when I compute the time required for writing data to SD card in a loop a byte at a time, I get like few dozen microsecs for most transactions and one odd write in the range 5-200 ms (or maybe more at times).

When we use the RTOS implementation is the same basic being followed? If yes, then would someone elaborate on the given situation
-The producer thread produces 1B every ms and that needs to be written to SD card.
-The consumer thread just writes to SD card.

The producer thread being of higher priority is woken up every ms and the task it does takes about 300micro secs(say). So when the SD library buffer fills up and its time to actually write to the SD card. The CPU makes access request to write to SD card and due to the latencies involved being of the order of several ms, the request is interrupted in between and the cpu switches to producer thread which writes to the FIFO as usual. After doing its task, the producer sleeps again and the request generated by the consumer thread, which was interrupted in between earlier, is re-initiated again resulting in generating similar write latencies as before. By this logic a successful write can never take place, but since it does I know that my understanding of its functioning is flawed.

I need to understand this so that i can structure my code accordingly. Thanks in advance. i know my explanatory (if thats a word) skills are a mess.


victor_pv
Sun Apr 09, 2017 1:53 am
When a thread is resumed, it doesn’t restart, it resumes from the point in which it was interrupted.

So you have your writer thread waiting for the SDcard to respond to a command, but the sdcard is busy doing whatever an sdcard does, and will take 50ms to respond. In this case the writer thread must be in a loop waiting for the correct reply right?
While in that loop, the scheduler interrupts it and resumes a different thread (call it sensor thread), which takes 500uS to complete, and then goes to sleep for 100mS. When the sensor thread goes to sleep, the scheduler looks at what is the next active thread to run. The writer thread was not sleeping, it was waiting in a loop and was interrupted, so when resumed goes back to that same loop, and keeps waiting for the correct reply from the sd card.
That thread can be interrupted and resumed many times while in the loop, but eventually the sdcard replies back and the writer thread continues with it’s writing process.

Does that clarify it?


sherinkapotein
Sun Apr 09, 2017 3:11 am
victor_pv wrote:When a thread is resumed, it doesn’t restart, it resumes from the point in which it was interrupted.

Pito
Sun Apr 09, 2017 9:43 am
The SDcard latency buffer size is estimated not by rtos, but by you at compile time..

Imagine worst case latency 250ms. And you want to write (sensor task) 10kB/sec onto the card (now it does not matter on task timings).
So the FIFO buffer shall be 0.25sec * 10kB/sec = 2.5kB large.
When assuming the writing to the SDcard is much faster as the writing speed of sensor’s task to the fifo buffer, it will not overrun during the 250ms.

The timing: as victor wrote it is a preemtive multitasker, so the sensor reader will write at 1ms intervals say 3Bytes (3kB/sec) into the FIFO buffer.

“In parallel” (that is the rtos’ trick) the SDcard writer attempts to write onto the SDcard from the FIFO buffer full speed (ie 1MB/sec) when FIFO is not empty. While the FIFO fills itself from left side at speed 3kB/sec, it empties itself from right side at speed 1MB/sec, all in parallel, sometimes blocked at the right side by the write latencies.

Normally the FIFO will be all the time empty. When the write latency hits, the FIFO starts to fill itself (like a bottle). The size of FIFO will be enough for planned latency. When latency finishes, the SDcard writer will flush the FIFO onto the SDcard full speed (1MB/sec).

When you run my example above, play with it and observe the .CSV data, you would see how it works in detail.

The switching:
My current understanding is, the switcher, when completes the “Sensor” task (say at 300us from the start of the 1ms tick as an example), switches over and starts the another task – the task SDwriter – immediately.
It happens say at 301st microsecond from the start of the 1ms tick as the example.
The Sensor task is scheduled to be run at 1ms intervals (the sampling period in ticks), but the SDwriter is “always” ready to continue with writing the data off the FIFO to the SDcard (when FIFO not empty)..
So the SDwriter does not wait till the next Xth 1ms tick to continue with writing to the SDcard..

When you have planned several tasks to be started and run at Xth tick, the scheduler attempts to run all planned tasks at the Xth tick (in round-robin sequence when tasks at same priority). When say all 3 tasks will finish at 820us from the start of the Xth tick, the SDwriter will continue immediately with writing onto the SDcard from 821st microsecond..

Scheduling.JPG
Scheduling.JPG (43.49 KiB) Viewed 783 times

sherinkapotein
Sun Apr 09, 2017 1:52 pm
Thank you very much for a detailed response. However, I need to confirm what I now understand is correct.

1) Lets assume as in the figure, the SD Write Task requests write access in the tick corresponding to t=0 and encounters latency (250ms assume). So until the 250th tick every time, the scheduler switches to the SD Write Task , the task only waits for the request to be served?

2) So what happens when the Sensor Task takes, say 800us to complete and the SD task is left with only 200us before the preemption takes place.

In this case since the actual write is of 512B (which is the cache size as defined in, I believe in “FatVolume.h” ;not the FIFO buffer declared by user at compile time) and may take like 1.6ms @ 300KBps (minus the latency). So to say, that the writing task (when actually writing to card) is interrupted (as it has only a 200us time slice) and it will complete the transaction in 8 such ticks (1.6ms / 200us).

forum.jpg
forum.jpg (53.18 KiB) Viewed 771 times

Pito
Sun Apr 09, 2017 2:27 pm
Firstly, writing to the SDcard (I use the SdFat library) works via DMA here, afik. Secondly, to write a 512b cache into an SDcard takes about 285us at 18MHz SPI (it is done in background, however).

My above Logger example does log the timings (in usecs) between the actual sampling periods onto the SDcard together with data, so you may inspect whether it dropped a measurement or not.

Of course, you can get FIFO overruns when the timing is critical and you do not have enough FIFO buffer space available. When the FIFO cannot be emptied fast you get overruns (they are logged as well together with the FIFO watermark at each sample measurement) and some amount of your data is lost until it recovers.
Simply try it and report ;)

The example logs with each sample measurement:
void PrntHeaders () {
// Print Data Record Headers into the CSV file
file.print(F("Time_us"));
file.write(',');
file.print(F("ADC0"));
file.write(',');
file.print(F("ADC1"));
file.write(',');
file.print(F("ADC2"));
file.write(',');
file.print(F("ADC3"));
file.write(',');
file.print(F("ADC4"));
file.write(',');
file.print(F("ADC5"));
file.write(',');
file.print(F("ADC6"));
file.write(',');
file.print(F("ADC7"));
file.write(',');
file.print(F("Overruns"));
file.write(',');
file.print(F("FifoFree"));
file.write(',');
file.println(F("FifoData"));
}


sherinkapotein
Sun Apr 09, 2017 4:32 pm
pito wrote:Firstly, writing to the SDcard (I use the SdFat library) works via DMA here, afik.

Pito
Sun Apr 09, 2017 5:45 pm
Ah, that clarifies a lot, if DMA comes into play. I’m still stuck with the 8 bit AVR modus operandi. In that case should there be problem if both the consumer and producer threads use different SPI devices (say an nrf24l01 and Sd card).
If two tasks use the same resource (ie SPI1) you have to use the mutexes to lock the resource when used.
If producer uses the SPI1 and consumer the SPI2 there is none problem.
FYI – the SPI1 runs up to 36MHz (@72MHz base). And the BluePill runs up to 128MHz overclocked. So forget 8bitters :)

sherinkapotein
Sun Apr 09, 2017 6:07 pm
If producer uses the SPI1 and consumer the SPI2 there is none problem.
How did I miss multiple SPI ports on the pills. I suddenly am starting to feel the richest man alive!!! Guess it will take some time for me to get used to the riches… gonna have a tough time trying to get some sleep tonight out of excitement!!! 8-)

Hope the STM32duino library base soon reaches the Teensy level. That would be the end of AVR era as we know.


Pito
Tue Apr 11, 2017 3:07 pm
Enclosed pls find a “Basic” version of my “SDCard LOGGER with FIFO” demo (original in my first post here).

Default:
. Sampling period set 1ms
. Writes about 20bytes per sample (a record) into a DATA.CSV file (time[us], ADC1, Overruns, FIFOFREE, FIFODATA)
. No FIR filter on ADC
. Uses 400 records deep FIFO

Here it runs without overruns with a mainstream SDcard. Below is the FIFODATA from excel indicating there were 3 latencies which took up to 190 records in FIFO. The jitter you may see from the logged data is 1-2usec from time to time (but 0 most of the time).

Compiled for Maple Mini, for BluePill or others you have to adjust the LED’s and SDcard’s /CS pins.

Fifo Data.JPG
Fifo Data.JPG (36.65 KiB) Viewed 724 times

sherinkapotein
Tue Apr 11, 2017 9:38 pm
This sounds promising. Will get back with my implementation of code along with RF thingy.
Thanks a lot!!

sherinkapotein
Wed Apr 12, 2017 8:45 pm
Just tried your code on my blue pill but does not seem to work. The script uploads fine but no activity whatsoever. I even tried putting a debug message right after Serial.begin() but in vain. The blink example in the mapleFreeRTOS however works fine.
Any suggestions?
Thanks

Pito
Wed Apr 12, 2017 10:49 pm
Do you use Serial1 (an external dongle)??
If not, you have to rename Serial1 to Serial (usb) everywhere in the source.
void setup() {
// Tasks creation statuses
portBASE_TYPE s1, s2, s3;

Serial1.begin(115200);


sherinkapotein
Thu Apr 13, 2017 10:38 am
pito wrote:If not, you have to rename Serial1 to Serial (usb) everywhere in the source.

Pito
Thu Apr 13, 2017 11:28 am
Happy to see somebody’s else latencies :P
Based on the results you may play with FIFO’s size. Mind a bigger latency may hit after a longer capture period.

Pito
Fri Apr 14, 2017 3:25 pm
In order to speed up the printing into the .CSV file (especially with 1ms sampling rate) you may try to use the SdFat’s special printField() function, which claims to be much faster than print(), for example:
// Print out the Data Record values into the CSV file

if (last) {
file.printField(p->usec - last,',');
} else {
file.write("NA , ");
}
last = p->usec;
file.printField(p->value1,',');
file.printField(p->value2,',');
file.printField(p->value3,',');
file.printField(p->errors,',');
file.printField(p->a,',');
file.printField(p->b,'\n');

// Advance the FIFO index


Pito
Fri Apr 14, 2017 4:28 pm
FYI: 43MB large log, 1ms sampling (with time, adc0, adc1, adc2, errors, fifodata items written to the sdcard as .csv text).
FIFO size 400 records. No data lost.
You may see how the fifo messed with critical timing (it seems the 1ms sampling with above amount of data is at the edge of system’s ability to process the data) and sdcard’s write latencies (only first 1.048mil samples shown for obvious reason):

Log 1ms 1mil.JPG
Log 1ms 1mil.JPG (71.08 KiB) Viewed 612 times

Pito
Sat Apr 15, 2017 8:31 am
And below the same exercise as above (the same sdcard, the same amount of data written, 1ms sampling period, the FIFO size set even smaller – only 300 records) but with SdFatEX. FIFO is relaxed, you may see a few latencies.
You may go with much smaller FIFO size, as the picture indicates.

The rd/wr speed of EX (Extended Transfer Class) is much higher than with a standard SdFat (and you do not need large caches to get such speeds).
In practice it means you may log much more data with the 1ms sampling. Try :)

Log 1ms 1mil SdFatEX.JPG
Log 1ms 1mil SdFatEX.JPG (29.15 KiB) Viewed 595 times

Pito
Sat Apr 15, 2017 9:41 am
And this is with the original SdCard Logger from the first post here, but with 1ms sampling, and with SdFatEX, FR 9.0, Maple Mini.
The sampling includes 8xADC and 67taps low-pass FIR filter applied on each ADC channel.
With FIFO size 100, I’ve got zero errors, no data lost.
We write 50-60bytes per sample (we write .csv text), that is 50-60kB/sec data onto the sdcard.

Log 1ms 1mil 8ADC FIR SdFatEX.JPG
Log 1ms 1mil 8ADC FIR SdFatEX.JPG (40.45 KiB) Viewed 578 times

zoomx
Fri Jun 22, 2018 9:14 am
Two simple questions:
If I want to log lesser channels I only have to comment out the others like in this example with only the three first cannels?
// Record the ADC data read from the Sensors into the FIFO
// Sampling of 8 ADC with 71taps fir filtering takes 285usecs
p->value1 = analogRead(PA0);
p->value2 = analogRead(PA1);
p->value3 = analogRead(PA2);
/*
p->value4 = analogRead(PA3);
p->value5 = analogRead(PA4);
p->value6 = analogRead(PA5);
p->value7 = analogRead(PA6);
p->value8 = analogRead(PA7);
*/
SampleFilter_put(&f[0], (int) p->value1);
p->value1 = SampleFilter_get(&f[0]);
SampleFilter_put(&f[1], (int) p->value2);
p->value2 = SampleFilter_get(&f[1]);
SampleFilter_put(&f[2], (int) p->value3);
p->value3 = SampleFilter_get(&f[2]);
SampleFilter_put(&f[3], (int) p->value4);
/*
p->value4 = SampleFilter_get(&f[3]);
SampleFilter_put(&f[4], (int) p->value5);
p->value5 = SampleFilter_get(&f[4]);
SampleFilter_put(&f[5], (int) p->value6);
p->value6 = SampleFilter_get(&f[5]);
SampleFilter_put(&f[6], (int) p->value7);
p->value7 = SampleFilter_get(&f[6]);
SampleFilter_put(&f[7], (int) p->value8);
p->value8 = SampleFilter_get(&f[7]);
*/
// Record the FIFO overruns for this sample
p->errors = overrunerrors;
overrunerrors = 0; // clear for the next sample

Pito
Fri Jun 22, 2018 8:25 pm
If I want to log lesser channels I only have to comment out the others like in this example with only the three first cannels?
You have to comment out all places where you define/mess with those values.. A “record” consists of those values, thus you save a lot of space then.. (FIFO works with records)..

Manolo88
Wed Sep 05, 2018 8:12 pm
Pito, i want to know if you have resolved the

https://www.stm32duino.com/viewtopic.php?t=1298#p16742

post, becouse i need to increase the tick frec and i can’t do it. I configure

#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )

in the RTOSconfig.h to 100 or to 2000, and no effect.

Can you help me??

At 1 ms tick it works perfect.

Thanks for all.


Leave a Reply

Your email address will not be published. Required fields are marked *