##// END OF EJS Templates
Added STM32F4 target, improved rules files, added additional Include folder var...
Added STM32F4 target, improved rules files, added additional Include folder var for project files.

File last commit:

r12:cc0fb1c881c0 default
r14:c6ae61909bfd default
Show More
sdcard.c
482 lines | 15.0 KiB | text/x-c | CLexer
/*------------------------------------------------------------------------------
-- This file is a part of the libuc, microcontroler library
-- Copyright (C) 2012, Alexis Jeandet
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to the Free Software
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-------------------------------------------------------------------------------
-- Author : Alexis Jeandet
-- Mail : alexis.jeandet@gmail.com
-------------------------------------------------------------------------------*/
#include "sdcard.h"
static volatile
unsigned int Timer1, Timer2; /* 1kHz decrement timer stopped at zero (disk_timerproc()) */
void sdcarddeselect (blkdevice* _this)
{
char d;
_this->select(0);
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,&d, 1); /* Dummy clock (force DO hi-z for multiple slave SPI) */
}
int sdcardselect (blkdevice* _this) /* 1:OK, 0:Timeout */
{
char d;
_this->select(1);
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,&d, 1); /* Dummy clock (force DO enabled) */
if (sdcardwait_ready(((sdcardDev*)_this->phy))) return 1; /* OK */
_this->select(0);
return 0; /* Timeout */
}
int sdcardwait_ready (sdcardDev* sdcard) /* 1:OK, 0:Timeout */
{
char d;
unsigned int tmr;
volatile unsigned int i=0;
for (tmr = 100000; tmr; tmr--) { /* Wait for ready in timeout of 500ms */
sdcard->rcvr_mmc(sdcard->phy,&d, 1);
if (d == 0xFF) break;
for(i=0;i<128;i++);
}
return tmr ? 1 : 0;
}
int sdcardrcvr_datablock (sdcardDev* sdcard,char *buff,unsigned int btr)
{
char d[2];
unsigned int tmr;
volatile unsigned int i=0;
for (tmr = 50000; tmr; tmr--) { /* Wait for data packet in timeout of 100ms */
sdcard->rcvr_mmc(sdcard->phy,d, 1);
if (d[0] != 0xFF) break;
for(i=0;i<64;i++);
}
if (d[0] != 0xFE) return 0; /* If not valid data token, return with error */
sdcard->rcvr_mmc(sdcard->phy,buff, btr); /* Receive the data block into buffer */
sdcard->rcvr_mmc(sdcard->phy,d, 2); /* Discard CRC */
return 1; /* Return with success */
}
int sdcardxmit_datablock (sdcardDev* sdcard,const char *buff,char token)
{
char d[2];
if (!sdcardwait_ready(sdcard)) return 0;
d[0] = token;
sdcard->xmit_mmc(sdcard->phy,d, 1); /* Xmit a token */
if (token != 0xFD) { /* Is it data token? */
sdcard->xmit_mmc(sdcard->phy,buff, 512); /* Xmit the 512 byte data block to MMC */
sdcard->rcvr_mmc(sdcard->phy,d, 2); /* Xmit dummy CRC (0xFF,0xFF) */
sdcard->rcvr_mmc(sdcard->phy,d, 1); /* Receive data response */
if ((d[0] & 0x1F) != 0x05) /* If not accepted, return with error */
return 0;
}
return 1;
}
char sdcardsend_cmd (blkdevice* _this,char cmd,unsigned int arg)
{
char n, d, buf[6];
if (cmd & 0x80) { /* ACMD<n> is the command sequense of CMD55-CMD<n> */
cmd &= 0x7F;
n = sdcardsend_cmd(_this,CMD55, 0);
if (n > 1) return n;
}
/* Select the card and wait for ready */
sdcarddeselect(_this);
if (!sdcardselect(_this)) return 0xFF;
/* Send a command packet */
buf[0] = 0x40 | cmd; /* Start + Command index */
buf[1] = (char)(arg >> 24); /* Argument[31..24] */
buf[2] = (char)(arg >> 16); /* Argument[23..16] */
buf[3] = (char)(arg >> 8); /* Argument[15..8] */
buf[4] = (char)arg; /* Argument[7..0] */
n = 0x01; /* Dummy CRC + Stop */
if (cmd == CMD0) n = 0x95; /* (valid CRC for CMD0(0)) */
if (cmd == CMD8) n = 0x87; /* (valid CRC for CMD8(0x1AA)) */
if (cmd == CMD55) n = 0x63;
buf[5] = n;
((sdcardDev*)_this->phy)->xmit_mmc(((sdcardDev*)_this->phy)->phy,buf, 6);
/* Receive command response */
if (cmd == CMD12) {((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,&d, 1);} /* Skip a stuff byte when stop reading */
n = 10; /* Wait for a valid response in timeout of 10 attempts */
do
{
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,&d, 1);
//libucprintf("resp=%d\n\r",d);
}
while ((d & 0x80) && --n);
return d; /* Return with the response value */
}
DSTATUS sdcarddisk_status (blkdevice* _this)
{
DSTATUS s = ((sdcardDev*)_this->phy)->Stat;
char ocr[4];
if ((_this==0) || !_this->detect()) {
s = STA_NODISK | STA_NOINIT;
} else {
s &= ~STA_NODISK;
if (_this->writeprotected()) /* Check card write protection */
s |= STA_PROTECT;
else
s &= ~STA_PROTECT;
if (!(s & STA_NOINIT)) {
if (sdcardsend_cmd(_this,CMD58, 0)) /* Check if the card is kept initialized */
s |= STA_NOINIT;
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,ocr, 4);
_this->select(0);
}
}
((sdcardDev*)_this->phy)->Stat = s;
return s;
}
DSTATUS sdcarddisk_initialize (blkdevice* _this)
{
char n, cmd, ty, ocr[4],ocr2[4];
const unsigned char dummy=0xff;
unsigned int speed = 1000000;
((sdcardDev*)_this->phy)->setspeed(((sdcardDev*)_this->phy)->phy,350000);
if (_this==0) return STA_NOINIT; /* Supports only drive 0 */
if (((sdcardDev*)_this->phy)->Stat & STA_NODISK) return ((sdcardDev*)_this->phy)->Stat; /* Is card existing in the soket? */
for (n = 10; n; n--) ((sdcardDev*)_this->phy)->xmit_mmc(((sdcardDev*)_this->phy)->phy,&dummy, 1); /* Send 80 dummy clocks */
ty = 0;
if (sdcardsend_cmd(_this,CMD0, 0) == 1) { /* Put the card SPI/Idle state */
Timer1 = 1000; /* Initialization timeout = 1 sec */
if (sdcardsend_cmd(_this,CMD8, 0x1AA) == 1) { /* SDv2? */
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,ocr, 4);
//for (n = 0; n < 4; n++) ocr[n] = xchg_spi(0xFF); /* Get 32 bit return value of R7 resp */
if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* Is the card supports vcc of 2.7-3.6V? */
while (Timer1 && sdcardsend_cmd(_this,ACMD41, 1UL << 30)) ; /* Wait for end of initialization with ACMD41(HCS) */
if (Timer1 && sdcardsend_cmd(_this,CMD58, 0) == 0) { /* Check CCS bit in the OCR */
//for (n = 0; n < 4; n++) ocr[n] = xchg_spi(0xFF);
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,ocr, 4);
ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* Card id SDv2 */
}
}
} else { /* Not SDv2 card */
if (sdcardsend_cmd(_this,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 && sdcardsend_cmd(_this,cmd, 0)) ; /* Wait for end of initialization */
if (!Timer1 || sdcardsend_cmd(_this,CMD16, 512) != 0) /* Set block length: 512 */
ty = 0;
}
}
((sdcardDev*)_this->phy)->CardType = ty; /* Card type */
if (ty) { /* OK */
((sdcardDev*)_this->phy)->Stat &= ~STA_NOINIT; /* Clear STA_NOINIT flag */
sdcardsend_cmd(_this,CMD58, 0);
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,ocr, 4);
do
{
speed+=1000000;
((sdcardDev*)_this->phy)->setspeed(((sdcardDev*)_this->phy)->phy,speed);
sdcardsend_cmd(_this,CMD58, 0);
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,ocr2, 4);
for(n=0;n<4;n++)
{
if(ocr[n]!=ocr2[n])
{
n=0;
break;
}
}
if(speed>(50*1000*1000))
{
break;
}
}while(n);
if(!n)
{
speed-=1000000;
((sdcardDev*)_this->phy)->setspeed(((sdcardDev*)_this->phy)->phy,speed);
}
} else { /* Failed */
//power_off();
((sdcardDev*)_this->phy)->Stat = STA_NOINIT;
}
sdcarddeselect (_this);
return ((sdcardDev*)_this->phy)->Stat;
}
DRESULT sdcarddisk_read (blkdevice* _this,char *buff,unsigned int sector,char count)
{
DSTATUS s;
s = sdcarddisk_status(_this);
if (s & STA_NOINIT) return RES_NOTRDY;
if (!count) return RES_PARERR;
if (!(((sdcardDev*)_this->phy)->CardType & CT_BLOCK)) sector *= 512; /* Convert LBA to byte address if needed */
if (count == 1) { /* Single block read */
if ((sdcardsend_cmd(_this,CMD17, sector) == 0) && sdcardrcvr_datablock((sdcardDev*)(_this->phy),buff, 512))
count = 0;
}
else { /* Multiple block read */
if (sdcardsend_cmd(_this,CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */
do {
if (!sdcardrcvr_datablock (((sdcardDev*)_this->phy),buff, 512)) break;
buff += 512;
} while (--count);
sdcardsend_cmd(_this,CMD12, 0); /* STOP_TRANSMISSION */
}
}
sdcarddeselect (_this);
return count ? RES_ERROR : RES_OK;
}
DRESULT sdcarddisk_write (blkdevice* _this,const char *buff,unsigned int sector,char count)
{
DSTATUS s;
s = sdcarddisk_status(_this);
if (s & STA_NOINIT) return RES_NOTRDY;
if (s & STA_PROTECT) return RES_WRPRT;
if (!count) return RES_PARERR;
if (!(((sdcardDev*)_this->phy)->CardType & CT_BLOCK)) sector *= 512; /* Convert LBA to byte address if needed */
if (count == 1) { /* Single block write */
if ((sdcardsend_cmd(_this,CMD24, sector) == 0) /* WRITE_BLOCK */
&& sdcardxmit_datablock (((sdcardDev*)_this->phy),buff, 0xFE))
count = 0;
}
else { /* Multiple block write */
if (((sdcardDev*)_this->phy)->CardType & CT_SDC) sdcardsend_cmd(_this,ACMD23, count);
if (sdcardsend_cmd(_this,CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */
do {
if (!sdcardxmit_datablock (((sdcardDev*)_this->phy),buff, 0xFC)) break;
buff += 512;
} while (--count);
if (!sdcardxmit_datablock (((sdcardDev*)_this->phy),0, 0xFD)) /* STOP_TRAN token */
count = 1;
}
}
sdcarddeselect (_this);
return count ? RES_ERROR : RES_OK;
}
DRESULT sdcarddisk_ioctl (blkdevice* _this,char ctrl,void *buff)
{
DRESULT res;
const unsigned char dummy=0xff;
char n, csd[16], *ptr = buff;
unsigned short csize;
unsigned long *dp, st, ed;
if (_this==0) return RES_PARERR; /* Check parameter */
if (((sdcardDev*)_this->phy)->Stat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */
res = RES_ERROR;
switch (ctrl) {
case CTRL_SYNC : /* Wait for end of internal write process of the drive */
if (sdcardselect (_this)) {
sdcarddeselect (_this);
res = RES_OK;
}
break;
case GET_SECTOR_COUNT : /* Get drive capacity in unit of sector (DWORD) */
if ((sdcardsend_cmd(_this,CMD9, 0) == 0) && sdcardrcvr_datablock (((sdcardDev*)_this->phy),csd, 16)) {
if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */
csize = csd[9] + ((unsigned short)csd[8] << 8) + 1;
*(unsigned long*)buff = (unsigned long)csize << 10;
} else { /* SDC ver 1.XX or MMC ver 3 */
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
csize = (csd[8] >> 6) + ((unsigned short)csd[7] << 2) + ((unsigned short)(csd[6] & 3) << 10) + 1;
*(unsigned long*)buff = (unsigned long)csize << (n - 9);
}
res = RES_OK;
}
break;
case GET_SECTOR_SIZE : /* Get sector size in unit of byte (WORD) */
*(unsigned short*)buff = 512;
res = RES_OK;
break;
case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */
if (((sdcardDev*)_this->phy)->CardType & CT_SD2) { /* SDC ver 2.00 */
if (sdcardsend_cmd(_this,ACMD13, 0) == 0) { /* Read SD status */
((sdcardDev*)_this->phy)->xmit_mmc(((sdcardDev*)_this->phy)->phy,&dummy, 1);
if (sdcardrcvr_datablock (((sdcardDev*)_this->phy),csd, 16)) { /* Read partial block */
for (n = 64 - 16; n; n--) ((sdcardDev*)_this->phy)->xmit_mmc(((sdcardDev*)_this->phy)->phy,&dummy, 1); /* Purge trailing data */
*(unsigned long*)buff = 16UL << (csd[10] >> 4);
res = RES_OK;
((sdcardDev*)_this->phy)->xmit_mmc(((sdcardDev*)_this->phy)->phy,&dummy, 1);
}
}
} else { /* SDC ver 1.XX or MMC */
if ((sdcardsend_cmd(_this,CMD9, 0) == 0) && sdcardrcvr_datablock (((sdcardDev*)_this->phy),csd, 16)) { /* Read CSD */
if (((sdcardDev*)_this->phy)->CardType & CT_SD1) { /* SDC ver 1.XX */
*(unsigned long*)buff = (((csd[10] & 63) << 1) + ((unsigned short)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1);
} else { /* MMC */
*(unsigned long*)buff = ((unsigned short)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1);
}
res = RES_OK;
}
}
break;
case CTRL_ERASE_SECTOR : /* Erase a block of sectors (used when _USE_ERASE == 1) */
if (!(((sdcardDev*)_this->phy)->CardType & CT_SDC)) break; /* Check if the card is SDC */
if (sdcarddisk_ioctl(_this, 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 (!(((sdcardDev*)_this->phy)->CardType & CT_BLOCK)) {
st *= 512; ed *= 512;
}
if (sdcardsend_cmd(_this,CMD32, st) == 0 && sdcardsend_cmd(_this,CMD33, ed) == 0 && sdcardsend_cmd(_this,CMD38, 0) == 0 && sdcardwait_ready(((sdcardDev*)_this->phy))) /* Erase sector block */
res = RES_OK; /* FatFs does not check result of this command */
break;
/* Following command are not used by FatFs module */
case MMC_GET_TYPE : /* Get MMC/SDC type (BYTE) */
*ptr = ((sdcardDev*)_this->phy)->CardType;
res = RES_OK;
break;
case MMC_GET_CSD : /* Read CSD (16 bytes) */
if (sdcardsend_cmd(_this,CMD9, 0) == 0 /* READ_CSD */
&& sdcardrcvr_datablock (((sdcardDev*)_this->phy),ptr, 16))
res = RES_OK;
break;
case MMC_GET_CID : /* Read CID (16 bytes) */
if (sdcardsend_cmd(_this,CMD10, 0) == 0 /* READ_CID */
&& sdcardrcvr_datablock (((sdcardDev*)_this->phy),ptr, 16))
res = RES_OK;
break;
case MMC_GET_OCR : /* Read OCR (4 bytes) */
if (sdcardsend_cmd(_this,CMD58, 0) == 0) { /* READ_OCR */
//for (n = 4; n; n--) *ptr++ =
((sdcardDev*)_this->phy)->rcvr_mmc(((sdcardDev*)_this->phy)->phy,ptr, 4);
res = RES_OK;
}
break;
case MMC_GET_SDSTAT : /* Read SD status (64 bytes) */
if (sdcardsend_cmd(_this,ACMD13, 0) == 0) { /* SD_STATUS */
((sdcardDev*)_this->phy)->xmit_mmc(((sdcardDev*)_this->phy)->phy,&dummy, 1);
if (sdcardrcvr_datablock (((sdcardDev*)_this->phy),ptr, 64))
res = RES_OK;
}
break;
default:
res = RES_PARERR;
}
sdcarddeselect (_this);
return res;
}
void sdcardmake(sdcardDev* sdcard,UHANDLE phy,void (*rcvr_mmc) (UHANDLE,char *,unsigned int ),void (*xmit_mmc) (UHANDLE,const char *,unsigned int ),void (*setspeed) (UHANDLE phy,unsigned int speed),unsigned int (*getspeed) (UHANDLE phy))
{
sdcard->phy = phy;
sdcard->rcvr_mmc = rcvr_mmc;
sdcard->xmit_mmc = xmit_mmc;
sdcard->setspeed = setspeed;
sdcard->getspeed = getspeed;
}
void sdcardmakeblkdev(blkdevice* dev,sdcardDev* sdcard, blkdevselect_t select,blkdevpower_t power,blkdevdetect_t detect,blkdevwriteprotected_t writeprotected)
{
dev->phy=sdcard;
dev->select=select;
dev->power = power;
dev->detect = detect;
dev->writeprotected = writeprotected;
dev->write = sdcarddisk_write;
dev->read = sdcarddisk_read;
dev->ioctl = sdcarddisk_ioctl;
dev->initialize = sdcarddisk_initialize;
dev->status = sdcarddisk_status;
}