⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mmc.c

📁 MP3播放器源代码, VS1003B
💻 C
字号:
/** \file mmc.c
 * MMC interface routines for storage.c.
 */

#include "mmc.h"
#include "board.h"
#include "storage.h" //for reporting back the serial number
#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){


  unsigned int i;


  /* Note: c1, c2 are used for temporary variables after use! */  

  // MMC_OFF = NO; //MMC should be powered, but switch power on just-in-case
  // MMCDeselect(); //put MMC on-line.
  // Provide clock edges before and after asserting MMC CS

  MMCDeselect();
  SPI8Clocks(8); 
  MMCSelect();
  SPI8Clocks(8); 

  i=0;
  // If card still seems to be busy, give it some time... 
  // changed 12/2005 to give quite a lot of time.
  while ((SPIGetChar()!=0xff) && (++i<60000));
    ; 
 
  // The bus should be stable high now
  if ((i=SPI_RESULT_BYTE) != 0xff){
    ConsoleWrite("\rUnexpected busy signal from MMC. ");
    ConsolePutHex16(i);
    MMCDeselect();
    Delay(1000);
    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.
 * 12/2005: Well, now we do write to mmc...
 */
unsigned char MmcWaitForData(){

  unsigned char c;
  unsigned int i; 
  
  DebugMessage("<t:"); //Token Wait

  i = 60000; //try max. 60000 bus cycles
  // Wait until something else than 0xff is read from the bus
  do {
    c=SPIGetChar();
    --i;
  } while ((c == 0xff) && (i));

  // 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
    ConsoleWrite(" NoData ");
    SPI8Clocks(200); /* Flush MMC by sending lots of FF's to it */
    SPI8Clocks(200);
    SPI8Clocks(200);
#endif

    MMCDeselect();
    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]);

  //mediaSerialNumber[0] = diskSect.raw.buf[10];
  //mediaSerialNumber[1] = diskSect.raw.buf[11];
  //mediaSerialNumber[2] = diskSect.raw.buf[12];
  //mediaSerialNumber[3] = 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{
    SPI8Clocks(8);      
    /* 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);

  MMCSelect();
  /* 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 */
  
  /* 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];
    }
    
    if (SeekSector(0)) return 2;//Storage powerup failure
    
    /* Send some extra SPI clocks */
    MMCDeselect();
    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 
 * If you have DMA, write this to use it!
 */
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.
  In English: Send the Read Sector command to MMC
*/
Public unsigned char SeekSector(unsigned long sectorN){
  unsigned char c, retries;

  retries = 0;
  do{
    retries++;
    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 works properly, it returns Busy (== Not idle) at this stage.    
    if (c!=0x00){
      if (c != 0xff){ 
        //MMC returns something else than Busy or "Idle Bus", print what it is
        ConsoleDecipherMMCResponse(c);
      }
      // Something is wrong, take the standard action...
      RebootMMC();
      if (retries > 10){  
        return 7; /* failed to execute mmc command */
      }
    }
  }while(c!=0x00); //repeat until we get busy signal from MMC.
    
  MMCDeselect();
  
  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 */

  MMCSelect();
  MmcWaitForData();
  PerformBlockRead();

  /* generate SPI clock edges to finish up the command */

  SPI8Clocks(4); //Send 8*4=32 clocks (4 ff's) to MMC to be nice.
  MMCDeselect();
  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(0x40 | 24, sectorAddress.b.b2, sectorAddress.b.b1,
	       sectorAddress.b.b0, 0);
  sectorAddress.l = sectorAddress.l >> 1; //convert back to blocks

  //ConsolePutChar('w');
  //ConsolePutHex8(c);
  
  if (c!=0x00) return (c); //Error - MMC did not go to write mode
  
/*
  while (c!=0x00) { //wait for BUSY token, if you get 0x01(idle), it's an ERROR!
    c = SPIGetChar();
    ConsolePutHex8(c);
  }      
*/  
  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();
  }
  //ConsolePutChar('-');

  c = SPIGetChar();  //crc 1st byte (sends 0xff)
  c = SPIGetChar(); //crc 2nd byte (sends 0xff)
  c = SPIGetChar();
  //ConsolePutHex8(c); //This prints xxx00101, (usually e5) when data ok
  
//  while (SPIGetChar()!=0xff)      //busy wait moved to mmcCommand
//    ; // Wait until MMC not busy.     

#ifdef MMCDEBUG
  ConsoleWrite("W>");
#endif  

  
  SPI8Clocks(16);
  MMCDeselect();
  SPI8Clocks(16);

  //RED_LED = LED_OFF;

  return 0;
}


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -