Gert van der Knokke
/
C1541III
C1541-III mbed edition
ata.c
- Committer:
- gertk
- Date:
- 2011-08-22
- Revision:
- 1:0cbbb66a6100
- Parent:
- 0:28557a4d2215
File content as of revision 1:0cbbb66a6100:
/*------------------------------------------------------------------------------------------*/ /*This is the lowlevel MMC/SD-card driver, */ /*in order to function with the allready available software, the routines use the name ATA */ /* */ /*Note that this driver is dirty because it polls the drive for it's flags. The */ /*routines in this driver should therefore only be called from a low priority task */ /*------------------------------------------------------------------------------------------*/ /* History: 2007-09-28 Minor layout changes 2006-08-25 Hurray, SanDisk wouldn't be SanDisk if they wouldn't have something different in the specifications that are to be considered as STANDARD !!! Why tell me why (as in the famous song of Anita Meijer) The 'manual-rs-mms.pdf' version of 13-5-2004, page 5-12 states: CMD55 (SPI) This optional MMCA command is not supported in the SanDisk MultiMediaCard/RS-MultiMediaCard. How are the big-boys able to work with these kind of standards what kind of info do they have that I don't have. Well, it's up to me now to handle this exception... <zucht> 2006-08-24 all card now work EXCEPT the SanDisk RS-MMC, it even claims to be an SD-card... at least in my code, wonder why that is, back to the documentation 2006-08-23 Hurray, the card that first would not function is now functional !!! COMPLETELY NUTS!!! Some MMC/SD-cards DO NOT, I repeat DO NOT function according specification, normally after giving the command 'CMD17' the command responds with an error token (should be 0 when all is OK) But for some reason some cards do not give this and simply answer 0xFF (instead of 0x00) this code is not even vaild according specs... I do not get it, but when I simply ignore the expected response and immediatley start polling for the start-of-data then there is no problem what so ever... Have they gone nuts or am I missing something... why is nobody following the protocol... this is RS232 connectors all over again. For those who are not familiar whith RS232 connectors, some people still do not know the difference between DTE and DCE and just mount a connector that fits to the device that is not meant to be connected in the way it is used. And wire the pins until it works and viola... no longer according specs, but it works in their own setup so it appears nothing is wrong, that is until you connect a device that is according specs... 2006-08-21 added more debugging info in order to find the problem regarding the problems with some card not being supported... added a retry mechanism on the SDCARD_init() routine renamed the routines SDCARD to CARD, this because it also handles MMC cards so the old name was incorrect 2006-02-02 improvements in code layout... i.o.w. making it more readable 2006-01-29 added first steps for byte addressable read routine (read X bytes) 2006-01-26 a routine waiting for 0xFE contained a ';' to much, so it did not timeout 2005-12-23 removal of the CS handling routines... not required for this project... 2005-12-11 (Dennis) added proper CS handling to enable sharing of SPI bus 2005-04-19 start of project */ /* TO DO: ------ - improve 'AtaRead_X_Bytes' */ /*------------------------------------------------------------------------------------------*/ /*--------------------------------------------------------*/ /* includes */ /*--------------------------------------------------------*/ #include "mbed.h" //#include <stdio.h> #include "ata.h" #include "hardware.h" /*--------------------------------------------------------*/ /* constants */ /*--------------------------------------------------------*/ #define FALSE 0 /*FALSE*/ #define TRUE 1 /*TRUE*/ #define MMCCARD 2 #define SDCARD 3 /*MMC commandset*/ #define CMD0 0x40 /*Resets the multimedia card*/ #define CMD1 0x41 /*Activates the card's initialization process*/ #define CMD2 0x42 /*--*/ #define CMD3 0x43 /*--*/ #define CMD4 0x44 /*--*/ #define CMD5 0x45 /*reseved*/ #define CMD6 0x46 /*reserved*/ #define CMD7 0x47 /*--*/ #define CMD8 0x48 /*reserved*/ #define CMD9 0x49 /*CSD : Ask the selected card to send its card specific data*/ #define CMD10 0x4a /*CID : Ask the selected card to send its card identification*/ #define CMD11 0x4b /*--*/ #define CMD12 0x4c /*STOP-command*/ #define CMD13 0x4d /*Ask the selected card to send its status register*/ #define CMD14 0x4e /*--*/ #define CMD15 0x4f /*--*/ #define CMD16 0x50 /*Select a block length (in bytes) for all following block commands (Read:between 1-512 and Write:only 512)*/ #define CMD17 0x51 /*Reads a block of the size selected by the SET_BLOCKLEN command, the start address and block length must be set so that the data transferred will not cross a physical block boundry*/ #define CMD18 0x52 /*--*/ #define CMD19 0x53 /*reserved*/ #define CMD20 0x54 /*--*/ #define CMD21 0x55 /*reserved*/ #define CMD22 0x56 /*reserved*/ #define CMD23 0x57 /*reserved*/ #define CMD24 0x58 /*Writes a block of the size selected by CMD16, the start address must be alligned on a sector boundry, the block length is always 512 bytes*/ #define CMD25 0x59 /*--*/ #define CMD26 0x5a /*--*/ #define CMD27 0x5b /*Programming of the programmable bits of the CSD*/ #define CMD28 0x5c /*If the card has write protection features, this command sets the write protection bit of the addressed group. The porperties of the write protection are coded in the card specific data (WP_GRP_SIZE)*/ #define CMD29 0x5d /*If the card has write protection features, this command clears the write protection bit of the addressed group*/ #define CMD30 0x5e /*If the card has write protection features, this command asks the card to send the status of the write protection bits. 32 write protection bits (representing 32 write protect groups starting at the specific address) followed by 16 CRD bits are transferred in a payload format via the data line*/ #define CMD31 0x5f /*reserved*/ #define CMD32 0x60 /*sets the address of the first sector of the erase group*/ #define CMD33 0x61 /*Sets the address of the last sector in a cont. range within the selected erase group, or the address of a single sector to be selected for erase*/ #define CMD34 0x62 /*Removes on previously selected sector from the erase selection*/ #define CMD35 0x63 /*Sets the address of the first erase group within a range to be selected for erase*/ #define CMD36 0x64 /*Sets the address of the last erase group within a continuos range to be selected for erase*/ #define CMD37 0x65 /*Removes one previously selected erase group from the erase selection*/ #define CMD38 0x66 /*Erases all previously selected sectors*/ #define CMD39 0x67 /*--*/ #define CMD40 0x68 /*--*/ #define CMD41 0x69 /*reserved*/ #define CMD42 0x6a /*reserved*/ #define CMD43 0x6b /*reserved*/ #define CMD44 0x6c /*reserved*/ #define CMD45 0x6d /*reserved*/ #define CMD46 0x6e /*reserved*/ #define CMD47 0x6f /*reserved*/ #define CMD48 0x70 /*reserved*/ #define CMD49 0x71 /*reserved*/ #define CMD50 0x72 /*reserved*/ #define CMD51 0x73 /*reserved*/ #define CMD52 0x74 /*reserved*/ #define CMD53 0x75 /*reserved*/ #define CMD54 0x76 /*reserved*/ #define CMD55 0x77 /*reserved*/ #define CMD56 0x78 /*reserved*/ #define CMD57 0x79 /*reserved*/ #define CMD58 0x7a /*reserved*/ #define CMD59 0x7b /*Turns the CRC option ON or OFF. A '1' in the CRC option bit will turn the option ON, a '0' will turn it OFF*/ #define CMD60 0x7c /*--*/ #define CMD61 0x7d /*--*/ #define CMD62 0x7e /*--*/ #define CMD63 0x7f /*--*/ /*--------------------------------------------------------*/ /* globals */ /*--------------------------------------------------------*/ unsigned char crc_7; /*contains CRC value*/ unsigned char response_1; /*byte that holds the first response byte*/ unsigned char response_2; /*byte that holds the second response byte*/ unsigned char response_3; /*byte that holds the third response byte*/ unsigned char response_4; /*byte that holds the fourth response byte*/ unsigned char response_5; /*byte that holds the fifth response byte*/ /*--------------------------------------------------------*/ /* local functions */ /*--------------------------------------------------------*/ void Command_R0(char cmd,unsigned short AdrH,unsigned short AdrL); void Command_R1(char cmd,unsigned short AdrH,unsigned short AdrL); void Command_R2(char cmd,unsigned short AdrH,unsigned short AdrL); void Command_R3(char cmd,unsigned short AdrH,unsigned short AdrL); void MmcAddCrc7(unsigned char c); void set_SPI_LowSpeed(void); void set_SPI_HighSpeed(void); unsigned char Card_CMD0(void); unsigned char Card_CMD1(void); unsigned char Card_BlockSize(void); unsigned char WaitForSOD(void); unsigned char Card_CID(void); unsigned char Card_CSD(void); /*************************************************************************************/ /*************************************************************************************/ /*External functions*/ /*************************************************************************************/ /*************************************************************************************/ SPI spicard(p11, p12, p13); // mosi, miso, sclk DigitalOut SDCARD_CS(p14); extern Timer timeout; #define mySPI spicard.write void set_SPI_LowSpeed(void) { spicard.frequency(100000); // low speed 100 kHz } void set_SPI_HighSpeed(void) { spicard.frequency(1000000); // Set to 1MHz for data transfer } /*wait for start of data transfer with timeout*/ unsigned char WaitForSOD(void) { timeout.reset(); while(mySPI(0xFF) != 0xFE) if (timeout.read_us() >= 1000) { return(FALSE); } return(TRUE); } /*Enable the SDcard correctly*/ unsigned char CARD_Init(void) { unsigned short lp; set_SPI_LowSpeed(); SDCARD_CS=1; /*SDcard Disabled*/ for(lp=0;lp<10;lp++) /*Set SDcard in SPI-Mode, Reset*/ mySPI(0xFF); /*10 * 8bits = 80 clockpulses*/ SDCARD_CS=0; /*SDcard Enabled*/ wait_ms(50); // for(lp=0;lp<50000;lp++); /*delay for a lot of milliseconds (at least 16 bus clock cycles)*/ if (Card_CMD0() == FALSE) return(FALSE); /*error, quit routine*/ Command_R1(CMD55,0,0); /*when the response is 0x04 (illegal command), this must be an MMC-card*/ if (response_1 == 0x05) /*determine MMC or SD*/ { /*An MMC-card has been detected, handle accordingly*/ /*-------------------------------------------------*/ if (Card_CMD1() == FALSE) return(FALSE); /*error, quit routine*/ } else { /*An SD-card has been detected, handle accordingly*/ /*-------------------------------------------------*/ timeout.reset(); response_1 = 1; while(response_1 != 0) { Command_R1(CMD41,0,0); Command_R1(CMD55,0,0); if (timeout.read_us() > 10000) /*timeout mechanism*/ { return(FALSE); /*error, quit routine*/ } } } if (Card_BlockSize() == FALSE) return(FALSE); /*error, quit routine*/ set_SPI_HighSpeed(); return(TRUE); } /*Enable the SDcard according SanDisk RS-MMC (a.k.a. the idiots not follow the world's standards*/ unsigned char CARD_AlternativeInit(void) { unsigned short lp; set_SPI_LowSpeed(); SDCARD_CS=1; /*SDcard Disabled*/ for(lp=0; lp < 10; lp++) /*Set SDcard in SPI-Mode, Reset*/ mySPI(0xFF); /*10 * 8bits = 80 clockpulses*/ SDCARD_CS=0; /*SDcard Enabled*/ wait_ms(50); // for(lp=0; lp < 50000; lp++); /*delay for a lot of milliseconds (at least 16 bus clock cycles)*/ if (Card_CMD0() == FALSE) return(FALSE); /*error, quit routine*/ if (Card_CMD1() == FALSE) return(FALSE); /*error, quit routine*/ if (Card_BlockSize() == FALSE) return(FALSE); /*error, quit routine*/ set_SPI_HighSpeed(); return(TRUE); } unsigned char Card_BlockSize(void) { unsigned char retry_counter; retry_counter = 100; /*this routine is verrrrrrrry important, and sometimes fails on some cards so a retry mechanism is crucial*/ while(retry_counter--) { Command_R1(CMD16,0,512); /*Set read block length to 512 bytes, by the way 512 is default, but since nobody if following standards...*/ if (response_1 == 0) break; if (retry_counter == 0) return(FALSE); } return(TRUE); } unsigned char Card_CID(void) { Command_R1(CMD10,0,0); /*read CID register (total of 16Bytes)*/ // if (response_1 !=0) // { // return(ERROR_CARDINIT_CID); /*exit if invalid response*/ // } if (WaitForSOD() == FALSE) /*wait for start-of-data (function features time-out)*/ return(FALSE); OutputToRS232(); /*set standard output to RS232*/ printf("\r\n\r\nCID registers (16bytes)\r\n-----------------------"); printf("\r\nCID(01) [manuf-ID] = %02X",(mySPI(0xFF))); /*manuf-ID, 3 bytes*/ printf("\r\nCID(02) [manuf-ID] = %02X",(mySPI(0xFF))); /*manuf-ID, 3 bytes*/ printf("\r\nCID(03) [manuf-ID] = %02X",(mySPI(0xFF))); /*manuf-ID, 3 bytes*/ printf("\r\nCID(04) [prodname] = %02X",(mySPI(0xFF))); /*productname, 7 bytes*/ printf("\r\nCID(05) [prodname] = %02X",(mySPI(0xFF))); /*productname, 7 bytes*/ printf("\r\nCID(06) [prodname] = %02X",(mySPI(0xFF))); /*productname, 7 bytes*/ printf("\r\nCID(07) [prodname] = %02X",(mySPI(0xFF))); /*productname, 7 bytes*/ printf("\r\nCID(08) [prodname] = %02X",(mySPI(0xFF))); /*productname, 7 bytes*/ printf("\r\nCID(09) [prodname] = %02X",(mySPI(0xFF))); /*productname, 7 bytes*/ printf("\r\nCID(10) [prodname] = %02X",(mySPI(0xFF))); /*productname, 7 bytes*/ printf("\r\nCID(11) [0x HW,FW] = %02X",(mySPI(0xFF))); /*revision, hi-nibble=HW, low-nibble=FW, 1 byte*/ printf("\r\nCID(12) [serial #] = %02X",(mySPI(0xFF))); /*serial number, 3 bytes*/ printf("\r\nCID(13) [serial #] = %02X",(mySPI(0xFF))); /*serial number, 3 bytes*/ printf("\r\nCID(14) [serial #] = %02X",(mySPI(0xFF))); /*serial number, 3 bytes*/ printf("\r\nCID(15) [0x Mo,Ye] = %02X",(mySPI(0xFF))); /*revision, hi-nibble=Month, low-nibble=Year, 1 byte*/ printf("\r\nCID(16) [7-1 =CRC] = %02X",(mySPI(0xFF))); /*7-1=CRC checksum, 0=not used, 1 byte*/ OutputToLCD(); /*set standard output to LCD*/ return(TRUE); } unsigned char Card_CSD(void) { unsigned short lp; Command_R1(CMD9,0,0); /*read CSD register*/ // if (response_1 !=0) // { // return(ERROR_CARDINIT_CSD); /*exit if invalid response*/ // } if (WaitForSOD() == FALSE) /*wait for start-of-data (function features time-out)*/ return(FALSE); OutputToRS232(); /*set standard output to RS232*/ printf("\r\n\r\nCSD registers (16bytes)\r\n-----------------------"); for(lp=0; lp < 17; lp++) { printf("\r\nCSD(%02d) = %02X",lp,(mySPI(0xFF))); } OutputToLCD(); /*set standard output to LCD*/ return(TRUE); } /*request the status register of the card*/ unsigned char CARD_Status(unsigned char *ResponseData) { Command_R2(CMD13,0,0); ResponseData[1] = response_1; /*return the first response byte (---) in reg[1]*/ ResponseData[0] = response_2; /*return the second response byte (LSB) in reg[0]*/ return(TRUE); } /*Request the OCR register (SD-card in SPI-mode that is to be done with CMD58), this will give us the operation condition sof the card*/ unsigned char CARD_OperatingConditions(unsigned char *ResponseData) { timeout.reset(); response_1 = 1; while(response_1 != 0) { Command_R3(CMD58,0,0); if (timeout.read_us()>255) /*timeout mechanism*/ { return(FALSE); } } ResponseData[3] = response_2; /*return the second response byte (MSB) in reg[3]*/ ResponseData[2] = response_3; /*return the third response byte (---) in reg[2]*/ ResponseData[1] = response_4; /*return the fourth response byte (---) in reg[1]*/ ResponseData[0] = response_5; /*return the fifth response byte (LSB) in reg[0]*/ return(TRUE); } /*Read XXX bytes from the card*/ /*In some suitations it is not relevant to read the entire sector,*/ /*Sometimes only one byte is required, then this routine comes in handy...*/ unsigned char AtaRead_X_Bytes(unsigned long lba, unsigned int offset, unsigned char *ReadData, unsigned int NmbrOfBytes) { unsigned long lp; unsigned short upper_lba, lower_lba; unsigned char *p,c; lba = lba * 512; /*calculate byte address*/ upper_lba = (lba/65536); lower_lba = (lba%65536); Command_R1(CMD17, upper_lba, lower_lba); /*read block start at ...,...*/ // if (response_1 !=0) /*usually you would check the response, BUT not all do this, don't ask me why, but all cards eventually come with the 0xFE (start of data)*/ // return(FALSE); /*exit if invalid response*/ if (WaitForSOD() == FALSE) /*wait for start-of-data (function features time-out)*/ return(FALSE); // /*read data and exit OK*/ // p=ReadData; // do // { // SSPBUF = 0xff; // while (!BF); // *(p++)= SSPBUF; // } // while(--NmbrOfBytes); // // Command_R1(CMD12, 0xff, 0xff); /*send the stop command*/ // return(TRUE); p=ReadData; for(lp=0; lp < 512; lp++) { c=mySPI(0xff); if(offset == 0) /*skip the inrelevant bytes*/ *(p++)=c; else { *(p)=c; offset--; } } mySPI(0xff); mySPI(0xff); return(TRUE); } /*Read single block (with block-size set by CMD16 to 512 by default)*/ unsigned char AtaReadSector(unsigned long lba, unsigned char *ReadData) { unsigned short upper_lba, lower_lba; unsigned char i; unsigned char *p; lba = lba * 512; /*calculate byte address*/ upper_lba = (lba/65536); lower_lba = (lba%65536); /*printf("\r\nlba=%ld, upper lba=%d, lower lba=%d",lba, upper_lba,lower_lba);*/ Command_R1(CMD17, upper_lba, lower_lba); /*read block start at ...,...*/ // if (response_1 !=0) // { // return(ERROR_ATAREAD_CMD17); /*exit if invalid response*/ // } if (WaitForSOD() == FALSE) /*wait for start-of-data (function features time-out)*/ return(ERROR_ATAREAD_TIMEOUT); /*read data and exit OK*/ p=ReadData; i=0; do { *(p++)=mySPI(0xff); } while(--i); do { *(p++)=mySPI(0xff); } while(--i); //for(lp=0; lp < 512; lp++) // ReadData[lp] = mySPI(0xFF); mySPI(0xff); mySPI(0xff); return(TRUE); } /*Write: 512 Byte-Mode, this will only work (read MMC and SD-card specs) with a sector/block size of 512*/ unsigned char AtaWriteSector(unsigned long lba, unsigned char *WriteData) { unsigned short upper_lba, lower_lba, lp; /*Variable 0...65535*/ unsigned char i; lba = lba * 512; /*since the MMC and SD cards are byte addressable and the FAT relies on a sector address (where a sector is 512bytes big), we must multiply by 512 in order to get the byte address*/ upper_lba = (lba/65536); lower_lba = (lba%65536); Command_R1(CMD24, upper_lba, lower_lba); if (response_1 != 0) { return(FALSE); } else { SDCARD_CS=0; /*SDcard Enabled*/ mySPI(0xFF); mySPI(0xFF); mySPI(0xFE); for(lp=0; lp < 512; lp++) { mySPI(WriteData[lp]); } mySPI(255); // Send 2 Byte's without a meaning... although required by protocol mySPI(255); i = mySPI(0xFF); // i &=0b.0001.1111; // if (i != 0b.0000.0101) // printf("Write error\n\r"); // else // printf("Write succeeded?"); while(mySPI(0xFF) !=0xFF); /*wait until the card has finished writing the data*/ return(TRUE); } } /*************************************************************************************/ /*Internal functions*/ /*************************************************************************************/ unsigned char Card_CMD0(void) { unsigned char retry_counter; retry_counter = 100; /*this routine is verrrrrrrry important, and sometimes fails on some cards so a retry mechanism is crucial*/ while(retry_counter--) { Command_R1(CMD0,0,0); /*CMD0: Reset all cards to IDLE state*/ if (response_1 == 1) break; if (retry_counter == 0) return(FALSE); /*error, quit routine*/ } return(TRUE); } unsigned char Card_CMD1(void) { timeout.reset(); response_1 = 1; while(response_1 != 0) { Command_R1(CMD1,0,0); /*activate the cards init process*/ if (timeout.read_us() > 10000) /*timeout mechanism*/ { return(FALSE); } } return(TRUE); } /*Send a command to the SDcard*/ void Command_R0(char cmd,unsigned short AdrH,unsigned short AdrL) { crc_7=0; mySPI(0xFF); /*flush SPI-bus*/ mySPI(cmd); MmcAddCrc7(cmd); /*update CRC*/ mySPI(AdrH/256); /*use upper 8 bits (everything behind the comma is discarded)*/ MmcAddCrc7(AdrH/256); /*update CRC*/ mySPI(AdrH%256); /*use lower 8 bits (shows the remaining part of the devision)*/ MmcAddCrc7(AdrH%256); /*update CRC*/ mySPI(AdrL/256); /*use upper 8 bits (everything behind the comma is discarded)*/ MmcAddCrc7(AdrL/256); /*update CRC*/ mySPI(AdrL%256); /*use lower 8 bits (shows the remaining part of the devision)*/ MmcAddCrc7(AdrL%256); /*update CRC*/ crc_7<<=1; /*shift all bits 1 position to the left, to free position 0*/ crc_7++; /*set LSB to '1'*/ mySPI(crc_7); /*transmit CRC*/ mySPI(0xFF); /*flush SPI-bus, or int other words process command*/ } /*Send a command to the SDcard, a one byte response is expected*/ void Command_R1(char cmd,unsigned short AdrH,unsigned short AdrL) { Command_R0(cmd, AdrH, AdrL); /*send command*/ response_1 = mySPI(0xFF); /*return the reponse in the correct register*/ } /*Send a command to the SDcard, a two byte response is expected*/ void Command_R2(char cmd,unsigned short AdrH,unsigned short AdrL) { Command_R0(cmd, AdrH, AdrL); /*send command*/ response_1 = mySPI(0xFF); /*return the reponse in the correct register*/ response_2 = mySPI(0xFF); } /*Send a command to the SDcard, a five byte response is expected*/ void Command_R3(char cmd,unsigned short AdrH,unsigned short AdrL) { Command_R0(cmd, AdrH, AdrL); /*send command*/ response_1 = mySPI(0xFF); /*return the reponse in the correct register*/ response_2 = mySPI(0xFF); response_3 = mySPI(0xFF); response_4 = mySPI(0xFF); response_5 = mySPI(0xFF); } /*calculate CRC7 checksum*/ void MmcAddCrc7(unsigned char c) { unsigned char i; i=8; do { crc_7<<=1; if(c&0x80) crc_7^=0x09; if(crc_7&0x80) crc_7^=0x09; c<<=1; } while(--i); }