/*********************************************************************
* Benchmark test for SD FAT32 sequential and random access
* M. Borgerson 8/24/2020
********************************************************************/
#include "SdFat.h"
#include "sdios.h"
#include <TimeLib.h>
// NOTE: I used SdFs because it will use either FAT32 or ExFAT, depending on the format of the disk
SdFs sd;
FsFile BenchFile;
#define SD_CONFIG SdioConfig(FIFO_SDIO)
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
#ifndef SDCARD_SS_PIN
const uint8_t SD_CS_PIN = SS;
#else // SDCARD_SS_PIN
// Assume built-in SD is used.
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
#endif // SDCARD_SS_PIN
const char compileTime [] = "SdFs Benchmark Compiled on " __DATE__ " " __TIME__;
/*****************************************************************************
Read the Teensy RTC and return a time_t (Unix Seconds) value
******************************************************************************/
time_t getTeensy3Time() {
return Teensy3Clock.get();
}
void setup() {
// put your setup code here, to run once:
while (!Serial) {}
Serial.begin(9600);
delay(1000);
Serial.println(compileTime);
if (!sd.begin(SD_CONFIG)) {
Serial.println("\nSDIO Card initialization failed.\n");
} else Serial.println("SDIO initialization done.");
if (sd.fatType() == FAT_TYPE_EXFAT) {
Serial.println("Type is exFAT");
} else {
Serial.printf("Type is FAT%d\n", int16_t(sd.fatType()));
}
// set date time callback function so file gets a good date
SdFile::dateTimeCallback(dateTime);
setSyncProvider(getTeensy3Time);
}
char fname[] = "btest.dat";
void loop() {
// put your main code here, to run repeatedly:
char ch;
if (Serial.available()) {
ch = Serial.read();
if (ch == 'w') WriteBenchFile(fname);
if (ch == 's') SeqReadBenchFile(fname);
if (ch == 'r') RandReadBenchFile(fname);
if (ch == 'd') sd.ls(LS_SIZE | LS_DATE | LS_R);
}
}
#define RBUFFSIZE 4096
// Write a 256MB contiguous file for benchmark testing
//
// data in the file will be random data from stack area
void WriteBenchFile(char *filename) {
uint64_t alloclength;
uint32_t i, num;
FsFile benchFile;
float seconds;
uint32_t startmilli, dmilli, blockmax, mbytes;
unsigned char benchbuff[RBUFFSIZE];
Serial.printf("\n\nWriting 256MB Benchmark file.\n");
// Open the file
if (!benchFile.open(filename, O_RDWR | O_CREAT | O_TRUNC)) {
Serial.printf("Unable to open <%s> for writing.", filename);
return;
}
// now write the data in blocks of RBUFFSIZE -- 65536 blocks
// send out a message every 10MB or 256 blocks
blockmax = 65536;
mbytes = 0;
startmilli = millis();
for(i=0; i<blockmax; i++){
benchFile.write(&benchbuff, 4096);
if((i%2560) == 0){
Serial.printf("%lu MBytes\n",mbytes);
mbytes+= 10;
}
}
dmilli = millis()-startmilli;
benchFile.close();
seconds = (float)dmilli/1000.0;
Serial.printf("\nWriting 256MBytes took %4.2f seconds", (float)dmilli/1000.0);
Serial.printf(" For an average rate of %6.3f Mbytes/second\n", (256)/seconds);
}
// Read 40 MBytes from benchFile and keep track of max and average read time
#define BUFFSTOREAD 10240 // 40MByte read
void SeqReadBenchFile(const char *filename) {
uint16_t idx, numread;
FsFile benchFile;
uint8_t rbuffer[RBUFFSIZE];
uint32_t startmicro, dmicro, maxmicro, gt500us;
float microsum;
if (!benchFile.open(filename, O_READ)) {
Serial.printf("\nCould not open <%s> for reading.", filename);
return;
}
startmicro = micros(); // save starting time
Serial.printf("\n\nReading %lu sequential buffers of %u bytes\n",BUFFSTOREAD, RBUFFSIZE);
maxmicro = 0;
microsum = 0.0;
gt500us = 0;
for(idx = 0; idx<BUFFSTOREAD; idx++){
startmicro = micros();
numread = benchFile.read(&rbuffer, RBUFFSIZE);
dmicro = micros()-startmicro;
if(dmicro > maxmicro) maxmicro = dmicro;
if(dmicro > 500) gt500us++;
microsum += dmicro;
}
Serial.printf("Buffer read times: average = %4.2f usec Maximum = %lu usec >500uSec: %lu\n",
microsum/BUFFSTOREAD, maxmicro, gt500us);
Serial.printf("Average Read Rate = %6.3f MBytes/second)", (float)(10240 *4096)/(float)microsum);
benchFile.close();
Serial.println();
}
void RandReadBenchFile(const char *filename) {
uint16_t idx, numread;
FsFile benchFile;
uint8_t rbuffer[RBUFFSIZE];
uint32_t startmicro, dmicro, maxmicro, gt500us, fpos;
float microsum;
if (!benchFile.open(filename, O_READ)) {
Serial.printf("\nCould not open <%s> for reading.", filename);
return;
}
startmicro = micros(); // save starting time
Serial.printf("\n\nReading %lu random buffers of %u bytes\n",BUFFSTOREAD, RBUFFSIZE);
maxmicro = 0;
microsum = 0.0;
gt500us = 0;
for(idx = 0; idx<BUFFSTOREAD; idx++){
startmicro = micros();
fpos = random(0,5000)*RBUFFSIZE;
benchFile.seek(fpos);
numread = benchFile.read(&rbuffer, RBUFFSIZE);
dmicro = micros()-startmicro;
if(dmicro > maxmicro) maxmicro = dmicro;
if(dmicro > 999) gt500us++;
microsum += dmicro;
}
Serial.printf("Buffer read times: average = %4.2f usec Maximum = %lu usec >999uSec: %lu\n",
microsum/BUFFSTOREAD, maxmicro, gt500us);
Serial.printf("Average Read Rate = %6.3f MBytes/second)", (float)(10240 *4096)/(float)microsum);
benchFile.close();
Serial.println();
}
//------------------------------------------------------------------------------
/*
User provided date time callback function.
See SdFile::dateTimeCallback() for usage.
*/
void dateTime(uint16_t* date, uint16_t* time) {
// use the year(), month() day() etc. functions from timelib
// return date using FAT_DATE macro to format fields
*date = FAT_DATE(year(), month(), day());
// return time using FAT_TIME macro to format fields
*time = FAT_TIME(hour(), minute(), second());
}