Low-Power logging and sync() in EXFat

Status
Not open for further replies.

mborgerson

Well-known member
While working on a project to build a low-power data logger using a Teensy3.6, I discovered something a bit counter-intuitive. I found that calling the sync() function after writing a block of data to the output file reduced the overall power drain by a factor of three! This is counter-intuitive because I thought that the sync() function would require an extra write to the SD card after updating the file directory. Apparently, the power reduction occurs because something in the sync() function puts the SD card into a low-power mode—which doesn’t automatically occur at the end of a block write.

The program uses Teensy 3.6 at 24 or 48MHz clock writing to SanDisk Ultra 128GB microSD card. The Teensy records its own current consumption by reading an ADC channel that is connected to the output of a MAX471 High-Side current sensor. The current sensor outputs a voltage according to this conversion function: mA = 200 * VSensor, or 1.0Volts = 200mA. The Teensy ADC is set to use the internal 1.2V reference to avoid issues with changes in the 3.3V rail that might be caused the the peak SDC currents of about 150mA. The program also records the output of an ElapsedMicroseconds timer to monitor possible sampling jitter. The current sensor is sampled at 10KHz.

All the tests were run using the EXFat file system from SDFat 2.0B (Thanks, Bill Greiman—EXFat with pre-allocation is just what I needed for reducing power consumption on very large data file logging). The file system was set up to use the hardware 4-bit SDIO interface.

Two storage algorithms were tested:

The first is a standard queued input algorithm. The ADC and timing data are collected in an interrupt handler called by and interval timer. The collected data is put into an input queue in the interrupt handler. The main program loop monitors the input queue length, and when data is available, it pulls the data from the queue and writes it directly to the output file. The file system internals handle buffering the 8-byte records into 512-byte blocks and writing the blocks as required. This algorithm is used quite often in data logging examples found in the Teensy example files. The queue is necessary because file writes can take up to 160mSec when the card needs to erase a block, then write the new data. Other SD cards may take even longer. The 32KB queue I used would handle delays up to 400mSec.

The second algorithm uses two large (32KByte or 64KByte) Ping-Pong buffers. The interrupt handler writes the collected data into one buffer, while the main loop writes the data from the other buffer as a single large block to the output file. This algorithm requires more RAM than the queue algorithm, but is more efficient in writing to the SD card as it can use multi-block writes. The algorithm is also more efficient in that data doesn’t have to be moved after the interrupt handler puts it in the buffer. The file writes in the main loop are simply passed a pointer to the buffer and the number of bytes to be written.

For both algorithms, an asm(“WFI\n”) instruction was added at the end of the foreground loop to minimize CPU power consumption by putting the CPU to sleep until the it was awakened by then next interrupt (generally the 10KHz ADC interval timer).

The test program collected 100 seconds of data in each file. in some cases, 10 files were collected for each test to evaluate variability in the average current.

Here are the results of the tests:

Algorithm mA with no sync() mA with sync()
—————————————————————————————————
Queue 24mHz 44.66 *(1)
Ping-Pong 32KB 24Mhz 43.60 14.31
Ping-Pong 64KB 24Mhz 43.94 13.91

Queue 48MHz 52.48 *(1)
Ping-Pong 32KB 48MHz 52.65 18.94
Ping-Pong 64KB 48MHz 51.75 18.36

*(1) Calling sync() after every file write (at 10KHz) is a BAD IDEA! It causes queue overruns
and other problems

Some other observations:

* Thanks to the time spent with the CPU asleep, boosting the CPU speed from 24 to 48MHz only adds about 5mA to the current drain. This would add lots more clock cycles for things like digital filters, more channels, etc.— but at the cost of more current drain.

* USB connections get pretty flaky at 24MHz. Quite often, neither my PC host program nor the Arduino Serial Monitor will connect to the Teensy. Connections are never a problem at 48MHz.

* Switching to a slower, 8GB SanDisk card, reduced average power consumption in the PP tests by about 1mA, but peak power drain was cut by more than a factor of two: from about 140mA to about 55mA. I suspect the reduction is due to lower erase currents for the smaller erase blocks on the 8GB card. Building an efficient power supply for a logger that has a 10:1 variation in current drain can be challenging. (Think large capacitors and quick transient response.)

* A modified test which eliminated all use of the USB serial port reduced power drain in the PP tests by about another 1-2mA. (I don’t have a USB cable long enough to connect to a logger moored at 100m depth in the equatorial Pacific!) I suspect that turning off other peripherals might have similar effects.
 
I didn't realize that the forum display didn't like my tab settings. Here is an easier-to-read version of the results table:
Code:
Algorithm                mA with no sync   mA with sync()	
———————————————————————————
Queue          24mHz         44.66     *(1)	
Ping-Pong 32KB 24Mhz         43.60     14.31	
Ping-Pong 64KB 24Mhz         43.94     13.91

Queue          48MHz          52.48    *(1)
Ping-Pong 32KB 48MHz          52.65    18.94
Ping-Pong 64KB 48MHz          51.75    18.36
 
Last edited:
Maybe it is simply that the frequent sync() are too short to be measurable for time constant of an integrating mA meter?
IMO,
definitely frequent sync() is required to avoid uSD blocking when uSD internally syncs data buffers.
but frequent syncs may reduce max write speed, as internal capacity of uSD is not exploited.
 
The current measured in the test data was not measured with a meter. It was measured with a current sensor collecting data at 10KHz. The only integration in the Max471 sensor comes from a 0.1uF capacitor across the 2K ohm output resistor. That combination has a time constant of 2x10-4 seconds.


plot1.png
plot2.png

As the figures show, the data collection scheme easily resolves both the regular write current pulses, and the larger write+erase pulses.
 

Attachments

  • plot1.png
    plot1.png
    26.8 KB · Views: 107
  • plot2.png
    plot2.png
    30.6 KB · Views: 105
In order to more clearly illustrate the effect of the sync() call, I modified the data collection sketch to call sync() on odd-numbered writes of the 32KB ping-pong buffers. The result is shown in the following plot:

plot3.png

The plot shows not only that the sync call immediately reduces current drain by the SD card, but also that the SD card will not automatically go into the low-power state by itself during the ~400mSec between block writes.
 
Very interesting figures.
I´m indeed interested in minimizing the impact of uSD write on overall power stability to control power stability of downstream ADC.
What I mean, proper dimensioned capacitors cross the 3.3V supply to uSD may minimize impact of the very high mA peaks.
So knowing time constants as you measured is very useful.
 
I've dug out a bit more information on the effect of sync() on SD Card power.

I added a member function to the SDioCard object to return the number of times that transferStop() has been called since the start of the program.

Using this function, I found that the 32KB file write DOES NOT call transferStop()! The sync() function calls transferStop() twice for each sync() call.

It seems that the file write function leaves the SD card ready to receive more multi-sector writes in between calls to the file write function. Sync() calls do two single-sector writes when clearing the cache and updating the directory entry. These single sector writes call the transferStop() function for each sector and that puts the SD card into the idle state.

The power-reduction effects of transfStop() were discussed in an earlier thread. I don't recall whether we ever arrived at a consensus about whether it was the CMD12 or the resetting of some flags in the controller that accomplished the power reduction.
 
Minimizing noise pickup from SD card writes is an ongoing project for me. I have found two main sources of noise:

1. The effect of the current peaks on the power supply. As you suggest, lots of capacitance can help here--but watch out for the effects of inrush currents to those caps. The effect of the SD card on the teensy 3.6 3.3V supply is the reason that the data in my experiments was collected using the internal 1.2V reference of the Teensy ADC. Using a separate regulator for the SD card might help with this problem.

2. SD cards generate significant RF noise when writing. I suspect that there is not only an internal high-speed controller with many mA of current drain when active, but there is probably also a switched-capacitor voltage booster to generate the erase voltages. I ran across the RF noise issue when collecting data from an oceanographic shear sensor. That sensor is a piezoelectric sensor that feeds into a very high input impedance (hundreds of MegOhms) current amplifier. That sensor and amplifier have to be separated from the SD card and very well shielded to minimize RF pickup.
 
Status
Not open for further replies.
Back
Top