/* SD Karten Interface */ #include "LPC24xx.h" #include "StdTypes.h" #include "startup.h" #include "sdio.h" #include "systick.h" #include "events.h" #include "gio.h" #include "conv.h" /* verwendete Portbits: P1.31 WPI low = Kontakt geschlossen P0.12 CDI low = SD-Karte vorhanden die verwendete Fassung macht Probleme: WPI ist ein Schalter gegen Masse, aber CDI ist ein Schalter gegen WPI !!! also WPI per Software immer auf Output=Low halten und nur beim Detektieren mal auf Input schalten */ inline bool is_WPI (void) { return ((FIO1PIN & 0x80000000)==0); } inline bool is_CDI (void) { return ((FIO0PIN & (1<<12))==0); } /* Variablen */ dword CSD_Tab[4]; /* Card Specific Data */ dword CID_Tab[4]; /* Card Identification Data */ dword RCA; /* momentane Card Adresse */ dword SCR; /* SDCard Conf Register */ dword SCR2; /* OEM SDCard Conf Register */ dword OCR; /* mit ACMD41 gelesen */ dword TRSTATUS; /* letzter Status bei Rd+Wr */ TSDCARDINFO SDCardInfo; bool SD_debounce; void MCI_IRQHandler (void) __irq; extern dword MCI_ReadFifo(dword * dest); extern dword MCI_WriteFifo(dword * src); /*************** zyklischer Test, ob SD Karte eingesetzt wurde ***************/ int SD_exdebounce; void SDCardTest (void) { if (SD_CardDetected) // wenn Karte bereits erkannt wurde { if (SD_debounce) return; // erstmal entprellen lassen if (!is_CDI()) // kein SDI im Betrieb mehr? --> Karte wurde entnommen! {// FIO1DIR |= 0x80000000; // WPI (SD-karte) auf 0 setzen // FIO1CLR = 0x80000000; // um SDI erkennen zu können ++SD_exdebounce; if (SD_exdebounce > 3) { SDCardTyp = 0; // Typ austragen SD_CardDetected = false; SD_readonly = false; MCI_POWER = 0; Add_Event(idSDverloren); } } else SD_exdebounce = 0; return; } if (!is_CDI()) return; // erstmal entprellen lassen if (!SD_CardDetected) Add_Delayed_Event(200, idSDentdeckt); // nur 1x Event erzeugen SD_CardDetected = true; SD_debounce = true; SD_exdebounce = 0; } /*************** Debug- und Testfunktionen ***********************************/ void Sage4 (dword* ptr) { HexL_Out(ptr[0], stdout); Char_Out('-', stdout); HexL_Out(ptr[1], stdout); Char_Out('-', stdout); HexL_Out(ptr[2], stdout); Char_Out('-', stdout); HexL_Out(ptr[3], stdout); CRLF_Out(stdout); } /*************** Kommandos & Antworten ***************************************/ // responses: #define Rbusy (1<<16) #define Rapp (1<<17) #define NoCRC (1<<18) #define Resp0 (0) // keine Antwort #define Resp1 (1<<6) // 48 bit #define Resp1b (1<<6)|Rbusy // 48 bit #define Resp2 (3<<6) // 136 bit CID oder CSD #define Resp3 (1<<6)|NoCRC // 48 bit OCD #define Resp4 (1<<6) // erstmal... ist NUR für SDIO Karten #define Resp6 (1<<6) // 48 bit RCA #define Resp7 (1<<6) // 48 bit supported Voltages #define CMD0 0|Resp0 // reset, gehe in Idle Mode #define CMD1 1|Resp1 // nur MMC: sende OpCond Register #define CMD2 2|Resp2 // sende CID Nummer #define CMD3 3|Resp6 // sende RCA und gehe in Data Transfer Mode #define CMD4 4|Resp0 // programmiert DSR #define CMD5 5|Resp4 // nur für SDIO-Karten #define ACMD6 6|Resp1|Rapp // setze Busbreite (1 oder 4) #define CMD7 7|Resp1b // select/deselect Karte #define CMD8 8|Resp7 // sende Interface Conds #define CMD9 9|Resp2 // sende CSD #define CMD10 10|Resp2 // sende CID #define CMD11 11|Resp1 // umschalten auf 1.8 V Signalpegel #define CMD12 12|Resp1 // stop Transmission #define CMD13 13|Resp1 // sende Status #define ACMD13 13|Resp1|Rapp // sende Status #define CMD15 15|Resp0 // deaktiviere Karte #define CMD16 16|Resp1 // setze Blocklänge (512 normalerweise) #define CMD17 17|Resp1 // read single block #define CMD18 18|Resp1 // read multiple blocks #define CMD19 19|Resp1 // send tuning block pattern #define CMD20 20|Resp1b // speed class control #define ACMD22 22|Resp1|Rapp // sende Anzahl fehlerfrei geschriebener Blöcke #define CMD23 23|Resp1 // setze Block-Count für Cmd 18 + 25 #define ACMD23 23|Resp1|Rapp // setze Anzahl der zu löschenden Blöcke #define CMD24 24|Resp1 // write block #define CMD25 25|Resp1 // write multiple blocks #define CMD27 27|Resp1 // programmiert Teile von CSD #define CMD28 28|Resp1b // setze Write-Protect (nicht bei SDHC, SDXC) #define CMD29 29|Resp1b // lösche Write-Protect (nicht bei SDHC, SDXC) #define CMD30 30|Resp1 // sende Write-Protect Status (nicht bei SDHC, SDXC) #define CMD32 32|Resp1 // setze Adresse für das Löschen des 1. Write Blockes #define CMD33 33|Resp1 // setze Adresse für das Löschen des letzten Write Blockes #define CMD38 38|Resp1b #define CMD40 40|Resp1 #define ACMD41 41|Resp3|Rapp // OpConds (HCS/OCR), NICHT bei MMC, NICHT bei SDIO #define CMD42 42|Resp1 #define ACMD42 42|Resp1|Rapp #define CMD48 48|Resp1 #define CMD49 49|Resp1 #define ACMD51 51|Resp1|Rapp #define CMD55 55|Resp1 // leitet ein ACMD-Kommando ein #define CMD56 56|Resp1 #define CMD58 58|Resp1 #define CMD59 59|Resp1 // statusbits, statisch #define CCRCFAIL 1 #define DCRCFAIL (1<<1) #define CTIMEOUT (1<<2) #define DTIMEOUT (1<<3) #define TXUNDERR (1<<4) #define RXOVERR (1<<5) #define CMDRESPEND (1<<6) #define CMDSENT (1<<7) #define DATAEND (1<<8) #define STBITERR (1<<9) #define DBCKEND (1<<10) // statusbits, dynamisch #define CMDACTIV (1<<11) #define TXACTIV (1<<12) #define RXACTIV (1<<13) #define TXFIFOHE (1<<14) #define RXFIFOHF (1<<15) #define TXFIFOF (1<<16) #define RXFIFOF (1<<17) #define TXFIFOE (1<<18) #define RXFIFOE (1<<19) bool SD_WaitForResponse (dword Command) { dword L; L = T0TC + 2; // maximal 2 ms warten if (Command & (1<<6)) // wenn Response erwartet wird warten wir // auf CMDRESPEND oder CMDCRCFAIL { while (((MCI_STATUS & (CMDRESPEND | CCRCFAIL | CTIMEOUT))==0) && (T0TC <= L)); if (MCI_STATUS & CMDRESPEND) return true; // bei CmdRespEnd --> OK. if (Command & NoCRC) { if (MCI_STATUS & CCRCFAIL) return true; } return false; } // kein Response erwartet, also warten auf CMDSENT while ((MCI_STATUS & (CMDSENT | CTIMEOUT))==0) if (T0TC > L) return false; if (MCI_STATUS & CMDSENT) return true; return false; } /* Kommando zur SD Karte ausgeben ============================== Format: 0000 0ePI LRKK KKKK 0..5 = Kommando 6 = ob Response 7 = Response ist lang 8 = Interrupt? 9 = Pending? 10 = enable 11..31 --> 0 */ bool SD_Command (dword Command, dword Argument) { bool b; if (Command & Rapp) // bei allen ACMDxx --> CMD55 davor { MCI_COMMAND = 0; // erstmal stopp MCI_CLEAR = 0x7FF; MCI_ARGUMENT = RCA<<16; MCI_COMMAND = CMD55 | (1<<10); // starten und enable b = SD_WaitForResponse(Command); if (b) b = (MCI_RESP0 & (1<<5)) != 0; // ob auch APP_CMD im Statuswort gesetzt ist if (!b) return false; } MCI_COMMAND = 0; // erstmal stopp MCI_CLEAR = 0x7FF; MCI_ARGUMENT = Argument; MCI_COMMAND = (Command & 0x7FF) | (1<<10); // starten und enable if (!SD_WaitForResponse (Command)) return false; return true; } int Get_SD_CurrentStatus (void) { bool b; int i; b = SD_Command(CMD13, RCA<<16); // sende Status if (!b) return -1; i = (MCI_RESP0 >> 9) & 15; return i; } /*************** Anschluß und SD-Karte initialisieren ************************/ bool SD_DriveInit (void) { bool b; bool v2compliant; dword i, L, irca; int64 isize; SD_CardSize = 0; FIO1DIR &= 0x7FFFFFFF; FIO1SET = 0x80000000; Warte_ms(100); SD_readonly = (FIO1PIN &0x80000000) != 0; FIO1DIR |= 0x80000000; // WPI (SD-karte) auf 0 setzen FIO1CLR = 0x80000000; // um SDI erkennen zu können SD_debounce = false; PCONP |= (1<<28); // SD-System einschalten MCI_POWER = 2 | (1<<6); // Powerup + CMD=Opendrain Warte_ms(200); MCI_POWER = 3 | (1<<6); // Poweron + CMD=Opendrain MCI_CLOCK = (SDCARD_CLK/400000) | (1<<8); // 400 kHz, enabled MCI_COMMAND = 0; // Command Maschine disable MCI_CLEAR = 0x7FF; // alle bisherigen Fehler löschen MCI_DATA_CTRL = 0; // Datentransfer disable MCI_MASK0 = 0; // erstmal keine Interrupts Warte_ms(500); SDCardTyp = SD_err; RCA = 0; VICVectAddr24 = (dword)MCI_IRQHandler; // hat Slot 24 VICIntSelect &= ~(1<<24); // kein FIQ VICVectPriority28 = 1; // Priorität 1 (zweithöchste) VICIntEnable = (1<<24); // enable MCI im VIC /********* Stufe 0: CMD0 = Resetkommando **********/ b = SD_Command(CMD0, 0); // CMD0 = Reset if (!b) return false; /********* Stufe 1: CMD1 = Test auf MMC **********/ String_Out("\r\nCMD1 ", stdout); b = SD_Command(CMD1, 0); if ((b) && (MCI_RESP0 & 0x80000000)) { SDCardTyp = MMC; String_Out("--> MMC\r\n", stdout); // erstmal nix bei MMC's return false; } String_Out("no\r\n", stdout); // kein CMD1 --> nix oder SD Karte /********* Stufe 2: CMD8 = Test auf SD Version 2.0 oder neuer **********/ String_Out("Cmd8 ", stdout); b = SD_Command(CMD8, 0x1AA); // CMD8 = send Interface Conds (Test auf R2.0 = SDHC) v2compliant = b && (MCI_RESP0==0x1AA); if (v2compliant) String_Out("SD2.0\r\n", stdout); else String_Out("SD1.x\r\n", stdout); /********* Stufe 3a: SD 1.1 initialisieren **********/ if (!v2compliant) { b = SD_Command(ACMD41, 0); // ohne HCS, ohne 1.8V if (!b) return false; // MMC oder Karte defekt oder für falsche VCC String_Out("SD1.x OCR= ", stdout); OCR = 0; L = T0TC + 1000; // 1 Sekunde max. while ((L>T0TC) && ((OCR & 0x80000000)==0)) { b = SD_Command(ACMD41, 0x00100000); // VCC 3.2V bis 3.3V if (!b) return false; OCR = MCI_RESP0; Warte_ms(3); } if (OCR & 0x80000000) { SDCardTyp = SD1_1; // SD Standard Card 1.1 goto _init1_1; } String_Out(" ..timeout\r\n", stdout); return false; } /********* Stufe 3b: SD 2.0 initialisieren **********/ OCR = 0; // SD ist 2.0 oder höher, wir machen erst mal nur 2.0 L = T0TC + 1000; // 1 Sekunde max. while ((L>T0TC) && ((OCR & 0x80000000)==0)) { b = SD_Command(ACMD41, 0x40100000); // HCS und VCC 3.2V bis 3.3V if (!b) return false; OCR = MCI_RESP0; Warte_ms(3); } if (OCR & 0x80000000) { SDCardTyp = SD2_0; // SD Standard Card 2.0 goto _init2_0; } String_Out(" ..timeout\r\n", stdout); SD_Error: SDCardTyp = SD_err; return false; /********* Stufe 3c: Test auf SDHC **********/ _init2_0: if (OCR & 0x40000000) { SDCardTyp = SDHC; String_Out("SDHC\r\n", stdout); } /********* Stufe 4: SD 1.x oder 2.0 initialisieren **********/ _init1_1: // ab hier auch init SD 1.1 Karte b = SD_Command(CMD2, 0); // CID lesen von 'allen' Karten CID_Tab[0] = MCI_RESP0; CID_Tab[1] = MCI_RESP1; CID_Tab[2] = MCI_RESP2; CID_Tab[3] = MCI_RESP3; String_Out("CID= ", stdout); Sage4(CID_Tab); b = SD_Command(CMD3, 0); // RCA lesen und in Datenmodus gehen if (!b) goto SD_Error; irca = (MCI_RESP0 >> 16) & 0xFFFF; RCA = irca; String_Out("RCA= ", stdout); HexW_Out(RCA, stdout); CRLF_Out(stdout); b = SD_Command(CMD9, irca<<16); // CSD lesen if (!b) goto SD_Error; CSD_Tab[0] = MCI_RESP0; // 127..96 CSD_Tab[1] = MCI_RESP1; // 95..64 CSD_Tab[2] = MCI_RESP2; // 63..32 CSD_Tab[3] = MCI_RESP3; // 31..0 String_Out("CSD= ", stdout); Sage4(CSD_Tab); b = SD_Command(CMD7, RCA<<16); // Karte selektieren if (!b) goto SD_Error; b = SD_Command(CMD16, 8); // Blocklänge auf 8 Bytes setzen if (!b) goto SD_Error; SCR = 0; SCR2 = 0; MCI_DATA_TMR = 0x10000; MCI_DATA_LEN = 8; MCI_DATA_CTRL = 1 | 2 | (3<<4); // enable, SD-->Controller, 8 Byte b = SD_Command(ACMD51, 0); // SCR lesen if (!b) goto SD_Error; while ((MCI_STATUS & ((1<<3)|(1<<8))) == 0); // wartet auf DataEnd oder DataTimeout if (MCI_STATUS & (1<<21)) // wenn Daten im RX Fifo { SCR = MCI_FIFO; String_Out("SCR = ", stdout); HexL_Out(SCR, stdout); } if (MCI_STATUS & (1<<21)) // wenn Daten im RX Fifo { SCR2 = MCI_FIFO; String_Out("\r\nSCR2 = ", stdout); HexL_Out(SCR2, stdout); } /********* Stufe 5: Übergang zu 4 Bit **********/ MCI_POWER = 3; // Poweron ohne OpenDrain i = Get_SD_CurrentStatus(); // über CMD13 String_Out("\r\nStatus = ", stdout); Dezi_Out(i, 1, stdout); CRLF_Out(stdout); if (i==5) SD_Command(CMD12, 0); String_Out("ACMD6 ", stdout); b = SD_Command(ACMD6, 2); // auf 4 Bit Transfer übergehen if (!b) goto SD_Error; String_Out("ok\r\n", stdout); // Clock und Datenbreite umschalten (30MHz SDCARD_CLK) // MCICLK = SDCARD_CLK / ( 2 * (divisor + 1) ); MCI_CLOCK = 2 | (1<<11) | (1<<8); // divisor = 1, also 7.5 MHz // Test... L = MCI_CLOCK & 255; L = SDCARD_CLK / (2 * (L+1)); L = L / 1000; Dezi_Out(L, 1, stdout); String_Out("kHz/4Bit ", stdout); b = SD_Command(CMD13, RCA<<16); // sende Status if (!b) goto SD_Error; String_Out("ok\r\nStatus: ", stdout); HexL_Out(MCI_RESP0, stdout); String_Out("\r\nTyp: ", stdout); switch (SDCardTyp) { case SDHC: String_Out("SDHC\r\n", stdout); break; case SD2_0: String_Out("SD2.0\r\n", stdout); break; case SD1_1: String_Out("SD1.1\r\n", stdout); break; default: String_Out("sonstige\r\n", stdout); break; } // Kapazität berechnen if (CSD_Tab[0] & 0xC0000000) { L = (CSD_Tab[1] & 0x3F) << 16; L = L | ((CSD_Tab[2] >> 16) & 0xFFFF); L = L + 1; isize = L; isize = isize * 512 * 1024; } else { L = (CSD_Tab[1] & 0x3FF) << 2; L = L | ((CSD_Tab[2] >> 30) & 3); L = L + 1; isize = L; L = (CSD_Tab[1] >> 16) & 15; L = 1<> 15) & 7; i = 4<", stdout); return true; } /*************** Interrupt ******************************/ // wird erstmal garnicht gebraucht. dword BlockCount; bool BlockDone; dword* BlockPointer; dword BlockErrors; byte IntMerk; // data crc, timeout, underrun, overrun, startbiterr #define DataErrors ((1<<1)|(1<<3)|(1<<4)|(1<<5)|(1<<9)) void MCI_IRQHandler (void) __irq { dword Status; Status = MCI_STATUS; if (Status & ((1<<15)|(1<<17))) // wenn RX halb ode ganz voll ist { MCI_ReadFifo(BlockPointer); // read 8 words from fifo BlockPointer += 8; BlockCount += 32; if (BlockCount >= 512) // block complete { BlockDone = true; } VICVectAddr = 0; IntMerk |= 1; return; } if (Status & ((1<<14)|(1<<18))) // wenn TX halb ode ganz voll ist { MCI_WriteFifo(BlockPointer); // write 8 words to fifo BlockPointer += 8; BlockCount += 32; if (BlockCount >= 512) // block complete { BlockDone = true; MCI_MASK0 = MCI_MASK1 = 0; } VICVectAddr = 0; IntMerk |= 2; return; } if (Status & DataErrors) { BlockErrors = Status; BlockDone = true; MCI_MASK0 = MCI_MASK1 = 0; VICVectAddr = 0; IntMerk |= 4; return; } if (Status & ((1<<8)|(1<<10)) ) /* Data End , Data Block End */ { BlockErrors = Status; BlockDone = true; MCI_MASK0 = MCI_MASK1 = 0; VICVectAddr = 0; IntMerk |= 8; return; } VICVectAddr = 0; } /*************** Lese- und Schreiboperationen ******************************/ bool SD_ReadBlock (dword SecNum, dword *readbuff) { long i; bool b; dword A; dword* BP; dword TO; BlockCount = 0; BlockErrors = 0; BlockDone = false; BP = BlockPointer = readbuff; IntMerk = 0; if (SDCardTyp <= 0) { TRSTATUS |= BadCardError | (MCI_STATUS & 0x7FF); return false; } MCI_CLEAR = 0x7FF; MCI_DATA_CTRL = 0; // Data Maschine erstmal aus SD_Command(CMD12, 0); i = Get_SD_CurrentStatus(); if (i!=4) { TRSTATUS |= BadCardState | (MCI_STATUS & 0x7FF); return false; } b = SD_Command(CMD16, 512); // Blocklänge auf 512 Bytes setzen if (!b) { TRSTATUS |= Bls512Error | (MCI_STATUS & 0x7FF); SD_Command(CMD12, 0); return false; } i = 512; A = SecNum; if (SDCardTyp != SDHC) A = A * 512; MCI_MASK0 = MCI_MASK1 = 0; MCI_DATA_TMR = 750000; // sollte 100 ms ergeben (7.5 MHz) MCI_DATA_LEN = 512; MCI_DATA_CTRL = 1 | 2 | (9<<4); // enable, SD-->Controller, 512 Byte b = SD_Command(CMD17, A); // read single Block if (!b) { TRSTATUS |= ErrRdCommand | (MCI_STATUS & 0x7FF); return false; } TO = T0TC + 1000; readloop: A = MCI_STATUS; if (A & (1<<15)) // Fifo halbvoll { *BP++ = MCI_FIFO; *BP++ = MCI_FIFO; *BP++ = MCI_FIFO; *BP++ = MCI_FIFO; *BP++ = MCI_FIFO; *BP++ = MCI_FIFO; *BP++ = MCI_FIFO; *BP++ = MCI_FIFO; i = i - 32; A = MCI_STATUS; } if (A & ((1<<1)|(1<<3)|(1<<5)|(1<<9))) // crc, timeout, overrun, startbit { TRSTATUS = A; SD_Command(CMD12, 0); return false; } if (A & ((1<<8)|(1<<10))) // dataend, blockdataend { while ((MCI_STATUS & (1<<21))&&(i>0)) { *BP++ = MCI_FIFO; i = i - 4; } SD_Command(CMD12, 0); return true; } if (TO < T0TC) { TRSTATUS |= TranTimeout | (MCI_STATUS & 0x7FF); MCI_CLEAR = 0x7FF; MCI_DATA_CTRL = 0; // Data Maschine aus SD_Command(CMD12, 0); return false; } goto readloop; } bool SD_WriteBlock (dword SecNum, dword *writebuff) { long i; bool b; dword A; dword* BP; dword TO; BlockCount = 0; BlockErrors = 0; BlockDone = false; BP = BlockPointer = writebuff; IntMerk = 0; if (SDCardTyp <= 0) { TRSTATUS |= BadCardError | (MCI_STATUS & 0x7FF); return false; } if (SD_readonly) { TRSTATUS |= WriteOnROnly | (MCI_STATUS & 0x7FF); return false; } MCI_CLEAR = 0x7FF; MCI_DATA_CTRL = 0; // Data Maschine erstmal aus SD_Command(CMD12, 0); i = Get_SD_CurrentStatus(); if (i!=4) { TRSTATUS |= BadCardState | (MCI_STATUS & 0x7FF); return false; } b = SD_Command(CMD16, 512); // Blocklänge auf 512 Bytes setzen if (!b) { TRSTATUS |= Bls512Error | (MCI_STATUS & 0x7FF); SD_Command(CMD12, 0); return false; } A = SecNum; if (SDCardTyp != SDHC) A = A * 512; // bei NichtSDHC Adresse*512 MCI_MASK0 = MCI_MASK1 = 0; b = SD_Command(CMD24, A); // write single Block if (!b) { TRSTATUS |= ErrWrCommand | (MCI_STATUS & 0x7FF); return false; } MCI_DATA_TMR = 3500000; // sollte 500 ms ergeben (7.5 MHz) MCI_DATA_LEN = 512; MCI_DATA_CTRL = 1|(0<<1)|(9<<4); // enable, Controller-->SD, 512 Byte TO = T0TC + 1000; i = 512; // Anzahl zu schreibender Bytes while (i>0) { if (MCI_STATUS & ((1<<1)| // data crc (1<<3)| // data timeout (1<<4)| // tx underrun (1<<9))) // startbit err { TRSTATUS |= MCI_STATUS & 0x7FF; MCI_CLEAR = 0x7FF; MCI_DATA_CTRL = 0; // Data Maschine aus SD_Command(CMD12, 0); return false; } if (MCI_STATUS & (1<<14)) // Fifo halbleer { MCI_FIFO = *BP++; MCI_FIFO = *BP++; MCI_FIFO = *BP++; MCI_FIFO = *BP++; MCI_FIFO = *BP++; MCI_FIFO = *BP++; MCI_FIFO = *BP++; MCI_FIFO = *BP++; i = i - 32; } if (TO < T0TC) { TRSTATUS |= TranTimeout | (MCI_STATUS & 0x7FF); MCI_CLEAR = 0x7FF; MCI_DATA_CTRL = 0; // Data Maschine aus SD_Command(CMD12, 0); return false; } } // Blockende erreicht while ((MCI_STATUS & ((1<<8)|(1<<10)))==0) // warten auf (dataend | blockdataend) { A = MCI_STATUS; if (A & ((1<<1)| // data crc (1<<3))) // data timeout { TRSTATUS |= MCI_STATUS & 0x7FF; MCI_CLEAR = 0x7FF; MCI_DATA_CTRL = 0; // Data Maschine aus SD_Command(CMD12, 0); return false; } } i = 0; while (i!=4) { i = Get_SD_CurrentStatus(); if (TO < T0TC) { TRSTATUS |= TranTimeout | (MCI_STATUS & 0x7FF); return false; } } SD_Command(CMD12, 0); return true; }