/*-----------------------------------------------------------------------*/ /* MMC/SDC (in SPI mode) control module (C)ChaN, 2007 */ /*-----------------------------------------------------------------------*/ /* Only rcvr_spi(), xmit_spi(), disk_timerproc() and some macros */ /* are platform dependent. */ /*-----------------------------------------------------------------------*/ #include "obj_spi_sd_stm32.h" #include "dev_spi_sdcard.h" /*-------------------------------------------------------------------------- Module Private Functions ---------------------------------------------------------------------------*/ //extern SPI_HandleTypeDef hspi2; #define SD_SPIDriver hspi2 static volatile DSTATUS Stat = STA_NOINIT; /* Disk status */ static volatile BYTE Timer1, Timer2; /* 100Hz decrement timer */ static BYTE CardType; /* b0:MMC, b1:SDC, b2:Block addressing */ static BYTE PowerFlag = 0; /* indicates if "power" is on */ static void SELECT(void) { //HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_RESET); } static void DESELECT(void) { //HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_SET); } static void xmit_spi(BYTE Data) { while (HAL_SPI_GetState(&SD_SPIDriver) != HAL_SPI_STATE_READY); HAL_SPI_Transmit(&SD_SPIDriver, &Data, 1, 5000); } static BYTE rcvr_spi(void) { unsigned char Dummy, Data; Dummy = 0xFF; Data = 0; while ((HAL_SPI_GetState(&SD_SPIDriver) != HAL_SPI_STATE_READY)) ; HAL_SPI_TransmitReceive(&SD_SPIDriver, &Dummy, &Data, 1, 5000); return Data; } static void rcvr_spi_m(BYTE *dst) { *dst = rcvr_spi(); } /*-----------------------------------------------------------------------*/ /* Wait for card ready */ /*-----------------------------z------------------------------------------*/ static BYTE wait_ready(void) { BYTE res; Timer2 = 50; rcvr_spi(); do res = rcvr_spi(); while ((res != 0xFF) && Timer2); return res; } /*-----------------------------------------------------------------------*/ /* Power Control (Platform dependent) */ /*-----------------------------------------------------------------------*/ /* When the target system does not support socket power control, there */ /* is nothing to do in these functions and chk_power always returns 1. */ static void power_on(void) { unsigned char i, cmd_arg[6]; unsigned int Count = 0x1FFF; DESELECT(); for (i = 0; i < 10; i++) xmit_spi(0xFF); SELECT(); cmd_arg[0] = (CMD0 | 0x40); cmd_arg[1] = 0; cmd_arg[2] = 0; cmd_arg[3] = 0; cmd_arg[4] = 0; cmd_arg[5] = 0x95; for (i = 0; i < 6; i++) xmit_spi(cmd_arg[i]); while ((rcvr_spi() != 0x01) && Count) Count--; DESELECT(); xmit_spi(0XFF); PowerFlag = 1; } static void power_off(void) { PowerFlag = 0; } static int chk_power(void) /* Socket power state: 0=off, 1=on */ { return PowerFlag; } /*-----------------------------------------------------------------------*/ /* Receive a data packet from MMC */ /*-----------------------------------------------------------------------*/ static bool rcvr_datablock(BYTE *buff, /* Data buffer to store received data */ UINT btr /* Byte count (must be even number) */ ) { BYTE token; Timer1 = 10; do { /* Wait for data packet in timeout of 100ms */ token = rcvr_spi(); } while ((token == 0xFF) && Timer1); if (token != 0xFE) return FALSE; /* If not valid data token, retutn with error */ do { /* Receive the data block into buffer */ rcvr_spi_m(buff++); rcvr_spi_m(buff++); } while (btr -= 2); rcvr_spi(); /* Discard CRC */ rcvr_spi(); return TRUE; /* Return with success */ } /*-----------------------------------------------------------------------*/ /* Send a data packet to MMC */ /*-----------------------------------------------------------------------*/ #if _READONLY == 0 static bool xmit_datablock(const BYTE *buff, /* 512 byte data block to be transmitted */ BYTE token /* Data/Stop token */ ) { BYTE resp, wc; uint32_t i = 0; if (wait_ready() != 0xFF) return FALSE; xmit_spi(token); /* Xmit data token */ if (token != 0xFD) { /* Is data token */ wc = 0; do { /* Xmit the 512 byte data block to MMC */ xmit_spi(*buff++); xmit_spi(*buff++); } while (--wc); rcvr_spi(); rcvr_spi(); while (i <= 64) { resp = rcvr_spi(); /* Reveive data response */ if ((resp & 0x1F) == 0x05) /* If not accepted, return with error */ break; i++; } while (rcvr_spi() == 0) ; } if ((resp & 0x1F) == 0x05) return TRUE; else return FALSE; } #endif /* _READONLY */ /*-----------------------------------------------------------------------*/ /* Send a command packet to MMC */ /*-----------------------------------------------------------------------*/ static BYTE send_cmd(BYTE cmd, /* Command byte */ DWORD arg /* Argument */ ) { BYTE n, res; if (wait_ready() != 0xFF) return 0xFF; /* Send command packet */ xmit_spi(cmd); /* Command */ xmit_spi((BYTE) (arg >> 24)); /* Argument[31..24] */ xmit_spi((BYTE) (arg >> 16)); /* Argument[23..16] */ xmit_spi((BYTE) (arg >> 8)); /* Argument[15..8] */ xmit_spi((BYTE) arg); /* Argument[7..0] */ n = 0; if (cmd == CMD0) n = 0x95; /* CRC for CMD0(0) */ if (cmd == CMD8) n = 0x87; /* CRC for CMD8(0x1AA) */ xmit_spi(n); /* Receive command response */ if (cmd == CMD12) rcvr_spi(); /* Skip a stuff byte when stop reading */ n = 10; /* Wait for a valid response in timeout of 10 attempts */ do res = rcvr_spi(); while ((res & 0x80) && --n); return res; /* Return with the response value */ } /*-------------------------------------------------------------------------- Public Functions ---------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/ /* Initialize Disk Drive */ /*-----------------------------------------------------------------------*/ DSTATUS SD_disk_initialize(BYTE drv /* Physical drive nmuber (0) */ ) { BYTE n, ty, ocr[4]; if (drv) return STA_NOINIT; /* Supports only single drive */ if (Stat & STA_NODISK) return Stat; /* No card in the socket */ power_on(); /* Force socket power on */ //send_initial_clock_train(); SELECT(); /* CS = L */ ty = 0; if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */ Timer1 = 100; /* Initialization timeout of 1000 msec */ if (send_cmd(CMD8, 0x1AA) == 1) { /* SDC Ver2+ */ for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */ do { if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 1UL << 30) == 0) break; /* ACMD41 with HCS bit */ } while (Timer1); if (Timer1 && send_cmd(CMD58, 0) == 0) { /* Check CCS bit */ for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); ty = (ocr[0] & 0x40) ? 6 : 2; } } } else { /* SDC Ver1 or MMC */ ty = (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) <= 1) ? 2 : 1; /* SDC : MMC */ do { if (ty == 2) { if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) == 0) break; /* ACMD41 */ } else { if (send_cmd(CMD1, 0) == 0) break; /* CMD1 */ } } while (Timer1); if (!Timer1 || send_cmd(CMD16, 512) != 0) /* Select R/W block length */ ty = 0; } } CardType = ty; DESELECT(); /* CS = H */ rcvr_spi(); /* Idle (Release DO) */ if (ty) /* Initialization succeded */ Stat &= ~STA_NOINIT; /* Clear STA_NOINIT */ else /* Initialization failed */ power_off(); return Stat; } /*-----------------------------------------------------------------------*/ /* Get Disk Status */ /*-----------------------------------------------------------------------*/ DSTATUS SD_disk_status(BYTE drv /* Physical drive nmuber (0) */ ) { if (drv) return STA_NOINIT; /* Supports only single drive */ return Stat; } /*-----------------------------------------------------------------------*/ /* Read Sector(s) */ /*-----------------------------------------------------------------------*/ DRESULT SD_disk_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) { if (pdrv || !count) return RES_PARERR; if (Stat & STA_NOINIT) return RES_NOTRDY; if (!(CardType & 4)) sector *= 512; /* Convert to byte address if needed */ SELECT(); /* CS = L */ if (count == 1) { /* Single block read */ if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */ && rcvr_datablock(buff, 512)) count = 0; } else { /* Multiple block read */ if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */ do { if (!rcvr_datablock(buff, 512)) break; buff += 512; } while (--count); send_cmd(CMD12, 0); /* STOP_TRANSMISSION */ } } DESELECT(); /* CS = H */ rcvr_spi(); /* Idle (Release DO) */ return count ? RES_ERROR : RES_OK; } /*-----------------------------------------------------------------------*/ /* Write Sector(s) */ /*-----------------------------------------------------------------------*/ #if _READONLY == 0 DRESULT SD_disk_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) { if (pdrv || !count) return RES_PARERR; if (Stat & STA_NOINIT) return RES_NOTRDY; if (Stat & STA_PROTECT) return RES_WRPRT; if (!(CardType & 4)) sector *= 512; /* Convert to byte address if needed */ SELECT(); /* CS = L */ if (count == 1) { /* Single block write */ if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */ && xmit_datablock(buff, 0xFE)) count = 0; } else { /* Multiple block write */ if (CardType & 2) { send_cmd(CMD55, 0); send_cmd(CMD23, count); /* ACMD23 */ } if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */ do { if (!xmit_datablock(buff, 0xFC)) break; buff += 512; } while (--count); if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */ count = 1; } } DESELECT(); /* CS = H */ rcvr_spi(); /* Idle (Release DO) */ return count ? RES_ERROR : RES_OK; } #endif /* _READONLY */ /*-----------------------------------------------------------------------*/ /* Miscellaneous Functions */ /*-----------------------------------------------------------------------*/ DRESULT SD_disk_ioctl(BYTE drv, /* Physical drive nmuber (0) */ BYTE ctrl, /* Control code */ void *buff /* Buffer to send/receive control data */ ) { DRESULT res; BYTE n, csd[16], *ptr = buff; WORD csize; if (drv) return RES_PARERR; res = RES_ERROR; if (ctrl == CTRL_POWER) { switch (*ptr) { case 0: /* Sub control code == 0 (POWER_OFF) */ if (chk_power()) 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) = (BYTE) chk_power(); res = RES_OK; break; default: res = RES_PARERR; } } else { if (Stat & STA_NOINIT) return RES_NOTRDY; SELECT(); /* CS = L */ switch (ctrl) { case GET_SECTOR_COUNT: /* Get number of sectors on the disk (DWORD) */ if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */ csize = csd[9] + ((WORD) csd[8] << 8) + 1; *(DWORD*) buff = (DWORD) csize << 10; } else { /* MMC or SDC ver 1.XX */ n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2; csize = (csd[8] >> 6) + ((WORD) csd[7] << 2) + ((WORD) (csd[6] & 3) << 10) + 1; *(DWORD*) buff = (DWORD) csize << (n - 9); } res = RES_OK; } break; case GET_SECTOR_SIZE: /* Get sectors on the disk (WORD) */ *(WORD*) buff = 512; res = RES_OK; break; case CTRL_SYNC: /* Make sure that data has been written */ if (wait_ready() == 0xFF) 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)) 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 = 0; n < 4; n++) *ptr++ = rcvr_spi(); res = RES_OK; } // case MMC_GET_TYPE : /* Get card type flags (1 byte) */ // *ptr = CardType; // res = RES_OK; // break; default: res = RES_PARERR; } DESELECT(); /* CS = H */ rcvr_spi(); /* Idle (Release DO) */ } return res; } /*-----------------------------------------------------------------------*/ /* Device Timer Interrupt Procedure (Platform dependent) */ /*-----------------------------------------------------------------------*/ /* This function must be called in period of 10ms */ void disk_timerproc(void) { // BYTE n, s; BYTE n; n = Timer1; /* 100Hz decrement timer */ if (n) Timer1 = --n; n = Timer2; if (n) Timer2 = --n; } volatile unsigned short int sdcard_timer; void inline sdcard_systick_timerproc(void) { ++sdcard_timer; if (sdcard_timer >= 100) { sdcard_timer = 0; disk_timerproc(); } } /*---------------------------------------------------------*/ /* User Provided Timer Function for FatFs module */ /*---------------------------------------------------------*/ /* This is a real time clock service to be called from */ /* FatFs module. Any valid time must be returned even if */ /* the system does not support a real time clock. */ DWORD SD_get_fattime(void) { return ((2007UL - 1980) << 25) // Year = 2007 | (6UL << 21) // Month = June | (5UL << 16) // Day = 5 | (11U << 11) // Hour = 11 | (38U << 5) // Min = 38 | (0U >> 1) // Sec = 0 ; }