coreyglynn
New Member
- Total Posts : 21
- Reward points : 0
- Joined: 2006/12/18 11:04:15
- Location: 0
- Status: offline
SD Card in SPI Mode
My adventure with SD Cards via SPI: You'd think that this was an easy task given the amount of documentation that is available. Yet, took a while longer that expected (and than I would have liked) to make it work. Is usually the case: am just a hobbyist and learning all the way. This set out as a simple journey to use the relatively low cost SD Card as RAM for my PIC project. Don't need Windows-compatible filesystems, just a place to put data. Test circuit, code snippets and handy resources are attached - thanks to uChip for making their Application Library with MMC samples available.... After a number of failed attempts, I crawled line-by-line through MicroChip's SD RAM library and Demonstration code with an ICD-2 debugger to figure it out. The basic difference between my efforts and uChips' was the initialization sequence: I followed Section 7 of the Hardware reference and used ACMD41. Microchip seems to use the native (non-SPI) startup sequence of commands, although sends them using SPI. A nifty trick, but not one that I would have guessed. While I was able to "read a single block" (CMD17), I couldn't "write block" (CMD24). Microchip's library takes account of different card types (v1 and v2) along with different capacities (Standard, less than 2GB; and, High Capacity, more than 2GB). The web resources that I found most useful: SD Card Simplified Spec, SPI timing diagram, some sample code, Microchip's Application Library. I implemented a handy function to see what SPI data was returned with each 8-bit transfer: "SPIcdebug" kept a copy of 8-bit data sent and corresponding 8-bit data received as side-by-side pairs in an array of 16-bit integers. Wouldn't have been able to debug my problems without it (and an oscilliscope and.....) A second stumbling block was when I asserted and unasserted the SD Card. While all commands reply with at least 8bits, some reply with more (R2, R7, for example) and some, such as READ_BLOCK reply with a whole 512-byte block, plus CRC. The Card needs to stay asserted until the command, and its data transfer, are complete. Most examples that I found use a single function to send SD commands via SPI and these must take in to account when the card is unasserted. Microchips is very elegant and uses a table to determine whether to unassert the card when the commend is complete, mine uses a less elegant if-then-else if set-up. SD Card functions // SD Card Utilities // Uses SPI configuration defined by SPIModule, SystemWideConfiguration and MyConfig #include "MyConfig.h" #include "SystemWideConfig.h" #include "SPIModule.h" #include "SDCardUtilities.h" #include "utilities.h" unsigned long write_sd_sector (SD_CARD *sd_card, unsigned long address, unsigned char *buffer) { // write 512 byte buffer to sd_card sector at address, return next available sector, 0 if error SD_RESPONSE sd_response; // r1 response from sd_card int i; // counter unsigned int timeout; // timeout value unsigned char rc; // char returned from SPI unsigned long next_address; // next available sector if (!sd_card->hc) // if card is std capacity address = (unsigned long)address << 9; // convert address to byte offset (blocks always 512bytes) sd_response = tx_cmd(WRITE_SINGLE_BLOCK,address,-1,NULL,NODBUG); // send CMD24, leave card asserted if (sd_response.byte != 0x00) { // check that command was accepted sd_card->w_err = 1; // write command not accepted UnAssertMySD(); // unassert (because main code will not expect to) return 0; // return error }else{ // command completed OK SPIc(0xFE); // send data start token for (i=0;i<512;i++) SPIc(buffer[i]); // send buffer SPIc(0xFF); SPIc(0xFF); // send crc (don't care) } rc = SPIc(0xFF); // next byte will indicate if data was accepted if ((rc & 0x0F) != 0x05) { // check for "data accepted" pattern in reponse sd_card->d_err = 1; // data not accepted UnAssertMySD(); // unassert card (because main code will not expect to) return 0; // return error }else{ // wait for write to complete timeout = 0xFFFF; // wait for huge timeout do { rc = SPIc(0xFF); // send clocks timeout--; // countdown } while ((rc == 0x00) && (timeout != 0x00)); // until reply is not 0 or times out sd_card->timeout = timeout; // keep timeout value (DEBUG) } if (timeout == 0x00) { // checkif timed out sd_card->no_w = 1; // timed out waiting for write to complete UnAssertMySD(); // unassert card (because main code will not expect to) return 0; // return error } SPIc(0xFF); // send 8 clocks to wrap up command UnAssertMySD(); // unassert card (because main code will expect this) next_address = (unsigned long)(address+0x01); // set next available sector return next_address; // return next address }; SD_RESPONSE tx_cmd(unsigned char command_index, unsigned long command_argument, char extra_bytes, unsigned char *location, unsigned char debug) { union sd_command command; // framed command SD_RESPONSE response; // response from SD Card (R1, R2, R7) unsigned int timeout; // timeout value unsigned char (*ptoSPIcall)(unsigned char c); // pointer to correct function for SPI send/rcv unsigned char counter; // gp counter if (debug == 0x01) ptoSPIcall = &SPIcdebug; // if debugging, use SPIcdebug else ptoSPIcall = &SPIc; // if not debugging, use SPIc AssertMySD(); // assert card // frame command properly command.fields.start_bit = 0; // always 0 command.fields.transmitter_bit = 1; // always 1 command.fields.index = command_index; // insert cmd index command.fields.argument = command_argument; // insert cmd argument if (command_index == GO_IDLE_STATE) // calculate any CRCs (SEVEN bits) command.fields.crc = 0x4A; else if (command_index == SEND_IF_COND) command.fields.crc = 0x43; else if (command_index == SEND_OP_COND) command.fields.crc = 0xF9; else if (command_index == READ_OCR) command.fields.crc = 0x25; else if (command_index == SEND_CSD) command.fields.crc = 0xAF; else if (command_index == CRC_ON_OFF) command.fields.crc = 0x25; else if (command_index == SET_BLOCKLEN) command.fields.crc = 0xFF; else command.fields.crc = 0x7F; // CRC for all else (111 1111) command.fields.end_bit = 1; // always 1 ptoSPIcall(command.bytes.cmd_w_starttx); // send 6 byte command ptoSPIcall(command.bytes.arg_msb); ptoSPIcall(command.bytes.arg_2); ptoSPIcall(command.bytes.arg_3); ptoSPIcall(command.bytes.arg_lsb); ptoSPIcall(command.bytes.crc_w_stop); timeout = 0xFF; do { response.byte = ptoSPIcall(0xFF); timeout--; }while((response.byte == 0xFF) && (timeout != 0x00)); if (timeout == 0) while (1); // card did not complete command if (extra_bytes > 0) { // if CMD expects more data for (counter = 0;counter < extra_bytes; counter++){ // get bytes *(location+counter)=ptoSPIcall(0xFF); // store in "location" } ptoSPIcall(0xFF); // clean up with 8 clocks UnAssertMySD(); // deassert return response; // return response byte } else if (extra_bytes == 0){ // if CMD expects NO data ptoSPIcall(0xFF); // clean up with 8 clocks UnAssertMySD(); // deassert return response; // return response byte } else { // else (<0) CMD expects more data.... ptoSPIcall(0xFF); // clean up with 8 clocks return response; // just return response byte } }; SD_CARD init_sdcard (void) { // used to initialize an SD Card SD_CARD sd_card; // sd_card union (see .h) SD_RESPONSE sd_response; // sd command response (see .h) unsigned char rc; // char returned from SPI unsigned int i,j; // counters unsigned char if_cond[4], ocr[4], csd[20], timeout; // sd card registers and timeout unsigned long c_size; // C_SIZE card size from csd register unsigned char block_len; // BLOCK_LEN block length from csd register unsigned char c_size_mult; // C_SIZE_MULT size multiploer from csd register sd_card.ok = 0; // card is initialized or status is OK sd_card.wp = 0; // card is write protected sd_card.cd = 0; // card is detected sd_card.hc = 0; // card is high capacity sd_card.v1 = 0; // card is version 1 sd_card.no_idle = 0; // card did not GO_STATE_IDLE sd_card.no_opc = 0; // send_op_cond timed out sd_card.no_ocr = 0; // send_ocr command timed out sd_card.no_pup = 0; // card not powered up after init request sd_card.no_csd = 0; // send_CSD command timed out sd_card.no_w = 0; // write did not complete (timed out) sd_card.csd_err = 0; // card did not issue 0xFE at start of CSD data sd_card.w_err = 0; // write error (command not accepted) sd_card.r_err = 0; // read error sd_card.d_err = 0; // data error (data not accepted) sd_card.pad = 0; sd_card.timeout = 0; // timeout value when ops timeout sd_card.size = 0; // card size in 512kbyte blocks (ie actual capacity is 1000 * m_size / 2 bytes) /* --------------------------toggle power and wait for card to start---------------------------------------*/ MySDPowerOFF; // Turn OFF the SD Card Delayms(500); // wait for Card to power down MySDPowerON; // Turn SD Card ON Delayms(500); // wait for Card to power up /* ---------------------------check for sd_card and wp status---------------------------------------------*/ sd_card.cd = MySDCD; // set chip detect status if (sd_card.cd != 0) return sd_card; // if no card, return sd_card.wp = MySDWP; // set write protect status if (sd_card.wp == 1) return sd_card; // if write protected, return /* ---------------------------start sending SPI info to start card----------------------------------------*/ UnAssertMySD(); // Make sure CS is high SPI_GOSLOW // set SPI to run at lowest setting Delayms(1); // wait for SPI module to sync for (i=0;i<10;i++) SPIc(0xFF); // send 74+ clocks (80) AssertMySD(); // assert the card Delayms(1); // wait /*----------------------- initialize card using Microchip's Startup Sequence-------------------------------*/ sd_response = tx_cmd(GO_IDLE_STATE,0x00000000,0,NULL,NODBUG); // send command to go idle if((sd_response.byte == 0xFF) || ((sd_response.byte & 0xF7) != 0x01)) { // check is card is idle or bus floating sd_card.no_idle = 1; // card failed to go idle return sd_card; // return card status with error } sd_response = tx_cmd(SEND_IF_COND,0x000001AA,4,if_cond,NODBUG); // send command to check voltage if (sd_response.byte != 0x01) { // v2 cards respond in idle state sd_card.v1 = 1; // v1 card return sd_card; // return card status } //for (i=0;i<4;i++) if_cond[i]=SPIc(0xff); // read interface condition if ((if_cond[3] == 0xaa) && (if_cond[2] ==0x01)) // check if echo pattern and supply voltage is correct { timeout = 200; // try 200 times to get operating condition do { sd_response = tx_cmd(SEND_OP_COND,0x40000000,0,NULL,NODBUG); // ask for op cond with HCS = 1 timeout--; } while ((sd_response.byte != 0x00) && (timeout != 0x00)); // until response is idle or timeout if (timeout == 0x00) { sd_card.no_opc = 1; // set time out warning return sd_card; // return with error } sd_response = tx_cmd(READ_OCR, 0x00000000, 4, ocr,NODBUG); // ask for ocr if (sd_response.byte != 0x00) { // failed to read ocr cmd sd_card.no_ocr = 1; // set ocr error return sd_card; // return with error } //for (i=0;i<4;i++) ocr[i]=SPIc(0xff); // read ocr if ((ocr[0] & 0x80) != 0x80) { sd_card.no_pup = 1; // card not in power-up state, ccs bit invalid return sd_card; } if ((ocr[0] & 0x40) == 0x40) { // check ccs bit sd_card.hc = 1; // card is high capacity }else{ sd_card.hc = 0; // card is low capacity } }else{ sd_card.hc = 0; // card is low capacity (didn't respond to SEND_IF_COND properly) do { sd_response = tx_cmd(SEND_OP_COND,0x00000000,0,NULL,NODBUG); // send op command with HCS = 0 timeout--; } while ((sd_response.byte != 0x00) && (timeout != 0x00)); if (timeout == 0x00){ // timeout trying to SEND_OP_CMD sd_card.no_opc = 1; // set op_cond error return sd_card; // return with error } } UnAssertMySD(); // unassert card SPI_GOMED // set SPI to medium speed AssertMySD(); // assert card timeout = 100; // try 100 times to get CSD register do { UnAssertMySD(); // in case SEND_CSD already sent sd_response = tx_cmd(SEND_CSD,0x00000000,-1,NULL,NODBUG); // CMD9: ask for csd timeout--; } while ((sd_response.byte != 0x00) && (timeout != 0x00)); if (timeout == 0x00) { sd_card.no_csd = 1; // timed out waiting for CSD UnAssertMySD(); return sd_card; } timeout = 30; // try 30 times to get data start token do { rc = SPIc(0xFF); timeout--; } while ((rc != 0xFE) && (timeout != 0x00)); if (timeout == 0x00) { sd_card.csd_err = 1; // no data start toekn from this card UnAssertMySD(); return sd_card; } for (i=0;i<17;i++) csd[i] = SPIc(0xFF); // read csd if(csd[0] & 0xC0) { //Check CSD_STRUCTURE field for v2+ struct device //Must be a v2 device (or a reserved higher version, that doesn't currently exist) //Extract the C_SIZE field from the response. It is a 22-bit number in bit position 69:48. This is different from v1. //It spans bytes 7, 8, and 9 of the response. c_size = (((unsigned long)csd[7] & 0x3F) << 16) | ((unsigned int)csd[8] << 8) | csd[9]; sd_card.size = ((unsigned long)(c_size + 1) * (unsigned int)(1024u)) - 1; }else{ //Must be a v1 device. //Extract the C_SIZE field from the response. It is a 12-bit number in bit position 73:62. //Although it is only a 12-bit number, it spans bytes 6, 7, and 8, since it isn't byte aligned. c_size = ((unsigned long)csd[6] << 16) | ((unsigned int)csd[7] << 8) | csd[8]; //Get the bytes in the correct positions c_size &= 0x0003FFC0; //Clear all bits that aren't part of the C_SIZE c_size = c_size >> 6; //Shift value down, so the 12-bit C_SIZE is properly right justified in the unsigned long. //Extract the C_SIZE_MULT field from the response. It is a 3-bit number in bit position 49:47. c_size_mult = ((unsigned int)((csd[9] & 0x03) << 1)) | ((unsigned int)((csd[10] & 0x80) >> 7)); //Extract the BLOCK_LEN field from the response. It is a 4-bit number in bit position 83:80. block_len = csd[5] & 0x0F; block_len = 1 << (block_len - 9); //-9 because we report the size in sectors of 512 bytes each sd_card.size = ((unsigned long)(c_size + 1) * (unsigned int)((unsigned int)1 << (c_size_mult + 2)) * block_len) - 1; } sd_response = tx_cmd(CRC_ON_OFF,0x00,0,NULL,NODBUG); // Turn off CRC7 if we can, might be an invalid cmd on some cards (CMD59) sd_response = tx_cmd(SET_BLOCKLEN,512u,0,NULL,NODBUG); // Now set the block length to media sector size. It should be already sd_card.ok = 1; // card is ready UnAssertMySD(); return sd_card; } /***************************************************************************** * EOF *****************************************************************************/ SD Card include file // definitions for SDCardUtilities.c ONLY: SPI Module configuration is in MyConfig.h #ifndef _SDMODULE #define _SDMODULE #define DBUG 1 #define NODBUG 0 struct command_fields { unsigned end_bit:1; // always 1 unsigned crc:7; // CRC unsigned long argument; // 32 bit argument unsigned index:6; // command index unsigned transmitter_bit: 1; // always 1 unsigned start_bit: 1; // always 0 }; struct command_bytes { unsigned char crc_w_stop; // crc with stop bit unsigned char do_not_send; // filler to account for compiler TODO: Why? unsigned char arg_lsb; // least significant argument byte unsigned char arg_3; unsigned char arg_2; unsigned char arg_msb; // most sign byte of arb unsigned char cmd_w_starttx; // command with start and tx bits }; union sd_command { struct command_fields fields; unsigned char bytewise [7]; struct command_bytes bytes; }; #define GO_IDLE_STATE 0 // CMD0 - resets card #define SEND_OP_COND 1 // CMD1 - send operating condition #define SEND_IF_COND 8 // CMD8 - send interface condition #define SEND_CSD 9 // CMD9 - send card status #define SET_BLOCKLEN 16 // CMD16 - set blocklength #define WRITE_SINGLE_BLOCK 24 // CMD24 - write (single) block #define APP_CMD 55 // CMD55 - next command is an application specific command, not standard #define READ_OCR 58 // CMD58 - read the ocr register #define CRC_ON_OFF 59 // CMD59 - turns crc on or off #define SD_STATUS 13 // ACMD13 - send sd card status #define SD_SEND_OP_COND 41 // ACMD41 - send operating condition #define READ_SINGLE_BLOCK 17 // CMD17 - read single block #define START_BLOCK 0xFE // used to indicate the start of a data block #define MMC_FLOATING_BUS 0xFF // Floating bus condition(?) // Summary: The format of an R1 type response // Description: This union represents different ways to access an SD card R1 type response packet. typedef union { unsigned char byte; // Byte-wise access // This structure allows bitwise access of the response struct { unsigned IN_IDLE_STATE:1; // Card is in idle state unsigned ERASE_RESET:1; // Erase reset flag unsigned ILLEGAL_CMD:1; // Illegal command flag unsigned CRC_ERR:1; // CRC error flag unsigned ERASE_SEQ_ERR:1; // Erase sequence error flag unsigned ADDRESS_ERR:1; // Address error flag unsigned PARAM_ERR:1; // Parameter flag unsigned B7:1; // Unused bit 7 }bits; } SD_RESPONSE; typedef struct { // struct to describe sd card unsigned ok:1; // card is initialized or status is OK unsigned wp:1; // card is write protected unsigned cd:1; // card is detected unsigned hc:1; // card is high capacity unsigned v1:1; // card is version 1 unsigned no_idle:1; // card did not GO_STATE_IDLE unsigned no_opc:1; // send_op_cond timed out unsigned no_ocr:1; // send_ocr command timed out unsigned no_pup:1; // card not powered up after init request unsigned no_csd:1; // send_CSD command timed out unsigned no_w:1; // write did not complete (timed out) unsigned csd_err:1; // card did not issue 0xFE at start of CSD data unsigned w_err:1; // write error (command not accepted) unsigned r_err:1; // read error unsigned d_err:1; // data error (data not accepted) unsigned pad:2; unsigned int timeout; // timeout value when ops timeout unsigned long size; // card size in 512kbyte blocks (ie actual capacity is 1000 * m_size / 2 bytes) } SD_CARD; // Prototypes unsigned long write_sd_sector (SD_CARD *sd_card, unsigned long address, unsigned char *buffer); SD_RESPONSE tx_cmd(unsigned char command_index, unsigned long command_argument, char extra_bytes, unsigned char *location, unsigned char debug); SD_CARD init_sdcard (void); #endif /***************************************************************************** * EOF *****************************************************************************/ SPI Utility functions unsigned char SPIc(unsigned char c) // exchange data between SPI master and slave // sends char c and returns received data { //while(MySPITBF); // Wait for tx buffer to clear MySPIBUF = c; // send character while(MySPIRBF ==0 ); // Wait for rx buffer to fill return MySPIBUF; // return received character } unsigned char SPIcdebug(unsigned char c) // exchange data between SPI master and slave // sends char c and returns received data // DEBUG version loads sent and received data in sd_spi_data array { extern int *arrayp; // pointer to next location in sd_spi_data array unsigned char c2; //while(MySPITBF); // Wait for tx buffer to clear MySPIBUF = c; // send character while(MySPIRBF == 0); // Wait for RBF, tx and rx is complete c2 = MySPIBUF; // store received character *(arrayp++) = (int)(c<<8)^(int)(c2&0xFF); // put pair in array return c2; // return stored character }
Attached Image(s)
|
DarioG
Allmächtig.
- Total Posts : 54081
- Reward points : 0
- Joined: 2006/02/25 08:58:22
- Location: Oesterreich
- Status: offline
Re:SD Card in SPI Mode
2010/10/20 03:41:12
(permalink)
Hmm, to me it worked pretty fine "out of the box" from Microchip code to a couple of generic SDcards I had in store...
|
deperkin
New Member
- Total Posts : 25
- Reward points : 0
- Joined: 2009/02/06 21:59:45
- Location: 0
- Status: offline
Re:SD Card in SPI Mode
2010/10/20 06:16:55
(permalink)
Thanks for posting this... I want to try this as soon as I get a chance... I hope it helps along my adventure as well.
|
coreyglynn
New Member
- Total Posts : 21
- Reward points : 0
- Joined: 2006/12/18 11:04:15
- Location: 0
- Status: offline
Re:SD Card in SPI Mode
2010/10/20 18:12:08
(permalink)
Dario - should mention that the Microchip code worked out of the box for me too - in fact, it saved me. What didn't work for me was starting from scratch with the SD Card documentation. If I needed Windows compatibility, would have simply used the uChip library - it takes out a LOT of complexity. -C.
|
DarioG
Allmächtig.
- Total Posts : 54081
- Reward points : 0
- Joined: 2006/02/25 08:58:22
- Location: Oesterreich
- Status: offline
Re:SD Card in SPI Mode
2010/10/22 12:58:57
(permalink)
Ok, yes, I see. I meant "to use that code as a starting point for further investigation". Probably what you did
|
coreyglynn
New Member
- Total Posts : 21
- Reward points : 0
- Joined: 2006/12/18 11:04:15
- Location: 0
- Status: offline
Re:SD Card in SPI Mode
2010/10/25 13:20:16
(permalink)
|
gb
Starting Member
- Total Posts : 83
- Reward points : 0
- Joined: 2004/09/24 14:17:01
- Location: novato, california
- Status: offline
Re:SD Card in SPI Mode
2011/02/14 16:22:35
(permalink)
Hi Please allow me to join this thread. I have a PCB developed from the PIC24FJ256DA210 demo board. I drive a 320x240 QVGA display with 8bit color, so the display buffer fits in the processor RAM. The SST25 flash has 1Mbit more RAM on SPI. I'd like to read images from an SD card to the display using SPI, and am not sure how to proceed. A full screen image converts to a 250K hex file - too large to handle. Any thoughts on how to do this? Perhaps I need to redesign using the SST39 parallel device on EPMP? Even so, the file must pass through a receive buffer in processor RAM. I hope I don't have to revert to using the SSD1926 and JPEG engine as I love the simplicity of the DA210. Graham Brown.
|
DarioG
Allmächtig.
- Total Posts : 54081
- Reward points : 0
- Joined: 2006/02/25 08:58:22
- Location: Oesterreich
- Status: offline
Re:SD Card in SPI Mode
2011/02/14 17:04:03
(permalink)
Can you store them as JPG? A decoder for Jpeg should be doable on a PIC24, maybe a little slow but should work (I'll probably work on such soon...)
|
RoboDLC
Super Member
- Total Posts : 402
- Reward points : 0
- Joined: 2006/03/03 10:49:45
- Location: Colorado
- Status: offline
Re:SD Card in SPI Mode
2011/07/12 08:48:30
(permalink)
☄ Helpfulby autotech 2014/10/02 03:03:28
I'll throw in to this thread in the spirit of sharing and helping folks out. I did NOT get the Microchip SD code to work on all cards, it did on some, but not on others. Specifically it does not work with SDHC cards (cards more than 2GB). It also does not use the SPI mode that I've found published for SPI interface to micro SDHC cards. The key to talking to an SD card is the initialization and realizing that you need to select, then de-select the card after each transaction. So, here is my init routine. It will find and correctly set up any SD or SDHC card of version 1.1 or version 2.0 persuasion. It may not work with SDXC cards, I don't have any of those and have not tested it. Another one of the big differences is that version 1.1 cards (typically only SD) use byte addressing to access blocks, and you need to start on a 512 byte block boundary and may need to tell the card to use 512 byte blocks (if you are doing FAT F/S stuff). The later cards are all addressed in 512 byte block number (not byte address) so you don't need to make sure of that 512 byte boundary. You can't make any assumptions, you might have a 2GB card, which is SD or version 1.1 compatible and actually be version 2.0! My init routine handles that too. The routines below set up the (configured) SPI port, show the SD command sequence and initialize the card on power up. You will need to handle the read and write routines yourself. I got lots of help to get this far, so I'm going to give back to the community to help anyone else who needs it. uint8_t SD_Write(uint8_t b) /** * Read and write a single 8-bit word to the SD/MMC card. * Using standard, non-buffered mode in 8 bit words. * **Always check SPI1RBF bit before reading the SPI2BUF register * **SPI1BUF is read and/or written to receive/send data * * PRECONDITION: SPI bus configured, SD card selected and ready to use * INPUTS: b = byte to transmit (or dummy byte if only a read done) * OUTPUTS: none * RETURNS: */ { SPI1BUF = b; // write to buffer for TX while( !SPI1STATbits.SPIRBF); // wait for transfer to complete SPI2STATbits.SPIROV = 0; // clear any overflow. return SPI1BUF; // read the received value } // Not worth code defining these since they are all the same as SD_Write() #define SD_Read() SD_Write( 0xFF) #define SD_Clock() SD_Write( 0xFF) #define SD_Disable() nMEM_CS = 1; SD_Clock() #define SD_Enable() nMEM_CS = 0 uint8_t SD_SendCmd(uint8_t cmd, LBA addr) /** * Send an SPI mode command to the SD card. * * PRECONDITION: SD card powered up, CRC7 table initialized. * INPUTS: cmd = SPI mode command to send * addr= 32bit address * OUTPUTS: none * RETURNS: status read back from SD card (0xFF is fault) * *** NOTE nMEM_CS is still low when this function exits. * * expected return responses: * FF - timeout * 00 - command accepted * 01 - command received, card in idle state after RESET * * R1 response codes: * bit 0 = Idle state * bit 1 = Erase Reset * bit 2 = Illegal command * bit 3 = Communication CRC error * bit 4 = Erase sequence error * bit 5 = Address error * bit 6 = Parameter error * bit 7 = Always 0 */ { uint16_t n; uint8_t res; uint8_t byte; uint8_t CRC7 = 0; SD_Enable(); // enable SD card byte = cmd | 0x40; SD_Write(byte); // send command packet (6 bytes) CRC7 = CRCAdd(CRC7, byte); byte = addr>>24; SD_Write(byte); // msb of the address CRC7 = CRCAdd(CRC7, byte); byte = addr>>16; SD_Write(byte); CRC7 = CRCAdd(CRC7, byte); byte = addr>>8; SD_Write(byte); CRC7 = CRCAdd(CRC7, byte); SD_Write( addr); // lsb CRC7 = CRCAdd(CRC7,addr); CRC7 = (CRC7 <<1) | 0x01; // CRC7 always has lsb = 1 SD_Write(CRC7); // Not used unless CRC mode is turned back on for SPI access. n = 9; // now wait for a response (allow for up to 8 bytes delay) do { res = SD_Read(); // check if ready if ( res != 0xFF) break; } while ( --n > 0); return (res); // return the result that we got from the SD card. } void SD_InitSPI( void) /** * Configure the SD card SPI bus hardware settings and software interface. * The SD SPI bus uses SPI1 on the PIC24FJ128GA106 chip, RP19, RP21 and RP26. * Using standard, non-buffered mode in 8 bit words. * *** Using the SD SPI mode spec settings instead of the MCHP example. * * PRECONDITION: none * INPUTS: none - The hardware is explicitly set up here, no alternates are considered. * OUTPUTS: none * RETURNS: none. */ { nMEM_CS = 1; // De-select the SD card if (sdcard.cardInit == 1) { return; } // init the spi module for a slow (init) clock speed, 8 bit byte mode SPI1STATbits.SPIEN = 0; // disable SPI for configuration //SPI1CON1 = 0x013c; // Master, CKE=1; CKP=0, sample middle, prescale 1:64 (250KHz)- works SPI1CON1 = 0x027c; // Master, CKE=0; CKP=1, sample end, prescale 1:64 (250KHz) - works SPI1CON2 = 0x0000; // No buffer, no frame mode SPI1STAT = 0x8000; // enable GenerateCRCTable(); // Get ready to do SD command CRC generation } uint8_t SD_InitMedia( void) /** * Discover the type and version of the installed SD card. This routine * will find any SD or SDHC card and properly set it up. * * PRECONDITION: none * INPUTS: none * OUTPUTS: none * RETURNS: 0 if successful, some other error if not. */ { uint16_t n; uint8_t res = 0; // If we get that far... uint32_t timer; uint8_t cmd; uint8_t db[16]; // for when we get some data back to look at if (sdcard.cardInit == 1) { return(0); // done, don't do it again. } SD_Disable(); // 1. start with the card not selected for ( n=0; n<10; n++) // 2. send 80 clock cycles so card can init registers SD_Clock(); SD_Enable(); // 3. now select the card res = SD_SendCmd( RESET, 0); SD_Disable(); // 4. send a reset command and look for "IDLE" if ( res != 1) { SD_Disable(); return(LOG_FAIL); // card did not respond with "idle", diagnostic value } res = SD_SendCmd(SEND_IF_COND, 0x000001AA); // 5. Check card voltage (type) for SD 1.0 or SD 2.0 if ( (res == 0xFF) || (res == 0x05)) { // didn't respond or responded with an "illegal cmd" sdcard.cardVer = 1; // means it's an SD 1.0 or MMC card timer = t_1ms + 300; // 6. send INIT until receive a 0 or 300ms passes while(timer > t_1ms) { res = SD_SendCmd(INIT,0); SD_Disable(); // SendSDCmd() enables SD card if (!res) { break; // The card is ready } } if (res != 0) { return(LOG_FAIL); // failed to reset. } SD_Disable(); // remember to disable the card } else { // need to pick up 4 bytes for v2 card voltage description sdcard.cardVer = 2; // SD version 2.0 card for (n=0; n<4; n++) { db[n] = SD_Read(); } // but we'll ignore it for now, we know what the card is SD_Disable(); cmd = SEND_APP_OP; // 6. send INIT or SEND_APP_OP repeatedly until receive a 0 timer = t_1ms + 300; // wait up to .3 seconds for signs of life res = SD_SendCmd(APP_CMD, 0); SD_Disable(); // will still be in idle mode (0x01) after this while (timer > t_1ms) { res = SD_SendCmd(cmd, 0x40000000); SD_Disable(); if ( (res &0x0F) == 0x05 ) { // ACMD41 not recognized, use CMD1 cmd = INIT; } else { cmd = SEND_APP_OP; } if (!res) { break; } } if (res != 0) { return(LOG_FAIL); // failed to reset. } res = SD_SendCmd(READ_OCR,0); // 7. Check for capacity of the card if (res != 0) { return(LOG_FAIL); // error, bad thing. } for (n=0; n<4; n++) { db[n] = SD_Read(); } SD_Disable(); if ( ((db[0] & 0x40) == 0x40) && (db[0] != 0xFF) ) { // check CCS bit (bit 30), PoweredUp (bit 31) set if ready. sdcard.cardCap = 1; // card is high capacity, uses block addressing } else{ sdcard.cardCap = 0; // card is low capacity, uses byte addressing } } sdcard.cardInit = 1; // successfully initialized the SD card. sdcard.cardBlock = SD_BSIZE; // for completeness' sake // Get the CSD register to find the size of the card res = SD_SendCmd(SEND_CSD,0); if (res != 0) { return(LOG_FAIL); } timer = t_1ms + 300; // wait for a response while(timer > t_1ms) { res = SD_Read(); if (res == DATA_START) { break; } } if (res == DATA_START) { // if it did not timeout, read a sector of data for (n=0; n< 16; n++) { db[n] = SD_Read(); // read the received value } // ignore CRC (for now) SD_Read(); SD_Read(); SD_Disable(); } else { return(LOG_FAIL); } if (sdcard.cardCap == 1) { // Uses the SDHC capacity calculation sdcard.cardSize = db[9] + 1; sdcard.cardSize += (uint32_t)(db[8] << 8); sdcard.cardSize += (uint32_t)(db[7] & 0x0F)<<12; sdcard.cardSize *= 524288; // multiply by 512KB // (C_SIZE + 1) * 512 * 1024 sdcard.cardNumBlocks = sdcard.cardSize/sdcard.cardBlock; } else { // Uses the SD capacity calculation sdcard.cardSize = ((uint16_t)((db[6] & 0x03)<<10) | (uint16_t)(db[7]<<2) | (uint16_t)((db[8] & 0xC0)>>6)) + 1; sdcard.cardSize = sdcard.cardSize <<(((uint16_t)((db[9] & 0x03)<<1) | (uint16_t)((db[10] & 0x80)>>7)) +2); sdcard.cardSize = sdcard.cardSize <<((uint16_t)(db[5] & 0x0F)); // (C_SIZE +1) <<(C_SIZE_MULT + 2) <<(READ_BL_LEN), then set SET_BLOCKLEN to be 512 next. sdcard.cardNumBlocks = sdcard.cardSize/sdcard.cardBlock; res = SD_SendCmd(SET_WBLEN,0x00000200); // Set block size to 512 bytes SD_Disable(); } // Now kick to full speed 8MHz mode. SPI1STATbits.SPIEN = 0; // disable SPI for configuration // SPI1CON1 = 0x0137; // Master, CKE=1; CKP=0, sample middle, prescale 1:4 (4MHz) all works // SPI1CON1 = 0x013b; // Master, CKE=1; CKP=0, sample middle, prescale 1:2 (8MHz) no write // SPI1CON1 = 0x003b; // Master, CKE=0; CKP=0, sample middle, prescale 1:2 (8MHz) write, no read // SPI1CON1 = 0x007b; // Master, CKE=0; CKP=1, sample middle, prescale 1:2 (8MHz) no write SPI1CON1 = 0x027b; // Master, CKE=0; CKP=1, sample end, prescale 1:2 (8MHz) all works SPI1STATbits.SPIEN = 1; // disable SPI for configuration return(res); } Note my experiments with the MCHP SPI modes. The one that I chose works with Ativa, Sandisk and Kingston SD and SDHC cards from 1GB to 4GB at low speed and the full PIC24F SPI speed of 8MHz. Those were all of the cards that I had. Note that all of the commands are "#defined", so you'll need to put your own defines in there. Also I have a background 1ms timer that I use and you'll need to choose your way of storing capacities and card specifics (my struct is sdcard). Use this struct to determine how to address blocks with reads and writes (address or block mode). Clearly, I have left some of the details for reading and writing as an exercise for the student. It is the journey that is the fun part after all! Here is where you can get the most recent (that I've found) SD card access specs allowed without being an SD association member: http://www.sdcard.org/dev.../pls/simplified_specs/ This fellow's init flow-chart got me going on the right track (Thank you!) http://elm-chan.org/docs/mmc/mmc_e.html Between these two sites and the init code above that arose from them, you will get your SD system working on any SD or SDHC card. Just keepin' it real, DLC
|
Anky
New Member
- Total Posts : 1
- Reward points : 0
- Joined: 2013/04/15 22:30:30
- Location: 0
- Status: offline
Re:SD Card in SPI Mode
2013/04/17 06:15:37
(permalink)
I read all the above responses, and it did help me a lot. However I am still facing issues with Init sequence for SD card. I am using DSPIC30F6010. Problem : SD card always responds "0" to CMD0(RESET) . That means it doesn't enter the idle mode. I deassert CS line and send 80 clock cycles and then assert CS line low and Send CMD0 : MySD_Write(0x40); // cmd0 MySD_Write(0x00); MySD_Write(0x00); MySD_Write(0x00); MySD_Write(0x00); MySD_Write(0x95); I observed on scope that MISO line is always high (3.2 V) on SD card side. As I mentioned earlier that the response to CMD 0 from SD card is always 0. I am doubting the HW connections. 1) Does the MISO(PIN 7 SD card) line always stays high ? 2) HW connection : No pull up resistors are used for MOSI, MISO, SCLK, and SS. Are they really required ? 3) Does SD card works only with CkP=1 and CKE =0 and SMP bit set ? As I am currently working with CKP =0, CKE=0 and SMP =0 I tried with a 2GB card and then with 8gb SDHC card. SPI settings are as mentioned below : SPI1CON = 0x0038; // Master mode enabled and SPI clock set to 234 K SPI1STATbits.SPIROV = 0; // Clear Overflow bit SPI1STATbits.SPIEN = 1; Thanking you in advance, Please guide me.
|
om2008
New Member
- Total Posts : 15
- Reward points : 0
- Joined: 2009/09/19 00:42:54
- Location: 0
- Status: offline
Re:SD Card in SPI Mode
2013/05/23 04:30:54
(permalink)
Anky I read all the above responses, and it did help me a lot. However I am still facing issues with Init sequence for SD card. I am using DSPIC30F6010. Problem : SD card always responds "0" to CMD0(RESET) . That means it doesn't enter the idle mode. I deassert CS line and send 80 clock cycles and then assert CS line low and Send CMD0 : MySD_Write(0x40); // cmd0 MySD_Write(0x00); MySD_Write(0x00); MySD_Write(0x00); MySD_Write(0x00); MySD_Write(0x95); I observed on scope that MISO line is always high (3.2 V) on SD card side. As I mentioned earlier that the response to CMD 0 from SD card is always 0. I am doubting the HW connections. 1) Does the MISO(PIN 7 SD card) line always stays high ? 2) HW connection : No pull up resistors are used for MOSI, MISO, SCLK, and SS. Are they really required ? 3) Does SD card works only with CkP=1 and CKE =0 and SMP bit set ? As I am currently working with CKP =0, CKE=0 and SMP =0 I tried with a 2GB card and then with 8gb SDHC card. SPI settings are as mentioned below : SPI1CON = 0x0038; // Master mode enabled and SPI clock set to 234 K SPI1STATbits.SPIROV = 0; // Clear Overflow bit SPI1STATbits.SPIEN = 1; Thanking you in advance, Please guide me. I had the same problem, with some search i found the solution. This is my code: void SDCard_InitializeSPI() { SDCard_WriteCS(1); unsigned char i, response = 0xFF, retry = 255, retry2 = 255; do { SDCard_WriteCS(0); for(i = 0; i < 10; i++) { SDCard_SPI(0xFF); } SDCard_SPI(0x40); SDCard_SPI(0x00); SDCard_SPI(0x00); SDCard_SPI(0x00); SDCard_SPI(0x00); SDCard_SPI(0x95); do { response = SDCard_SPI(0xFF); } while((response == 0xFF) && (retry2-- > 0)); SDCard_WriteCS(1); } while((response != 0x01) && (retry-- > 0)); } There are two things to notice, which i don't understand why: - The first 80 clocks are being sent after asserting CS. SDCard_WriteCS(0); for(i = 0; i < 10; i++) { SDCard_SPI(0xFF); } - You should make more than one try to get the response. do { response = SDCard_SPI(0xFF); } while((response == 0xFF) && (retry2-- > 0)); If the response is 0xFF then the card is not responding, otherwise we will check for 0x01 (Idle State) response. I have attached the source code which i learned this from.
post edited by om2008 - 2013/05/23 06:15:34
|
mr_rastegari
New Member
- Total Posts : 1
- Reward points : 0
- Joined: 2013/07/27 05:07:01
- Location: 0
- Status: offline
Re:SD Card in SPI Mode
2013/07/27 05:16:40
(permalink)
Hi. Where is definition of "MySDCD", "MySDWP" and "SPIGOSLOW"? How can I get it?
post edited by mr_rastegari - 2013/07/27 05:19:27
|
Techbee
Starting Member
- Total Posts : 69
- Reward points : 0
- Joined: 2015/03/31 06:17:48
- Location: 0
- Status: offline
Re:SD Card in SPI Mode
2015/05/19 01:45:56
(permalink)
hey... can you post your myconfig,systemwideconfig,spimodule
|
Aussie Susan
Super Member
- Total Posts : 3832
- Reward points : 0
- Joined: 2008/08/18 22:20:40
- Location: Melbourne, Australia
- Status: offline
Re:SD Card in SPI Mode
2015/05/19 20:33:02
(permalink)
Techbee - this thread is nearly 2 years old. You are MUCH better off not hijacking an old thread but starting a new one with your question and let people who are still in this forum try to answer it. Susan
|
tmig
Senior Member
- Total Posts : 163
- Reward points : 0
- Joined: 2007/04/18 09:34:33
- Location: cape cod
- Status: offline
Re:SD Card in SPI Mode
2016/11/01 10:42:59
(permalink)
Reply to Dennis Clark Good overview! As old as this thread is some may still refer to it, so I will add this - I think this line in the code for the card size has a mistake: sdcard.cardSize += (uint32_t)(db[7] & 0x0F)<<12; I think it should be: sdcard.cardSize += (uint32_t)(db[7] & 0x3F)<<16; For smaller cards you said you used this byte (db[7]) would be zero, so this error would go unnoticed. Regards, Tmig
post edited by tmig - 2016/11/01 11:13:23
|
Aussie Susan
Super Member
- Total Posts : 3832
- Reward points : 0
- Joined: 2008/08/18 22:20:40
- Location: Melbourne, Australia
- Status: offline
Re:SD Card in SPI Mode
2016/11/01 19:34:08
(permalink)
You can tell it must be around Halloween - the zombies are rising (yet) again. A 2013 thread resurrected in 2015 and *again* in 2016! Susan
|