Hi everyone. Just getting started with Daisy, so bear with me. I have a Pod and another standalone Seed with an SD card hooked up to the SDIO lines. I’ve been having SD card read issues on both, so I wrote some smaller code to measure performance and troubleshoot the problem.
The initial performance test I wrote to be as simple as possible, and I had no FatFs errors. Then I added simple blank audio callbacks and it immediately reproduced.
I thought it might have something to do with the ffconf.h timeout config:
#define _FS_TIMEOUT 1000 /**< Timeout period in unit of time ticks */
If this is in the same 200 MHz tick units as the daisy timer, then that would only be 5 us. I tried changing to 100000 (500 us if using 200 MHz tick), recompiling daisy lib, then my example project, but still got the error when audio was enabled.
Side note on the SD card SDIO performance, I’m hoping we seem some improvements when libdaisy goes to DMA and clock speed increases.
Hopefully pasting this code block works. This can be compiled by replacing SDMMC.cpp in the SDMMC Seed example. The new code waits for the user to open the USB CDC console by requiring a button press before starting. On the Pod, that is the first button. On the Seed, you can ground pin 27.
Would be great if someone could try reproducing. I’ve tried multiple Daisy’s and SD cards. Any help appreciated!
// SD card performance test
#include
#include
#include
#include
#include "daisy_pod.h"
#include "fatfs.h"
#define TEST_FILE_NAME "sdtest.txt"
#define BUFFER_SIZE (64 * 1024)
#define TEST_PATTERN(index) ((index % 16) + '0')
#define TICKS_TO_US 200.0
#define AUDIO_BLOCK_SIZE 48
using namespace daisy;
static DaisySeed hw;
static Switch button;
static SdmmcHandler sd;
uint8_t testBuffer[BUFFER_SIZE];
#define DBG_BUFFER_SIZE 4096
char dbgBuf[DBG_BUFFER_SIZE];
char *dbgPtr = dbgBuf;
void dbgPrintf(const char *fmt, ...) {
if ((dbgPtr - dbgBuf) >= (int)sizeof(dbgBuf)) {
return;
}
va_list valist;
va_start(valist, fmt);
dbgPtr += vsnprintf(dbgPtr, &dbgBuf[sizeof(dbgBuf)] - dbgPtr, fmt, valist);
va_end(valist);
}
void dbgDump() {
dbgBuf[sizeof(dbgBuf) - 1] = 0;
hw.usb_handle.TransmitInternal((uint8_t *)dbgBuf, strlen(dbgBuf));
dbgPtr = dbgBuf;
dsy_system_delay(10);
}
static volatile bool waitingForUsbEnter = false;
void waitForUser() {
// TODO - USB CDC Rx does not seem to work
#if 0
waitingForUsbEnter = true;
while (waitingForUsbEnter) {
dsy_system_delay(5);
if (dbgPtr != dbgBuf) {
dbgDump();
}
}
#else
while (true) {
button.Debounce();
if (button.RisingEdge())
break;
dsy_system_delay(1);
}
#endif
}
void UsbCallback(uint8_t *buf, uint32_t *len) {
dbgPrintf("usb cb\n\r");
if (!buf || !len) {
return;
}
dbgPrintf("usb %ld %c\n\r", *len, buf[0]);
for (size_t i = 0; i < *len; i++) {
if (buf[i] == '\n') {
waitingForUsbEnter = false;
}
if (buf[i] == 'a') {
waitingForUsbEnter = false;
}
}
}
void AudioCallback(float *in, float *out, size_t size) {
// Blank the audio
for(size_t i = 0; i < size; i += 2) {
out[i] = out[i + 1] = s162f(0) * 0.5f;
}
}
void fillPattern(uint8_t *buffer, size_t length) {
for (size_t i = 0; i < length; i++) {
buffer[i] = TEST_PATTERN(i);
}
}
bool testWrite(const char *testname, uint8_t *buffer, size_t length, size_t filelength)
{
bool status = true;
size_t byteswritten = 0;
uint32_t start, end;
size_t numwrites = filelength / length;
uint64_t ticks = 0;
// Open test file on the SD Card
FRESULT result = f_open(&SDFile, TEST_FILE_NAME, (FA_CREATE_ALWAYS) | (FA_WRITE));
if (result != FR_OK) {
dbgPrintf("%s: f_open error: %d\n\r", testname, result);
status = false;
} else {
for (size_t i = 0; i < numwrites; i++) {
byteswritten = 0;
start = dsy_tim_get_tick();
result = f_write(&SDFile, buffer, length, &byteswritten);
end = dsy_tim_get_tick();
ticks += end - start;
if ((result != FR_OK) || (byteswritten != length)) {
dbgPrintf("%s: f_write error: %d, bytes written = %ld, loop %d\n\r",
testname, result, byteswritten, i);
status = false;
break;
}
}
f_close(&SDFile);
}
if (status) {
double timeMicroseconds = (double)ticks / TICKS_TO_US;
double bytesPerS = (double)filelength * 1000000.0 / timeMicroseconds;
dbgPrintf("%s: Passed: %10ld us, %10ld bytes/s\n\r", testname, (uint32_t)timeMicroseconds, (uint32_t)bytesPerS);
} else {
dbgPrintf("%s: Failed\n\r", testname);
}
dbgDump();
return status;
}
bool testRead(const char *testname, uint8_t *buffer, size_t length, size_t filelength) {
bool status = true;
size_t bytesread = 0;
uint32_t start, end;
size_t numreads = filelength / length;
uint64_t ticks = 0;
// Open the test file on the SD Card
FRESULT result = f_open(&SDFile, TEST_FILE_NAME, (FA_OPEN_EXISTING | FA_READ));
if (result != FR_OK) {
dbgPrintf("%s: f_open error: %d\n\r", testname, result);
status = false;
} else {
for (size_t i = 0; i < numreads; i++) {
bytesread = 0;
start = dsy_tim_get_tick();
result = f_read(&SDFile, buffer, length, &bytesread);
end = dsy_tim_get_tick();
ticks += end - start;
if ((result != FR_OK) || (bytesread != length)) {
dbgPrintf("%s: f_read error: %d, bytes read = %ld, loop %d\n\r",
testname, result, bytesread, i);
status = false;
break;
}
}
f_close(&SDFile);
}
if (status) {
double timeMicroseconds = (double)ticks / TICKS_TO_US;
double bytesPerS = (double)filelength * 1000000.0 / timeMicroseconds;
dbgPrintf("%s: Passed: %10ld us, %10ld bytes/s\n\r", testname, (uint32_t)timeMicroseconds, (uint32_t)bytesPerS);
} else {
dbgPrintf("%s: Failed\n\r", testname);
}
dbgDump();
return status;
}
int main(void) {
// Init hardware
hw.Configure();
hw.Init();
dsy_tim_start();
// Set button to pin 27, to be updated at a 1kHz samplerate
// This is the first button on pod, or hit pin 27 on seed to ground
button.Init(hw.GetPin(27),1000);
// Init USB serial
hw.usb_handle.Init(UsbHandle::FS_INTERNAL);
hw.usb_handle.SetReceiveCallback(UsbCallback, UsbHandle::FS_INTERNAL);
// Wait for a button or pin to be grounded while user opens USB console
waitForUser();
// Init SD Card
sd.Init();
// Links libdaisy i/o to fatfs driver.
dsy_fatfs_init();
bool fileCreated = false;
bool audioEnabled = false;
bool mounted = false;
FRESULT result;
dbgPrintf("Running tests without audio running\n\r\n\r");
dbgDump();
while (true) {
// Mount SD Card
if ((!mounted) && ((result = f_mount(&SDFatFS, SDPath, 1)) != FR_OK)) {
dbgPrintf("Error: Couldn't mount SD card, f_mount error: %d\n\r", result);
dbgDump();
} else {
// Don't try to mount again
mounted = true;
// Fill buffer before write tests with known pattern
fillPattern(testBuffer, sizeof(testBuffer));
static const int filesize = 32 * 1024 * 1024;
// Don't need to run this if already created
#if 1
if (!fileCreated) {
fileCreated = true;
testWrite("Write 32 MB, 512 chunk", testBuffer, 512, filesize);
// Don't want to stress writing the card yet
#if 0
testWrite("Write 32 MB, 1K chunk", testBuffer, 1024, filesize);
testWrite("Write 32 MB, 2K chunk", testBuffer, 2 * 1024, filesize);
testWrite("Write 32 MB, 4K chunk", testBuffer, 4 * 1024, filesize);
testWrite("Write 32 MB, 8K chunk", testBuffer, 8 * 1024, filesize);
testWrite("Write 32 MB, 16K chunk", testBuffer, 16 * 1024, filesize);
#endif
}
#endif
// Test a couple of single chunk reads to time
testRead( "Read 512, 512 chunk", testBuffer, 512, 512);
testRead( "Read 512, 512 chunk", testBuffer, 512, 512);
testRead( "Read 512, 512 chunk", testBuffer, 512, 512);
testRead( "Read 32 MB, 512 chunk", testBuffer, 512, filesize);
testRead( "Read 32 MB, 1K chunk", testBuffer, 1024, filesize);
testRead( "Read 32 MB, 2K chunk", testBuffer, 2 * 1024, filesize);
testRead( "Read 32 MB, 4K chunk", testBuffer, 4 * 1024, filesize);
testRead( "Read 32 MB, 8K chunk", testBuffer, 8 * 1024, filesize);
testRead( "Read 32 MB, 16K chunk", testBuffer, 16 * 1024, filesize);
testRead( "Read 32 MB, 32K chunk", testBuffer, 32 * 1024, filesize);
}
// 2nd time around, enable audio
if (!audioEnabled) {
audioEnabled = true;
dbgPrintf("\n\rRunning tests with audio running\n\r\n\r");
dbgDump();
// Init Audio
hw.SetAudioBlockSize(AUDIO_BLOCK_SIZE);
hw.StartAudio(AudioCallback);
} else {
waitForUser();
}
}
}