#pragma GCC optimize ("O2") /* (c) 2017 night_ghost@ykoctpa.ru SD card and Dataflash low-level driver */ #include "sd.h" #include "../diskio.h" #include "../ff.h" #include #include #include #define CS_HIGH() spi_chipSelectHigh() #define CS_LOW() spi_chipSelectLow(1) #define MMC_CD spi_detect() /* Card detect (yes:true, no:false, default:true) */ #define MMC_WP 0 /* Write protected (yes:true, no:false, default:false) */ /*-------------------------------------------------------------------------- Module Private Functions ---------------------------------------------------------------------------*/ /* MMC/SD command */ #define CMD0 (0) /* GO_IDLE_STATE */ #define CMD1 (1) /* SEND_OP_COND (MMC) */ #define ACMD41 (0x80+41) /* SEND_OP_COND (SDC) */ #define CMD8 (8) /* SEND_IF_COND */ #define CMD9 (9) /* SEND_CSD */ #define CMD10 (10) /* SEND_CID */ #define CMD12 (12) /* STOP_TRANSMISSION */ #define CMD13 (13) /* Get_STATUS */ #define ACMD13 (0x80+13) /* SD_STATUS (SDC) */ #define CMD16 (16) /* SET_BLOCKLEN */ #define CMD17 (17) /* READ_SINGLE_BLOCK */ #define CMD18 (18) /* READ_MULTIPLE_BLOCK */ #define CMD23 (23) /* SET_BLOCK_COUNT (MMC) */ #define ACMD23 (0x80+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */ #define CMD24 (24) /* WRITE_BLOCK */ #define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */ #define CMD32 (32) /* ERASE_ER_BLK_START */ #define CMD33 (33) /* ERASE_ER_BLK_END */ #define CMD38 (38) /* ERASE */ #define CMD55 (55) /* APP_CMD */ #define CMD58 (58) /* READ_OCR */ static inline uint32_t micros() { return timer_get_count32(TIMER5); } static inline uint32_t millis() { return systick_uptime(); } typedef uint8_t (*spi_WaitFunc)(uint8_t b); // utility function extern uint8_t spi_spiSend(uint8_t b); extern uint8_t spi_spiRecv(void); extern uint8_t spi_spiXchg(uint8_t b); extern void spi_spiTransfer(const uint8_t *send, uint32_t send_len, uint8_t *recv, uint32_t recv_len); extern void spi_chipSelectHigh(void); extern bool spi_chipSelectLow(bool take_sem); extern void spi_yield(); extern uint8_t spi_waitFor(uint8_t out, spi_WaitFunc cb, uint32_t dly); extern uint8_t spi_detect(); extern uint32_t get_fattime (); static volatile DSTATUS Stat = STA_NOINIT; /* Physical drive status */ static volatile uint16_t Timer1, Timer2; /* 1kHz decrement timer stopped at zero (disk_timerproc()) */ static uint32_t sd_max_sectors=0; extern int printf(const char *msg, ...); #if defined(BOARD_SDCARD_CS_PIN) #define WAIT_IN_ISR static uint8_t CardType; /* Card type flags */ static uint8_t no_CMD13 = 0; static uint8_t csd[16]; // for DMA reads static int8_t xmit_datablock(const uint8_t *buff, uint8_t token); uint8_t sd_get_type() { return CardType; } /*-----------------------------------------------------------------------*/ /* SPI controls (Platform dependent) */ /*-----------------------------------------------------------------------*/ /* Initialize MMC interface */ /* Exchange a byte */ static inline uint8_t xchg_spi ( uint8_t dat /* Data to send */ ) { return spi_spiXchg(dat); } static inline uint8_t rcvr_spi(){ return xchg_spi(0xFF); } /* Receive multiple byte */ static inline void rcvr_spi_multi ( uint8_t *buff, /* Pointer to data buffer */ uint16_t btr /* Number of bytes to receive (even number) */ ){ spi_spiTransfer(NULL,0, buff, btr); } /* Send multiple byte */ static inline void xmit_spi_multi ( const uint8_t *buff, /* Pointer to the data */ uint16_t btx /* Number of bytes to send (even number) */ ) { spi_spiTransfer(buff, btx, NULL, 0); } /* void power_on(void){ // Power on the SD-Card Socket if hardware allows } void power_off(void){ } */ /*-----------------------------------------------------------------------*/ /* Wait for card ready */ /*-----------------------------------------------------------------------*/ // TODO: remove all active waiting by transferring check to callback static uint8_t wait_ff(uint8_t b){ return b==0xff; } static int wait_ready ( /* 1:Ready, 0:Timeout */ uint16_t wt /* Timeout [ms] */ ) { uint8_t d; #if defined(WAIT_IN_ISR) d=spi_waitFor(0xFF,wait_ff,wt*1000); return d == 0xFF; #else Timer2 = wt; do { d = xchg_spi(0xFF); spi_yield(); /* This loop takes a time. Insert rot_rdq() here for multitask envilonment. */ } while (d != 0xFF && Timer2); /* Wait for card goes ready or timeout */ if(d == 0xFF) { return 1; } return 0; #endif } /*-----------------------------------------------------------------------*/ /* Deselect card and release SPI */ /*-----------------------------------------------------------------------*/ static void deselect_cs (void) { CS_HIGH(); /* Set CS# high */ xchg_spi(0xFF); /* Dummy clock (force DO hi-z for multiple slave SPI) */ } /*-----------------------------------------------------------------------*/ /* Select card and wait for ready */ /*-----------------------------------------------------------------------*/ static int select_cs (void) /* 1:OK, 0:Timeout */ { if(!CS_LOW()) { /* take semaphore and Set CS# low */ return 0; } xchg_spi(0xFF); /* Dummy clock (force DO enabled) */ if (wait_ready(500)) return 1; /* Wait for card ready */ deselect_cs(); return 0; /* Timeout */ } /*-----------------------------------------------------------------------*/ /* Receive a data packet from the MMC */ /*-----------------------------------------------------------------------*/ static uint8_t wait_noFF(uint8_t b){ return b!=0xff; } static int8_t rcvr_datablock ( /* 1:OK, 0:Error */ uint8_t *buff, /* Data buffer */ uint16_t btr /* Data block length (byte) */ ) { uint8_t ret=0; #if defined(WAIT_IN_ISR) ret=spi_waitFor(0xff, wait_noFF, 200000); if(ret != 0xFE) goto done; // Function fails if invalid DataStart token or timeout #else uint8_t token; Timer1 = 200; do { /* Wait for DataStart token in timeout of 200ms */ token = xchg_spi(0xFF); spi_yield(); /* This loop will take a time. Insert rot_rdq() here for multitask environment. */ } while ((token == 0xFF) && Timer1); if(token != 0xFE) { goto done; /* Function fails if invalid DataStart token or timeout */ } #endif rcvr_spi_multi(buff, btr); /* Store trailing data to the buffer */ xchg_spi(0xFF); xchg_spi(0xFF); /* Discard CRC */ ret=1; /* Function succeeded */ done: return ret; } /*-----------------------------------------------------------------------*/ /* Send a data packet to the MMC */ /*-----------------------------------------------------------------------*/ static int8_t xmit_datablock ( /* 1:OK, 0:Failed */ const uint8_t *buff, /* Ponter to 512 byte data to be sent */ uint8_t token /* Token */ ) { uint8_t resp; if (!wait_ready(500)) return 0; /* Wait for card ready */ xchg_spi(token); /* Send token */ if (token != 0xFD) { /* Send data if token is other than StopTran */ xmit_spi_multi(buff, 512); /* Data */ xchg_spi(0xFF); xchg_spi(0xFF); /* Dummy CRC */ resp = xchg_spi(0xFF); /* Receive data resp */ if ((resp & 0x1F) != 0x05) { return 0; /* Function fails if the data packet was not accepted */ } } return 1; } /*-----------------------------------------------------------------------*/ /* Send a command packet to the MMC */ /*-----------------------------------------------------------------------*/ static uint8_t buf[6]; // to use DMA static uint8_t wait_0x80(uint8_t b){ return (b & 0x80)==0; } static uint8_t send_cmd ( /* Return value: R1 resp (bit7==1:Failed to send) */ uint8_t cmd, /* Command index */ uint32_t arg /* Argument */ ) { uint8_t crc, res; if (cmd & 0x80) { /* Send a CMD55 prior to ACMD */ cmd &= 0x7F; res = send_cmd(CMD55, 0); if (res > 1) { return res; } } /* Select the card and wait for ready except to stop multiple block read */ if (cmd != CMD12) { deselect_cs(); // spi_yield(); // sync quant so no interrupts when receiving answer if (!select_cs()) { return 0xFF; } } crc = 0x01; /* Dummy CRC + Stop */ if (cmd == CMD0) crc = 0x95; /* Valid CRC for CMD0(0) */ if (cmd == CMD8) crc = 0x87; /* Valid CRC for CMD8(0x1AA) */ /* Send command packet */ buf[0] = 0x40 | cmd; /* Start + command index */ buf[1] = (uint8_t)(arg >> 24); /* Argument[31..24] */ buf[2] = (uint8_t)(arg >> 16); /* Argument[23..16] */ buf[3] = (uint8_t)(arg >> 8); /* Argument[15..8] */ buf[4] = (uint8_t)arg; /* Argument[7..0] */ buf[5] = crc; /* CRC + Stop */ xmit_spi_multi(buf, 6); // entire command in one packet /* Receive command resp */ if (cmd == CMD12) xchg_spi(0xFF); /* Discard following one byte when CMD12 */ #if defined(WAIT_IN_ISR) res=spi_waitFor(0xff, wait_0x80, 20000); return res; #else uint8_t n = 255; /* Wait for response (10 bytes max) */ do { res = xchg_spi(0xFF); spi_yield(); /* This loop will take a time. Insert rot_rdq() here for multitask environment. */ } while ((res & 0x80) && --n); return res; /* Return received response */ #endif } /*-------------------------------------------------------------------------- Public Functions ---------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/ /* Initialize disk drive */ /*-----------------------------------------------------------------------*/ DSTATUS sd_initialize() { uint8_t n, cmd, ty, ocr[4] ; if (Stat & STA_NODISK) return Stat; /* Is card existing in the soket? */ if(!(Stat & STA_NOINIT) ) return Stat; // already done ty = 0; n = send_cmd(CMD0, 0); if (n == 1 || n==0) { /* Put the card SPI/Idle state */ Timer1 = 5000; n=send_cmd(CMD8, 0x1AA); /* Initialization timeout = 5 sec */ if((n&4) == 0) rcvr_spi_multi(ocr, 4); /* Get 32 bit return value of R7 resp if not illegal command */ if (n == 1) { /* SDv2? */ if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* Is the card supports vcc of 2.7-3.6V? and did echoing */ while(Timer1){ /* Wait for end of initialization with ACMD41(HCS) */ n = send_cmd(ACMD41, 1UL << 30); if(n==0) break; } if (Timer1){ n = send_cmd(CMD58, 0); if((n&4) == 0) rcvr_spi_multi(ocr, 4); /* Get 32 bit return value of R7 resp if not illegal command */ if( n <= 1) { /* Check CCS bit in the OCR */ ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* Card id SDv2 */ } else { printf("\nSD CMD58 failed!\n"); } }else{ printf("\nSD timeout!\n"); } } } else { /* Not SDv2 card */ if (send_cmd(ACMD41, 0) <= 1) { /* SDv1 or MMC? */ ty = CT_SD1; cmd = ACMD41; /* SDv1 (ACMD41(0)) */ } else { ty = CT_MMC; cmd = CMD1; /* MMCv3 (CMD1(0)) */ } while(Timer1) { /* Wait for end of initialization */ n=send_cmd(cmd, 0); if(n==0) break; } if (Timer1){ if(send_cmd(CMD16, 512) != 0) { /* Set block length: 512 */ ty = 0; printf("\nSD CMD16 failed!\n"); } } else { ty = 0; printf("\nSD timeout!\n"); } } } else { ty=0; } CardType = ty; /* Card type */ if (ty) { /* OK */ Stat &= ~STA_NOINIT; /* Clear STA_NOINIT flag */ uint8_t i; for(i=0;i<15;i++){ // 15 tries to get size sd_getSectorCount(&sd_max_sectors); if(sd_max_sectors!=0) break; } } else { /* Failed */ Stat = STA_NOINIT; } deselect_cs(); return Stat; } static uint8_t restart_card(){ Stat = STA_NOINIT; sd_initialize(); if(!(Stat & STA_NOINIT) ) return true; // OK return false; } uint8_t sd_getSectorCount(uint32_t *ptr){ uint32_t csize; if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */ csize = csd[9] + ((uint16_t)csd[8] << 8) + ((uint32_t)(csd[7] & 63) << 16) + 1; *ptr = csize << 10; } else { /* SDC ver 1.XX or MMC ver 3 */ uint8_t n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2; csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1; *ptr = csize << (n - 9); } return RES_OK; } return RES_ERROR; } /*-----------------------------------------------------------------------*/ /* Get disk status */ /*-----------------------------------------------------------------------*/ DSTATUS sd_status (){ return Stat; /* Return disk status */ } uint8_t sd_get_state(){ if(!no_CMD13) { if(send_cmd(CMD13, 0)<=1){ uint8_t ret = xchg_spi(0xFF); return ret; } no_CMD13=1; } return 0; // CMD13 not supported so all OK /* 7 - out of range / CSD overwrite 6 • Erase param: An invalid selection for erase, sectors or groups. 5 • Write protect violation: The command tried to write a write-protected block. 4 • Card ECC failed: Card internal ECC was applied but failed to correct the data. 3 • CC error: Internal card controller error. 2 • Error: A general or an unknown error occurred during the operation. 1 • Write protect erase skip | lock/unlock command failed: This status bit has two functions over- loaded. It is set when the host attempts to erase a write-protected sector or makes a sequence or password errors during card lock/unlock operation. 0 • Card is locked: Set when the card is locked by the user. Reset when it is unlocked. */ } /*-----------------------------------------------------------------------*/ /* Read sector(s) */ /*-----------------------------------------------------------------------*/ static bool single_sector_card=false; DRESULT sd_read ( uint8_t *buff, /* Pointer to the data buffer to store read data */ uint32_t sector, /* Start sector number (LBA) */ uint16_t count /* Number of sectors to read (1..128) */ ) { uint8_t ret; uint16_t sectorInc=1; if (!count) return RES_PARERR; /* Check parameter */ if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */ if(sd_max_sectors && sector > sd_max_sectors) return RES_PARERR; /* Check parameter */ if (!(CardType & CT_BLOCK)) { sectorInc = 512; sector *= sectorInc; /* LBA to BA conversion (byte addressing cards) */ } if (count == 1) { /* Single sector read */ if (( (ret=send_cmd(CMD17, sector)) == 0) /* READ_SINGLE_BLOCK */ && rcvr_datablock(buff, 512)) { count = 0; } } else { /* Multiple sector read */ if(single_sector_card) { do { if ((ret=send_cmd(CMD17, sector)) == 0){ /* READ_SINGLE_BLOCK */ if(rcvr_datablock(buff, 512)) { buff += 512; sector += sectorInc; } else { break; } } else { break; } deselect_cs(); spi_yield(); } while(--count); } else { // send_cmd(CMD23, count); if (CardType & CT_SDC) send_cmd(ACMD23, count); /* Predefine number of sectors */ uint16_t got=0; if ((ret=send_cmd(CMD18, sector)) == 0) { /* READ_MULTIPLE_BLOCK */ do { if (!rcvr_datablock(buff, 512)) { break; } got++; buff += 512; } while (--count); if(got) send_cmd(CMD12, 0); /* STOP_TRANSMISSION */ } if(count) { // readed only 1 block or none single_sector_card=true; sector+=got*sectorInc; restart_card(); return sd_read(buff, sector/sectorInc, count); // read remaining in single sector mode } } } deselect_cs(); if(count==0) return RES_OK; /* Return result */ Stat = STA_NOINIT; return RES_ERROR; } /*-----------------------------------------------------------------------*/ /* Write sector(s) */ /*-----------------------------------------------------------------------*/ DRESULT sd_write ( const uint8_t *buff, /* Ponter to the data to write */ uint32_t sector, /* Start sector number (LBA) */ uint16_t count /* Number of sectors to write (1..128) */ ) { if (!count) return RES_PARERR; /* Check parameter */ if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check drive status */ if (Stat & STA_PROTECT) return RES_WRPRT; /* Check write protect */ if(sd_max_sectors && sector > sd_max_sectors) return RES_PARERR; /* Check parameter */ uint16_t sectorInc = 1; if (!(CardType & CT_BLOCK)){ sectorInc = 512; sector *= sectorInc; /* LBA ==> BA conversion (byte addressing cards) */ } if (count == 1) { /* Single sector write */ if (send_cmd(CMD24, sector) == 0) { /* WRITE_BLOCK */ if( xmit_datablock(buff, 0xFE)) { count = 0; } } } else { if(single_sector_card) { do { if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */ && xmit_datablock(buff, 0xFE)) { sector+=sectorInc; buff += 512; } else break; // error } while(--count); } else { /* Multiple sector write */ if (CardType & CT_SDC) send_cmd(ACMD23, count); /* Predefine number of sectors */ uint32_t sent=0; if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */ do { if (!xmit_datablock(buff, 0xFC)) break; buff += 512; sent++; } while (--count); if (!xmit_datablock(0, 0xFD)) count = 1; /* STOP_TRAN token */ } if(count) { single_sector_card=true; sector+=sent*sectorInc; restart_card(); return sd_write(buff, sector/sectorInc, count); // write remaining in single sector mode } } } if(count) { deselect_cs(); Stat = STA_NOINIT; return RES_ERROR; } uint8_t ret = sd_get_state(); // reset errors deselect_cs(); if(ret) return RES_ERROR; return RES_OK; /* Return result */ } /*-----------------------------------------------------------------------*/ /* Miscellaneous drive controls other than data read/write */ /*-----------------------------------------------------------------------*/ DRESULT sd_ioctl ( uint8_t cmd, /* Control command code */ void *buff /* Pointer to the conrtol data */ ) { DRESULT res; uint8_t n; uint32_t *dp, st, ed; uint8_t *ptr = (uint8_t *)buff; res = RES_ERROR; if (cmd== CTRL_POWER){ switch (*ptr) { case 0: // Sub control code == 0 (POWER_OFF) // if (SD_PRESENT()) // power_off(); // Power off res = RES_OK; break; case 1: // Sub control code == 1 (POWER_ON) // power_on(); // Power on res = RES_OK; break; case 2: // Sub control code == 2 (POWER_GET) *(ptr + 1) = (uint8_t) MMC_CD; break; default: res = RES_PARERR; break; } } else { if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */ switch (cmd) { case CTRL_SYNC : /* Wait for end of internal write process of the drive */ if (select_cs()) res = RES_OK; break; case GET_SECTOR_COUNT : /* Get drive capacity in unit of sector (uint32_t) */ res=sd_getSectorCount((uint32_t*)buff); break; case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (uint32_t) */ if (CardType & CT_SD2) { /* SDC ver 2.00 */ if (send_cmd(ACMD13, 0) == 0) { /* Read SD status */ xchg_spi(0xFF); if (rcvr_datablock(csd, 16)) { /* Read partial block */ for (n = 64 - 16; n; n--) xchg_spi(0xFF); /* Purge trailing data */ *(uint32_t*)buff = 16UL << (csd[10] >> 4); res = RES_OK; } } } else { /* SDC ver 1.XX or MMC */ if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { /* Read CSD */ if (CardType & CT_SD1) { /* SDC ver 1.XX */ *(uint32_t*)buff = (((csd[10] & 63) << 1) + ((uint16_t)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1); } else { /* MMC */ *(uint32_t*)buff = ((uint16_t)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1); } res = RES_OK; } } break; case CTRL_TRIM : /* Erase a block of sectors (used when _USE_ERASE == 1) */ if (!(CardType & CT_SDC)) break; /* Check if the card is SDC */ if (sd_ioctl(MMC_GET_CSD, csd)) break; /* Get CSD */ if (!(csd[0] >> 6) && !(csd[10] & 0x40)) break; /* Check if sector erase can be applied to the card */ dp = buff; st = dp[0]; ed = dp[1]; /* Load sector block */ if (!(CardType & CT_BLOCK)) { st *= 512; ed *= 512; } if (send_cmd(CMD32, st) == 0 && send_cmd(CMD33, ed) == 0 && send_cmd(CMD38, 0) == 0 && wait_ready(30000)) { /* Erase sector block */ res = RES_OK; /* FatFs does not check result of this command */ } break; /* case MMC_GET_TYPE: // Get card type flags (1 byte) *ptr = g_card_type; res = RES_OK; break; */ case MMC_GET_CSD: /* Receive CSD as a data block (16 bytes) */ if (send_cmd(CMD9, 0) == 0 /* READ_CSD */ && rcvr_datablock(ptr, 16)) res = RES_OK; break; case MMC_GET_CID: /* Receive CID as a data block (16 bytes) */ if (send_cmd(CMD10, 0) == 0 /* READ_CID */ && rcvr_datablock(ptr, 16)){ /* sdcard.manufacturerID = cid[0]; sdcard.oemID = (cid[1] << 8) | cid[2]; sdcard.productName[0] = cid[3]; sdcard.productName[1] = cid[4]; sdcard.productName[2] = cid[5]; sdcard.productName[3] = cid[6]; sdcard.productName[4] = cid[7]; sdcard.productRevisionMajor = cid[8] >> 4; sdcard.productRevisionMinor = cid[8] & 0x0F; sdcard.productSerial = (cid[9] << 24) | (cid[10] << 16) | (cid[11] << 8) | cid[12]; sdcard.productionYear = (((cid[13] & 0x0F) << 4) | (cid[14] >> 4)) + 2000; sdcard.productionMonth = cid[14] & 0x0F; */ res = RES_OK; } break; case MMC_GET_OCR: /* Receive OCR as an R3 resp (4 bytes) */ if (send_cmd(CMD58, 0) == 0) { /* READ_OCR */ // for (n = 4; n; n--) *ptr++ = rcvr_spi(); rcvr_spi_multi(ptr, 4); res = RES_OK; } break; case MMC_GET_SDSTAT: /* Receive SD status as a data block (64 bytes) */ if (send_cmd(ACMD13, 0) == 0) { /* SD_STATUS */ rcvr_spi(); if (rcvr_datablock(ptr, 64)) res = RES_OK; } break; default: res = RES_PARERR; } deselect_cs(); } return res; } /*-----------------------------------------------------------------------*/ /* Device timer function */ /*-----------------------------------------------------------------------*/ /* This function must be called from timer interrupt routine in period / of 1 ms to generate card control timing. */ void sd_timerproc (void) { uint16_t n; uint8_t s; n = Timer1; /* 1kHz decrement timer stopped at 0 */ if (n) Timer1 = --n; n = Timer2; if (n) Timer2 = --n; s = Stat; if (MMC_WP) { /* Write protected */ s |= STA_PROTECT; } else { /* Write enabled */ s &= ~STA_PROTECT; } if (MMC_CD) { /* Card is in socket */ s &= ~STA_NODISK; } else { /* Socket empty */ s |= (STA_NODISK | STA_NOINIT); } Stat = s; } #elif defined(BOARD_DATAFLASH_FATFS) // emulate SD card on board's dataflash // flash size #define DF_NUM_PAGES 0x1f00 #define DF_PAGE_SIZE 256L #define DF_RESET BOARD_DATAFLASH_CS_PIN //Winbond M25P16 Serial Flash Embedded Memory 16 Mb, 3V #define JEDEC_WRITE_ENABLE 0x06 #define JEDEC_WRITE_DISABLE 0x04 #define JEDEC_READ_STATUS 0x05 #define JEDEC_WRITE_STATUS 0x01 #define JEDEC_READ_DATA 0x03 #define JEDEC_FAST_READ 0x0b #define JEDEC_DEVICE_ID 0x9F #define JEDEC_PAGE_WRITE 0x02 #define JEDEC_WREAR 0xC5 #define JEDEC_ENTER_4B_MODE 0xB7 #define JEDEC_BULK_ERASE 0xC7 // full chip erase #define JEDEC_SECTOR_ERASE 0x20 // 4k erase #define JEDEC_PAGE_ERASE 0xD8 // 64K erase #define JEDEC_STATUS_BUSY 0x01 #define JEDEC_STATUS_WRITEPROTECT 0x02 #define JEDEC_STATUS_BP0 0x04 #define JEDEC_STATUS_BP1 0x08 #define JEDEC_STATUS_BP2 0x10 #define JEDEC_STATUS_TP 0x20 #define JEDEC_STATUS_SEC 0x40 #define JEDEC_STATUS_SRP0 0x80 //#define expect_memorytype 0x20 //#define expect_capacity 0x15 #define MAX_ERASE_SIZE 16384 static bool flash_died=false; static uint8_t df_manufacturer; static uint16_t df_device; static uint32_t erase_size = BOARD_DATAFLASH_ERASE_SIZE; static uint8_t addr_cmd_length = 4; static uint8_t addr_cmd_start = 1; static bool addr_4bit = 0; #if BOARD_DATAFLASH_ERASE_SIZE >= 65536 static uint8_t erase_cmd=JEDEC_PAGE_ERASE; #else static uint8_t erase_cmd=JEDEC_SECTOR_ERASE; #endif #pragma pack(push,1) static struct { uint8_t cmd[5]; // for DMA transfer uint8_t sector[DF_PAGE_SIZE]; // we ALWAYS can use DMA! } buf; #pragma pack(pop) //[ task management for USB MSC mode void hal_set_task_active(void * handle); void hal_context_switch_isr(); void *hal_register_task(voidFuncPtr task, uint32_t stack); void hal_set_task_priority(void * handle, uint8_t prio); bool hal_is_armed(); //] #define FLASH_ERASE_QUEUE_SIZE 16 static void *_flash_erase_task IN_CCM; static volatile uint16_t fe_read_ptr IN_CCM; static volatile uint16_t fe_write_ptr IN_CCM; typedef struct { uint32_t b; uint32_t e; } fe_item; static fe_item flash_erase_queue[FLASH_ERASE_QUEUE_SIZE] IN_CCM; static void * _flash_erase_task IN_CCM; static void sd_flash_eraser(); static bool chip_is_clear=false; uint8_t sd_get_type() { return 0; } /*-----------------------------------------------------------------------*/ /* SPI controls (Platform dependent) */ /*-----------------------------------------------------------------------*/ /* Initialize MMC interface */ /* Exchange a byte */ static inline uint8_t spi_write( uint8_t dat /* Data to send */ ) { return spi_spiXchg(dat); } static inline uint8_t spi_read(){ return spi_spiXchg(0xFF); } /* Receive multiple byte */ static inline void read_spi_multi ( uint8_t *buff, /* Pointer to data buffer */ uint16_t btr /* Number of bytes to receive (even number) */ ){ spi_spiTransfer(NULL,0, buff, btr); } /* Send multiple byte */ static inline void write_spi_multi ( const uint8_t *buff, /* Pointer to the data */ uint16_t btx /* Number of bytes to send (even number) */ ) { spi_spiTransfer(buff, btx,NULL, 0); } static inline void cs_release (void) { CS_HIGH(); /* Set CS# high */ } static bool cs_assert(void) /* 1:OK, 0:Timeout */ { if(!CS_LOW()) { /* take semaphore and Set CS# low */ return 0; } return 1; } // This function is mainly to test the device static void ReadManufacturerID() { // activate dataflash command decoder if (!cs_assert()) return; // Read manufacturer and ID command... buf.cmd[0] = JEDEC_DEVICE_ID; spi_spiTransfer(buf.cmd, 1, buf.cmd, addr_cmd_length); df_manufacturer = buf.cmd[0]; df_device = (buf.cmd[1] << 8) | buf.cmd[2]; //capacity // release SPI bus for use by other sensors cs_release(); } // Read the status register static uint8_t ReadStatusReg() { uint8_t tmp; if (!cs_assert()) return JEDEC_STATUS_BUSY; // Read status command buf.cmd[0] = JEDEC_READ_STATUS; spi_spiTransfer(buf.cmd, 1, &buf.cmd[1], 1); tmp = buf.cmd[1]; cs_release(); return tmp; } static uint8_t ReadStatus() { // We only want to extract the READY/BUSY bit uint8_t status = ReadStatusReg(); return status & JEDEC_STATUS_BUSY; } /*-----------------------------------------------------------------------*/ // Wait for dataflash /*-----------------------------------------------------------------------*/ static int wait_ready ( /* 1:Ready, 0:Timeout */ uint16_t wt /* Timeout [ms] */ ) { if(flash_died) return 0; Timer2 = wt; do { if(ReadStatus()==0) return 1; //ready spi_yield(); /* This loop takes a time. Insert rot_rdq() here for multitask envilonment. */ } while(Timer2); /* Wait for card goes ready or timeout */ flash_died = true; return 0; } static void Flash_Jedec_WriteEnable(void){ if (!wait_ready(500)) return; /* Wait for card ready */ if (!cs_assert()) return; spi_write(JEDEC_WRITE_ENABLE); cs_release(); } static void Flash_Enter4B_Mode(){ if (!wait_ready(500)) return; /* Wait for card ready */ if (!cs_assert()) return; spi_write(JEDEC_ENTER_4B_MODE); cs_release(); } static bool read_page( uint8_t *ptr, uint32_t pageNum){ uint32_t PageAdr = pageNum * DF_PAGE_SIZE; if (!wait_ready(500)) return 0; /* Wait for card ready */ if (!cs_assert()) return 0; { uint8_t i = 0; buf.cmd[i++] = JEDEC_READ_DATA; if(addr_4bit) { buf.cmd[i++] = (PageAdr >> 24) & 0xff; } buf.cmd[i++] = (PageAdr >> 16) & 0xff; buf.cmd[i++] = (PageAdr >> 8) & 0xff; buf.cmd[i++] = (PageAdr >> 0) & 0xff; } spi_spiTransfer(&buf.cmd[0], addr_cmd_length, buf.sector, DF_PAGE_SIZE); cs_release(); uint16_t i; for(i=0; i> 24) & 0xff; } buf.cmd[i++] = (PageAdr >> 16) & 0xff; buf.cmd[i++] = (PageAdr >> 8) & 0xff; buf.cmd[i++] = (PageAdr >> 0) & 0xff; #if 0 write_spi_multi(&buf.cmd[addr_cmd_start], addr_cmd_length); write_spi_multi(buf.sector, DF_PAGE_SIZE); #else write_spi_multi(&buf.cmd[addr_cmd_start], addr_cmd_length + DF_PAGE_SIZE); #endif cs_release(); return 1; } static bool erase_page(uint16_t pageNum) { Flash_Jedec_WriteEnable(); uint32_t PageAdr = pageNum * DF_PAGE_SIZE; uint8_t i=0; buf.cmd[i++] = erase_cmd; if(addr_4bit) { buf.cmd[i++] = (PageAdr >> 24) & 0xff; } buf.cmd[i++] = (PageAdr >> 16) & 0xff; buf.cmd[i++] = (PageAdr >> 8) & 0xff; buf.cmd[i++] = (PageAdr >> 0) & 0xff; if (!cs_assert()) return 0; write_spi_multi(buf.cmd, addr_cmd_length); cs_release(); return 1; } static void ChipErase() { buf.cmd[0] = JEDEC_BULK_ERASE; Flash_Jedec_WriteEnable(); if (!cs_assert()) return; write_spi_multi(buf.cmd, 1); chip_is_clear=true; cs_release(); } uint8_t sd_getSectorCount(uint32_t *ptr){ uint8_t capacity = df_device & 0xFF; uint8_t memtype = (df_device>>8) & 0xFF; uint32_t size=0; const char * mfg=NULL; switch(df_manufacturer){ case 0xEF: // Winbond Serial Flash if (memtype == 0x40) { mfg="Winbond"; size = (1 << ((capacity & 0x0f) + 8)); /* const uint8_t _capID[11] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x43}; const uint32_t _memSize[11] = {64L*K, 128L*K, 256L*K, 512L*K, 1L*M, 2L*M, 4L*M, 8L*M, 16L*M, 32L*M, 8L*M}; megabytes const uint32_t _memSize[11] = {64L*K, 128L*K, 256L*K, 512L*K, 1L*M, 2L*M, 4L*M, 8L*M, 16L*M, 32L*M, 8L*M}; megabytes */ erase_size=4096; erase_cmd=JEDEC_SECTOR_ERASE; } break; case 0xbf: // SST if (memtype == 0x25) { mfg="Microchip"; size = (1 << ((capacity & 0x07) + 12)); } break; case 0x20: // micron if (memtype == 0xba){// JEDEC_ID_MICRON_N25Q128 0x20ba18 mfg="Micron"; size = (1 << ((capacity & 0x0f) + 8)); erase_size=4096; erase_cmd=JEDEC_SECTOR_ERASE; } else if(memtype==0x20) { // JEDEC_ID_MICRON_M25P16 0x202015 mfg="Micron"; size = (1 << ((capacity & 0x0f) + 8)); } break; case 0xC2: //JEDEC_ID_MACRONIX_MX25L6406E 0xC22017 if (memtype == 0x20) { mfg="MACRONIX"; size = (1 << ((capacity & 0x0f) + 8)); erase_size=4096; erase_cmd=JEDEC_SECTOR_ERASE; } break; case 0x9D: // ISSI if (memtype == 0x40 || memtype == 0x30) { mfg = "ISSI"; size = (1 << ((capacity & 0x0f) + 8)); } break; default: break; } if(mfg && size) { printf("%s SPI Flash found sectors=%ld\n", mfg, size); } else { printf("unknown Flash! mfg=%x type=%x cap=%x\n ",df_manufacturer, memtype, capacity); size = BOARD_DATAFLASH_PAGES; // as defined } if(size>65537) { addr_cmd_length = 5; addr_cmd_start = 0; addr_4bit = true; Flash_Enter4B_Mode(); } if(erase_size > MAX_ERASE_SIZE) size -= (erase_size/DF_PAGE_SIZE); // reserve for RMW ops *ptr = size / (FAT_SECTOR_SIZE/DF_PAGE_SIZE); // in 512b blocks return RES_OK; } /*-------------------------------------------------------------------------- Public Functions ---------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/ /* Initialize disk drive */ /*-----------------------------------------------------------------------*/ DSTATUS sd_initialize () { static bool initialized=0; if(initialized) return Stat; ReadManufacturerID(); sd_getSectorCount(&sd_max_sectors); initialized=1; Stat=0; return Stat; } /*-----------------------------------------------------------------------*/ /* Get disk status */ /*-----------------------------------------------------------------------*/ DSTATUS sd_status (){ return Stat; /* Return disk status */ } /*-----------------------------------------------------------------------*/ /* Read sector(s) */ /*-----------------------------------------------------------------------*/ DRESULT sd_read ( uint8_t *buff, /* Pointer to the data buffer to store read data */ uint32_t sector, /* Start sector number (LBA) */ uint16_t count /* Number of sectors to read (1..128) */ ) { if (!count) return RES_PARERR; /* Check parameter */ if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */ if(sector > sd_max_sectors) return RES_PARERR; /* Check parameter */ count *= FAT_SECTOR_SIZE/DF_PAGE_SIZE; // 256bytes page from 512byte sector sector *= FAT_SECTOR_SIZE/DF_PAGE_SIZE; do { if(!read_page(buff, sector)) break; buff += DF_PAGE_SIZE; sector += 1; } while(--count); return count ? RES_ERROR : RES_OK; /* Return result */ } /*-----------------------------------------------------------------------*/ /* Write sector(s) */ /*-----------------------------------------------------------------------*/ bool sd_move_block(uint32_t sec_from, uint32_t sec_to, const uint8_t *buff, uint32_t sec, uint16_t count); /* в системе нет памяти, а выполнять Read-Modify-Write для флешки как-то надо. придется использовать свободный блок флеши для буфферизации. Ресурс это конечно уменьшит, зато быстрее перепаяют на нормальную :) по-уму надо читать FAT и выбирать случайный свободный кластер, но использование для этого функций самой FatFs приводит к смещению окна в неожиданное время, например когда запись вызывается для сброса измененных структур FAT */ bool sd_move_block(uint32_t sec_from, uint32_t sec_to, const uint8_t *buff, uint32_t sec, uint16_t count){ if(!erase_page(sec_to)) return RES_ERROR; uint8_t *sbuffer = malloc(DF_PAGE_SIZE); // read just the needed sector, not in stack for not in CCM if(!sbuffer) return false; uint16_t cnt; for(cnt=erase_size/DF_PAGE_SIZE; cnt>0; cnt--){ if(buff && sec_from >= sec && sec_from < (sec + count) ) { // if replacement data fit to sector memcpy(sbuffer, buff, DF_PAGE_SIZE); // will use it buff+=DF_PAGE_SIZE; } else { // read from source if(!read_page(sbuffer, sec_from)) break; } if(!write_page(sbuffer, sec_to)) break; sec_from++; sec_to++; } free(sbuffer); return cnt==0; } DRESULT sd_write ( const uint8_t *buff, /* Ponter to the data to write */ uint32_t sector, /* Start sector number (LBA) */ uint16_t count /* Number of sectors to write (1..128) */ ) { if (!count) return RES_PARERR; /* Check parameter */ if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check drive status */ if(sector > sd_max_sectors) return RES_PARERR; /* Check parameter */ uint16_t pos = sector % erase_size/FAT_SECTOR_SIZE; // number of sector in erase block if( pos == 0 && count >= erase_size/FAT_SECTOR_SIZE){ // begin of erase block - write full cluster count *= FAT_SECTOR_SIZE/DF_PAGE_SIZE; // 256bytes page from 512byte sector sector *= FAT_SECTOR_SIZE/DF_PAGE_SIZE; if(!erase_page(sector)) return RES_ERROR; do { if(!write_page(buff, sector)) break; buff += DF_PAGE_SIZE; sector += 1; } while(--count); } else { // read-modify-write uint8_t *ptr; bool need_erase = false; // check for free space for write { uint8_t *sbuffer = malloc(FAT_SECTOR_SIZE*count); // read just the needed sectors if(!sbuffer) return RES_ERROR; // no memory at all ptr = sbuffer; uint32_t r_sector = sector; uint16_t r_count = count; r_sector *= FAT_SECTOR_SIZE/DF_PAGE_SIZE; r_count *= FAT_SECTOR_SIZE/DF_PAGE_SIZE; // read data will be rewritten do { if(!read_page(ptr, r_sector)) break; ptr += DF_PAGE_SIZE; r_sector += 1; } while(--r_count); const uint8_t *pp; for(ptr = sbuffer, pp=buff; ptr < sbuffer+FAT_SECTOR_SIZE*count;ptr++,pp++){ // if(~*ptr & *pp){ // у нас не должно быть нулей там где нужна 1 // filesystem is inverted, so - у нас не должно быть 1 там где нужен 0 // пример: чистая FF - инверсия 00, можно писАть любой байт // считали F0 - инверсия 0F - можно писАть *0 if(*ptr & *pp){ need_erase=true; break; } } free(sbuffer);// don't need more } if(need_erase){// write do dirty block uint8_t *cluster = malloc(erase_size); if(!cluster) { // we can try to allocate up to 64K so absense of memory should not be error! if(erase_size <= MAX_ERASE_SIZE) return RES_ERROR; // we have no reserved page // we can use any free sector of flash as buffer, too uint32_t fr_sec = sd_max_sectors * (FAT_SECTOR_SIZE/DF_PAGE_SIZE); // the last page // read data from beginning of erase block up to part that will be rewritten uint32_t r_sector = (sector * (FAT_SECTOR_SIZE/DF_PAGE_SIZE)) & ~(erase_size/DF_PAGE_SIZE - 1); // move data to sectors of free block if(!sd_move_block(r_sector, fr_sec, NULL, 0, 0)) return RES_ERROR; // move back with inserting of data to write if(!sd_move_block(fr_sec, r_sector, buff, sector * (FAT_SECTOR_SIZE/DF_PAGE_SIZE), count * (FAT_SECTOR_SIZE/DF_PAGE_SIZE))) return RES_ERROR; // move back count=0; // all OK } else { // we have enough memory for cluster buffer ptr = cluster; // read data from beginning of erase block up to part that will be rewritten uint32_t r_sector = (sector * (FAT_SECTOR_SIZE/DF_PAGE_SIZE)) & ~(erase_size/DF_PAGE_SIZE - 1); uint32_t w_sector = r_sector; uint16_t r_count = erase_size/DF_PAGE_SIZE; // read full page do { if(r_sector >= sector * (FAT_SECTOR_SIZE/DF_PAGE_SIZE)) break; if(!read_page(ptr, r_sector)) break; ptr += DF_PAGE_SIZE; r_sector += 1; } while(--r_count); // Yes I know that we could to go out from the buffer end - but this used only for FAT_FS which writes FAT per one sector // memcpy(cluster+FAT_SECTOR_SIZE*pos, (uint8_t *)buff, FAT_SECTOR_SIZE*count); // insert sectors to write to right place memcpy(ptr, (uint8_t *)buff, FAT_SECTOR_SIZE*count); // insert sectors to write to right place // skip part that will be rewritten r_count -= (FAT_SECTOR_SIZE/DF_PAGE_SIZE) * count; r_sector += (FAT_SECTOR_SIZE/DF_PAGE_SIZE) * count; ptr += FAT_SECTOR_SIZE * count; // read the tail while(r_count) { if(!read_page(ptr, r_sector)) break; ptr += DF_PAGE_SIZE; r_sector += 1; --r_count; }; if(r_count) return RES_ERROR; // erase page if(!erase_page(w_sector)) return RES_ERROR; ptr = cluster; uint16_t w_count = erase_size/DF_PAGE_SIZE; do { if(!write_page(ptr, w_sector)) break; ptr += DF_PAGE_SIZE; w_sector += 1; } while(--w_count); count = w_count; free(cluster); } } else { // block is ready to write - just write needed part count *= FAT_SECTOR_SIZE/DF_PAGE_SIZE; // 256bytes page from 512byte sector sector *= FAT_SECTOR_SIZE/DF_PAGE_SIZE; do { if(!write_page(buff, sector)) break; buff += DF_PAGE_SIZE; sector += 1; } while(--count); } } return count ? RES_ERROR : RES_OK; /* Return result */ } /*-----------------------------------------------------------------------*/ /* Miscellaneous drive controls other than data read/write */ /*-----------------------------------------------------------------------*/ static inline void enqueue_flash_erase(uint32_t from, uint32_t to){ int16_t new_wp = fe_write_ptr+1; if(new_wp >= FLASH_ERASE_QUEUE_SIZE) { // move write pointer new_wp=0; // ring } while(new_wp == fe_read_ptr) hal_yield(0); // buffer overflow flash_erase_queue[fe_write_ptr].b = from; flash_erase_queue[fe_write_ptr].e = to; fe_write_ptr=new_wp; // move forward hal_set_task_active(_flash_erase_task); } DRESULT sd_ioctl ( uint8_t ctl, /* Control command code */ void *buff /* Pointer to the conrtol data */ ) { DRESULT res; uint8_t *ptr = (uint8_t *)buff; res = RES_ERROR; if (ctl== CTRL_POWER){ switch (*ptr) { case 0: // Sub control code == 0 (POWER_OFF) res = RES_OK; break; case 1: // Sub control code == 1 (POWER_ON) res = RES_OK; break; case 2: // Sub control code == 2 (POWER_GET) *(ptr + 1) = (uint8_t) MMC_CD; break; default: res = RES_PARERR; break; } } else { if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */ switch (ctl) { case CTRL_SYNC : /* Wait for end of internal write process of the drive */ res = RES_OK; break; case GET_SECTOR_COUNT : /* Get drive capacity in unit of sector (uint32_t ) */ *(uint32_t *)buff = sd_max_sectors; res = RES_OK; break; case GET_BLOCK_SIZE : /* Get erase block size in unit of FAT sector (uint32_t ) */ *(uint32_t *)buff = erase_size/FAT_SECTOR_SIZE; res = RES_OK; break; case GET_SECTOR_SIZE: *(uint32_t *)buff = FAT_SECTOR_SIZE; // always res = RES_OK; break; case CTRL_TRIM : { /* Erase a block of sectors (used when _USE_TRIM == 1) */ uint32_t start_sector = ((uint32_t *)buff)[0]; uint32_t end_sector = ((uint32_t *)buff)[1]; uint32_t block, last_block=-1; if(start_sector>=sd_max_sectors || end_sector>=sd_max_sectors) return RES_PARERR; if(chip_is_clear) { // just after full erase res = RES_OK; chip_is_clear=false; return res; } if(hal_is_armed()) return RES_OK; #if 0 // in separate thread erase_page(start_sector * (FAT_SECTOR_SIZE/DF_PAGE_SIZE)); // clear 1st sector of DataFlash enqueue_flash_erase(start_sector+(erase_size/DF_PAGE_SIZE), end_sector); //and enqueue #else uint32_t sector; for(sector=start_sector; sector <= end_sector;sector++){ uint32_t df_sect = sector * (FAT_SECTOR_SIZE/DF_PAGE_SIZE); // sector in DataFlash block = df_sect / (erase_size/DF_PAGE_SIZE); // number of EraseBlock if(last_block!=block){ last_block=block; if(!erase_page(df_sect)) return RES_ERROR; putch('.'); extern void digitalToggle(uint8_t pin); digitalToggle(HAL_GPIO_A_LED_PIN); } } #endif res = RES_OK; } break; case CTRL_FORMAT:{ ChipErase(); res = RES_OK; } break; default: res = RES_PARERR; } } return res; } /*-----------------------------------------------------------------------*/ /* Device timer function */ /*-----------------------------------------------------------------------*/ /* This function must be called from timer interrupt routine in period / of 1 ms to generate card control timing. */ void sd_timerproc (void) { uint16_t n; n = Timer1; /* 1kHz decrement timer stopped at 0 */ if (n) Timer1 = --n; n = Timer2; if (n) Timer2 = --n; } #endif //defined(BOARD_DATAFLASH_FATFS)