📄 mmc.c
字号:
/** \file mmc.c * MMC interface routines for storage.c. */#include "mmc.h"#include "board.h"#include "console.h"#include "buffer.h"//#define MMCDEBUG//#define MMCLONGDEBUG/** Do one MMC command and return the MMC SPI R1 response. * Returns 0xff in case of timeout (relies on weak pull-up on the MISO pin). * Note that the parameter bytes are * used for temporary storage after they are written out. */unsigned char MmcCommand(unsigned char c1, unsigned char c2, unsigned char c3, unsigned char c4, unsigned char c5){ xdata char c; /* Note: c1, c2 are used for temporary variables after use! */ MMC_OFF = NO; //MMC should be powered, but switch power on just-in-case MMC_XCS = MMC_NOT_SELECTED; //put MMC on-line. // Provide clock edges before and after asserting MMC CS SPI8Clocks(8); MMC_XCS = MMC_SELECTED; SPI8Clocks(8); c=0; // If card still seems to be busy, give it some time... while ((SPIGetChar()!=0xff) && (c<100)){ ConsolePutChar('.'); c++; } // The bus should be stable high now if (SPI_RESULT_BYTE != 0xff){ ConsoleWrite("\rUnexpected busy signal from MMC. "); MMC_XCS = MMC_NOT_SELECTED; //drop MMC offline. return 0x81; //MMC unexpectedly Busy }#ifdef MMCDEBUG DebugMessage(" \x1b[7m"); ConsolePutChar('C'); ConsolePutHex8(c1); ConsolePutHex8(c2); ConsolePutHex8(c3); ConsolePutHex8(c4); ConsolePutHex8(c5); ConsolePutChar('|');#endif // Send the MMC command SPIPutCharWithoutWaiting(c1); SPIPutChar(c2); SPIPutChar(c3); SPIPutChar(c4); SPIPutChar(c5); SPIPutChar(0x95); /* Valid CRC for init, then don't care */ SPIWait(); /* Now ok to use c1..c5 as temporaries (dirty but kool) */ // Wait for R1 style response (bit 7 low) from MMC#ifdef MMCDEBUG { c1=100; /* try max. 100 times */ while((c1--)&&((c2=SPIGetChar())&0x80)){ //wait for R1 or timeout // R1 response not detected, if it's not 0xff, print it for debugging. if (c2!=0xff){ConsolePutHex8(c2);ConsolePutChar('|');} } ConsolePutHex8(c2); DebugMessage("\x1b[0m "); }#else { c1=100; while((c1--)&&((c2=SPIGetChar())&0x80)) //wait for R1 or timeout ; }#endif return c2; //return the R1 response}/** MMC Wait For Data start token 0xfe. * First any 0xFF's are read off the mmc. * The first non-ff octet is examined. If it's the 0xfe data start token, * everything is fine. If not, then some error has occurred. * Since we're in an embedded system, it's unclear what we should do then. * Current approach is to say all ok but make read block return 0xff's * by dropping the MMC card offline. Before that we read "lots" of * octets from the MMC to flush any pending data. Finally we return "ALL OK". * It's not disasterous at least as long as we don't WRITE to MMC. */unsigned char MmcWaitForData(){ unsigned char c; unsigned int i = 32000; DebugMessage("<t:"); //Token Wait // Wait until something else than 0xff is read from the bus do { c=SPIGetChar();#ifdef MMCDEBUG if (c!=0xFF) ConsolePutHex8(c); //0xfe token or some erroneus byte#endif } while ((c == 0xff)&&(--i)); if (!i){ //timeout } ConsoleWrite("\rTimeout while waiting for block.\r"); MMC_XCS = MMC_NOT_SELECTED; return 5; //return error } // Something was received from the bus? Might it actually be te // desired 0xFE data start token? if (c != 0xfe){ // No data start token, read fail. In an OS an error message would display. // Since we're in an embedded system, it's unclear what we should do now. // Current approach is to say all ok but make read block return 0xff's. // It's not disasterous at least as long as we don't WRITE to MMC. // Flush any data that might be pending from the MMC.#ifdef MMCLONGDEBUG { unsigned int i; ConsoleWrite("\rMMCWaitForData failed. "); ConsoleWrite("Expected 0xFE token, received: "); for (i=0;i<550;i++){ ConsolePutHex8(c); c=SPIGetChar(); } }#else SPI8Clocks(200); /* Flush MMC by sending lots of FF's to it */ SPI8Clocks(200); SPI8Clocks(200);#endif MMC_XCS = MMC_NOT_SELECTED; //Drop MMC offline now. DebugMessage("!t>"); return 5; //Return error } DebugMessage("t>"); //Exit Token Wait return 0;}/** Read a Data Block from MMC. * Use shared global pointer dataBufPtr declared in buffer.h. * Does not affect MMC chip select signal. * \warning this is not the fastest way to read, use PerformBlockRead to read entire disk block.*/void MmcGetData(unsigned int amountOctets){ DebugMessage("<G"); dataBufPtr = diskSect.raw.buf; while (amountOctets--){ *dataBufPtr++=SPIGetChar(); } DebugMessage("G>");}/** Returns MMC Card Identification Register in diskSect */unsigned char GetStorageInformation(){ if (MmcCommand(0x4a,0,0,0,0)&0xfe) return 4; /* no storage info */ MmcWaitForData(); MmcGetData(30); ConsoleWrite("\rMMC Manufacturer#: "); ConsolePutUInt(diskSect.raw.buf[0]); ConsoleWrite("Product Name: "); ConsolePutChar(diskSect.raw.buf[3]); ConsolePutChar(diskSect.raw.buf[4]); ConsolePutChar(diskSect.raw.buf[5]); ConsolePutChar(diskSect.raw.buf[6]); ConsolePutChar(diskSect.raw.buf[7]); ConsolePutChar(diskSect.raw.buf[8]); ConsoleWrite(" Production date: "); ConsolePutUInt(diskSect.raw.buf[14]>>4); ConsoleWrite("/ "); ConsolePutUInt((diskSect.raw.buf[14]&0x0f)+1997); ConsolePutChar('\r'); ConsoleWrite("Media serial number: "); ConsolePutHex8(diskSect.raw.buf[10]); ConsolePutHex8(diskSect.raw.buf[11]); ConsolePutHex8(diskSect.raw.buf[12]); ConsolePutHex8(diskSect.raw.buf[13]); ConsolePutChar('\r'); /* print out card ident register for (c=0; c<30; c++){ if ((c&0x03)==0) ConsolePutChar(' '); ConsolePutHex8(diskSect.raw.buf[c]); } */ return 0; /* All OK return */}/** Try to switch on the MMC */unsigned char RebootMMC(){ unsigned char c; //Try (indefinitely) to switch on the MMC card do{ /* MMC Don't use CRC - seems to be required by some MMC cards? */ MmcCommand(0x7B,0,0,0,0); /* MMC Init, command 0x40 should return 0x01 (idle) if all is ok. */ }while (MmcCommand(0x40,0,0,0,0)!=0x01); // 0x01(idle) response detected. ConsoleWrite("Card found, starting... "); /*Try max 255 times MMC Wake-up call: set to Not Idle (mmc returns 0x00)*/ c=255; while ((c--)&&(MmcCommand(0x41,0,0,0,16))){ Delay(10); if (c==1){ //timeout ConsoleWrite("Failed.\r"); return 2; /* Not able to power up mmc */ } } ConsoleWrite("Ok.\r"); return 0;}Public unsigned char InitMMC(){ unsigned char c; //Switch off the MMC power supply MMC_OFF = YES; ConsoleWrite ("Init: MMC\r"); Delay(100); //Switch on the MMC power supply MMC_OFF = NO; Delay(100); /* Allow MMC some time and clock cycles to reset */ for (c=0; c<200; c++){ SPIPutCharWithoutWaiting(0xff); SPIWait(); } Delay(20); if (RebootMMC()) return 1; //not able to powerup; //An existing MMC card should be able to respond now. GetStorageInformation(); /* Set Block Size of 512 bytes (2 == 512 << 8) */ if ((c=MmcCommand(0x50,0,0,2,0))) return c|0x80; /* blocksize error */ ConsoleWrite("Scanning storage... "); /* Check if MMC supports interrupted data transfer */ /* This does a simple checksum check to see if interrupted and * non-interrupted read blocks are the same. */ /* This could be a function, so it is in braces for clarity purposes */ { if (SeekSector(0)) return 2; //Storage powerup failure if (ReadPhysicalSector()) return 2; //Storage powerup failure temp.i = diskSect.raw.buf[511]; for (c=0; c<250; c++){ temp.i += diskSect.raw.buf[c]; } ConsoleWrite("Testing for seek mode..."); if (SeekSector(0)) return 2;//Storage powerup failure /* Send some extra SPI clocks */ MMC_XCS = MMC_NOT_SELECTED; SPIPutCharWithoutWaiting(0xff); for (c=0; c<100; c++){ SPIPutChar(0xff); } SPIWait(); if (ReadPhysicalSector()){ ConsoleWrite("Interrupted read failed.\r"); ConsoleWrite("Using compatibility mode.\r"); return 0x0e; //ok but no support for seek-before-read }else{ //Check if received data was same temp.i -= diskSect.raw.buf[511]; for (c=0; c<250; c++){ temp.i -= diskSect.raw.buf[c]; } } if (temp.i) { /* Checksum does not match */ ConsoleWrite("This MMC has no support for interrupted read. "); ConsoleWrite("Using compatibility mode.\r"); return 0x0e; //ok but no support for seek-before-read } ConsoleWrite("\rInitMMC ok.\r"); } /* All OK return */ return 0; //ok and MMC supports seek-before-read}/** Perform the actual reading of 512 octets from MMC. * Note: This is the fast routine to read complete disk block */void PerformBlockRead(){#ifdef MMCDEBUG ConsoleWrite("<R"); //Actual Read#endif /* Use shared global buffer pointer for speed*/ /* Loop unrolled 16 times for SPEED! :) */ dataBufPtr = diskSect.raw.buf; while (dataBufPtr < diskSect.raw.buf+512){ SPIPutCharWithoutWaiting(0xff); SPIWait(); *dataBufPtr++=SPI_RESULT_BYTE; SPIPutCharWithoutWaiting(0xff); SPIWait(); *dataBufPtr++=SPI_RESULT_BYTE; SPIPutCharWithoutWaiting(0xff); SPIWait(); *dataBufPtr++=SPI_RESULT_BYTE; SPIPutCharWithoutWaiting(0xff); SPIWait(); *dataBufPtr++=SPI_RESULT_BYTE; SPIPutCharWithoutWaiting(0xff); SPIWait(); *dataBufPtr++=SPI_RESULT_BYTE; SPIPutCharWithoutWaiting(0xff); SPIWait(); *dataBufPtr++=SPI_RESULT_BYTE; SPIPutCharWithoutWaiting(0xff); SPIWait(); *dataBufPtr++=SPI_RESULT_BYTE; SPIPutCharWithoutWaiting(0xff); SPIWait(); *dataBufPtr++=SPI_RESULT_BYTE; SPIPutCharWithoutWaiting(0xff); SPIWait(); *dataBufPtr++=SPI_RESULT_BYTE; SPIPutCharWithoutWaiting(0xff); SPIWait(); *dataBufPtr++=SPI_RESULT_BYTE; SPIPutCharWithoutWaiting(0xff); SPIWait(); *dataBufPtr++=SPI_RESULT_BYTE; SPIPutCharWithoutWaiting(0xff); SPIWait(); *dataBufPtr++=SPI_RESULT_BYTE; SPIPutCharWithoutWaiting(0xff); SPIWait(); *dataBufPtr++=SPI_RESULT_BYTE; SPIPutCharWithoutWaiting(0xff); SPIWait(); *dataBufPtr++=SPI_RESULT_BYTE; SPIPutCharWithoutWaiting(0xff); SPIWait(); *dataBufPtr++=SPI_RESULT_BYTE; SPIPutCharWithoutWaiting(0xff); SPIWait(); *dataBufPtr++=SPI_RESULT_BYTE; }#ifdef MMCDEBUG ConsoleWrite("R>");#endif}/** Print a human-readable R1 response to console */void ConsoleDecipherMMCResponse(unsigned char c){ ConsoleWrite ("\rSeek failed, MMC returns "); ConsolePutHex8 (c); ConsoleWrite ("h ("); if (c&128) ConsoleWrite("which is NOT an R1 response!!"); else{ if (c&64) ConsoleWrite("ParameterError "); if (c&32) ConsoleWrite("AddressError "); if (c&16) ConsoleWrite("EraseSequenceError "); if (c&8) ConsoleWrite("CommandCrcError "); if (c&4) ConsoleWrite("IllegalCommandCode "); if (c&2) ConsoleWrite("EraseCancelled "); if (c&1) ConsoleWrite("Idle "); } ConsoleWrite (") for sector "); ConsolePutUInt (sectorAddress.l); ConsoleWrite(".\rFurther reading data returns: \r"); for (c=0; c<255; c++){ ConsolePutHex8(SPIGetChar()); } ConsolePutChar(13); while (!KEY_BUTTON);}/* Unconditionally (really just do it!) seek MMC at offset sectorN*512 */Public unsigned char SeekSector(unsigned long sectorN){ unsigned char c; #ifdef MMCDEBUG ConsoleWrite("{s"); //Seek called#endif do{ sectorAddress.l = sectorN * 2; //convert to bytes (combined with 8bit shift) c=MmcCommand(0x51,sectorAddress.b.b2,sectorAddress.b.b1, sectorAddress.b.b0, 0); sectorAddress.l = sectorAddress.l >> 1; //convert back to blocks //if MMC returs "nothing" (0xff) or 0x81(busy), //toggle chip select and retry if ((c==0xff)||(c==0x81)){ c=0xff; RebootMMC(); } if (c==0x01){ //MMC says "busy" c=0xff; //try again } }while(c==0xff); //repeat until we get signal from MMC. if ((c & 0xfe)){ //MMC returns something else than idle or busy signal ConsoleDecipherMMCResponse(c); MMC_XCS = MMC_NOT_SELECTED;#ifdef MMCDEBUG ConsoleWrite("!s}");#endif return 7; /* failed to execute mmc command */ } MMC_XCS = MMC_NOT_SELECTED; #ifdef MMCDEBUG ConsoleWrite("s}");#endif return 0; //ok return}/* Wait for data start token and read 512 bytes to global buffer */Public unsigned char ReadPhysicalSector(){ RED_LED = LED_ON; /* Disk Read LED on */ MMC_XCS = MMC_SELECTED; //ConsoleWrite("<wait>"); MmcWaitForData(); //ConsoleWrite("<read>"); PerformBlockRead(); //ConsoleWrite("<deselect>"); /* generate SPI clock edges to finish up the command */ SPI8Clocks(4); //Send 8*4=32 clocks (4 ff's) to MMC to be nice. MMC_XCS = MMC_NOT_SELECTED; SPI8Clocks(4); //Again, give the poor MMC some clocks, it likes them. RED_LED = LED_OFF; /* Disk Read LED off */ return 0; //ok return}/** Perform MMC block write from diskSect */unsigned char WritePhysicalSector(){ unsigned char c; #ifdef MMCDEBUG ConsoleWrite("<W");#endif RED_LED = LED_ON; sectorAddress.l = sectorAddress.l * 2; //convert to bytes (combined with 8bit shift) c=MmcCommand(24 | 0x40 ,sectorAddress.b.b2,sectorAddress.b.b1, sectorAddress.b.b0, 0); sectorAddress.l = sectorAddress.l >> 1; //convert back to blocks MMC_XCS = MMC_SELECTED; dataBufPtr = diskSect.raw.buf; SPIPutCharWithoutWaiting(0xFE); SPIWait(); for (c=0;c<128;c++){ SPIPutCharWithoutWaiting(*dataBufPtr++); SPIWait(); SPIPutCharWithoutWaiting(*dataBufPtr++); SPIWait(); SPIPutCharWithoutWaiting(*dataBufPtr++); SPIWait(); SPIPutCharWithoutWaiting(*dataBufPtr++); SPIWait(); } MmcWaitForData(); //Wait for 0xFE token while (SPIGetChar()==0) ; // Wait until MMC not busy. #ifdef MMCDEBUG ConsoleWrite("W>");#endif SPI8Clocks(16); MMC_XCS = MMC_NOT_SELECTED; SPI8Clocks(16); RED_LED = LED_OFF;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -