#include #include #include "spi.h" #include "sd.h" /* 512 bytes for each sector */ #define SD_SECTOR_SIZE 512 /* token for write operation */ #define TOKEN_SINGLE_BLOCK 0xFE #define TOKEN_MULTI_BLOCK 0xFC #define TOKEN_STOP_TRAN 0xFD /* Local variables */ static uint8_t CardType; static SDCFG SDCfg; static struct rt_device sdcard_device; static struct dfs_partition part; /* Local Function Prototypes */ static bool LPC17xx_SD_Init (void); static uint8_t LPC17xx_SD_SendCmd (uint8_t cmd, uint32_t arg); static bool LPC17xx_SD_ReadSector (uint32_t sector, uint8_t *buff, uint32_t count); static bool LPC17xx_SD_ReadDataBlock ( uint8_t *buff, uint32_t cnt); static bool LPC17xx_SD_WriteSector (uint32_t sector, const uint8_t *buff, uint32_t count); static bool LPC17xx_SD_WirteDataBlock (const uint8_t *buff, uint8_t token); static bool LPC17xx_SD_ReadCfg (SDCFG *cfg); static bool LPC17xx_SD_WaitForReady (void); /* wait until the card is not busy */ static bool LPC17xx_SD_WaitForReady (void) { uint8_t res; /* timeout should be large enough to make sure the write operaion can be completed. */ uint32_t timeout = 400000; LPC17xx_SPI_SendByte(0xFF); do { res = LPC17xx_SPI_RecvByte(); } while ((res != 0xFF) && timeout--); return (res == 0xFF ? true : false); } /* Initialize SD/MMC card. */ static bool LPC17xx_SD_Init (void) { uint32_t i, timeout; uint8_t cmd, ct, ocr[4]; bool ret = false; /* Initialize SPI interface and enable Flash Card SPI mode. */ LPC17xx_SPI_Init (); /* At least 74 clock cycles are required prior to starting bus communication */ for (i = 0; i < 80; i++) { /* 80 dummy clocks */ LPC17xx_SPI_SendByte (0xFF); } ct = CT_NONE; if (LPC17xx_SD_SendCmd (GO_IDLE_STATE, 0) == 0x1) { timeout = 50000; if (LPC17xx_SD_SendCmd(CMD8, 0x1AA) == 1) { /* SDHC */ /* Get trailing return value of R7 resp */ for (i = 0; i < 4; i++) ocr[i] = LPC17xx_SPI_RecvByte(); if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */ /* Wait for leaving idle state (ACMD41 with HCS bit) */ while (timeout-- && LPC17xx_SD_SendCmd(SD_SEND_OP_COND, 1UL << 30)); /* Check CCS bit in the OCR */ if (timeout && LPC17xx_SD_SendCmd(READ_OCR, 0) == 0) { for (i = 0; i < 4; i++) ocr[i] = LPC17xx_SPI_RecvByte(); ct = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; } } else { /* SDSC or MMC */ if (LPC17xx_SD_SendCmd(SD_SEND_OP_COND, 0) <= 1) { ct = CT_SD1; cmd = SD_SEND_OP_COND; /* SDSC */ } else { ct = CT_MMC; cmd = SEND_OP_COND; /* MMC */ } /* Wait for leaving idle state */ while (timeout-- && LPC17xx_SD_SendCmd(cmd, 0)); /* Set R/W block length to 512 */ if (!timeout || LPC17xx_SD_SendCmd(SET_BLOCKLEN, SD_SECTOR_SIZE) != 0) ct = CT_NONE; } } } CardType = ct; LPC17xx_SPI_Release(); if (ct) { /* Initialization succeeded */ ret = true; if ( ct == CT_MMC ) { LPC17xx_SPI_SetSpeed(SPI_SPEED_20MHz); } else { LPC17xx_SPI_SetSpeed(SPI_SPEED_20MHz); } } else { /* Initialization failed */ LPC17xx_SPI_Select (); LPC17xx_SD_WaitForReady (); LPC17xx_SPI_DeInit(); } return ret; } /***************************************************************************** Send a Command to Flash card and get a Response cmd: cmd index arg: argument for the cmd return the received response of the commond *****************************************************************************/ static uint8_t LPC17xx_SD_SendCmd (uint8_t cmd, uint32_t arg) { uint32_t r1, n; if (cmd & 0x80) { /* ACMD is the command sequence of CMD55-CMD */ cmd &= 0x7F; r1 = LPC17xx_SD_SendCmd(APP_CMD, 0); /* CMD55 */ if (r1 > 1) return r1; /* cmd send failed */ } /* Select the card and wait for ready */ LPC17xx_SPI_DeSelect(); LPC17xx_SPI_Select(); if (LPC17xx_SD_WaitForReady() == false ) return 0xFF; LPC17xx_SPI_SendByte (0xFF); /* prepare 8 clocks */ LPC17xx_SPI_SendByte (cmd); LPC17xx_SPI_SendByte (arg >> 24); LPC17xx_SPI_SendByte (arg >> 16); LPC17xx_SPI_SendByte (arg >> 8); LPC17xx_SPI_SendByte (arg); /* Checksum, should only be valid for the first command.CMD0 */ n = 0x01; /* Dummy CRC + Stop */ if (cmd == GO_IDLE_STATE) n = 0x95; /* Valid CRC for CMD0(0) */ if (cmd == CMD8) n = 0x87; /* Valid CRC for CMD8(0x1AA) */ LPC17xx_SPI_SendByte(n); if (cmd == STOP_TRAN) LPC17xx_SPI_RecvByte (); /* Skip a stuff byte when stop reading */ n = 10; /* Wait for a valid response in timeout of 10 attempts */ do { r1 = LPC17xx_SPI_RecvByte (); } while ((r1 & 0x80) && --n); return (r1); /* Return with the response value */ } /***************************************************************************** Read "count" Sector(s) starting from sector index "sector", buff <- [sector, sector+1, ... sector+count-1] if success, return true, otherwise return false *****************************************************************************/ static bool LPC17xx_SD_ReadSector (uint32_t sector, uint8_t *buff, uint32_t count) { /* Convert to byte address if needed */ if (!(CardType & CT_BLOCK)) sector *= SD_SECTOR_SIZE; if (count == 1) { /* Single block read */ if ((LPC17xx_SD_SendCmd(READ_BLOCK, sector) == 0) && LPC17xx_SD_ReadDataBlock(buff, SD_SECTOR_SIZE)) count = 0; } else { /* Multiple block read */ if (LPC17xx_SD_SendCmd(READ_MULT_BLOCK, sector) == 0) { do { if (!LPC17xx_SD_ReadDataBlock(buff, SD_SECTOR_SIZE)) break; buff += SD_SECTOR_SIZE; } while (--count); LPC17xx_SD_SendCmd(STOP_TRAN, 0); /* STOP_TRANSMISSION */ } } LPC17xx_SPI_Release(); return count ? false : true; } /***************************************************************************** read specified number of data to specified buffer. buff: Data buffer to store received data cnt: Byte count (must be multiple of 4, normally 512) *****************************************************************************/ static bool LPC17xx_SD_ReadDataBlock ( uint8_t *buff, uint32_t cnt) { uint8_t token; uint32_t timeout; timeout = 20000; do { /* Wait for data packet in timeout of 100ms */ token = LPC17xx_SPI_RecvByte(); } while ((token == 0xFF) && timeout--); if(token != 0xFE) return false; /* If not valid data token, return with error */ #if USE_FIFO LPC17xx_SPI_RecvBlock_FIFO (buff, cnt); #else do { /* Receive the data block into buffer */ *buff++ = LPC17xx_SPI_RecvByte (); *buff++ = LPC17xx_SPI_RecvByte (); *buff++ = LPC17xx_SPI_RecvByte (); *buff++ = LPC17xx_SPI_RecvByte (); } while (cnt -= 4); #endif /* USE_FIFO */ LPC17xx_SPI_RecvByte (); /* Discard CRC */ LPC17xx_SPI_RecvByte (); return true; /* Return with success */ } /***************************************************************************** Write "count" Sector(s) starting from sector index "sector", buff -> [sector, sector+1, ... sector+count-1] if success, return true, otherwise return false *****************************************************************************/ static bool LPC17xx_SD_WriteSector (uint32_t sector, const uint8_t *buff, uint32_t count) { if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert to byte address if needed */ if (count == 1) { /* Single block write */ if ((LPC17xx_SD_SendCmd(WRITE_BLOCK, sector) == 0) && LPC17xx_SD_WirteDataBlock(buff, TOKEN_SINGLE_BLOCK)) count = 0; } else { /* Multiple block write */ if (CardType & CT_SDC) LPC17xx_SD_SendCmd(SET_WR_BLK_ERASE_COUNT, count); if (LPC17xx_SD_SendCmd(WRITE_MULT_BLOCK, sector) == 0) { do { if (!LPC17xx_SD_WirteDataBlock(buff, TOKEN_MULTI_BLOCK)) break; buff += 512; } while (--count); #if 1 if (!LPC17xx_SD_WirteDataBlock(0, TOKEN_STOP_TRAN)) /* STOP_TRAN token */ count = 1; #else LPC17xx_SPI_SendByte(TOKEN_STOP_TRAN); #endif } } LPC17xx_SPI_Release(); return count ? false : true; } /***************************************************************************** Write 512 bytes buffer: 512 byte data block to be transmitted token: 0xFE -> single block 0xFC -> multi block 0xFD -> Stop *****************************************************************************/ static bool LPC17xx_SD_WirteDataBlock (const uint8_t *buff, uint8_t token) { uint8_t resp, i; i = i; // avoid warning LPC17xx_SPI_SendByte (token); /* send data token first*/ if (token != TOKEN_STOP_TRAN) { #if USE_FIFO LPC17xx_SPI_SendBlock_FIFO (buff); #else /* Send data. */ for (i = 512/4; i ; i--) { LPC17xx_SPI_SendByte (*buff++); LPC17xx_SPI_SendByte (*buff++); LPC17xx_SPI_SendByte (*buff++); LPC17xx_SPI_SendByte (*buff++); } #endif /* USE_FIFO */ LPC17xx_SPI_SendByte(0xFF); /* 16-bit CRC (Dummy) */ LPC17xx_SPI_SendByte(0xFF); resp = LPC17xx_SPI_RecvByte(); /* Receive data response */ if ((resp & 0x1F) != 0x05) /* If not accepted, return with error */ return false; if ( LPC17xx_SD_WaitForReady() == false) /* Wait while Flash Card is busy. */ return false; } return true; } /* Read MMC/SD Card device configuration. */ static bool LPC17xx_SD_ReadCfg (SDCFG *cfg) { uint8_t i; uint16_t csize; uint8_t n, csd[16]; bool retv = false; /* Read the OCR - Operations Condition Register. */ if (LPC17xx_SD_SendCmd (READ_OCR, 0) != 0x00) goto x; for (i = 0; i < 4; i++) cfg->ocr[i] = LPC17xx_SPI_RecvByte (); /* Read the CID - Card Identification. */ if ((LPC17xx_SD_SendCmd (SEND_CID, 0) != 0x00) || (LPC17xx_SD_ReadDataBlock (cfg->cid, 16) == false)) goto x; /* Read the CSD - Card Specific Data. */ if ((LPC17xx_SD_SendCmd (SEND_CSD, 0) != 0x00) || (LPC17xx_SD_ReadDataBlock (cfg->csd, 16) == false)) goto x; cfg -> sectorsize = SD_SECTOR_SIZE; /* Get number of sectors on the disk (DWORD) */ if ((cfg->csd[0] >> 6) == 1) { /* SDC ver 2.00 */ csize = cfg->csd[9] + ((uint16_t)cfg->csd[8] << 8) + 1; cfg -> sectorcnt = (uint32_t)csize << 10; } else { /* SDC ver 1.XX or MMC*/ n = (cfg->csd[5] & 15) + ((cfg->csd[10] & 128) >> 7) + ((cfg->csd[9] & 3) << 1) + 2; // 19 csize = (cfg->csd[8] >> 6) + ((uint16_t)cfg->csd[7] << 2) + ((uint16_t)(cfg->csd[6] & 3) << 10) + 1; // 3752 cfg -> sectorcnt = (uint32_t)csize << (n - 9); // 3842048 } cfg->size = cfg -> sectorcnt * cfg -> sectorsize; // 512*3842048=1967128576Byte (1.83GB) /* Get erase block size in unit of sector (DWORD) */ if (CardType & CT_SD2) { /* SDC ver 2.00 */ if (LPC17xx_SD_SendCmd(SD_STATUS /*ACMD13*/, 0) == 0) { /* Read SD status */ LPC17xx_SPI_RecvByte(); if (LPC17xx_SD_ReadDataBlock(csd, 16)) { /* Read partial block */ for (n = 64 - 16; n; n--) LPC17xx_SPI_RecvByte(); /* Purge trailing data */ cfg->blocksize = 16UL << (csd[10] >> 4); retv = true; } } } else { /* SDC ver 1.XX or MMC */ if ((LPC17xx_SD_SendCmd(SEND_CSD, 0) == 0) && LPC17xx_SD_ReadDataBlock(csd, 16)) { /* Read CSD */ if (CardType & CT_SD1) { /* SDC ver 1.XX */ cfg->blocksize = (((csd[10] & 63) << 1) + ((uint16_t)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1); } else { /* MMC */ // cfg->blocksize = ((uint16_t)((buf[10] & 124) >> 2) + 1) * (((buf[11] & 3) << 3) + ((buf[11] & 224) >> 5) + 1); cfg->blocksize = ((uint16_t)((cfg->csd[10] & 124) >> 2) + 1) * (((cfg->csd[10] & 3) << 3) + ((cfg->csd[11] & 224) >> 5) + 1); } retv = true; } } x: LPC17xx_SPI_Release(); return (retv); } static rt_err_t rt_sdcard_init(rt_device_t dev) { return RT_EOK; } static rt_err_t rt_sdcard_open(rt_device_t dev, rt_uint16_t oflag) { return RT_EOK; } static rt_err_t rt_sdcard_close(rt_device_t dev) { return RT_EOK; } static rt_size_t rt_sdcard_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size) { bool status; rt_uint32_t nr = size / SD_SECTOR_SIZE; status = LPC17xx_SD_ReadSector(part.offset * SECTOR_SIZE + pos, buffer, nr); if (status == true) return nr * SECTOR_SIZE; rt_kprintf("read failed: %d, buffer 0x%08x\n", status, buffer); return 0; } static rt_size_t rt_sdcard_write (rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size) { bool status; rt_uint32_t nr = size / SD_SECTOR_SIZE; status = LPC17xx_SD_WriteSector(part.offset * SECTOR_SIZE + pos, buffer, nr); if (status == true) return nr * SECTOR_SIZE; rt_kprintf("write failed: %d, buffer 0x%08x\n", status, buffer); return 0; } static rt_err_t rt_sdcard_control(rt_device_t dev, rt_uint8_t cmd, void *args) { return RT_EOK; } void rt_hw_sdcard_init() { if (LPC17xx_SD_Init() && LPC17xx_SD_ReadCfg(&SDCfg)) { bool status; rt_uint8_t *sector; /* get the first sector to read partition table */ sector = (rt_uint8_t*) rt_malloc (512); if (sector == RT_NULL) { rt_kprintf("allocate partition sector buffer failed\n"); return; } status = LPC17xx_SD_ReadSector(0, sector, 512); if (status == true) { /* get the first partition */ if (dfs_filesystem_get_partition(&part, sector, 0) != 0) { /* there is no partition */ part.offset = 0; part.size = 0; } } else { /* there is no partition table */ part.offset = 0; part.size = 0; } /* release sector buffer */ rt_free(sector); /* register sdcard device */ sdcard_device.type = RT_Device_Class_Block; sdcard_device.init = rt_sdcard_init; sdcard_device.open = rt_sdcard_open; sdcard_device.close = rt_sdcard_close; sdcard_device.read = rt_sdcard_read; sdcard_device.write = rt_sdcard_write; sdcard_device.control = rt_sdcard_control; /* no private */ sdcard_device.private = &SDCfg; rt_device_register(&sdcard_device, "sd0", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE | RT_DEVICE_FLAG_STANDALONE); return; } rt_kprintf("sdcard init failed\n"); }