blob: 87a25fff05287d1b8dd885c8bff0358da2ee8b95 [file]
#define TRUE 1
#define FALSE 0
#include "stm32h7xx_hal.h"
#include <stdbool.h>
#include <string.h>
#include "ff.h"
#include "diskio.h"
#include "fatfs_sd.h"
static volatile DSTATUS Stat = STA_NOINIT; /* Disk Status */
static uint8_t CardType; /* Type 0:MMC, 1:SDC, 2:Block addressing */
static uint8_t PowerFlag = 0; /* Power flag */
/***************************************
* SPI functions
**************************************/
/* slave select */
static void SELECT(void)
{
HAL_GPIO_WritePin(SD_CS_PORT, SD_CS_PIN, GPIO_PIN_RESET);
}
/* slave deselect */
static void DESELECT(void)
{
HAL_GPIO_WritePin(SD_CS_PORT, SD_CS_PIN, GPIO_PIN_SET);
}
static void SPI_Start(SPI_TypeDef *spi, uint16_t len)
{
spi->CR2 = len;
uint32_t cr1 = spi->CR1 | SPI_CR1_SPE;
spi->CR1 = cr1; // enable
spi->CR1 = cr1 | SPI_CR1_CSTART; // start
}
static void SPI_End(SPI_TypeDef *spi)
{
// clear EOT/TXTF
spi->IFCR |= SPI_IFCR_EOTC | SPI_IFCR_TXTFC;
spi->CR1 &= ~SPI_CR1_SPE; // disable
}
/* SPI transmit a byte */
static void SPI_TxByte(uint8_t data)
{
SPI_TypeDef *spi = (HSPI_SDCARD)->Instance;
SPI_Start(spi, 1);
// wait for tx space
while(!(spi->SR & SPI_FLAG_TXP));
*((__IO uint8_t *)&spi->TXDR) = data;
// wait for end
while(!(spi->SR & SPI_FLAG_EOT));
// end
SPI_End(spi);
}
/* SPI receive a byte */
static uint8_t SPI_RxByte(void)
{
uint8_t data;
SPI_TypeDef *spi = (HSPI_SDCARD)->Instance;
SPI_Start(spi, 1);
while(!(spi->SR & SPI_FLAG_TXP));
*((__IO uint8_t *)&spi->TXDR) = 0xFF;
// wait for a byte
while(!(spi->SR & (SPI_FLAG_RXWNE | SPI_FLAG_FRLVL)));
data = *((__IO uint8_t *)&spi->RXDR);
// wait for end
while(!(spi->SR & SPI_FLAG_EOT));
// end
SPI_End(spi);
return data;
}
/* SPI receive a byte via pointer */
static void SPI_RxBytePtr(uint8_t *buff)
{
*buff = SPI_RxByte();
}
/***************************************
* SD functions
**************************************/
/* wait SD ready */
static uint8_t SD_ReadyWait(void)
{
uint8_t res;
/* timeout 500ms */
uint32_t timeout = HAL_GetTick() + 500;
/* if SD goes ready, receives 0xFF */
do {
res = SPI_RxByte();
} while ((res != 0xFF) && HAL_GetTick() < timeout);
return res;
}
/* power on */
static void SD_PowerOn(void)
{
uint32_t cnt = 0x1FFF;
/* transmit bytes to wake up */
DESELECT();
for(int i = 0; i < 10; i++)
{
SPI_TxByte(0xFF);
}
/* slave select */
SELECT();
/* make idle state */
// SD_SendCmd?
SPI_TxByte(CMD0); /* CMD0:GO_IDLE_STATE */
SPI_TxByte(0);
SPI_TxByte(0);
SPI_TxByte(0);
SPI_TxByte(0);
SPI_TxByte(0x95); /* CRC */
/* wait response */
while ((SPI_RxByte() != 0x01) && cnt)
{
cnt--;
}
DESELECT();
SPI_TxByte(0XFF);
PowerFlag = 1;
}
/* power off */
static void SD_PowerOff(void)
{
PowerFlag = 0;
}
/* check power flag */
static uint8_t SD_CheckPower(void)
{
return PowerFlag;
}
/* receive data block */
static bool SD_RxDataBlock(BYTE *buff, uint16_t len)
{
uint8_t token;
/* timeout 200ms */
uint32_t timeout = HAL_GetTick() + 200;
/* loop until receive a response or timeout */
do {
token = SPI_RxByte();
} while((token == 0xFF) && HAL_GetTick() < timeout);
/* invalid response */
if(token != 0xFE) return FALSE;
/* receive data */
if(len > 16)
{
// manual txrx
SPI_Start((HSPI_SDCARD)->Instance, len + 2);
// the only length that will be used here is 512
uint32_t *buff32 = (uint32_t *)buff;
const uint32_t *end = buff32 + len / 4;
// fill the 16 byte fifo
for(int i = 0; i < 4; i++)
*((__IO uint32_t *)&(HSPI_SDCARD)->Instance->TXDR) = 0xFFFFFFFF;
while(true)
{
if(((HSPI_SDCARD)->Instance->SR & SPI_FLAG_RXWNE))
{
*(buff32++) = *((__IO uint32_t *)&(HSPI_SDCARD)->Instance->RXDR);
if(buff32 != end)
*((__IO uint32_t *)&(HSPI_SDCARD)->Instance->TXDR) = 0xFFFFFFFF; // refill tx
else
break;
}
}
// get CRC
*((__IO uint16_t *)&(HSPI_SDCARD)->Instance->TXDR) = 0xFFFF;
while(!((HSPI_SDCARD)->Instance->SR & SPI_SR_RXPLVL_1));
uint16_t crc = *((__IO uint16_t *)&(HSPI_SDCARD)->Instance->RXDR);
while(!((HSPI_SDCARD)->Instance->SR & SPI_FLAG_EOT)); // wait for end
// end
SPI_End((HSPI_SDCARD)->Instance);
}
else
{
while(len--)
SPI_RxBytePtr(buff++);
/* discard CRC */
SPI_RxByte();
SPI_RxByte();
}
return TRUE;
}
/* transmit data block */
#if FF_FS_READONLY == 0
static bool SD_TxDataBlock(const uint8_t *buff, BYTE token)
{
uint8_t resp;
uint8_t i = 0;
/* wait SD ready */
if (SD_ReadyWait() != 0xFF) return FALSE;
/* transmit token */
SPI_TxByte(token);
/* if it's not STOP token, transmit data */
if (token != 0xFD)
{
// manual tx
const int len = 512;
SPI_Start((HSPI_SDCARD)->Instance, len);
// the only length that will be used here is 512
uint32_t *buff32 = (uint32_t *)buff;
const uint32_t *end = buff32 + len / 4;
// fill the 16 byte fifo
for(int i = 0; i < 4; i++)
*((__IO uint32_t *)&(HSPI_SDCARD)->Instance->TXDR) = *buff32++;
while(buff32 != end)
{
if(((HSPI_SDCARD)->Instance->SR & SPI_FLAG_TXP))
*((__IO uint32_t *)&(HSPI_SDCARD)->Instance->TXDR) = *buff32++;
}
while(!((HSPI_SDCARD)->Instance->SR & SPI_FLAG_EOT)); // wait for end
// end
SPI_End((HSPI_SDCARD)->Instance);
/* discard CRC */
SPI_RxByte();
SPI_RxByte();
/* receive response */
while (i <= 64)
{
resp = SPI_RxByte();
/* transmit 0x05 accepted */
if ((resp & 0x1F) == 0x05) break;
i++;
}
/* recv buffer clear */
while (SPI_RxByte() == 0);
}
else
return TRUE;
/* transmit 0x05 accepted */
if ((resp & 0x1F) == 0x05) return TRUE;
return FALSE;
}
#endif /* FF_FS_READONLY */
/* transmit command */
static BYTE SD_SendCmd(BYTE cmd, uint32_t arg)
{
uint8_t crc, res;
/* wait SD ready */
if (SD_ReadyWait() != 0xFF) return 0xFF;
/* transmit command */
SPI_TxByte(cmd); /* Command */
SPI_TxByte((uint8_t)(arg >> 24)); /* Argument[31..24] */
SPI_TxByte((uint8_t)(arg >> 16)); /* Argument[23..16] */
SPI_TxByte((uint8_t)(arg >> 8)); /* Argument[15..8] */
SPI_TxByte((uint8_t)arg); /* Argument[7..0] */
/* prepare CRC */
if(cmd == CMD0) crc = 0x95; /* CRC for CMD0(0) */
else if(cmd == CMD8) crc = 0x87; /* CRC for CMD8(0x1AA) */
else crc = 1;
/* transmit CRC */
SPI_TxByte(crc);
/* Skip a stuff byte when STOP_TRANSMISSION */
if (cmd == CMD12) SPI_RxByte();
/* receive response */
uint8_t n = 10;
do {
res = SPI_RxByte();
} while ((res & 0x80) && --n);
return res;
}
/***************************************
* user_diskio.c functions
**************************************/
/* initialize SD */
DSTATUS SD_disk_initialize(BYTE drv)
{
uint8_t n, type, ocr[4];
/* single drive, drv should be 0 */
if(drv) return STA_NOINIT;
/* no disk */
if(Stat & STA_NODISK) return Stat;
/* power on */
SD_PowerOn();
/* slave select */
SELECT();
/* check disk type */
type = 0;
/* send GO_IDLE_STATE command */
if (SD_SendCmd(CMD0, 0) == 1)
{
/* timeout 1 sec */
uint32_t timeout = HAL_GetTick() + 1000;
/* SDC V2+ accept CMD8 command, http://elm-chan.org/docs/mmc/mmc_e.html */
if (SD_SendCmd(CMD8, 0x1AA) == 1)
{
/* operation condition register */
for (n = 0; n < 4; n++)
{
ocr[n] = SPI_RxByte();
}
/* voltage range 2.7-3.6V */
if (ocr[2] == 0x01 && ocr[3] == 0xAA)
{
/* ACMD41 with HCS bit */
do {
if (SD_SendCmd(CMD55, 0) <= 1 && SD_SendCmd(CMD41, 1UL << 30) == 0) break;
} while (HAL_GetTick() < timeout);
/* READ_OCR */
if (HAL_GetTick() < timeout && SD_SendCmd(CMD58, 0) == 0)
{
/* Check CCS bit */
for (n = 0; n < 4; n++)
{
ocr[n] = SPI_RxByte();
}
/* SDv2 (HC or SC) */
type = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;
}
}
}
else
{
/* SDC V1 or MMC */
type = (SD_SendCmd(CMD55, 0) <= 1 && SD_SendCmd(CMD41, 0) <= 1) ? CT_SD1 : CT_MMC;
do
{
if (type == CT_SD1)
{
if (SD_SendCmd(CMD55, 0) <= 1 && SD_SendCmd(CMD41, 0) == 0) break; /* ACMD41 */
}
else
{
if (SD_SendCmd(CMD1, 0) == 0) break; /* CMD1 */
}
} while (HAL_GetTick() < timeout);
/* SET_BLOCKLEN */
if (!(HAL_GetTick() >= timeout) || SD_SendCmd(CMD16, 512) != 0) type = 0;
}
}
CardType = type;
/* Idle */
DESELECT();
SPI_RxByte();
/* Clear STA_NOINIT */
if (type)
{
Stat &= ~STA_NOINIT;
}
else
{
/* Initialization failed */
SD_PowerOff();
}
return Stat;
}
/* return disk status */
DSTATUS SD_disk_status(BYTE drv)
{
if (drv) return STA_NOINIT;
return Stat;
}
/* read sector */
DRESULT SD_disk_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count)
{
/* pdrv should be 0 */
if (pdrv || !count) return RES_PARERR;
/* no disk */
if (Stat & STA_NOINIT) return RES_NOTRDY;
/* convert to byte address */
if (!(CardType & CT_BLOCK)) sector *= 512;
SELECT();
if (count == 1)
{
/* READ_SINGLE_BLOCK */
if ((SD_SendCmd(CMD17, sector) == 0) && SD_RxDataBlock(buff, 512)) count = 0;
}
else
{
/* READ_MULTIPLE_BLOCK */
if (SD_SendCmd(CMD18, sector) == 0)
{
do {
if (!SD_RxDataBlock(buff, 512)) break;
buff += 512;
} while (--count);
/* STOP_TRANSMISSION */
SD_SendCmd(CMD12, 0);
}
}
/* Idle */
DESELECT();
SPI_RxByte();
return count ? RES_ERROR : RES_OK;
}
/* write sector */
#if FF_FS_READONLY == 0
DRESULT SD_disk_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count)
{
/* pdrv should be 0 */
if (pdrv || !count) return RES_PARERR;
/* no disk */
if (Stat & STA_NOINIT) return RES_NOTRDY;
/* write protection */
if (Stat & STA_PROTECT) return RES_WRPRT;
/* convert to byte address */
if (!(CardType & CT_BLOCK)) sector *= 512;
SELECT();
if (count == 1)
{
/* WRITE_BLOCK */
if ((SD_SendCmd(CMD24, sector) == 0) && SD_TxDataBlock(buff, 0xFE))
count = 0;
}
else
{
/* WRITE_MULTIPLE_BLOCK */
if (CardType & CT_SD1)
{
SD_SendCmd(CMD55, 0);
SD_SendCmd(CMD23, count); /* ACMD23 */
}
if (SD_SendCmd(CMD25, sector) == 0)
{
do {
if(!SD_TxDataBlock(buff, 0xFC)) break;
buff += 512;
} while (--count);
/* STOP_TRAN token */
if(!SD_TxDataBlock(0, 0xFD))
{
count = 1;
}
}
}
/* Idle */
DESELECT();
SPI_RxByte();
return count ? RES_ERROR : RES_OK;
}
#endif /* FF_FS_READONLY */
/* ioctl */
DRESULT SD_disk_ioctl(BYTE drv, BYTE ctrl, void *buff)
{
DRESULT res;
uint8_t n, csd[16], *ptr = (uint8_t*)buff;
DWORD csize;
/* pdrv should be 0 */
if (drv) return RES_PARERR;
res = RES_ERROR;
if (ctrl == CTRL_POWER)
{
switch (*ptr)
{
case 0:
SD_PowerOff(); /* Power Off */
res = RES_OK;
break;
case 1:
SD_PowerOn(); /* Power On */
res = RES_OK;
break;
case 2:
*(ptr + 1) = SD_CheckPower();
res = RES_OK; /* Power Check */
break;
default:
res = RES_PARERR;
}
}
else
{
/* no disk */
if (Stat & STA_NOINIT) return RES_NOTRDY;
SELECT();
switch (ctrl)
{
case GET_SECTOR_COUNT:
/* SEND_CSD */
if ((SD_SendCmd(CMD9, 0) == 0) && SD_RxDataBlock(csd, 16))
{
if ((csd[0] >> 6) == 1)
{
/* SDC V2 */
csize = csd[9] + ((WORD) csd[8] << 8) + ((DWORD) (csd[7] & 0x3F) << 16) + 1;
*(DWORD*) buff = (DWORD) csize << 10;
}
else
{
/* MMC or SDC V1 */
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:
*(WORD*) buff = 512;
res = RES_OK;
break;
case CTRL_SYNC:
if (SD_ReadyWait() == 0xFF) res = RES_OK;
break;
default:
res = RES_PARERR;
}
DESELECT();
SPI_RxByte();
}
return res;
}