📄 ffs_port_mmc.c
字号:
/***********************************************************
* FFS/FAT Port to MMC/SD-slot using SPI-mode *
* *
* by Frank Goetze - www.embedded-os.de *
************************************************************
* FFS_Port_MMC.c *
* FFS/FAT to MMC(SPI) PORT *
************************************************************
* - code generation Frank Goetze 06/2007 *
* - redesign & SDHC-support Frank Goetze 09/2007 *
* - split SD1.xx and (HD-)MMC Frank Goetze 01/2008 *
* detection *
* - add support for older Frank Goetze 01/2008 *
* MMCs and none-standard SDs *
* - add support for MMCplus Frank Goetze 02/2008 *
* (HS-MMC) and HD-MMC cards *
* -> HD-MMC not tested !!! *
* - add lost lines for HD-MMC Frank Goetze 03/2008 *
* - add support for SAM7SExx Frank Goetze 04/2008 *
* and DMA mode for sectors *
* - small bugfix for HD-MMC Frank Goetze 01/2009 *
* *
***********************************************************/
#include "../../inc/MMC/FFS_Port_MMC.h"
#define MMC_EXT_CSD_CHECK // optional support for HS-MMC(MMCplus) and HD-MMC (--> tested on AT91SAM7SExx)
/****************** constant variables ********************/
OS_CONST U08 OS_ROMDATA MMC_m[] = {10,12,13,15,20,25,30,35,40,45,50,55,60,70,80};
/******************* global variables *********************/
FFS_SIZE FFS_values; // device-informations
U08 FFSp_data[16]; // array for reading Rx, CID, CSD (and later ext-CSD)
U08 FFSp_ver; // holder for version (to support SDHC and HD-MMC)
U08 FFSp_sshift; // holder for sec-to-addr shifter (on SDHC and HD-MMC its 0 since we use sector-numbers)
U32 MMC_Nac; // time between CMD-R1 & data on reading (also Ncx)
U32 MMC_Nbs; // time between data-responce & finish on writing (max busy-time)
#ifdef MMC_EXT_CSD_CHECK
U08 FFSp_exCSD[512];
#endif
/*
************************************************************
* write a command to the MMC/SD
************************************************************
*/
U08 MMCWriteCMD(U08 cmd, U32 arg, U08 end)
{
U08 cnt, res;
cnt = 0;
switch(cmd) { // get CRC7s for cmds in front of CRC-OFF
case MMC_GO_IDLE_STATE: res = 0x95; break; // valid CRC7 for RESET
case SD_SEND_IF_COND: res = 0x86; break; // valid CRC7 for IF_COND(0x1AA) on SD/SDHC-cards
default: res = 0xFF; break;
}
FFSPort_MMC_Send(0xFF); // wait "Nrc" in front of CMD
FFSPort_MMC_CS_ON; // enable MMC-interface
FFSPort_MMC_Send(cmd | 0x40); // write CMD plus start-flag
FFSPort_MMC_Send((U08)(arg >> 24)); // write parameter (automatic endian corrected)
FFSPort_MMC_Send((U08)(arg >> 16));
FFSPort_MMC_Send((U08)(arg >> 8));
FFSPort_MMC_Send((U08)(arg));
FFSPort_MMC_Send(res); // write (dummy)-CRC7
if(cmd == MMC_STOP_TRANSMISSION)
FFSPort_MMC_Send(0xFF); // skip stuff-byte after STOP_TRANSMISSION cmd
do {
res = FFSPort_MMC_Send(0xFF); cnt++; // wait "Ncr" for R1 or R1b (normally 1..8 pings)
} while((res == 0xFF) && (cnt < 8));
if(res == 0xFE) res = FFSPort_MMC_Send(0xFF) >> 1; // older MMC-cards comes 1-bit to early on R1
if(end) FFSPort_MMC_CS_OFF; // disable MMC-interface
return(res); // last value
}
/*
************************************************************
* write a R2..R7-command to the MMC/SD
************************************************************
*/
U08 MMCWriteCMD_Rx(U08 cmd, U32 arg, U08 OS_HUGE *buff, U32 count)
{
U32 i;
U08 r1;
r1 = MMCWriteCMD(cmd, arg, 0); // send CMD(arg) and collect first 8bits of Rx (as R1)
for(i = 0; i < count; i++) { // (older MMCs do not support the used cmds - so need here no correction)
buff[i] = FFSPort_MMC_Send(0xFF); // read additional Rx bytes
}
FFSPort_MMC_CS_OFF; // disable MMC-interface
return(r1); // return r1 for caller
}
/*
************************************************************
* read status from the MMC/SD
************************************************************
*/
U08 MMCReadStatus(U08 r1)
{
U08 r[3];
r[0] = r1; // notice R1
r[1] = MMCWriteCMD(MMC_SEND_STATUS, 0, 0); // read R2
r[2] = FFSPort_MMC_Send(0xFF); // control-read (should be 0xFF as readed data)
FFSPort_MMC_CS_OFF; // disable MMC-interface
if(r[0] == 0xFF) return(FFS_PORT_ERR); // if no direct error-code given from caller, return
// you can decode R1 & R2 here
return(r1); // return r1 for caller
}
/*
************************************************************
* read a block from the MMC/SD
************************************************************
*/
U08 MMCReadBlock(U08 cmd, U32 arg, U08 OS_HUGE *buff, U32 count)
{
U32 i;
U08 res;
i = 0;
res = MMCWriteCMD(cmd, arg, 0);
if(res != FFS_NO_ERR) { // wrong response! ..
FFSPort_MMC_CS_OFF; // .. disable MMC-interface ...
return(MMCReadStatus(res)); // ... read status & return the error
}
do {
res = FFSPort_MMC_Send(0xFF); i++; // wait "Nac"/"Ncx" for startbyte (0xFE)
} while((res == 0xFF) && (i < MMC_Nac));
if(i >= MMC_Nac) { // startbyte received ? ..
FFSPort_MMC_CS_OFF; // .. disable MMC-interface ...
return(MMCReadStatus(res)); // ... read status & return the error
}
if(!(res & 0x02)) { // older MMC-cards comes 1-bit to early
U08 s;
for(i = 0; i < count; i++) {
s = res << 7; // save old bits of new byte
res = FFSPort_MMC_Send(0xFF); // get new byte
buff[i] = (res >> 1) | s; // store valid bits of new and saved bits
}
} else { // --> actual cards
#ifdef USE_MMC_DMA
FFSPort_MMC_RxBlock(buff, count); // - read data via DMA
#else
for(i = 0; i < count; i++) {
buff[i] = FFSPort_MMC_Send(0xFF); // - read data
}
#endif
}
FFSPort_MMC_Send(0xFF); FFSPort_MMC_Send(0xFF); // read 2 bytes CRC16 (not used)
FFSPort_MMC_CS_OFF; // disable MMC-interface
return(FFS_NO_ERR);
}
/*
************************************************************
* initialise the FFS-Port (no media-init)
************************************************************
*/
U08 FFSPort_Init(void)
{
MMC_Nac = 8; // Ncx as first "wait for data on read" value - up to CSD was read
MMC_Nbs = 1024; // Nbusy set, to be shure its not zero - up to CSD was read
if(FFSPort_MMC_Init()) return(FFS_PORT_ERR); // init low-level (hardware specific)
return(FFS_NO_ERR);
}
/*
************************************************************
* check media presence
************************************************************
*/
U08 FFSPort_MediaDetect(void)
{
if(FFSPort_MMC_CD()) { // check if a card present
MMC_Nac = 8; // Ncx as first "wait for data on read" value - up to CSD was read
MMC_Nbs = 1024; // Nbusy set, to be shure its not zero - up to CSD was read
return(FFS_NO_MEDIA);
}
return(FFS_NO_ERR);
}
#ifdef MMC_EXT_CSD_CHECK
/*
************************************************************
* check and handle EXT-CSD from MMCplus- & HD-MMC- cards
************************************************************
*/
U32 MMCHandle_ExtCsd(U08 card_type, U32 s_speed, U32 c_size, U16 c_size_mult)
{
U32 h_speed, tmp;
h_speed = 0; // default no higher-speed is set
if((((FFSp_data[0] & 0xC0) >> 6) >= 2) && (((FFSp_data[0] & 0x3C) >> 2) >= 4)) { // be shure it's a MMC4.x card
// why some cards are MMC4.x cards but do not support EXT_CSD ???
// ... and why some cards are not MMC4.x but supports EXT_CSD ???
if(MMCReadBlock(MMC_SEND_EXTCSD, 0, (U08 OS_HUGE *)FFSp_exCSD, 512) == FFS_NO_ERR) { // read EXT_CSD
tmp = ((U32)(FFSp_exCSD[215]) << 24) | ((U32)(FFSp_exCSD[214]) << 16) | ((U32)(FFSp_exCSD[213]) << 8) | FFSp_exCSD[212];
if((FFSp_exCSD[192] >= 2)) { // EXT_CSD.sec_count can only be valid for HD-MMC if EXT_CSD Rev. 1.2 and higher
if((c_size == 0x0FFF) && (c_size_mult == (0x0001 << 9)) && (tmp != 0)) {
FFS_values.maxsectors = tmp; // MMC4.2 says, "c_size" & "c_size_mult" should be set to "max" and a valid sec_count in EXT_CSD
FFS_values.sectorsize = 512; // this sec_count is based on fix 512bytes/sector (also as default blocklength)
if(card_type == FFSp_MARK_HDMMC) { // if HD-MMC -> we work block-based
FFSp_sshift = 0; // code-compatibility -> no shift needed
}
}
}
if(FFSp_exCSD[196] == 0x03) { // if hs-timing possible
if(MMCWriteCMD(MMC_SWITCH, 0x03B90100, 1) == FFS_NO_ERR) { // 03-switch mode(write-byte) , B9-185(hs_timing) , 01-value , 00-set
h_speed = FFSPort_MMC_SetBR(52000000); // was switched -> set "EXT_CSD - high-speed" and return used-speed
}
} else h_speed = FFSPort_MMC_SetBR(26000000); // set "EXT_CSD - normal-speed" and return used-speed
for(tmp = 0; tmp < 0xFFFF; tmp++) { _NOP(); _NOP(); _NOP(); } // wait for resync card-internally (processor-speed depending)
if(h_speed <= s_speed) h_speed = 0; // nothing changed (no timing recalculation needed)
}
}
return(h_speed); // if not zero -> timing recalculation is needed
}
#endif
/*
************************************************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -