I have a Sparkfun Things Plus RP2040 with 16MB of onboard flash. I am trying to write a program which uses the flash as a buffer (let's say to store 5MB of data) before writing to an SD card. The idea was that using the flash memory would be less power hungry than constantly writing to the SD card.
I'm programming the RP2040 using Arduino IDE which runs MBedOS on the board. The problem I am having is that the write speeds are very slow. It takes ~3000ms to write 200kB of binary data. Ideally writing 200kB should take 1/10th of the time (300ms) for the application we have in mind. I have tested using LittleFileSystem which is included in MBedOS and Little_FS_MBed which is a similar Arduino library. I also tested on another board (Arduino Nano RP2040) with similar results.
My question is whether this is a fundamental limitation of the hardware or whether is there some other way to speed up writing data to flash memory?
The sketch used to test is below (an example adapted from here).
/**
*
* Test writing to the internal flash of the pico.
**/
#include "LittleFileSystem.h"
#include "mbed.h"
#include <stdio.h>
#include <errno.h>
#include <functional>
#include "BlockDevice.h"
// Maximum number of elements in buffer
#define BUFFER_MAX_LEN 10
#define FORCE_REFORMAT true
// This will take the system's default block device
mbed::BlockDevice *bd = mbed::BlockDevice::get_default_instance();
// For example: HeapBlockDevice with size of 2048 bytes, read size 1, write size 1 and erase size 512.
//mbed::BlockDevice *bd = new mbed::HeapBlockDevice(4096, 1, 1, 512);
#define count 10000
//write
uint16_t data[count];
void setup() {
Serial.begin(9600);
delay(1000);
Serial.println("Inialise litte file system");
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
//mbed::LittleFileSystem fs("fs", bd, 2, 1024, 4096, 64);
mbed::LittleFileSystem fs("fs");
/*** Format the filesystem ***/
Serial.println("Mounting the filesystem... ");
//fflush(stdout);
int err=0;
//err = fs.mount(bd);
Serial.println(bd->get_read_size());
Serial.println(bd->get_erase_size());
Serial.println(bd->get_program_size());
Serial.println((err ? "Fail :(" : "OK"));
if (err || FORCE_REFORMAT) {
// Reformat if we can't mount the filesystem
Serial.println("formatting... ");
//fflush(stdout);
err = fs.reformat(bd);
Serial.println((err ? "Fail :(" : "OK"));
if (err) {
Serial.println(strerror(-err));
}
}
/**** Write a file *****/
// Open the numbers file
Serial.println("Opening \"/fs/numbers.bin\"... ");
fflush(stdout);
FILE *f = fopen("/fs/numbers.bin", "rb+");
Serial.println((!f ? "Fail :(" : "OK"));
if (!f) {
// Create the numbers file if it doesn't exist
Serial.println("No file found, creating a new file... ");
fflush(stdout);
f = fopen("/fs/numbers.bin", "wb+");
Serial.println((!f ? "Fail :(" : "OK"));
if (!f) {
Serial.println(strerror(-err));
}
}
// for (int i = 0; i < 500; i++) {
// Serial.print("Writing numbers ");
// Serial.println(i);
// fflush(stdout);
// err = fprintf(f, " %d\n", i);
// if (err < 0) {
// printf("Fail :(\n");
// Serial.println(strerror(-err));
// }
// }
//write some data to a file
unsigned long time1 = millis();
for (int i = 0; i < 10; i++) {
fwrite (data , sizeof(uint16_t), count, f);
}
unsigned long totalTime = millis()-time1;
Serial.print("Time to write: ");
Serial.println(totalTime); // prints the time it took to write to the SD card
//test seeking file
Serial.println("Seeking file... ");
fflush(stdout);
err = fseek(f, 0, SEEK_SET);
Serial.println((err < 0 ? "Fail :(" : "OK"));
if (err < 0) {
Serial.println(strerror(-err));
}
/**** Determine the size of the file ***/
Serial.println("Calculating the size of the file... ");
fflush(stdout);
err = fseek(f, 0L, SEEK_END);
Serial.println((err < 0 ? "Fail :(" : "OK"));
if (err < 0) {
Serial.println(strerror(-err));
}
int sz = ftell(f);
Serial.print("Size is: ");
Serial.println(sz);
// Close the file which also flushes any cached writes
Serial.println("Closing \"/fs/numbers1.txt\"... ");
fflush(stdout);
err = fclose(f);
Serial.println((err < 0 ? "Fail :(" : "OK"));
if (err < 0) {
Serial.println(strerror(-err));
}
/*** Read what is in the root directory ****/
// Display the root directory
Serial.println("Opening the root directory... ");
fflush(stdout);
DIR *d = opendir("/fs/");
Serial.println((!d ? "Fail :(" : "OK"));
if (!d) {
Serial.println(strerror(errno));
}
Serial.println("root directory:");
while (true) {
struct dirent *e = readdir(d);
if (!e) {
break;
}
Serial.println(e->d_name);
}
// Tidy up
Serial.println("Unmounting... ");
//fflush(stdout);
err = fs.unmount();
Serial.println((err < 0 ? "Fail :(" : "OK"));
if (err < 0) {
Serial.println(strerror(-err));
}
}
void loop() {
// put your main code here, to run repeatedly:
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(500); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(500);
}
The output is...
Inialise litte file system
Mounting the filesystem...
OK
formatting...
OK
Opening "/fs/numbers.bin"...
Fail
No file found, creating a new file...
OK
Time to write: 3534 <-milliseconds
Seeking file...
OK
Calculating the size of the file...
OK
Size is: 200000
Closing "/fs/numbers1.txt"...