Skip to content

Commit f22299d

Browse files
committed
sdcardio: Use CMD25 across multiple writeblocks() calls
This increases write rates (of gifio from #5490) from about 2.4fps to over 5fps by making more efficient use of the SD card protocol. Because of details of oofatfs, it usually manages 64 writes in a single CMD25, then two writes in a different area of the SD card (presumably, filesystem metadata). I couldn't find where to increase "64" to a higher number. This may depend on the allocation size of the filesystem. I tried preallocating too, but oddly it significantly lowered the write rate. Any trailing data is committed when the file is close()d, or when the `sync` method of the SDCard object is called.
1 parent 0218257 commit f22299d

File tree

2 files changed

+65
-39
lines changed

2 files changed

+65
-39
lines changed

shared-module/sdcardio/SDCard.c

Lines changed: 63 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -93,19 +93,53 @@ static uint8_t CRC7(const uint8_t *data, uint8_t n) {
9393
}
9494

9595
#define READY_TIMEOUT_NS (300 * 1000 * 1000) // 300ms
96-
STATIC void wait_for_ready(sdcardio_sdcard_obj_t *self) {
96+
STATIC int wait_for_ready(sdcardio_sdcard_obj_t *self) {
9797
uint64_t deadline = common_hal_time_monotonic_ns() + READY_TIMEOUT_NS;
9898
while (common_hal_time_monotonic_ns() < deadline) {
9999
uint8_t b;
100100
common_hal_busio_spi_read(self->bus, &b, 1, 0xff);
101101
if (b == 0xff) {
102-
break;
102+
return 0;
103+
}
104+
}
105+
return -ETIMEDOUT;
106+
}
107+
108+
// Note: this is never called while "in cmd25" (in fact, it's only used by `exit_cmd25`)
109+
STATIC bool cmd_nodata(sdcardio_sdcard_obj_t *self, int cmd, int response) {
110+
uint8_t cmdbuf[2] = {cmd, 0xff};
111+
112+
assert(!self->in_cmd25);
113+
114+
common_hal_busio_spi_write(self->bus, cmdbuf, sizeof(cmdbuf));
115+
116+
// Wait for the response (response[7] == response)
117+
for (int i = 0; i < CMD_TIMEOUT; i++) {
118+
common_hal_busio_spi_read(self->bus, cmdbuf, 1, 0xff);
119+
if (cmdbuf[0] == response) {
120+
return 0;
103121
}
104122
}
123+
return -EIO;
124+
}
125+
126+
127+
STATIC int exit_cmd25(sdcardio_sdcard_obj_t *self) {
128+
if (self->in_cmd25) {
129+
DEBUG_PRINT("exit cmd25\n");
130+
self->in_cmd25 = false;
131+
return cmd_nodata(self, TOKEN_STOP_TRAN, 0);
132+
}
133+
return 0;
105134
}
106135

107136
// In Python API, defaults are response=None, data_block=True, wait=True
108137
STATIC int cmd(sdcardio_sdcard_obj_t *self, int cmd, int arg, void *response_buf, size_t response_len, bool data_block, bool wait) {
138+
int r = exit_cmd25(self);
139+
if (r < 0) {
140+
return r;
141+
}
142+
109143
DEBUG_PRINT("cmd % 3d [%02x] arg=% 11d [%08x] len=%d%s%s\n", cmd, cmd, arg, arg, response_len, data_block ? " data" : "", wait ? " wait" : "");
110144
uint8_t cmdbuf[6];
111145
cmdbuf[0] = cmd | 0x40;
@@ -116,7 +150,10 @@ STATIC int cmd(sdcardio_sdcard_obj_t *self, int cmd, int arg, void *response_buf
116150
cmdbuf[5] = CRC7(cmdbuf, 5);
117151

118152
if (wait) {
119-
wait_for_ready(self);
153+
r = wait_for_ready(self);
154+
if (r < 0) {
155+
return r;
156+
}
120157
}
121158

122159
common_hal_busio_spi_write(self->bus, cmdbuf, sizeof(cmdbuf));
@@ -161,21 +198,6 @@ STATIC int block_cmd(sdcardio_sdcard_obj_t *self, int cmd_, int block, void *res
161198
return cmd(self, cmd_, block * self->cdv, response_buf, response_len, true, true);
162199
}
163200

164-
STATIC bool cmd_nodata(sdcardio_sdcard_obj_t *self, int cmd, int response) {
165-
uint8_t cmdbuf[2] = {cmd, 0xff};
166-
167-
common_hal_busio_spi_write(self->bus, cmdbuf, sizeof(cmdbuf));
168-
169-
// Wait for the response (response[7] == response)
170-
for (int i = 0; i < CMD_TIMEOUT; i++) {
171-
common_hal_busio_spi_read(self->bus, cmdbuf, 1, 0xff);
172-
if (cmdbuf[0] == response) {
173-
return 0;
174-
}
175-
}
176-
return -EIO;
177-
}
178-
179201
STATIC const compressed_string_t *init_card_v1(sdcardio_sdcard_obj_t *self) {
180202
for (int i = 0; i < CMD_TIMEOUT; i++) {
181203
if (cmd(self, 41, 0, NULL, 0, true, true) == 0) {
@@ -298,6 +320,7 @@ void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self) {
298320
if (!self->bus) {
299321
return;
300322
}
323+
common_hal_sdcardio_sdcard_sync(self);
301324
self->bus = 0;
302325
common_hal_digitalio_digitalinout_deinit(&self->cs);
303326
}
@@ -423,41 +446,42 @@ STATIC int _write(sdcardio_sdcard_obj_t *self, uint8_t token, void *buf, size_t
423446
STATIC int writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {
424447
common_hal_sdcardio_check_for_deinit(self);
425448
uint32_t nblocks = buf->len / 512;
426-
if (nblocks == 1) {
427-
// Use CMD24 to write a single block
428-
int r = block_cmd(self, 24, start_block, NULL, 0, true, true);
429-
if (r < 0) {
430-
return r;
431-
}
432-
r = _write(self, TOKEN_DATA, buf->buf, buf->len);
433-
if (r < 0) {
434-
return r;
435-
}
436-
} else {
449+
450+
DEBUG_PRINT("cmd25? %d next_block %d start_block %d\n", self->in_cmd25, self->next_block, start_block);
451+
452+
if (!self->in_cmd25 || start_block != self->next_block) {
453+
DEBUG_PRINT("entering CMD25 at %d\n", (int)start_block);
437454
// Use CMD25 to write multiple block
438455
int r = block_cmd(self, 25, start_block, NULL, 0, true, true);
439456
if (r < 0) {
440457
return r;
441458
}
459+
self->in_cmd25 = true;
460+
}
442461

443-
uint8_t *ptr = buf->buf;
444-
while (nblocks--) {
445-
r = _write(self, TOKEN_CMD25, ptr, 512);
446-
if (r < 0) {
447-
return r;
448-
}
449-
ptr += 512;
450-
}
462+
self->next_block = start_block;
451463

452-
cmd_nodata(self, TOKEN_STOP_TRAN, 0);
464+
uint8_t *ptr = buf->buf;
465+
while (nblocks--) {
466+
int r = _write(self, TOKEN_CMD25, ptr, 512);
467+
if (r < 0) {
468+
self->in_cmd25 = false;
469+
return r;
470+
}
471+
self->next_block++;
472+
ptr += 512;
453473
}
474+
454475
return 0;
455476
}
456477

457478
int common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self) {
458479
common_hal_sdcardio_check_for_deinit(self);
459480
mp_printf(&mp_plat_print, "sd sync\n");
460-
return 0;
481+
lock_and_configure_bus(self);
482+
int r = exit_cmd25(self);
483+
extraclock_and_unlock_bus(self);
484+
return r;
461485
}
462486

463487
int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {

shared-module/sdcardio/SDCard.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ typedef struct {
4141
int cdv;
4242
int baudrate;
4343
uint32_t sectors;
44+
uint32_t next_block;
45+
bool in_cmd25;
4446
} sdcardio_sdcard_obj_t;
4547

4648
void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *spi, mcu_pin_obj_t *cs, int baudrate);

0 commit comments

Comments
 (0)