0,0 → 1,487 |
/*-----------------------------------------------------------------------*/ |
/* MMC/SDC (in SPI mode) control module (C)ChaN, 2006 */ |
/*-----------------------------------------------------------------------*/ |
/* Only rcvr_spi(), xmit_spi(), disk_timerproc(), disk_initialize () and */ |
/* some macros are platform dependent. */ |
/*-----------------------------------------------------------------------*/ |
|
|
#include <avr/io.h> |
#include "diskio.h" |
|
|
/* Definitions for MMC/SDC command */ |
#define CMD0 (0x40+0) /* GO_IDLE_STATE */ |
#define CMD1 (0x40+1) /* SEND_OP_COND */ |
#define CMD8 (0x40+8) /* SEND_IF_COND */ |
#define CMD9 (0x40+9) /* SEND_CSD */ |
#define CMD10 (0x40+10) /* SEND_CID */ |
#define CMD12 (0x40+12) /* STOP_TRANSMISSION */ |
#define CMD16 (0x40+16) /* SET_BLOCKLEN */ |
#define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */ |
#define CMD18 (0x40+18) /* READ_MULTIPLE_BLOCK */ |
#define CMD23 (0x40+23) /* SET_BLOCK_COUNT */ |
#define CMD24 (0x40+24) /* WRITE_BLOCK */ |
#define CMD25 (0x40+25) /* WRITE_MULTIPLE_BLOCK */ |
#define CMD41 (0x40+41) /* SEND_OP_COND (ACMD) */ |
#define CMD55 (0x40+55) /* APP_CMD */ |
#define CMD58 (0x40+58) /* READ_OCR */ |
|
|
/* Control signals (Platform dependent) */ |
#define SELECT() PORTB &= ~_BV(PB2) /* MMC CS = L */ |
#define DESELECT() PORTB |= _BV(PB2) /* MMC CS = H */ |
|
#define SOCKPORT PINB /* Socket contact port */ |
#define SOCKINS 0x01 /* Card detect switch (PB0) */ |
|
|
|
/*-------------------------------------------------------------------------- |
|
Module Private Functions |
|
---------------------------------------------------------------------------*/ |
|
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 */ |
|
|
|
/*-----------------------------------------------------------------------*/ |
/* Transmit a byte to MMC via SPI (Platform dependent) */ |
/*-----------------------------------------------------------------------*/ |
|
#define xmit_spi(dat) SPDR=(dat); loop_until_bit_is_set(SPSR,SPIF) |
|
|
|
/*-----------------------------------------------------------------------*/ |
/* Receive a byte from MMC via SPI (Platform dependent) */ |
/*-----------------------------------------------------------------------*/ |
|
static |
BYTE rcvr_spi (void) |
{ |
SPDR = 0xFF; |
loop_until_bit_is_set(SPSR, SPIF); |
return SPDR; |
} |
|
/* Alternative macro to receive data fast */ |
#define rcvr_spi_m(dst) SPDR=0xFF; loop_until_bit_is_set(SPSR,SPIF); *(dst)=SPDR |
|
|
|
/*-----------------------------------------------------------------------*/ |
/* Wait for card ready */ |
/*-----------------------------------------------------------------------*/ |
|
static |
BYTE wait_ready (void) |
{ |
BYTE res; |
|
|
Timer2 = 50; /* Wait for ready in timeout of 500ms */ |
rcvr_spi(); |
do |
res = rcvr_spi(); |
while ((res != 0xFF) && Timer2); |
|
return res; |
} |
|
|
|
/*-----------------------------------------------------------------------*/ |
/* 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; |
|
|
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); |
xmit_spi(0xFF); /* CRC (Dummy) */ |
xmit_spi(0xFF); |
resp = rcvr_spi(); /* Reveive data response */ |
if ((resp & 0x1F) != 0x05) /* If not accepted, return with error */ |
return FALSE; |
} |
|
return TRUE; |
} |
#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 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 */ |
|
for (n = 10; n; n--) rcvr_spi(); /* 80 dummy clocks */ |
|
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 */ |
Stat |= STA_NOINIT; /* Set STA_NOINIT */ |
} |
|
return Stat; |
} |
|
|
|
/*-----------------------------------------------------------------------*/ |
/* Get Disk Status */ |
/*-----------------------------------------------------------------------*/ |
|
DSTATUS disk_status ( |
BYTE drv /* Physical drive nmuber (0) */ |
) |
{ |
if (drv) return STA_NOINIT; /* Supports only single drive */ |
return Stat; |
} |
|
|
|
/*-----------------------------------------------------------------------*/ |
/* Read Sector(s) */ |
/*-----------------------------------------------------------------------*/ |
|
DRESULT disk_read ( |
BYTE drv, /* Physical drive nmuber (0) */ |
BYTE *buff, /* Pointer to the data buffer to store read data */ |
DWORD sector, /* Start sector number (LBA) */ |
BYTE count /* Sector count (1..255) */ |
) |
{ |
if (drv || !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 disk_write ( |
BYTE drv, /* Physical drive nmuber (0) */ |
const BYTE *buff, /* Pointer to the data to be written */ |
DWORD sector, /* Start sector number (LBA) */ |
BYTE count /* Sector count (1..255) */ |
) |
{ |
if (drv || !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 disk_ioctl ( |
BYTE drv, /* Physical drive nmuber (0) */ |
BYTE ctrl, /* Control code */ |
void *buff /* Buffer to send/receive data block */ |
) |
{ |
DRESULT res; |
BYTE n, csd[16], *ptr = buff; |
WORD csize; |
|
|
if (drv) return RES_PARERR; |
|
SELECT(); /* CS = L */ |
|
res = RES_ERROR; |
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 (Stat & STA_NOINIT) return RES_NOTRDY; |
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 (Stat & STA_NOINIT) return RES_NOTRDY; |
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 (Stat & STA_NOINIT) return RES_NOTRDY; |
if (send_cmd(CMD58, 0) == 0) { /* READ_OCR */ |
for (n = 0; n < 4; n++) |
*ptr++ = rcvr_spi(); |
res = RES_OK; |
} |
break; |
|
default: |
res = RES_PARERR; |
} |
|
DESELECT(); /* CS = H */ |
rcvr_spi(); /* Idle (Release DO) */ |
|
return res; |
} |
|
|
|
/*---------------------------------------*/ |
/* Device timer interrupt procedure */ |
/* This must be called in period of 10ms */ |
/* (Platform dependent) */ |
|
void disk_timerproc (void) |
{ |
static BYTE pv; |
BYTE n, s; |
|
|
n = Timer1; /* 100Hz decrement timer */ |
if (n) Timer1 = --n; |
n = Timer2; |
if (n) Timer2 = --n; |
|
n = pv; |
pv = SOCKPORT & (SOCKINS); /* Sample socket switch */ |
|
if (n == pv) { /* Have contacts stabled? */ |
s = Stat; |
if (pv & SOCKINS) /* INS = H (Socket empty) */ |
s |= (STA_NODISK | STA_NOINIT); |
else /* INS = L (Card inserted) */ |
s &= ~STA_NODISK; |
|
Stat = s; |
} |
} |
|