VLSI Solution Oy VLSI Solution Oy Evaluation MP3 Player Source Code Documentation

Main Page | Class List | File List | Class Members | File Members | Related Pages

mmc.c

Go to the documentation of this file.
00001 00006 #include "mmc.h" 00007 #include "board.h" 00008 #include "console.h" 00009 #include "buffer.h" 00010 00011 //#define MMCDEBUG 00012 //#define MMCLONGDEBUG 00013 00014 00019 unsigned char MmcCommand(unsigned char c1, 00020 unsigned char c2, 00021 unsigned char c3, 00022 unsigned char c4, 00023 unsigned char c5){ 00024 00025 00026 xdata char c; 00027 00028 /* Note: c1, c2 are used for temporary variables after use! */ 00029 00030 MMC_OFF = NO; //MMC should be powered, but switch power on just-in-case 00031 MMC_XCS = MMC_NOT_SELECTED; //put MMC on-line. 00032 00033 // Provide clock edges before and after asserting MMC CS 00034 SPI8Clocks(8); 00035 MMC_XCS = MMC_SELECTED; 00036 SPI8Clocks(8); 00037 00038 c=0; 00039 // If card still seems to be busy, give it some time... 00040 while ((SPIGetChar()!=0xff) && (c<100)){ 00041 ConsolePutChar('.'); 00042 c++; 00043 } 00044 00045 00046 // The bus should be stable high now 00047 if (SPI_RESULT_BYTE != 0xff){ 00048 ConsoleWrite("\rUnexpected busy signal from MMC. "); 00049 MMC_XCS = MMC_NOT_SELECTED; //drop MMC offline. 00050 return 0x81; //MMC unexpectedly Busy 00051 } 00052 00053 #ifdef MMCDEBUG 00054 DebugMessage(" \x1b[7m"); 00055 ConsolePutChar('C'); ConsolePutHex8(c1); ConsolePutHex8(c2); ConsolePutHex8(c3); 00056 ConsolePutHex8(c4); ConsolePutHex8(c5); ConsolePutChar('|'); 00057 #endif 00058 00059 // Send the MMC command 00060 SPIPutCharWithoutWaiting(c1); 00061 SPIPutChar(c2); 00062 SPIPutChar(c3); 00063 SPIPutChar(c4); 00064 SPIPutChar(c5); 00065 SPIPutChar(0x95); /* Valid CRC for init, then don't care */ 00066 SPIWait(); 00067 /* Now ok to use c1..c5 as temporaries (dirty but kool) */ 00068 00069 // Wait for R1 style response (bit 7 low) from MMC 00070 #ifdef MMCDEBUG 00071 { 00072 c1=100; /* try max. 100 times */ 00073 while((c1--)&&((c2=SPIGetChar())&0x80)){ //wait for R1 or timeout 00074 // R1 response not detected, if it's not 0xff, print it for debugging. 00075 if (c2!=0xff){ConsolePutHex8(c2);ConsolePutChar('|');} 00076 } 00077 ConsolePutHex8(c2); 00078 DebugMessage("\x1b[0m "); 00079 } 00080 #else 00081 { 00082 c1=100; 00083 while((c1--)&&((c2=SPIGetChar())&0x80)) //wait for R1 or timeout 00084 ; 00085 } 00086 #endif 00087 00088 return c2; //return the R1 response 00089 } 00090 00091 00102 unsigned char MmcWaitForData(){ 00103 00104 unsigned char c; 00105 00106 DebugMessage("<t:"); //Token Wait 00107 00108 00109 // Wait until something else than 0xff is read from the bus 00110 do { 00111 c=SPIGetChar(); 00112 #ifdef MMCDEBUG 00113 if (c!=0xFF) ConsolePutHex8(c); //0xfe token or some erroneus byte 00114 #endif 00115 } while ((c == 0xff)); 00116 00117 // Something was received from the bus? Might it actually be te 00118 // desired 0xFE data start token? 00119 if (c != 0xfe){ 00120 // No data start token, read fail. In an OS an error message would display. 00121 // Since we're in an embedded system, it's unclear what we should do now. 00122 // Current approach is to say all ok but make read block return 0xff's. 00123 // It's not disasterous at least as long as we don't WRITE to MMC. 00124 00125 // Flush any data that might be pending from the MMC. 00126 #ifdef MMCLONGDEBUG 00127 { 00128 unsigned int i; 00129 ConsoleWrite("\rMMCWaitForData failed. "); 00130 ConsoleWrite("Expected 0xFE token, received: "); 00131 for (i=0;i<550;i++){ 00132 ConsolePutHex8(c); 00133 c=SPIGetChar(); 00134 } 00135 } 00136 #else 00137 SPI8Clocks(200); /* Flush MMC by sending lots of FF's to it */ 00138 SPI8Clocks(200); 00139 SPI8Clocks(200); 00140 #endif 00141 00142 MMC_XCS = MMC_NOT_SELECTED; //Drop MMC offline now. 00143 DebugMessage("!t>"); 00144 return 5; //Return error 00145 00146 } 00147 00148 DebugMessage("t>"); //Exit Token Wait 00149 return 0; 00150 00151 } 00152 00153 00159 void MmcGetData(unsigned int amountOctets){ 00160 DebugMessage("<G"); 00161 00162 dataBufPtr = diskSect.raw.buf; 00163 while (amountOctets--){ 00164 *dataBufPtr++=SPIGetChar(); 00165 } 00166 DebugMessage("G>"); 00167 } 00168 00169 00171 unsigned char GetStorageInformation(){ 00172 if (MmcCommand(0x4a,0,0,0,0)&0xfe) 00173 return 4; /* no storage info */ 00174 MmcWaitForData(); 00175 MmcGetData(30); 00176 00177 ConsoleWrite("\rMMC Manufacturer#: "); 00178 ConsolePutUInt(diskSect.raw.buf[0]); 00179 ConsoleWrite("Product Name: "); 00180 ConsolePutChar(diskSect.raw.buf[3]); 00181 ConsolePutChar(diskSect.raw.buf[4]); 00182 ConsolePutChar(diskSect.raw.buf[5]); 00183 ConsolePutChar(diskSect.raw.buf[6]); 00184 ConsolePutChar(diskSect.raw.buf[7]); 00185 ConsolePutChar(diskSect.raw.buf[8]); 00186 ConsoleWrite(" Production date: "); 00187 ConsolePutUInt(diskSect.raw.buf[14]>>4); 00188 ConsoleWrite("/ "); 00189 ConsolePutUInt((diskSect.raw.buf[14]&0x0f)+1997); 00190 ConsolePutChar('\r'); 00191 ConsoleWrite("Media serial number: "); 00192 ConsolePutHex8(diskSect.raw.buf[10]); 00193 ConsolePutHex8(diskSect.raw.buf[11]); 00194 ConsolePutHex8(diskSect.raw.buf[12]); 00195 ConsolePutHex8(diskSect.raw.buf[13]); 00196 ConsolePutChar('\r'); 00197 00198 /* print out card ident register 00199 for (c=0; c<30; c++){ 00200 if ((c&0x03)==0) ConsolePutChar(' '); 00201 ConsolePutHex8(diskSect.raw.buf[c]); 00202 } 00203 */ 00204 00205 return 0; /* All OK return */ 00206 } 00207 00208 00209 00211 unsigned char RebootMMC(){ 00212 unsigned char c; 00213 00214 //Try (indefinitely) to switch on the MMC card 00215 do{ 00216 /* MMC Don't use CRC - seems to be required by some MMC cards? */ 00217 MmcCommand(0x7B,0,0,0,0); 00218 /* MMC Init, command 0x40 should return 0x01 (idle) if all is ok. */ 00219 }while (MmcCommand(0x40,0,0,0,0)!=0x01); 00220 // 0x01(idle) response detected. 00221 00222 ConsoleWrite("Card found, starting... "); 00223 /*Try max 255 times MMC Wake-up call: set to Not Idle (mmc returns 0x00)*/ 00224 c=255; 00225 while ((c--)&&(MmcCommand(0x41,0,0,0,16))){ 00226 Delay(10); 00227 if (c==1){ 00228 //timeout 00229 ConsoleWrite("Failed.\r"); 00230 return 2; /* Not able to power up mmc */ 00231 } 00232 } 00233 00234 ConsoleWrite("Ok.\r"); 00235 return 0; 00236 } 00237 00238 00239 Public unsigned char InitMMC(){ 00240 unsigned char c; 00241 00242 //Switch off the MMC power supply 00243 MMC_OFF = YES; 00244 ConsoleWrite ("Init: MMC\r"); 00245 Delay(100); 00246 00247 //Switch on the MMC power supply 00248 MMC_OFF = NO; 00249 Delay(100); 00250 00251 /* Allow MMC some time and clock cycles to reset */ 00252 for (c=0; c<200; c++){ 00253 SPIPutCharWithoutWaiting(0xff); 00254 SPIWait(); 00255 } 00256 Delay(20); 00257 00258 if (RebootMMC()) return 1; //not able to powerup; 00259 //An existing MMC card should be able to respond now. 00260 00261 GetStorageInformation(); 00262 00263 /* Set Block Size of 512 bytes (2 == 512 << 8) */ 00264 if ((c=MmcCommand(0x50,0,0,2,0))) return c|0x80; /* blocksize error */ 00265 00266 /* Check if MMC supports interrupted data transfer */ 00267 /* This does a simple checksum check to see if interrupted and 00268 * non-interrupted read blocks are the same. */ 00269 /* This could be a function, so it is in braces for clarity purposes */ 00270 { 00271 if (SeekSector(0)) return 2; //Storage powerup failure 00272 if (ReadPhysicalSector()) return 2; //Storage powerup failure 00273 00274 temp.i = diskSect.raw.buf[511]; 00275 for (c=0; c<250; c++){ 00276 temp.i += diskSect.raw.buf[c]; 00277 } 00278 00279 if (SeekSector(0)) return 2;//Storage powerup failure 00280 00281 /* Send some extra SPI clocks */ 00282 MMC_XCS = MMC_NOT_SELECTED; 00283 SPIPutCharWithoutWaiting(0xff); 00284 for (c=0; c<100; c++){ 00285 SPIPutChar(0xff); 00286 } 00287 SPIWait(); 00288 00289 if (ReadPhysicalSector()){ 00290 ConsoleWrite("Interrupted read failed.\r"); 00291 ConsoleWrite("Using compatibility mode.\r"); 00292 return 0x0e; //ok but no support for seek-before-read 00293 00294 }else{ 00295 //Check if received data was same 00296 temp.i -= diskSect.raw.buf[511]; 00297 for (c=0; c<250; c++){ 00298 temp.i -= diskSect.raw.buf[c]; 00299 } 00300 } 00301 00302 if (temp.i) { /* Checksum does not match */ 00303 ConsoleWrite("This MMC has no support for interrupted read. "); 00304 ConsoleWrite("Using compatibility mode.\r"); 00305 return 0x0e; //ok but no support for seek-before-read 00306 } 00307 00308 ConsoleWrite("\rInitMMC ok.\r"); 00309 } 00310 00311 /* All OK return */ 00312 return 0; //ok and MMC supports seek-before-read 00313 00314 } 00315 00316 00319 void PerformBlockRead(){ 00320 00321 #ifdef MMCDEBUG 00322 ConsoleWrite("<R"); //Actual Read 00323 #endif 00324 00325 /* Use shared global buffer pointer for speed*/ 00326 /* Loop unrolled 16 times for SPEED! :) */ 00327 dataBufPtr = diskSect.raw.buf; 00328 while (dataBufPtr < diskSect.raw.buf+512){ 00329 SPIPutCharWithoutWaiting(0xff); 00330 SPIWait(); 00331 *dataBufPtr++=SPI_RESULT_BYTE; 00332 SPIPutCharWithoutWaiting(0xff); 00333 SPIWait(); 00334 *dataBufPtr++=SPI_RESULT_BYTE; 00335 SPIPutCharWithoutWaiting(0xff); 00336 SPIWait(); 00337 *dataBufPtr++=SPI_RESULT_BYTE; 00338 SPIPutCharWithoutWaiting(0xff); 00339 SPIWait(); 00340 *dataBufPtr++=SPI_RESULT_BYTE; 00341 SPIPutCharWithoutWaiting(0xff); 00342 SPIWait(); 00343 *dataBufPtr++=SPI_RESULT_BYTE; 00344 SPIPutCharWithoutWaiting(0xff); 00345 SPIWait(); 00346 *dataBufPtr++=SPI_RESULT_BYTE; 00347 SPIPutCharWithoutWaiting(0xff); 00348 SPIWait(); 00349 *dataBufPtr++=SPI_RESULT_BYTE; 00350 SPIPutCharWithoutWaiting(0xff); 00351 SPIWait(); 00352 *dataBufPtr++=SPI_RESULT_BYTE; 00353 SPIPutCharWithoutWaiting(0xff); 00354 SPIWait(); 00355 *dataBufPtr++=SPI_RESULT_BYTE; 00356 SPIPutCharWithoutWaiting(0xff); 00357 SPIWait(); 00358 *dataBufPtr++=SPI_RESULT_BYTE; 00359 SPIPutCharWithoutWaiting(0xff); 00360 SPIWait(); 00361 *dataBufPtr++=SPI_RESULT_BYTE; 00362 SPIPutCharWithoutWaiting(0xff); 00363 SPIWait(); 00364 *dataBufPtr++=SPI_RESULT_BYTE; 00365 SPIPutCharWithoutWaiting(0xff); 00366 SPIWait(); 00367 *dataBufPtr++=SPI_RESULT_BYTE; 00368 SPIPutCharWithoutWaiting(0xff); 00369 SPIWait(); 00370 *dataBufPtr++=SPI_RESULT_BYTE; 00371 SPIPutCharWithoutWaiting(0xff); 00372 SPIWait(); 00373 *dataBufPtr++=SPI_RESULT_BYTE; 00374 SPIPutCharWithoutWaiting(0xff); 00375 SPIWait(); 00376 *dataBufPtr++=SPI_RESULT_BYTE; 00377 } 00378 00379 #ifdef MMCDEBUG 00380 ConsoleWrite("R>"); 00381 #endif 00382 00383 } 00384 00386 void ConsoleDecipherMMCResponse(unsigned char c){ 00387 00388 ConsoleWrite ("\rSeek failed, MMC returns "); 00389 ConsolePutHex8 (c); 00390 ConsoleWrite ("h ("); 00391 00392 if (c&128) ConsoleWrite("which is NOT an R1 response!!"); 00393 else{ 00394 if (c&64) ConsoleWrite("ParameterError "); 00395 if (c&32) ConsoleWrite("AddressError "); 00396 if (c&16) ConsoleWrite("EraseSequenceError "); 00397 if (c&8) ConsoleWrite("CommandCrcError "); 00398 if (c&4) ConsoleWrite("IllegalCommandCode "); 00399 if (c&2) ConsoleWrite("EraseCancelled "); 00400 if (c&1) ConsoleWrite("Idle "); 00401 } 00402 00403 ConsoleWrite (") for sector "); 00404 ConsolePutUInt (sectorAddress.l); 00405 ConsoleWrite(".\rFurther reading data returns: \r"); 00406 for (c=0; c<255; c++){ 00407 ConsolePutHex8(SPIGetChar()); 00408 } 00409 ConsolePutChar(13); 00410 while (!KEY_BUTTON); 00411 00412 00413 } 00414 00415 00416 00417 /* Unconditionally (really just do it!) seek MMC at offset sectorN*512 */ 00418 Public unsigned char SeekSector(unsigned long sectorN){ 00419 00420 unsigned char c; 00421 00422 #ifdef MMCDEBUG 00423 ConsoleWrite("{s"); //Seek called 00424 #endif 00425 00426 do{ 00427 sectorAddress.l = sectorN * 2; //convert to bytes (combined with 8bit shift) 00428 c=MmcCommand(0x51,sectorAddress.b.b2,sectorAddress.b.b1, 00429 sectorAddress.b.b0, 0); 00430 sectorAddress.l = sectorAddress.l >> 1; //convert back to blocks 00431 00432 //if MMC returs "nothing" (0xff) or 0x81(busy), 00433 //toggle chip select and retry 00434 if ((c==0xff)||(c==0x81)){ 00435 c=0xff; 00436 RebootMMC(); 00437 } 00438 if (c==0x01){ //MMC says "busy" 00439 c=0xff; //try again 00440 } 00441 00442 }while(c==0xff); //repeat until we get signal from MMC. 00443 00444 if ((c & 0xfe)){ //MMC returns something else than idle or busy signal 00445 ConsoleDecipherMMCResponse(c); 00446 MMC_XCS = MMC_NOT_SELECTED; 00447 00448 #ifdef MMCDEBUG 00449 ConsoleWrite("!s}"); 00450 #endif 00451 return 7; /* failed to execute mmc command */ 00452 } 00453 00454 MMC_XCS = MMC_NOT_SELECTED; 00455 00456 #ifdef MMCDEBUG 00457 ConsoleWrite("s}"); 00458 #endif 00459 return 0; //ok return 00460 } 00461 00462 00463 /* Wait for data start token and read 512 bytes to global buffer */ 00464 Public unsigned char ReadPhysicalSector(){ 00465 00466 RED_LED = LED_ON; /* Disk Read LED on */ 00467 00468 MMC_XCS = MMC_SELECTED; 00469 MmcWaitForData(); 00470 PerformBlockRead(); 00471 00472 /* generate SPI clock edges to finish up the command */ 00473 00474 SPI8Clocks(4); //Send 8*4=32 clocks (4 ff's) to MMC to be nice. 00475 MMC_XCS = MMC_NOT_SELECTED; 00476 SPI8Clocks(4); //Again, give the poor MMC some clocks, it likes them. 00477 00478 RED_LED = LED_OFF; /* Disk Read LED off */ 00479 00480 return 0; //ok return 00481 } 00482 00483 00485 unsigned char WritePhysicalSector(){ 00486 unsigned char c; 00487 00488 #ifdef MMCDEBUG 00489 ConsoleWrite("<W"); 00490 #endif 00491 00492 RED_LED = LED_ON; 00493 sectorAddress.l = sectorAddress.l * 2; //convert to bytes (combined with 8bit shift) 00494 c=MmcCommand(24 | 0x40 ,sectorAddress.b.b2,sectorAddress.b.b1, 00495 sectorAddress.b.b0, 0); 00496 sectorAddress.l = sectorAddress.l >> 1; //convert back to blocks 00497 MMC_XCS = MMC_SELECTED; 00498 00499 00500 dataBufPtr = diskSect.raw.buf; 00501 SPIPutCharWithoutWaiting(0xFE); 00502 SPIWait(); 00503 00504 for (c=0;c<128;c++){ 00505 SPIPutCharWithoutWaiting(*dataBufPtr++); 00506 SPIWait(); 00507 SPIPutCharWithoutWaiting(*dataBufPtr++); 00508 SPIWait(); 00509 SPIPutCharWithoutWaiting(*dataBufPtr++); 00510 SPIWait(); 00511 SPIPutCharWithoutWaiting(*dataBufPtr++); 00512 SPIWait(); 00513 } 00514 00515 MmcWaitForData(); //Wait for 0xFE token 00516 while (SPIGetChar()==0) 00517 ; // Wait until MMC not busy. 00518 00519 #ifdef MMCDEBUG 00520 ConsoleWrite("W>"); 00521 #endif 00522 00523 00524 SPI8Clocks(16); 00525 MMC_XCS = MMC_NOT_SELECTED; 00526 SPI8Clocks(16); 00527 00528 RED_LED = LED_OFF; 00529 00530 } 00531 00532

All software copyright 2000-2004 VLSI Solution OY. Redistribution of these software modules are limited to promotional use only and only with the VS1011 / VS1002 / VS1003 MP3-Evakit evaluation boards. Free or commercial use of these software modules in MP3 players is ok if the product includes MP3 decoder chip(s) from VLSI. You can request the complete (compilable) package from mp3@vlsi.fi