• AVR Freaks

Helpful ReplySD Card in SPI Mode

Author
coreyglynn
New Member
  • Total Posts : 21
  • Reward points : 0
  • Joined: 2006/12/18 11:04:15
  • Location: 0
  • Status: offline
2010/10/19 18:27:14 (permalink)
0

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)

#1
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)
0
Hmm, to me it worked pretty fine "out of the box" from Microchip code to a couple of generic SDcards I had in store...

GENOVA :D :D ! GODO
#2
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)
0
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.
 
#3
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)
0
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.
#4
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)
0
Ok, yes, I see.
I meant "to use that code as a starting point for further investigation". Probably what you did Smile

GENOVA :D :D ! GODO
#5
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)
0
Exactly what I did.....
#6
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)
0
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.
 

graham brown
#7
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)
0
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...)

GENOVA :D :D ! GODO
#8
dlc@frii.com
Super Member
  • Total Posts : 370
  • Reward points : 0
  • Joined: 2006/03/03 10:49:45
  • Location: 0
  • Status: offline
Re:SD Card in SPI Mode 2011/07/12 08:48:30 (permalink) ☄ Helpfulby autotech 2014/10/02 03:03:28
5 (1)
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

 
#9
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)
0
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.
     
#10
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)
0
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
#11
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)
0
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
#12
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)
0
hey... can you post your myconfig,systemwideconfig,spimodule
#13
Aussie Susan
Super Member
  • Total Posts : 3609
  • 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)
4 (2)
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
#14
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)
3 (1)
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
#15
Aussie Susan
Super Member
  • Total Posts : 3609
  • 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)
4 (2)
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
#16
Jump to:
© 2019 APG vNext Commercial Version 4.5