Driver for SD/MMC cards (#239)
* Driver for SD/MMC cards * SDIO: read_register() bug fixed, schematics resized
This commit is contained in:
parent
b23dd64419
commit
2994a566a6
7 changed files with 829 additions and 0 deletions
4
examples/sdio_raw/Makefile
Normal file
4
examples/sdio_raw/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
PROGRAM = sdio_raw
|
||||
EXTRA_COMPONENTS = extras/sdio
|
||||
#ESPBAUD = 460800
|
||||
include ../../common.mk
|
117
examples/sdio_raw/main.c
Normal file
117
examples/sdio_raw/main.c
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Example of using SDIO driver
|
||||
*
|
||||
* Part of esp-open-rtos
|
||||
* Copyright (C) 2016 Ruslan V. Uss <unclerus@gmail.com>
|
||||
* BSD Licensed as described in the file LICENSE
|
||||
*/
|
||||
#include <esp/uart.h>
|
||||
#include <espressif/esp_common.h>
|
||||
#include <stdio.h>
|
||||
#include <esp/spi.h>
|
||||
#include <sdio/sdio.h>
|
||||
#include <string.h>
|
||||
#include <esp/hwrand.h>
|
||||
|
||||
// Blink while IO :)
|
||||
#define CS_GPIO_PIN 2
|
||||
// Set SPI frequency to 20MHz
|
||||
#define SDIO_SPI_FREQ_DIV SPI_FREQ_DIV_20M
|
||||
|
||||
static const char *errors[] = {
|
||||
[SDIO_ERR_NONE] = NULL,
|
||||
[SDIO_ERR_TIMEOUT] = "Timeout",
|
||||
[SDIO_ERR_UNSUPPORTED] = "Unsupported",
|
||||
[SDIO_ERR_IO] = "General I/O error",
|
||||
[SDIO_ERR_CRC] = "CRC check failed"
|
||||
};
|
||||
|
||||
static const char *types[] = {
|
||||
[SDIO_TYPE_UNKNOWN] = "Unknown",
|
||||
[SDIO_TYPE_MMC] = "MMC",
|
||||
[SDIO_TYPE_SD1] = "SD v1.x",
|
||||
[SDIO_TYPE_SD2] = "SD v2.x",
|
||||
[SDIO_TYPE_SDHC] = "SDHC",
|
||||
};
|
||||
|
||||
static void dump_line(const uint8_t *data)
|
||||
{
|
||||
for (uint8_t i = 0; i < 16; i ++)
|
||||
printf(" %02x", data[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static uint8_t buffer[SDIO_BLOCK_SIZE];
|
||||
|
||||
#define TEST_COUNT 1000
|
||||
|
||||
inline static void test_read(sdio_card_t *card)
|
||||
{
|
||||
printf("Simple random access test speed: ");
|
||||
uint32_t start = sdk_system_get_time();
|
||||
//sdk_system_update_cpu_freq(160);
|
||||
for (uint32_t i = 0; i < TEST_COUNT; i ++)
|
||||
{
|
||||
printf("%08u / %08u\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08", i + 1, TEST_COUNT);
|
||||
if (sdio_read_sectors(card, hwrand() % card->sectors, buffer, 1) != SDIO_ERR_NONE)
|
||||
{
|
||||
printf("Error: %d (%s)\n", card->error, errors[card->error]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
uint32_t time = sdk_system_get_time() - start;
|
||||
float speed = (float)TEST_COUNT / time * 1000000;
|
||||
float stime = (time / 1000000.0) / TEST_COUNT;
|
||||
uint32_t bs = (uint32_t)speed * 512;
|
||||
printf("\nDone. Time: %u ms, speed: %.4f sectors/s, sector in %.4f s, %u bytes/s \n", time, speed, stime, bs);
|
||||
}
|
||||
|
||||
inline static void dump_card(sdio_card_t *card)
|
||||
{
|
||||
char product_name[6];
|
||||
memcpy(product_name, card->cid.bits.product_name, 5);
|
||||
product_name[5] = 0;
|
||||
|
||||
printf("-------------------------------------------\n");
|
||||
printf(" SD/MMC card info\n");
|
||||
printf("-------------------------------------------\n");
|
||||
printf("%20s : %s\n", "Card type", types[card->type]);
|
||||
printf("%20s : %s\n", "CRC enabled", card->crc_enabled ? "yes" : "no");
|
||||
printf("%20s : %d\n", "512 byte sectors", card->sectors);
|
||||
printf("%20s : 0x%02x\n", "Manufacturer ID", card->cid.bits.manufacturer_id);
|
||||
printf("%20s : %c%c\n", "OEM ID", card->cid.bits.oem_id[0], card->cid.bits.oem_id[1]);
|
||||
printf("%20s : %s\n", "Product name", product_name);
|
||||
printf("%20s : %d.%d\n", "Product revision", card->cid.bits.product_rev_major, card->cid.bits.product_rev_minor);
|
||||
printf("%20s : 0x%08x\n", "Serial #", card->cid.bits.product_serial);
|
||||
printf("%20s : %02d / %04d\n", "Manufacturing date", card->cid.bits.date_month, card->cid.bits.date_year_h * 10 + card->cid.bits.date_year_l + 2000);
|
||||
printf("%20s : 0x%08x\n", "OCR", card->ocr.data);
|
||||
printf("%20s :", "CID");
|
||||
dump_line(card->cid.data);
|
||||
printf("%20s :", "CSD");
|
||||
dump_line(card->csd.data);
|
||||
test_read(card);
|
||||
}
|
||||
|
||||
void user_init(void)
|
||||
{
|
||||
uart_set_baud(0, 115200);
|
||||
printf("SDK version:%s\n\n", sdk_system_get_sdk_version());
|
||||
|
||||
while (true)
|
||||
{
|
||||
printf("\nInitializing card...\n");
|
||||
do
|
||||
{
|
||||
sdio_card_t card;
|
||||
if (sdio_init(&card, CS_GPIO_PIN, SDIO_SPI_FREQ_DIV))
|
||||
{
|
||||
printf("Error: %d (%s)\n", card.error, errors[card.error]);
|
||||
break;
|
||||
}
|
||||
dump_card(&card);
|
||||
} while (0);
|
||||
|
||||
for (size_t i = 0; i < 1000; i ++)
|
||||
sdk_os_delay_us(1000);
|
||||
}
|
||||
}
|
BIN
examples/sdio_raw/schematics.png
Normal file
BIN
examples/sdio_raw/schematics.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
7
extras/sdio/component.mk
Normal file
7
extras/sdio/component.mk
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Component makefile for extras/sdio
|
||||
INC_DIRS += $(sdio_ROOT)..
|
||||
|
||||
# args for passing into compile rule generation
|
||||
sdio_SRC_DIR = $(sdio_ROOT)
|
||||
|
||||
$(eval $(call component_compile_rules,sdio))
|
449
extras/sdio/sdio.c
Normal file
449
extras/sdio/sdio.c
Normal file
|
@ -0,0 +1,449 @@
|
|||
/*
|
||||
* Hardware SPI driver for MMC/SD/SDHC cards
|
||||
*
|
||||
* Part of esp-open-rtos
|
||||
* Copyright (C) 2016 Ruslan V. Uss <unclerus@gmail.com>
|
||||
* BSD Licensed as described in the file LICENSE
|
||||
*/
|
||||
#include <esp/gpio.h>
|
||||
#include <esp/spi.h>
|
||||
#include <espressif/esp_common.h>
|
||||
#include "sdio.h"
|
||||
|
||||
#define BUS 1
|
||||
|
||||
#define BV(x) (1 << (x))
|
||||
|
||||
#define MS 1000
|
||||
|
||||
#define INIT_TIMEOUT_US (2000 * MS)
|
||||
#define IO_TIMEOUT_US (500 * MS)
|
||||
|
||||
#define MAX_ERR_COUNT 0xff
|
||||
|
||||
#define R1_IDLE_STATE 0
|
||||
#define R1_ERASE_RESET 1
|
||||
#define R1_ILLEGAL_CMD 2
|
||||
#define R1_CRC_ERR 3
|
||||
#define R1_ERASE_SEQ_ERR 4
|
||||
#define R1_ADDR_ERR 5
|
||||
#define R1_PARAM_ERR 6
|
||||
#define R1_BUSY 7
|
||||
#define R2_LOCKED 8
|
||||
#define R2_WPE_SKIP_LF 9
|
||||
#define R2_ERROR 10
|
||||
#define R2_CC_ERROR 11
|
||||
#define R2_ECC_FAILED 12
|
||||
#define R2_WP_VIOLATION 13
|
||||
#define R2_ERASE_PARAM 14
|
||||
#define R2_OUT_OF_RANGE 15
|
||||
|
||||
#define OCR_CCS 30
|
||||
#define OCR_BUSY 31
|
||||
#define OCR_SDHC (BV(OCR_CCS) | BV(OCR_BUSY))
|
||||
|
||||
#define TOKEN_SINGLE_TRAN 0xfe
|
||||
#define TOKEN_MULTI_TRAN 0xfc
|
||||
#define TOKEN_STOP_TRAN 0xfd
|
||||
#define WRITE_RES_MASK 0x1f
|
||||
#define WRITE_RES_OK 0x05
|
||||
|
||||
|
||||
#define CMD0 0x00 // GO_IDLE_STATE - Resets the SD Memory Card
|
||||
#define CMD1 0x01 // SEND_OP_COND - Sends host capacity support information
|
||||
// and activates the card's initialization process.
|
||||
#define CMD6 0x06 // SWITCH_FUNC - Checks switchable function (mode 0) and
|
||||
// switches card function (mode 1).
|
||||
#define CMD8 0x08 // SEND_IF_COND - Sends SD Memory Card interface condition
|
||||
// that includes host supply voltage information and asks
|
||||
// the accessed card whether card can operate in supplied
|
||||
// voltage range.
|
||||
#define CMD9 0x09 // SEND_CSD - Asks the selected card to send its
|
||||
// card-specific data (CSD register)
|
||||
#define CMD10 0x0a // SEND_CID - Asks the selected card to send its card
|
||||
// identification (CID register)
|
||||
#define CMD12 0x0c // STOP_TRANSMISSION - Forces the card to stop transmission
|
||||
// in Multiple Block Read Operation
|
||||
#define CMD13 0x0d // SEND_STATUS - Asks the selected card to send its
|
||||
// status register.
|
||||
#define CMD16 0x10 // SET_BLOCKLEN - Sets a block length (in bytes) for all
|
||||
// following block commands (read and write) of a Standard
|
||||
// Capacity Card. Block length of the read and write
|
||||
// commands are fixed to 512 bytes in a High Capacity Card.
|
||||
// The length of LOCK_UNLOCK command is set by this command
|
||||
// in both capacity cards.
|
||||
#define CMD17 0x11 // READ_SINGLE_BLOCK - Reads a block of the size selected
|
||||
// by the SET_BLOCKLEN command.
|
||||
#define CMD18 0x12 // READ_MULTIPLE_BLOCK - Continuously transfers data blocks
|
||||
// from card to host until interrupted by a
|
||||
// STOP_TRANSMISSION command.
|
||||
#define CMD24 0x18 // WRITE_BLOCK - Writes a block of the size selected by the
|
||||
// SET_BLOCKLEN command.
|
||||
#define CMD25 0x19 // WRITE_MULTIPLE_BLOCK - Continuously writes blocks of
|
||||
// data until ’Stop Tran’ token is sent (instead ’Start
|
||||
// Block’).
|
||||
#define CMD27 0x1b // PROGRAM_CSD - Programming of the programmable bits of
|
||||
// the CSD.
|
||||
#define CMD28 0x1c // SET_WRITE_PROT
|
||||
#define CMD29 0x1d // CLR_WRITE_PROT
|
||||
#define CMD32 0x20 // ERASE_WR_BLK_START - Sets the address of the first block
|
||||
// to be erased.
|
||||
#define CMD33 0x21 // ERASE_WR_BLK_END - Sets the address of the last block of
|
||||
// the continuous range to be erased.
|
||||
#define CMD38 0x26 // ERASE - Erases all previously selected blocks.
|
||||
#define CMD55 0x37 // APP_CMD - Defines to the card that the next command is
|
||||
// an application specific command rather than a standard
|
||||
// command.
|
||||
#define CMD58 0x3a // READ_OCR - Reads the OCR register of a card.
|
||||
#define CMD59 0x3b // CRC_ON_OFF - Turns the CRC option on or off.
|
||||
|
||||
#define ACMD23 0x17 // SET_WR_BLK_ERASE_COUNT - Sets the number of write blocks
|
||||
// to be pre-erased before writing
|
||||
#define ACMD41 0x29 // SD_SEND_OP_COMD - Sends host capacity support information
|
||||
// and activates the card's initialization process
|
||||
|
||||
static uint8_t crc7(const uint8_t* data, uint8_t n)
|
||||
{
|
||||
uint8_t crc = 0;
|
||||
for (uint8_t i = 0; i < n; i++)
|
||||
{
|
||||
uint8_t d = data[i];
|
||||
for (uint8_t j = 0; j < 8; j++)
|
||||
{
|
||||
crc <<= 1;
|
||||
if ((d & 0x80) ^ (crc & 0x80))
|
||||
crc ^= 0x09;
|
||||
d <<= 1;
|
||||
}
|
||||
}
|
||||
return (crc << 1) | 1;
|
||||
}
|
||||
|
||||
static uint16_t crc_ccitt(const uint8_t *data, size_t n)
|
||||
{
|
||||
uint16_t crc = 0;
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
crc = (uint8_t)(crc >> 8) | (crc << 8);
|
||||
crc ^= data[i];
|
||||
crc ^= (uint8_t)(crc & 0xff) >> 4;
|
||||
crc ^= crc << 12;
|
||||
crc ^= (crc & 0xff) << 5;
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
#define spi_cs_low(card) do { gpio_write(card->cs_pin, false); } while(0)
|
||||
#define spi_cs_high(card) do { gpio_write(card->cs_pin, true); } while(0)
|
||||
#define spi_read_byte() (spi_transfer_8(BUS, 0xff))
|
||||
#define spi_read_word() (((uint16_t)spi_read_byte() << 8) | spi_read_byte())
|
||||
#define spi_read_dword() (((uint32_t)spi_read_byte() << 24) | ((uint32_t)spi_read_byte() << 16) | ((uint32_t)spi_read_byte() << 8) | spi_read_byte())
|
||||
#define spi_skip_word() do { spi_read_byte(); spi_read_byte(); } while(0)
|
||||
#define spi_skip_dword() do { spi_read_byte(); spi_read_byte(); spi_read_byte(); spi_read_byte(); } while(0)
|
||||
|
||||
inline static uint16_t spi_write_word(uint16_t word)
|
||||
{
|
||||
return (spi_transfer_8(BUS, word >> 8) << 8) | spi_transfer_8(BUS, word);
|
||||
}
|
||||
|
||||
inline static void spi_read_bytes(uint8_t *dst, size_t size)
|
||||
{
|
||||
for (uint8_t *offs = dst; offs < dst + size; offs ++)
|
||||
*offs = spi_read_byte();
|
||||
}
|
||||
|
||||
static bool wait()
|
||||
{
|
||||
uint32_t stop = sdk_system_get_time() + IO_TIMEOUT_US;
|
||||
while (spi_read_byte() != 0xff)
|
||||
if (sdk_system_get_time() >= stop)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint8_t command(sdio_card_t *card, uint8_t cmd, uint32_t arg)
|
||||
{
|
||||
uint8_t buf[6] = {
|
||||
cmd | 0x40,
|
||||
arg >> 24,
|
||||
arg >> 16,
|
||||
arg >> 8,
|
||||
arg
|
||||
};
|
||||
if (card->crc_enabled)
|
||||
buf[5] = crc7(buf, 5);
|
||||
else
|
||||
buf[5] = cmd == CMD0 ? 0x95 : 0x87;
|
||||
|
||||
spi_cs_low(card);
|
||||
wait();
|
||||
spi_transfer(BUS, buf, NULL, 6, SPI_8BIT);
|
||||
|
||||
// R1b response
|
||||
if (cmd == CMD12 || cmd == CMD28 || cmd == CMD29)
|
||||
spi_read_byte();
|
||||
|
||||
uint8_t res;
|
||||
for (uint8_t i = 0; i < MAX_ERR_COUNT; i ++)
|
||||
{
|
||||
res = spi_read_byte();
|
||||
if (!(res & BV(R1_BUSY)))
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
inline static uint8_t app_command(sdio_card_t *card, uint8_t cmd, uint32_t arg)
|
||||
{
|
||||
command(card, CMD55, 0);
|
||||
return command(card, cmd, arg);
|
||||
}
|
||||
|
||||
inline static sdio_error_t set_error(sdio_card_t *card, sdio_error_t err)
|
||||
{
|
||||
card->error = err;
|
||||
spi_cs_high(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static sdio_error_t read_data(sdio_card_t *card, uint8_t *dst, size_t size)
|
||||
{
|
||||
uint32_t timeout = sdk_system_get_time() + IO_TIMEOUT_US;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (sdk_system_get_time() >= timeout)
|
||||
return set_error(card, SDIO_ERR_TIMEOUT);
|
||||
|
||||
uint8_t b = spi_read_byte();
|
||||
if (b == TOKEN_SINGLE_TRAN)
|
||||
break;
|
||||
|
||||
if (b != 0xff)
|
||||
return set_error(card, SDIO_ERR_IO);
|
||||
}
|
||||
|
||||
spi_read_bytes(dst, size);
|
||||
|
||||
uint16_t crc = spi_read_word();
|
||||
if (card->crc_enabled && crc_ccitt(dst, size) != crc)
|
||||
return set_error(card, SDIO_ERR_CRC);
|
||||
|
||||
return SDIO_ERR_NONE;
|
||||
}
|
||||
|
||||
static sdio_error_t read_register(sdio_card_t *card, uint8_t cmd, void *dst)
|
||||
{
|
||||
if (command(card, cmd, 0))
|
||||
return set_error(card, SDIO_ERR_IO);
|
||||
return read_data(card, dst, 16);
|
||||
}
|
||||
|
||||
static sdio_error_t write_data_block(sdio_card_t *card, uint8_t token, uint8_t *src)
|
||||
{
|
||||
if (!wait())
|
||||
return set_error(card, SDIO_ERR_TIMEOUT);
|
||||
spi_transfer_8(BUS, token);
|
||||
spi_transfer(BUS, src, NULL, SDIO_BLOCK_SIZE, SPI_8BIT);
|
||||
spi_write_word(card->crc_enabled ? crc_ccitt(src, SDIO_BLOCK_SIZE) : 0xffff);
|
||||
|
||||
if ((spi_read_byte() & WRITE_RES_MASK) != WRITE_RES_OK)
|
||||
return set_error(card, SDIO_ERR_IO);
|
||||
|
||||
return SDIO_ERR_NONE;
|
||||
}
|
||||
|
||||
sdio_error_t sdio_init(sdio_card_t *card, uint8_t cs_pin, uint32_t high_freq_divider)
|
||||
{
|
||||
card->cs_pin = cs_pin;
|
||||
card->type = SDIO_TYPE_UNKNOWN;
|
||||
|
||||
// setup SPI at 125kHz
|
||||
spi_settings_t s = {
|
||||
.mode = SPI_MODE0,
|
||||
.freq_divider = SPI_FREQ_DIV_125K,
|
||||
.msb = true,
|
||||
.endianness = SPI_LITTLE_ENDIAN,
|
||||
.minimal_pins = true
|
||||
};
|
||||
spi_set_settings(BUS, &s);
|
||||
gpio_enable(card->cs_pin, GPIO_OUTPUT);
|
||||
|
||||
uint32_t timeout = sdk_system_get_time() + INIT_TIMEOUT_US;
|
||||
|
||||
spi_cs_low(card);
|
||||
spi_cs_high(card);
|
||||
for (uint8_t i = 0; i < 10; i++)
|
||||
spi_read_byte();
|
||||
|
||||
// Set card to the SPI idle mode
|
||||
while (command(card, CMD0, 0) != BV(R1_IDLE_STATE))
|
||||
{
|
||||
if (sdk_system_get_time() >= timeout)
|
||||
return set_error(card, SDIO_ERR_TIMEOUT);
|
||||
}
|
||||
|
||||
// Enable CRC
|
||||
card->crc_enabled = command(card, CMD59, 1) == BV(R1_IDLE_STATE);
|
||||
|
||||
// Get card type
|
||||
while (true)
|
||||
{
|
||||
if (command(card, CMD8, 0x1aa) & BV(R1_ILLEGAL_CMD))
|
||||
{
|
||||
card->type = SDIO_TYPE_SD1;
|
||||
break;
|
||||
}
|
||||
if ((spi_read_dword() & 0xff) == 0xaa)
|
||||
{
|
||||
card->type = SDIO_TYPE_SD2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sdk_system_get_time() >= timeout)
|
||||
return set_error(card, SDIO_ERR_TIMEOUT);
|
||||
}
|
||||
|
||||
if (card->type == SDIO_TYPE_SD1)
|
||||
{
|
||||
// SD1 or MMC3
|
||||
if (app_command(card, ACMD41, 0) > 1)
|
||||
{
|
||||
card->type = SDIO_TYPE_MMC;
|
||||
while (command(card, CMD1, 0))
|
||||
if (sdk_system_get_time() >= timeout)
|
||||
return set_error(card, SDIO_ERR_TIMEOUT);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (app_command(card, ACMD41, 0))
|
||||
if (sdk_system_get_time() >= timeout)
|
||||
return set_error(card, SDIO_ERR_TIMEOUT);
|
||||
}
|
||||
|
||||
if (command(card, CMD16, SDIO_BLOCK_SIZE))
|
||||
return set_error(card, SDIO_ERR_UNSUPPORTED);
|
||||
}
|
||||
else
|
||||
{
|
||||
// SD2 or SDHC
|
||||
while (app_command(card, ACMD41, BV(30)) != 0)
|
||||
if (sdk_system_get_time() >= timeout)
|
||||
return set_error(card, SDIO_ERR_TIMEOUT);
|
||||
}
|
||||
// read OCR
|
||||
if (command(card, CMD58, 0))
|
||||
return set_error(card, SDIO_ERR_IO);
|
||||
card->ocr.data = spi_read_dword();
|
||||
|
||||
if (card->type == SDIO_TYPE_SD2 && (card->ocr.data & OCR_SDHC) == OCR_SDHC)
|
||||
card->type = SDIO_TYPE_SDHC;
|
||||
|
||||
spi_set_frequency_div(BUS, high_freq_divider);
|
||||
|
||||
if (read_register(card, CMD10, &card->cid.data) != SDIO_ERR_NONE)
|
||||
return card->error;
|
||||
if (read_register(card, CMD9, &card->csd.data) != SDIO_ERR_NONE)
|
||||
return card->error;
|
||||
|
||||
// Card size
|
||||
if (card->csd.v1.csd_ver == 0)
|
||||
card->sectors = (uint32_t)(((card->csd.v1.c_size_high << 10) | (card->csd.v1.c_size_mid << 2) | card->csd.v1.c_size_low) + 1)
|
||||
<< (((card->csd.v1.c_size_mult_high << 1) | card->csd.v1.c_size_mult_low) + card->csd.v1.read_bl_len - 7);
|
||||
else if (card->csd.v2.csd_ver == 1)
|
||||
card->sectors = (((uint32_t)card->csd.v2.c_size_high << 16) + ((uint32_t)card->csd.v2.c_size_mid << 8) + card->csd.v2.c_size_low + 1) << 10;
|
||||
else
|
||||
return set_error(card, SDIO_ERR_UNSUPPORTED);
|
||||
|
||||
return set_error(card, SDIO_ERR_NONE);
|
||||
}
|
||||
|
||||
sdio_error_t sdio_read_sectors(sdio_card_t *card, uint32_t sector, uint8_t *dst, uint32_t count)
|
||||
{
|
||||
if (!count)
|
||||
return set_error(card, SDIO_ERR_IO);
|
||||
|
||||
if (card->type != SDIO_TYPE_SDHC)
|
||||
sector <<= 9;
|
||||
|
||||
bool multi = count > 1;
|
||||
if (command(card, multi ? CMD18 : CMD17, sector))
|
||||
return set_error(card, SDIO_ERR_IO);
|
||||
|
||||
while (count--)
|
||||
{
|
||||
if (read_data(card, dst, SDIO_BLOCK_SIZE) != SDIO_ERR_NONE)
|
||||
return card->error;
|
||||
dst += SDIO_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
if (multi && command(card, CMD12, 0))
|
||||
return set_error(card, SDIO_ERR_IO);
|
||||
|
||||
return set_error(card, SDIO_ERR_NONE);
|
||||
}
|
||||
|
||||
sdio_error_t sdio_write_sectors(sdio_card_t *card, uint32_t sector, uint8_t *src, uint32_t count)
|
||||
{
|
||||
if (!count)
|
||||
return set_error(card, SDIO_ERR_IO);
|
||||
|
||||
if (card->type != SDIO_TYPE_SDHC)
|
||||
sector <<= 9;
|
||||
|
||||
if (count == 1)
|
||||
{
|
||||
// single block
|
||||
if (command(card, CMD24, sector))
|
||||
return set_error(card, SDIO_ERR_IO);
|
||||
return set_error(card, write_data_block(card, TOKEN_SINGLE_TRAN, src));
|
||||
}
|
||||
|
||||
// send pre-erase count
|
||||
if ((card->type == SDIO_TYPE_SD1
|
||||
|| card->type == SDIO_TYPE_SD2
|
||||
|| card->type == SDIO_TYPE_SDHC)
|
||||
&& app_command(card, ACMD23, count))
|
||||
{
|
||||
return set_error(card, SDIO_ERR_IO);
|
||||
}
|
||||
|
||||
if (command(card, CMD25, sector))
|
||||
return set_error(card, SDIO_ERR_IO);
|
||||
|
||||
while (count--)
|
||||
{
|
||||
if (write_data_block(card, TOKEN_MULTI_TRAN, src) != SDIO_ERR_NONE)
|
||||
return card->error;
|
||||
src += SDIO_BLOCK_SIZE;
|
||||
}
|
||||
spi_transfer_8(BUS, TOKEN_STOP_TRAN);
|
||||
|
||||
return set_error(card, SDIO_ERR_NONE);
|
||||
}
|
||||
|
||||
sdio_error_t sdio_erase_sectors(sdio_card_t *card, uint32_t first, uint32_t last)
|
||||
{
|
||||
if (!card->csd.v1.erase_blk_en)
|
||||
{
|
||||
uint8_t mask = (card->csd.v1.sector_size_high << 1) | card->csd.v1.sector_size_low;
|
||||
if ((first & mask) || ((last + 1) & mask))
|
||||
return set_error(card, SDIO_ERR_UNSUPPORTED);
|
||||
}
|
||||
|
||||
if (card->type != SDIO_TYPE_SDHC)
|
||||
{
|
||||
first <<= 9;
|
||||
last <<= 9;
|
||||
}
|
||||
|
||||
if (command(card, CMD32, first)
|
||||
|| command(card, CMD33, last)
|
||||
|| command(card, CMD38, 0))
|
||||
{
|
||||
return set_error(card, SDIO_ERR_IO);
|
||||
}
|
||||
|
||||
return set_error(card, wait() ? SDIO_ERR_NONE : SDIO_ERR_TIMEOUT);
|
||||
}
|
||||
|
98
extras/sdio/sdio.h
Normal file
98
extras/sdio/sdio.h
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Hardware SPI driver for MMC/SD/SDHC cards
|
||||
*
|
||||
* Part of esp-open-rtos
|
||||
* Copyright (C) 2016 Ruslan V. Uss <unclerus@gmail.com>
|
||||
* BSD Licensed as described in the file LICENSE
|
||||
*/
|
||||
#ifndef _EXTRAS_SDIO_H_
|
||||
#define _EXTRAS_SDIO_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "sdio_impl.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SDIO_BLOCK_SIZE 512
|
||||
|
||||
typedef enum {
|
||||
SDIO_ERR_NONE = 0,
|
||||
SDIO_ERR_TIMEOUT,
|
||||
SDIO_ERR_UNSUPPORTED,
|
||||
SDIO_ERR_IO,
|
||||
SDIO_ERR_CRC
|
||||
} sdio_error_t;
|
||||
|
||||
typedef enum {
|
||||
SDIO_TYPE_UNKNOWN = 0,
|
||||
SDIO_TYPE_MMC,
|
||||
SDIO_TYPE_SD1,
|
||||
SDIO_TYPE_SD2,
|
||||
SDIO_TYPE_SDHC
|
||||
} sdio_card_type_t;
|
||||
|
||||
/**
|
||||
* SD card descriptor
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
sdio_error_t error; //!< Last operation result
|
||||
uint8_t cs_pin; //!< Chip Select GPIO pin
|
||||
sdio_card_type_t type; //!< Card type
|
||||
bool crc_enabled; //!< True if CRC enabled for IO
|
||||
sdio_ocr_t ocr; //!< OCR register
|
||||
sdio_csd_t csd; //!< CSD register
|
||||
sdio_cid_t cid; //!< CID register
|
||||
uint32_t sectors; //!< Card size in 512 byte sectors
|
||||
} sdio_card_t;
|
||||
|
||||
/**
|
||||
* \brief Init SD card
|
||||
* Device descriptor (registers, sectors count and so on) will be filled during initialization
|
||||
* SPI_FREQ_DIV_40M is good for modern SD cards, older SD can use SPI_FREQ_DIV_20M and lower
|
||||
* \param card Pointer to the device descriptor
|
||||
* \param cs_pin GPIO pin used for CS
|
||||
* \param high_freq_divider SPI bus frequency divider
|
||||
* \return Operation result
|
||||
*/
|
||||
sdio_error_t sdio_init(sdio_card_t *card, uint8_t cs_pin, uint32_t high_freq_divider);
|
||||
|
||||
/**
|
||||
* \brief Read 512 byte sectors from SD card
|
||||
* \param card Pointer to the device descriptor
|
||||
* \param sector Start sector
|
||||
* \param dst Receive buffer
|
||||
* \param count Number of sectors to read
|
||||
* \return Operation result
|
||||
*/
|
||||
sdio_error_t sdio_read_sectors(sdio_card_t *card, uint32_t sector, uint8_t *dst, uint32_t count);
|
||||
|
||||
/**
|
||||
* \brief Write 512 byte sectors to SD card
|
||||
* \param card Pointer to the device descriptor
|
||||
* \param sector Start sector
|
||||
* \param src Data to write
|
||||
* \param count Number of sectors to read
|
||||
* \return Operation result
|
||||
*/
|
||||
sdio_error_t sdio_write_sectors(sdio_card_t *card, uint32_t sector, uint8_t *src, uint32_t count);
|
||||
|
||||
/**
|
||||
* \brief Erase 512 byte sectors from SD card
|
||||
* \param card Pointer to the device descriptor
|
||||
* \param first First sector to erase
|
||||
* \param last Last sector to erase
|
||||
* \return Operation result
|
||||
*/
|
||||
sdio_error_t sdio_erase_sectors(sdio_card_t *card, uint32_t first, uint32_t last);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _EXTRAS_SDIO_H_ */
|
154
extras/sdio/sdio_impl.h
Normal file
154
extras/sdio/sdio_impl.h
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Hardware SPI driver for MMC/SD/SDHC cards
|
||||
*
|
||||
* Part of esp-open-rtos
|
||||
* Copyright (C) 2016 Ruslan V. Uss <unclerus@gmail.com>
|
||||
* BSD Licensed as described in the file LICENSE
|
||||
*/
|
||||
#ifndef _EXTRAS_SDIO_IMPL_H_
|
||||
#define _EXTRAS_SDIO_IMPL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef union
|
||||
{
|
||||
uint32_t data;
|
||||
struct
|
||||
{
|
||||
uint16_t reserverd1 :15;
|
||||
uint8_t vcc_2v7_2v8 :1;
|
||||
uint8_t vcc_2v8_2v9 :1;
|
||||
uint8_t vcc_2v9_3v0 :1;
|
||||
uint8_t vcc_3v0_3v1 :1;
|
||||
uint8_t vcc_3v1_3v2 :1;
|
||||
uint8_t vcc_3v2_3v3 :1;
|
||||
uint8_t vcc_3v3_3v4 :1;
|
||||
uint8_t vcc_3v4_3v5 :1;
|
||||
uint8_t vcc_3v5_3v6 :1;
|
||||
uint8_t reserverd2 :6;
|
||||
uint8_t ccs :1;
|
||||
uint8_t busy :1;
|
||||
} __attribute__((packed)) bits;
|
||||
} sdio_ocr_t;
|
||||
|
||||
typedef union
|
||||
{
|
||||
uint8_t data[16];
|
||||
struct
|
||||
{
|
||||
uint8_t manufacturer_id;
|
||||
char oem_id[2];
|
||||
char product_name[5];
|
||||
uint8_t product_rev_minor :4;
|
||||
uint8_t product_rev_major :4;
|
||||
uint32_t product_serial;
|
||||
uint8_t reserved1 :4;
|
||||
uint8_t date_year_h :4;
|
||||
uint8_t date_year_l :4;
|
||||
uint8_t date_month :4;
|
||||
uint8_t always1 :1;
|
||||
uint8_t crc :7;
|
||||
} __attribute__((packed)) bits;
|
||||
} sdio_cid_t;
|
||||
|
||||
typedef union
|
||||
{
|
||||
uint8_t data[16];
|
||||
struct
|
||||
{
|
||||
uint8_t reserved1 :6;
|
||||
uint8_t csd_ver :2;
|
||||
uint8_t taac;
|
||||
uint8_t nsac;
|
||||
uint8_t tran_speed;
|
||||
uint8_t ccc_high;
|
||||
uint8_t read_bl_len :4;
|
||||
uint8_t ccc_low :4;
|
||||
uint16_t c_size_high :2;
|
||||
uint8_t reserved2 :2;
|
||||
uint8_t dsr_imp :1;
|
||||
uint8_t read_blk_misalign :1;
|
||||
uint8_t write_blk_misalign :1;
|
||||
uint8_t read_bl_partial :1;
|
||||
uint8_t c_size_mid;
|
||||
uint8_t vdd_r_curr_max :3;
|
||||
uint8_t vdd_r_curr_min :3;
|
||||
uint8_t c_size_low :2;
|
||||
uint8_t c_size_mult_high :2;
|
||||
uint8_t vdd_w_cur_max :3;
|
||||
uint8_t vdd_w_curr_min :3;
|
||||
uint8_t sector_size_high :6;
|
||||
uint8_t erase_blk_en :1;
|
||||
uint8_t c_size_mult_low :1;
|
||||
uint8_t wp_grp_size :7;
|
||||
uint8_t sector_size_low :1;
|
||||
uint8_t write_bl_len_high :2;
|
||||
uint8_t r2w_factor :3;
|
||||
uint8_t reserved3 :2;
|
||||
uint8_t wp_grp_enable :1;
|
||||
uint8_t reserved4 :5;
|
||||
uint8_t write_partial :1;
|
||||
uint8_t write_bl_len_low :2;
|
||||
uint8_t reserved5 :2;
|
||||
uint8_t file_format :2;
|
||||
uint8_t tmp_write_protect :1;
|
||||
uint8_t perm_write_protect :1;
|
||||
uint8_t copy :1;
|
||||
uint8_t file_format_grp :1;
|
||||
uint8_t always1 :1;
|
||||
uint8_t crc :7;
|
||||
} __attribute__((packed)) v1;
|
||||
|
||||
struct
|
||||
{
|
||||
uint8_t reserved1 :6;
|
||||
uint8_t csd_ver :2;
|
||||
uint8_t taac;
|
||||
uint8_t nsac;
|
||||
uint8_t tran_speed;
|
||||
uint8_t ccc_high;
|
||||
uint8_t read_bl_len :4;
|
||||
uint8_t ccc_low :4;
|
||||
uint8_t reserved2 :4;
|
||||
uint8_t dsr_imp :1;
|
||||
uint8_t read_blk_misalign :1;
|
||||
uint8_t write_blk_misalign :1;
|
||||
uint8_t read_bl_partial :1;
|
||||
uint16_t c_size_high :6;
|
||||
uint8_t reserved3 :2;
|
||||
uint8_t c_size_mid;
|
||||
uint8_t c_size_low;
|
||||
uint8_t sector_size_high :6;
|
||||
uint8_t erase_blk_en :1;
|
||||
uint8_t reserved4 :1;
|
||||
uint8_t wp_grp_size :7;
|
||||
uint8_t sector_size_low :1;
|
||||
uint8_t write_bl_len_high :2;
|
||||
uint8_t r2w_factor :3;
|
||||
uint8_t reserved5 :2;
|
||||
uint8_t wp_grp_enable :1;
|
||||
uint8_t reserved6 :5;
|
||||
uint8_t write_partial :1;
|
||||
uint8_t write_bl_len_low :2;
|
||||
uint8_t reserved7 :2;
|
||||
uint8_t file_format :2;
|
||||
uint8_t tmp_write_protect :1;
|
||||
uint8_t perm_write_protect :1;
|
||||
uint8_t copy :1;
|
||||
uint8_t file_format_grp :1;
|
||||
uint8_t always1 :1;
|
||||
uint8_t crc :7;
|
||||
} __attribute__((packed)) v2;
|
||||
} sdio_csd_t;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _EXTRAS_SDIO_IMPL_H_ */
|
Loading…
Reference in a new issue