📄 mmc.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 + -