diff --git a/software/modules/rp2_sd/module.c b/software/modules/rp2_sd/module.c index 085f36c..825a7b0 100644 --- a/software/modules/rp2_sd/module.c +++ b/software/modules/rp2_sd/module.c @@ -1,11 +1,14 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2025 Matthias Blankertz +#include + #include "py/obj.h" #include "sd.h" // Include MicroPython API. #include "py/mperrno.h" +#include "py/mpprint.h" #include "py/runtime.h" // This module is RP2 specific @@ -13,6 +16,14 @@ #include +void sd_printf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + mp_vprintf(&mp_sys_stdout_print, fmt, ap); + va_end(ap); +} + const mp_obj_type_t sdcard_type; struct sdcard_obj { mp_obj_base_t base; @@ -89,11 +100,14 @@ static mp_obj_t sdcard_writeblocks(mp_obj_t self_obj, mp_obj_t block_obj, mp_obj if (bufinfo.len % SD_SECTOR_SIZE != 0) mp_raise_ValueError(MP_ERROR_TEXT("Buffer length is invalid")); const int nblocks = bufinfo.len / SD_SECTOR_SIZE; - for (int block = 0; block < nblocks; block++) { - // TODO: Implement CMD25 write multiple blocks - if (!sd_writeblock(&self->sd_context, start_block + block, bufinfo.buf + block * SD_SECTOR_SIZE)) - mp_raise_OSError(MP_EIO); + bool ret; + if (nblocks > 1) { + ret = sd_writeblocks(&self->sd_context, start_block, nblocks, bufinfo.buf); + } else { + ret = sd_writeblock(&self->sd_context, start_block, bufinfo.buf); } + if (!ret) + mp_raise_OSError(MP_EIO); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_3(sdcard_writeblocks_obj, sdcard_writeblocks); diff --git a/software/modules/rp2_sd/sd.c b/software/modules/rp2_sd/sd.c index d459719..4f81a1f 100644 --- a/software/modules/rp2_sd/sd.c +++ b/software/modules/rp2_sd/sd.c @@ -6,7 +6,7 @@ #include #include -// #define SD_DEBUG +extern void sd_printf(const char *fmt, ...); #define SD_R1_ILLEGAL_COMMAND (1 << 2) @@ -26,14 +26,14 @@ static bool sd_early_init(void) for (int i = 0; i < 500; ++i) { if (sd_cmd(0, 0, 1, &buf)) { #ifdef SD_DEBUG - printf("CMD0 resp %02hhx\n", buf); + sd_printf("CMD0 resp %02x\n", buf); #endif if (buf == 0x01) { return true; } } #ifdef SD_DEBUG - printf("CMD0 timeout, try again...\n"); + sd_printf("CMD0 timeout, try again...\n"); #endif } return false; @@ -44,14 +44,14 @@ static bool sd_check_interface_condition(void) uint8_t buf[5]; if (sd_cmd(8, 0x000001AA, 5, buf)) { if ((buf[3] & 0xf) != 0x1 || buf[4] != 0xAA) { - printf("sd_init: check interface condition failed\n"); + sd_printf("sd_init: check interface condition failed\n"); return false; } } else { if (buf[0] & SD_R1_ILLEGAL_COMMAND) { - printf("sd_init: check interface condition returned illegal command - old card?\n"); + sd_printf("sd_init: check interface condition returned illegal command - old card?\n"); } else { - printf("sd_init: check interface condition failed\n"); + sd_printf("sd_init: check interface condition failed\n"); return false; } } @@ -72,12 +72,12 @@ static bool sd_send_op_cond(void) if (!result) { if (use_acmd && buf & SD_R1_ILLEGAL_COMMAND) { #ifdef SD_DEBUG - printf("sd_init: card does not understand ACMD41, try CMD1...\n"); + sd_printf("sd_init: card does not understand ACMD41, try CMD1...\n"); #endif use_acmd = false; continue; } else if (buf != 0x01) { - printf("sd_init: send_op_cond failed\n"); + sd_printf("sd_init: send_op_cond failed\n"); return false; } else { continue; @@ -87,7 +87,7 @@ static bool sd_send_op_cond(void) return true; } } - printf("sd_init: send_op_cond: timeout waiting for !idle\n"); + sd_printf("sd_init: send_op_cond: timeout waiting for !idle\n"); return false; } @@ -107,7 +107,7 @@ static void sd_dump_cid [[maybe_unused]] (void) const uint8_t crc = sd_crc7(15, buf); const uint8_t card_crc = buf[15] >> 1; if (card_crc != crc) { - printf("CRC mismatch: Got %02hhx, expected %02hhx\n", card_crc, crc); + sd_printf("CRC mismatch: Got %02x, expected %02x\n", card_crc, crc); // Some cheap SD cards always report CRC=0, don't fail in that case if (card_crc != 0) { return; @@ -122,8 +122,8 @@ static void sd_dump_cid [[maybe_unused]] (void) uint32_t psn = buf[9] << 24 | buf[10] << 16 | buf[11] << 8 | buf[12]; int mdt_year = 2000 + ((buf[13] & 0xf) << 4 | (buf[14] & 0xf0) >> 4); int mdt_month = buf[14] & 0x0f; - printf("CID: mid: %02hhx, oid: %.2s, pnm: %.5s, prv: %02hhx, psn: %08" PRIx32 ", mdt_year: %d, mdt_month: %d\n", - mid, oid, pnm, prv, psn, mdt_year, mdt_month); + sd_printf("CID: mid: %02x, oid: %.2s, pnm: %.5s, prv: %02x, psn: %08" PRIx32 ", mdt_year: %d, mdt_month: %d\n", + mid, oid, pnm, prv, psn, mdt_year, mdt_month); } } @@ -131,13 +131,13 @@ static bool sd_read_csd(struct sd_context *sd_context) { uint8_t buf[16]; if (!sd_cmd_read(9, 0, 16, buf)) { - printf("Failed to read CSD\n"); + sd_printf("Failed to read CSD\n"); return false; } const uint8_t crc = sd_crc7(15, buf); const uint8_t card_crc = buf[15] >> 1; if (card_crc != crc) { - printf("CRC mismatch: Got %02hhx, expected %02hhx\n", card_crc, crc); + sd_printf("CRC mismatch: Got %02x, expected %02x\n", card_crc, crc); // Some cheap SD cards always report CRC=0, don't fail in that case if (card_crc != 0) { return false; @@ -151,12 +151,12 @@ static bool sd_read_csd(struct sd_context *sd_context) switch (csd_ver) { case 0: { if (sd_context->sdhc_sdxc) { - printf("sd_init: Got CSD v1.0 but card is SDHC/SDXC?\n"); + sd_printf("sd_init: Got CSD v1.0 but card is SDHC/SDXC?\n"); return false; } const unsigned read_bl_len = buf[5] & 0xf; if (read_bl_len < 9 || read_bl_len > 11) { - printf("Invalid read_bl_len in CSD 1.0\n"); + sd_printf("Invalid read_bl_len in CSD 1.0\n"); return false; } blocksize = 1 << (buf[5] & 0xf); @@ -176,15 +176,15 @@ static bool sd_read_csd(struct sd_context *sd_context) break; } case 2: { - printf("sd_init: Got CSD v3.0, but SDUC does not support SPI.\n"); + sd_printf("sd_init: Got CSD v3.0, but SDUC does not support SPI.\n"); return false; } } sd_context->blocks = blocks; sd_context->blocksize = blocksize; #ifdef SD_DEBUG - printf("CSD version %u.0, blocksize %u, blocks %u, capacity %llu MiB, max speed %u\n", version, blocksize, blocks, - ((uint64_t)blocksize * blocks) / (1024 * 1024), max_speed); + sd_printf("CSD version %u.0, blocksize %u, blocks %u, capacity %u MiB, max speed %u\n", version, blocksize, blocks, + (uint32_t)(((uint64_t)blocksize * blocks) / (1024 * 1024)), max_speed); #endif return true; } @@ -205,11 +205,11 @@ bool sd_init(struct sd_context *sd_context, int mosi, int miso, int sck, int ss, uint32_t ocr; if (!sd_read_ocr(&ocr)) { - printf("sd_init: read OCR failed\n"); + sd_printf("sd_init: read OCR failed\n"); goto out_spi; } if ((ocr & 0x00380000) != 0x00380000) { - printf("sd_init: unsupported card voltage range\n"); + sd_printf("sd_init: unsupported card voltage range\n"); goto out_spi; } @@ -219,11 +219,11 @@ bool sd_init(struct sd_context *sd_context, int mosi, int miso, int sck, int ss, sd_spi_set_bitrate(rate); if (!sd_read_ocr(&ocr)) { - printf("sd_init: read OCR failed\n"); + sd_printf("sd_init: read OCR failed\n"); goto out_spi; } if (!(ocr & (1 << 31))) { - printf("sd_init: card not powered up but !idle?\n"); + sd_printf("sd_init: card not powered up but !idle?\n"); goto out_spi; } sd_context->sdhc_sdxc = (ocr & (1 << 30)); @@ -234,20 +234,20 @@ bool sd_init(struct sd_context *sd_context, int mosi, int miso, int sck, int ss, if (sd_context->blocksize != SD_SECTOR_SIZE) { if (sd_context->blocksize != 1024 && sd_context->blocksize != 2048) { - printf("sd_init: Unsupported block size %u\n", sd_context->blocksize); + sd_printf("sd_init: Unsupported block size %u\n", sd_context->blocksize); goto out_spi; } // Attempt SET_BLOCKLEN command uint8_t resp[1]; if (!sd_cmd(16, SD_SECTOR_SIZE, 1, resp)) { - printf("sd_init: SET_BLOCKLEN failed\n"); + sd_printf("sd_init: SET_BLOCKLEN failed\n"); goto out_spi; } // Successfully set blocksize to SD_SECTOR_SIZE, adjust context sd_context->blocks *= sd_context->blocksize / SD_SECTOR_SIZE; #ifdef SD_DEBUG - printf("Adjusted blocksize from %u to 512, card now has %u blocks\n", sd_context->blocksize, - sd_context->blocks); + sd_printf("Adjusted blocksize from %u to 512, card now has %u blocks\n", sd_context->blocksize, + sd_context->blocks); #endif sd_context->blocksize = SD_SECTOR_SIZE; } @@ -307,7 +307,7 @@ bool sd_readblock_complete(struct sd_context *sd_context) bool sd_readblock_is_complete(struct sd_context *sd_context) { return sd_cmd_read_is_complete(); } -bool sd_writeblock(struct sd_context *sd_context, size_t sector_num, uint8_t buffer[const static SD_SECTOR_SIZE]) +bool sd_writeblock(struct sd_context *sd_context, const size_t sector_num, uint8_t buffer[const static SD_SECTOR_SIZE]) { if (!sd_context->initialized || sector_num >= sd_context->blocks) return false; @@ -319,3 +319,20 @@ bool sd_writeblock(struct sd_context *sd_context, size_t sector_num, uint8_t buf } return sd_cmd_write(24, addr, SD_SECTOR_SIZE, buffer); } + +bool sd_writeblocks(struct sd_context *sd_context, const size_t sector_num, const size_t sectors, uint8_t *const buffer) +{ + if (!sd_context->initialized || sector_num + sectors >= sd_context->blocks) + return false; + + if (!sd_context->sdhc_sdxc) { + // Don't use multi-block writes for SDSC for now + // Need to configure WRITE_BL_LEN correctly + for (size_t sector = 0; sector < sectors; ++sector) { + if (!sd_writeblock(sd_context, sector_num + sector, buffer + sector * SD_SECTOR_SIZE)) + return false; + } + return true; + } + return sd_cmd_write_multiple(25, sector_num, sectors, SD_SECTOR_SIZE, buffer); +} diff --git a/software/modules/rp2_sd/sd.h b/software/modules/rp2_sd/sd.h index dede6fd..bb3f4ad 100644 --- a/software/modules/rp2_sd/sd.h +++ b/software/modules/rp2_sd/sd.h @@ -24,3 +24,4 @@ bool sd_readblock_complete(struct sd_context *context); bool sd_readblock_is_complete(struct sd_context *context); bool sd_writeblock(struct sd_context *context, size_t sector_num, uint8_t buffer[const static SD_SECTOR_SIZE]); +bool sd_writeblocks(struct sd_context *sd_context, const size_t sector_num, const size_t sectors, uint8_t *buffer); diff --git a/software/modules/rp2_sd/sd_spi.c b/software/modules/rp2_sd/sd_spi.c index 2722b31..846be90 100644 --- a/software/modules/rp2_sd/sd_spi.c +++ b/software/modules/rp2_sd/sd_spi.c @@ -12,6 +12,8 @@ #include #include +extern void sd_printf(const char *fmt, ...); + typedef enum { DMA_READ_TOKEN, DMA_READ, DMA_IDLE } sd_dma_state; struct sd_dma_context { @@ -222,7 +224,7 @@ bool sd_cmd_read_complete(void) sd_spi_read_blocking(0xff, &buf, 1); if (sd_spi_context.sd_dma_context.read_token_buf != 0xfe) { #ifdef SD_DEBUG - printf("read failed: invalid read token %02hhx\n", sd_spi_context.sd_dma_context.read_token_buf); + sd_printf("read failed: invalid read token %02x\n", sd_spi_context.sd_dma_context.read_token_buf); #endif return false; } @@ -231,7 +233,7 @@ bool sd_cmd_read_complete(void) const uint16_t act_crc = sd_spi_context.sd_dma_context.crc_buf[0] << 8 | sd_spi_context.sd_dma_context.crc_buf[1]; if (act_crc != expect_crc) { #ifdef SD_DEBUG - printf("read CRC fail: got %04hx, expected %04hx\n", act_crc, expect_crc); + sd_printf("read CRC fail: got %04x, expected %04x\n", act_crc, expect_crc); #endif return false; } @@ -239,10 +241,9 @@ bool sd_cmd_read_complete(void) return true; } -bool sd_cmd_write(uint8_t cmd, uint32_t arg, unsigned datalen, uint8_t data[const static datalen]) +static bool sd_cmd_write_begin(uint8_t cmd, uint32_t arg) { - uint8_t buf[2]; - const uint16_t crc = sd_crc16(datalen, data); + uint8_t buf[1]; sd_spi_cmd_send(cmd, arg); // Read up to 8 garbage bytes (0xff), followed by R1 (MSB is zero) bool got_r1 = false; @@ -253,9 +254,20 @@ bool sd_cmd_write(uint8_t cmd, uint32_t arg, unsigned datalen, uint8_t data[cons break; } } - if (!got_r1 || buf[0] != 0x00) - goto abort; - buf[0] = 0xfe; + if (!got_r1 || buf[0] != 0x00) { +#ifdef SD_DEBUG + sd_printf("write cmd fail: %02x\n", buf[0]); +#endif + return false; + } + return true; +} + +static bool sd_cmd_write_block(uint8_t token, unsigned datalen, uint8_t data[const static datalen]) +{ + uint8_t buf[2]; + const uint16_t crc = sd_crc16(datalen, data); + buf[0] = token; sd_spi_write_blocking(buf, 1); sd_spi_write_blocking(data, datalen); buf[0] = crc >> 8; @@ -264,11 +276,16 @@ bool sd_cmd_write(uint8_t cmd, uint32_t arg, unsigned datalen, uint8_t data[cons sd_spi_read_blocking(0xff, buf, 1); if ((buf[0] & 0x1f) != 0x5) { #ifdef SD_DEBUG - printf("Write fail: %2hhx\n", buf[0]); + sd_printf("Write fail: %2x\n", buf[0]); #endif - goto abort; + return false; } + return true; +} +static bool sd_cmd_write_wait_nbusy(void) +{ + uint8_t buf[1]; int timeout = 0; bool got_done = false; for (timeout = 0; timeout < 131072; ++timeout) { @@ -279,18 +296,73 @@ bool sd_cmd_write(uint8_t cmd, uint32_t arg, unsigned datalen, uint8_t data[cons } } #ifdef SD_DEBUG - printf("dbg write end: %d, %2hhx\n", timeout, buf[0]); + sd_printf("dbg write end: %d, %2x\n", timeout, buf[0]); #endif - if (!got_done) + return got_done; +} + +bool sd_cmd_write(uint8_t cmd, uint32_t arg, unsigned datalen, uint8_t data[const static datalen]) +{ +#ifdef SD_DEBUG + sd_printf("write 1 block at %u\n", arg); +#endif + uint8_t buf[2]; + if (!sd_cmd_write_begin(cmd, arg)) + goto abort; + if (!sd_cmd_write_block(0xfe, datalen, data)) + goto abort; + if (!sd_cmd_write_wait_nbusy()) goto abort; gpio_put(sd_spi_context.ss, true); sd_spi_read_blocking(0xff, buf, 1); +#ifdef SD_DEBUG + sd_printf("write ok\n"); +#endif return true; abort: gpio_put(sd_spi_context.ss, true); sd_spi_read_blocking(0xff, buf, 1); +#ifdef SD_DEBUG + sd_printf("write fail\n"); +#endif + return false; +} + +bool sd_cmd_write_multiple(uint8_t cmd, uint32_t arg, unsigned blocks, unsigned datalen, uint8_t *const data) +{ +#ifdef SD_DEBUG + sd_printf("write %u blocks at %u\n", blocks, arg); +#endif + uint8_t buf[2]; + if (!sd_cmd_write_begin(cmd, arg)) + goto abort; + for (unsigned i = 0; i < blocks; ++i) { + if (!sd_cmd_write_block(0b11111100, datalen, data + datalen * i)) + goto abort; + if (!sd_cmd_write_wait_nbusy()) + goto abort; + } + buf[0] = 0b11111101; + buf[1] = 0xff; + sd_spi_write_blocking(buf, 2); + if (!sd_cmd_write_wait_nbusy()) + goto abort; + + gpio_put(sd_spi_context.ss, true); + sd_spi_read_blocking(0xff, buf, 1); +#ifdef SD_DEBUG + sd_printf("write ok\n"); +#endif + return true; + +abort: + gpio_put(sd_spi_context.ss, true); + sd_spi_read_blocking(0xff, buf, 1); +#ifdef SD_DEBUG + sd_printf("write fail\n"); +#endif return false; } diff --git a/software/modules/rp2_sd/sd_spi.h b/software/modules/rp2_sd/sd_spi.h index be44e3d..9ffd8c9 100644 --- a/software/modules/rp2_sd/sd_spi.h +++ b/software/modules/rp2_sd/sd_spi.h @@ -27,3 +27,4 @@ bool sd_cmd_read_complete(void); bool sd_cmd_read_is_complete(void); bool sd_cmd_write(uint8_t cmd, uint32_t arg, unsigned datalen, uint8_t data[const static datalen]); +bool sd_cmd_write_multiple(uint8_t cmd, uint32_t arg, unsigned blocks, unsigned datalen, uint8_t *const data); diff --git a/software/tools/standalone-mp3/CMakeLists.txt b/software/tools/standalone-mp3/CMakeLists.txt index 148d438..b69602f 100644 --- a/software/tools/standalone-mp3/CMakeLists.txt +++ b/software/tools/standalone-mp3/CMakeLists.txt @@ -15,6 +15,8 @@ option(ENABLE_PLAY_TEST "Enable mp3 playback test" OFF) option(ENABLE_SD_READ_CRC "Enable crc check when reading from sd card" OFF) option(ENABLE_SD_DEBUG "Enable debug output for sd card driver" OFF) +set(PICO_USE_FASTEST_SUPPORTED_CLOCK 1) + # initialize the Raspberry Pi Pico SDK pico_sdk_init() @@ -62,7 +64,7 @@ add_subdirectory(../../lib/helix_mp3 helix_mp3) target_link_libraries(standalone_mp3 PRIVATE pico_stdlib hardware_dma hardware_spi hardware_sync hardware_pio helix_mp3) target_include_directories(standalone_mp3 PRIVATE ${SD_LIB_DIR}) -target_compile_options(standalone_mp3 PRIVATE -Og -DSD_DEBUG) +target_compile_options(standalone_mp3 PRIVATE -Og) pico_add_extra_outputs(standalone_mp3) diff --git a/software/tools/standalone-mp3/main.c b/software/tools/standalone-mp3/main.c index b03ec70..012a20c 100644 --- a/software/tools/standalone-mp3/main.c +++ b/software/tools/standalone-mp3/main.c @@ -2,6 +2,7 @@ #include "sd.h" #include +#include #include #include @@ -20,6 +21,14 @@ extern void sd_spi_dbg_clk(const int div, const int frac); extern void sd_spi_dbg_loop(void); +void sd_printf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + #define MAX_VOLUME 0x8000u void __time_critical_func(volume_adjust)(int16_t *restrict buf, size_t samples, uint16_t scalef) @@ -157,7 +166,7 @@ static void write_test(struct sd_context *sd_context) data_buffer[i] ^= 0xff; } - if (!sd_writeblock(sd_context, 0, data_buffer)) { + if (!sd_writeblocks(sd_context, 0, sizeof(data_buffer) / SD_SECTOR_SIZE, data_buffer)) { printf("sd_writeblock failed\n"); return; }