Start work on SD card driver
Some checks failed
Check code formatting / Check-C-Format (push) Failing after 8s
Check code formatting / Check-Python-Flake8 (push) Failing after 9s
Run unit tests on host / Run-Unit-Tests (push) Failing after 15s

Working:
 - card initialization
 - card size & type detection
 - read block

TODO:
 - write block
 - Use interrupts and DMA for read/write block
This commit is contained in:
2024-10-29 20:56:13 +01:00
parent 1909fc2f06
commit 7727cf0b80
7 changed files with 445 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.13)
# initialize pico-sdk from submodule
# note: this must happen before project()
include(../../lib/micropython/lib/pico-sdk/pico_sdk_init.cmake)
project(standalone_mp3)
# initialize the Raspberry Pi Pico SDK
pico_sdk_init()
add_executable(standalone_mp3
main.c
sd.c
sd_spi.c
)
target_link_libraries(standalone_mp3 PRIVATE pico_stdlib hardware_spi)
pico_add_extra_outputs(standalone_mp3)
pico_enable_stdio_uart(standalone_mp3 1)

View File

@@ -0,0 +1,27 @@
#include "sd.h"
#include <stdio.h>
#include <hardware/gpio.h>
#include <hardware/spi.h>
#include <pico/stdio.h>
int main()
{
stdio_init_all();
if (!sd_init()) {
return 1;
}
uint8_t buf[512];
if (sd_readblock(0, buf)) {
for(int row = 0;row < 512/16;++row) {
printf("%03x: ", row*16);
for (int col = 0; col < 16;++col)
printf("%02hhx%c", buf[row*16+col], (col==15)?'\n':' ');
}
}
printf("Done.\n");
}

View File

@@ -0,0 +1,237 @@
#include "sd.h"
#include "sd_util.h"
#include "sd_spi.h"
#include <stdio.h>
#include <string.h>
#define SD_DEBUG
#define SD_R1_ILLEGAL_COMMAND (1 << 2)
struct sd_context {
size_t blocks;
bool initialized;
bool old_card;
bool sdhc_sdxc;
};
static struct sd_context sd_context;
static bool sd_acmd(const uint8_t cmd, const uint32_t arg, unsigned resplen, uint8_t res[static resplen])
{
if (!sd_cmd(55, 0, 1, res))
return false;
if ((res[0] & 0x7e) != 0x00)
return false;
return sd_cmd(cmd, arg, resplen, res);
}
static bool sd_early_init(void)
{
uint8_t buf;
for (int i = 0; i < 5; ++i) {
if (sd_cmd(0, 0, 1, &buf)) {
#ifdef SD_DEBUG
printf("CMD0 resp %02hhx\n", buf);
#endif
if (buf == 0x01) {
return true;
}
}
#ifdef SD_DEBUG
printf("CMD0 timeout, try again...\n");
#endif
}
return false;
}
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");
return false;
}
} else {
if (buf[0] & SD_R1_ILLEGAL_COMMAND) {
printf("sd_init: check interface condition returned illegal command - old card?\n");
} else {
printf("sd_init: check interface condition failed\n");
return false;
}
}
return true;
}
static bool sd_send_op_cond(void)
{
uint8_t buf[1];
bool use_acmd = true;
for (int timeout = 0; timeout < 500; ++timeout) {
bool result = false;
if (use_acmd)
result = sd_acmd(41, 0x40000000, 1, buf);
else
result = sd_cmd(1, 0x40000000, 1, buf);
if (!result) {
if (use_acmd && buf[0] & SD_R1_ILLEGAL_COMMAND) {
printf("sd_init: card does not understand ACMD41, try CMD1...\n");
continue;
} else if (buf[0] != 0x01) {
printf("sd_init: send_op_cond failed\n");
return false;
} else {
continue;
}
}
if (buf[0] == 0x00) {
return true;
}
}
printf("sd_init: send_op_cond: timeout waiting for !idle\n");
return false;
}
static bool sd_read_ocr(uint32_t *const ocr)
{
uint8_t buf[5];
if (!sd_cmd(58, 0, 5, buf))
return false;
*ocr = buf[1] << 24 | buf[2] << 16 | buf[3] << 8 | buf[4];
return true;
}
static void sd_dump_cid(void)
{
uint8_t buf[16];
if (sd_cmd_read(10, 0, 16, buf)) {
const uint8_t crc = sd_crc7(15, buf);
if (buf[15] >> 1 != crc) {
printf("CRC mismatch: Got %02hhx, expected %02hhx\n", buf[15] >> 1, crc);
return;
}
uint8_t mid = buf[0];
char oid[2], pnm[5];
memcpy(oid, buf + 1, 2);
memcpy(pnm, buf + 3, 5);
uint8_t prv = buf[8];
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: %08x, mdt_year: %d, mdt_month: %d\n", mid,
oid, pnm, prv, psn, mdt_year, mdt_month);
}
}
static bool sd_read_csd(void)
{
uint8_t buf[16];
if (sd_cmd_read(9, 0, 16, buf)) {
const uint8_t crc = sd_crc7(15, buf);
if (buf[15] >> 1 != crc) {
printf("CRC mismatch: Got %02hhx, expected %02hhx\n", buf[15] >> 1, crc);
return false;
}
const unsigned csd_ver = buf[0] >> 6;
unsigned blocksize = 0;
unsigned blocks = 0;
unsigned version = 0;
switch (csd_ver) {
case 0: {
if (sd_context.sdhc_sdxc) {
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");
return false;
}
blocksize = 1 << (buf[5] & 0xf);
const unsigned c_size_mult = (buf[9] & 0x1) << 2 | (buf[10] & 0xc0) >> 6;
const unsigned c_size = (buf[6] & 0x3) << 10 | (buf[7] << 2) | (buf[8] & 0xc0) >> 6;
blocks = (c_size + 1) * (1 << (c_size_mult + 2));
version = 1;
break;
}
case 1: {
blocksize = 512;
const unsigned c_size = (buf[7] & 0x3f) << 16 | buf[8] << 8 | buf[9];
blocks = (c_size + 1) * 1024;
version = 2;
break;
}
case 2: {
printf("sd_init: Got CSD v3.0, but SDUC does not support SPI.\n");
return false;
}
}
sd_context.blocks = blocks;
#ifdef SD_DEBUG
printf("CSD version %u.0, blocksize %u, blocks %u, capacity %llu MiB\n", version, blocksize, blocks,
((uint64_t)blocksize * blocks) / (1024 * 1024));
#endif
}
return true;
}
bool sd_init(void)
{
sd_spi_init();
if (!sd_early_init()) {
return false;
}
if (!sd_check_interface_condition()) {
return false;
}
uint32_t ocr;
if (!sd_read_ocr(&ocr)) {
printf("sd_init: read OCR failed\n");
return false;
}
if ((ocr & 0x00380000) != 0x00380000) {
printf("sd_init: unsupported card voltage range\n");
return false;
}
if (!sd_send_op_cond())
return false;
sd_spi_set_bitrate(SD_BITRATE);
if (!sd_read_ocr(&ocr)) {
printf("sd_init: read OCR failed\n");
return false;
}
if (!(ocr & (1 << 31))) {
printf("sd_init: card not powered up but !idle?\n");
return false;
}
sd_context.sdhc_sdxc = (ocr & (1 << 30));
if (!sd_read_csd()) {
return false;
}
#ifdef SD_DEBUG
sd_dump_cid();
#endif
sd_context.initialized = true;
return true;
}
bool sd_readblock(size_t sector_num, uint8_t buffer[static 512])
{
if (!sd_context.initialized || sector_num >= sd_context.blocks)
return false;
return sd_cmd_read(17, sector_num, 512, buffer);
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
bool sd_init(void);
bool sd_readblock(size_t sector_num, uint8_t buffer[static 512]);

View File

@@ -0,0 +1,101 @@
#include "sd_spi.h"
#include "sd_util.h"
#include <hardware/gpio.h>
#include <hardware/spi.h>
#include <stdio.h>
#include <string.h>
static void sd_spi_cmd_send(const uint8_t cmd, const uint32_t arg)
{
uint8_t buf[6] = {0x40 | cmd, arg >> 24, arg >> 16, arg >> 8, arg, 0};
buf[5] = sd_crc7(5, buf) << 1 | 1;
gpio_put(SD_CS, false);
// Write command, argument and CRC
spi_write_blocking(SD_SPI, buf, 6);
}
bool sd_cmd(const uint8_t cmd, const uint32_t arg, unsigned resplen, uint8_t resp[static resplen])
{
sd_spi_cmd_send(cmd, arg);
resp[0] = 0;
// Read up to 8 garbage bytes (0xff), followed by R1 (MSB is zero)
bool got_r1 = false;
for (int timeout = 0; timeout < 8; ++timeout) {
spi_read_blocking(SD_SPI, 0xff, resp, 1);
if (!(resp[0] & 0x80)) {
got_r1 = true;
break;
}
}
if (got_r1 && (resp[0] & 0x7e) == 0) {
// read rest of response if R1 does not indicate an error
spi_read_blocking(SD_SPI, 0xff, resp + 1, resplen - 1);
}
gpio_put(SD_CS, true);
const uint8_t buf = 0xff;
// Ensure 8 SPI clock cycles after CS deasserted
spi_write_blocking(SD_SPI, &buf, 1);
return got_r1 && (resp[0] & 0x7e) == 0;
}
bool sd_cmd_read(uint8_t cmd, uint32_t arg, unsigned datalen, uint8_t data[static datalen])
{
uint8_t buf[2];
sd_spi_cmd_send(cmd, arg);
// Read up to 8 garbage bytes (0xff), followed by R1 (MSB is zero)
bool got_r1 = false;
for (int timeout = 0; timeout < 8; ++timeout) {
spi_read_blocking(SD_SPI, 0xff, buf, 1);
if (!(buf[0] & 0x80)) {
got_r1 = true;
break;
}
}
if (!got_r1 || buf[0] != 0x00)
goto abort;
while (true) { // TODO: what is a sensible timeout here?
spi_read_blocking(SD_SPI, 0xff, buf, 1);
if (buf[0] != 0xff)
break;
}
if (buf[0] != 0xfe) // unexpected read token
goto abort;
spi_read_blocking(SD_SPI, 0xff, data, datalen);
spi_read_blocking(SD_SPI, 0xff, buf, 2);
// we ignore the CRC16 for performance reasons
gpio_put(SD_CS, true);
spi_read_blocking(SD_SPI, 0xff, buf, 1);
return true;
abort:
gpio_put(SD_CS, true);
spi_read_blocking(SD_SPI, 0xff, buf, 1);
return false;
}
void sd_spi_init(void)
{
gpio_set_function(SD_MISO, GPIO_FUNC_SPI);
gpio_set_function(SD_SCK, GPIO_FUNC_SPI);
gpio_set_function(SD_MOSI, GPIO_FUNC_SPI);
gpio_init(SD_CS);
gpio_set_dir(SD_CS, true);
sd_spi_set_bitrate(SD_INIT_BITRATE);
spi_set_format(SD_SPI, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
gpio_put(SD_CS, true);
uint8_t buf[16];
memset(buf, 0xff, 16);
// Ensure at least 74 SPI clock cycles without CS asserted
spi_write_blocking(SD_SPI, buf, 10);
}
void sd_spi_set_bitrate(const int rate)
{
const unsigned actual_rate = spi_init(SD_SPI, rate);
printf("SPI init: Requested bitrate %u, got %u\n", rate, actual_rate);
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#define SD_MISO 4
#define SD_SCK 2
#define SD_MOSI 3
#define SD_CS 5
#define SD_SPI spi0
#define SD_INIT_BITRATE 400000
#define SD_BITRATE 25000000
bool sd_cmd(const uint8_t cmd, const uint32_t arg, unsigned resplen, uint8_t resp[static resplen]);
bool sd_cmd_read(uint8_t cmd, uint32_t arg, unsigned datalen, uint8_t data[static datalen]);
void sd_spi_init(void);
void sd_spi_set_bitrate(const int rate);

View File

@@ -0,0 +1,30 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
inline static uint8_t sd_crc7(size_t len, const uint8_t data[const static len])
{
const uint8_t poly = 0b1001;
uint8_t crc = 0;
for (size_t pos = 0; pos < len; ++pos) {
crc ^= data[pos];
for (int bit = 0; bit < 8; ++bit) {
crc = (crc << 1) ^ ((crc & 0x80) ? (poly << 1) : 0);
}
}
return crc >> 1;
}
/* inline static uint16_t sd_crc16(size_t len, const uint8_t data[const static len]) */
/* { */
/* const uint16_t poly = 0b1000000100001; */
/* uint16_t crc = 0; */
/* for (size_t pos = 0; pos < len; ++pos) { */
/* crc ^= data[pos] << 8; */
/* for (int bit = 0; bit < 8; ++bit) { */
/* crc = (crc << 1) ^ ((crc & 0x8000) ? poly : 0); */
/* } */
/* } */
/* return crc; */
/* } */