##// END OF EJS Templates
Added Oplayer BSP, Fixed bug on GPIO library(gpiosetval change all the port...
Added Oplayer BSP, Fixed bug on GPIO library(gpiosetval change all the port instead of the desired bit).

File last commit:

r60:17402611bd25 dev_alexis
r60:17402611bd25 dev_alexis
Show More
sdcard.c
502 lines | 15.9 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>
#include <sdcard-spi.h>
#include <stdio.h>
#include <core.h>
int sdcardselect (blkdeviceptr _this);
void sdcarddeselect (blkdeviceptr _this);
int sdcardwait_ready (sdcardDev* sdcard);
int sdcardxmit_datablock (sdcardDev* sdcard,const char *buff,char token);
int sdcardrcvr_datablock (sdcardDev* sdcard,char *buff,uint32_t btr);
char sdcardsend_cmd (blkdeviceptr _this,char cmd,uint32_t arg);
DSTATUS sdcarddisk_status (blkdeviceptr _this);
DSTATUS sdcarddisk_initialize (blkdeviceptr _this);
DRESULT sdcarddisk_read (blkdeviceptr _this,char *buff,uint32_t sector,char count);
DRESULT sdcarddisk_write (blkdeviceptr _this,const char *buff,uint32_t sector,char count);
DRESULT sdcarddisk_ioctl (blkdeviceptr _this,char ctrl,void *buff);
static volatile
unsigned int Timer1, Timer2; /* 1kHz decrement timer stopped at zero (disk_timerproc()) */
void sdcarddeselect (blkdeviceptr _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 (blkdeviceptr _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=0;
unsigned int tmr;
volatile unsigned int i=0;
for (tmr = 1000; tmr>0; tmr--) { /* Wait for ready in timeout of 500ms */
sdcard->rcvr_mmc(sdcard->phy,&d, 1);
if (d == 0xFF) break;
delay_100us(10);
}
return tmr != 0;
}
int sdcardrcvr_datablock (sdcardDev* sdcard,char *buff,uint32_t 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 (blkdeviceptr _this,char cmd,uint32_t 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)) {printf("Can't select SDCARD\n");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 (blkdeviceptr _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 (blkdeviceptr _this)
{
char cmd, ty, ocr[4],ocr2[4];
int n;
const char dummy=0xff;
uint32_t speed = 1000000;
((sdcardDev*)_this->phy)->setspeed(((sdcardDev*)_this->phy)->phy,350000);
if (_this==0) return STA_NOINIT; /* Supports only drive 0 */
/* Is card existing in the soket? */
if (((sdcardDev*)_this->phy)->Stat & STA_NODISK)
{
printf("No SDCARD\n");
return ((sdcardDev*)_this->phy)->Stat;
}
printf("SDCARD Detected\n");
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 */
printf("SDCARD in Idle mode\n");
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 (blkdeviceptr _this,char *buff,uint32_t 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 (blkdeviceptr _this,const char *buff,uint32_t 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 (blkdeviceptr _this,char ctrl,void *buff)
{
DRESULT res;
const char dummy=0xff;
char n, csd[16], *ptr = buff;
uint16_t csize;
uint32_t *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] + ((uint16_t)csd[8] << 8) + 1;
*(uint32_t*)buff = (uint32_t)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) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1;
*(uint32_t*)buff = (uint32_t)csize << (n - 9);
}
res = RES_OK;
}
break;
case GET_SECTOR_SIZE : /* Get sector size in unit of byte (WORD) */
*(uint16_t*)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 */
*(uint32_t*)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 */
*(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_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 sdcardspimake(sdcardDev* sdcard,UHANDLE phy,void (*rcvr_mmc) (UHANDLE,char *,uint32_t),void (*xmit_mmc) (UHANDLE,const char *,uint32_t ),void (*setspeed) (UHANDLE phy,uint32_t speed),uint32_t (*getspeed) (UHANDLE phy))
{
sdcard->phy = phy;
sdcard->rcvr_mmc = rcvr_mmc;
sdcard->xmit_mmc = xmit_mmc;
sdcard->setspeed = setspeed;
sdcard->getspeed = getspeed;
}
void sdcardspimakeblkdev(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;
}