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…
	
	Add table
		Add a link
		
	
		Reference in a new issue