1. Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.
    Dismiss Notice

SD Card Project

Discussion in 'Microcontrollers' started by AtomSoft, Aug 11, 2008.

  1. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    Once you've got it initialized into SPI mode (meaning your send-command function works properly), the rest is very simple. All the error checking for reads and writes is a good idea, but for now you can simplify a bit and just read and write without all that.
     
    Last edited: Aug 21, 2008
  2. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    omg i think i got it lol. I used the CD (card detect and the 100ms delay) to tell when card is in and delay then initialize the card and write the data then read into buffer and both contained the same data. Does that mean a success ?
     
  3. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    Most likely. Read sector zero and see if you see the partition table (16 bytes) starting at offset $1be. The rest of that sector will be mostly $00's. Compare with what you would expect to see there. Here's a good link with the beginnings of what you need to know.
     
  4. dave

    Dave New Member

    Joined:
    Jan 12, 1997
    Messages:
    -
    Likes:
    0


     
  5. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US

    how would i read it? What program?

    EDIT: here is a dump. Does it look ok? I think i see how it is. But why is it in the begging? I dumped sectors 1.
     

    Attached Files:

    • a.txt
      File size:
      512 bytes
      Views:
      119
    Last edited: Aug 22, 2008
  6. Eurisko

    Eurisko New Member

    Joined:
    Aug 19, 2008
    Messages:
    7
    Likes:
    1
    AtomSoft,

    If you have a USB SD card reader, you can read sectors from the SD card with a program called DSKPROBE.EXE

    Do a Google search, there should be many sites that offer it for downloading.

    I understand that it is included with WindowsXP service pack2, check the support\tools directory.

    I looked at your sector dump, it is two series of bytes increasing from 0x00 to 0xFF.

    If that is what you wrote to the sector, it must have been a success.

    The Master Boot Record is at sector 0.
    The sectors from 1 to 63 (or more) are reserved, and usually contain no data, just zeros.
     
  7. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    Yes that is i think what i wrote since i looped from 0 - 255. 2 times. But why are they so far apart? Aren't they supposed to be closer? And where would i write to to store data then? Only sector 0?

    My Code:
    Code (text):

    #include <p18f2550.h>
    #include <stdio.h>
    #include <delays.h>

    #pragma config WDT = OFF, FOSC = HS, LVP = OFF
    typedef unsigned LBA; // logic block address, 32 bit wide

    //Funtions
    void main(void);
    void initSD( void);
    void delay_s(unsigned char);
    void delay_ms(unsigned char);
    void delay_us(unsigned char);

    unsigned char writeSPI( unsigned char b);

    int sendSDCmd( unsigned char c, unsigned a);
    int initMedia( void);
    int writeSECTOR( LBA a, char *p, char *p2);
    int readSECTOR( LBA a, char *p, char *p2);

    // I/O definitions
    #define SDCS LATCbits.LATC6
    #define SDCS_T TRISCbits.TRISC6
    #define SDCD PORTCbits.RC0
    #define SDCD_T TRISCbits.TRISC0
    #define LED_T TRISBbits.TRISB2
    #define LED LATBbits.LATB2

    // Macros

    #define readSPI() writeSPI( 0xFF)
    #define clockSPI() writeSPI( 0xFF)
    #define disableSD() SDCS = 1; clockSPI()
    #define enableSD() SDCS = 0

    // SD card commands
    #define RESET 0 // a.k.a. GO_IDLE (CMD0)
    #define INIT 1 // a.k.a. SEND_OP_COND (CMD1)

    #define READ_SINGLE 17
    #define WRITE_SINGLE 24

    #define I_TIMEOUT 10000
    #define RI_TIMEOUT 10000
    #define R_TIMEOUT 25000
    #define W_TIMEOUT 250000

    #define FALSE 0
    #define FAIL FALSE

    #define E_COMMAND_ACK 0x80
    #define E_INIT_TIMEOUT 0x81

    #define DATA_START 0xFE
    #define DATA_ACCEPT 0x05


    #pragma udata data1
    char data1[256];
    #pragma udata buffer1
    char buffer1[256];


    int writeSECTOR( LBA a, char *p, char *p2)
    // a LBA of sector requested
    // p pointer to sector buffer
    // returns TRUE if successful
    {
        unsigned r, i;
    // 1. send WRITE command
        r = sendSDCmd( WRITE_SINGLE, ( a << 9));
        if ( r == 0) // check if command was accepted
        {
    // 2. send data
            writeSPI( DATA_START);
    // send 512 bytes of data
            for( i=0; i<256; i++)
                writeSPI( *p++);
            for( i=0; i<256; i++)
                writeSPI( *p2++);
    // 3. send dummy CRC
            clockSPI();
            clockSPI();
    // 4. check if data accepted
            r = readSPI();
            if ( (r & 0xf) == DATA_ACCEPT)
            {
    // 5. wait for write completion
                for( i=0; i<W_TIMEOUT; i++)
                {
                    r = readSPI();
                    if ( r != 0 )
                        break;
                }
            } // accepted
            else
            r = FAIL;
       } // command accepted
    // 6. remember to disable the card
        disableSD();
        return ( r); // return TRUE if successful
    }

    int readSECTOR( LBA a, char *p, char *p2)
    // a LBA of sector requested
    // p pointer to sector buffer
    // returns TRUE if successful
    {
        int r, i;
    // 1. send READ command
        r = sendSDCmd( READ_SINGLE, ( a << 9));
        if ( r == 0) // check if command was accepted
        {
    // 2. wait for a response
            for( i=0; i<R_TIMEOUT; i++)
            {
                r = readSPI();
                if ( r == DATA_START)
                break;
            }
    // 3. if it did not timeout, read 512 byte of data
            if ( i != R_TIMEOUT)
            {
                i = 256;
                do{
                    *p++ = readSPI();
                } while (--i>0);

                do{
                    *p2++ = readSPI();
                } while (--i>0);
    // 4. ignore CRC
            readSPI();
            readSPI();
        } // data arrived
    } // command accepted
    // 5. remember to disable the card
        disableSD();
        return ( r == DATA_START); // return TRUE if successful
    }

    void initSD( void)
    {
        ADCON1 = 0x0F;
        TRISB = 0x01;
        TRISC = 0;

        LED_T = 0;
        SDCD_T = 1;
        SDCS = 1; // initially keep the SD card disabled
        SDCS_T = 0; // make Card select an output pin
        // init the SPI2 module for a slow (safe) clock speed first
        SSPSTAT = 0b00000000;       //SMP(7)=0, CKE(6)=0 (clock edge idle to active), others don't care
        SSPCON1 = 0b00010010;       //CKP(4)=1 clock polarity (idle high)
                                    //SSPM3:SSPM0(3:0)=010 spi clock FOSC/64 (<400kHz)
        SSPCON1bits.SSPEN=1;        //SSPEN(5)=1 enable SPI
    }

    unsigned char writeSPI( unsigned char b)
    {
        // send one byte of data and receive one back at the same time
        SSPBUF=b; // write to buffer for TX
        while( !SSPSTATbits.BF); // wait transfer complete
        return SSPBUF; // read the received value
    }

    int sendSDCmd( unsigned char c, unsigned a)
    // c command code
    // a byte address of data block
    {
    int i, r;
        // enable SD card
        enableSD();
        // send a comand packet (6 bytes)
        writeSPI( c | 0x40); // send command
        writeSPI( a>>24); // msb of the address
        writeSPI( a>>16);
        writeSPI( a>>8);
        writeSPI( a); // lsb
        writeSPI( 0x95); // send CMD0 CRC
        // now wait for a response, allow for up to 8 bytes delay
        for( i=0; i<8; i++)
        {
            r=readSPI();
            if ( r != 0xFF)
            break;
        }
        return ( r);
        // NOTE CSCD is still low!
    }

    int initMedia( void)
    // returns 0 if successful
    // E_COMMAND_ACK failed to acknowledge reset command
    // E_INIT_TIMEOUT failed to initialize
    {
        int i, r;
    // 1. with the card NOT selected
        disableSD();
    // 2. send 80 clock cycles start up
        for ( i=0; i<10; i++)
        clockSPI();
    // 3. now select the card
        enableSD();
    // 4. send a single RESET command
        r = sendSDCmd( RESET, 0); disableSD();
        if ( r != 1) // must return Idle
        return E_COMMAND_ACK; // comand rejected
    // 5. send repeatedly INIT until Idle terminates
        for (i=0; i<I_TIMEOUT; i++)
        {
            r = sendSDCmd( INIT, 0); disableSD();
            if ( !r)
            break;
        }
        if ( i == RI_TIMEOUT)
            return E_INIT_TIMEOUT; // init timed out
    // 6. increase speed
        SSPCON1bits.SSPEN=0; // disable the SPI module
        SSPCON1 = 0x30;
        SSPCON1bits.SSPEN=1; // enable SPI
        return 0;
    }
    ////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////

    void main(void)
    {
        int result,i,addr;
        initSD();

        while(SDCD);
        delay_ms(100);

       
        result = initMedia();
        while(result != 0) result = initMedia();

        for(i=0;i<256;i++)
            data1[i] = i;
    retryW:
            delay_ms(100);
            addr = 10000;
            for(i=0;i<1000;i++){
                if(!writeSECTOR(addr+i,data1,data1))
                        goto retryW;
            }
    LED = 1;
    retryV:
            delay_ms(100);
            addr=10000;             //verify write
            for(i=0;i<1000;i++){
                if(!readSECTOR(addr+i,buffer1,buffer1))
                        goto retryV;
            }

        if(memcmp(data1,buffer1,256)){  //verify
            while(1);                   //mismatch - 4 blinks
        }

        while(1);
    }

    ////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////

    void delay_s(unsigned char x)
    {
        char var1;
        for(var1=0;var1<x;var1++)
        {              
            Delay10KTCYx(200);
        }
    }

    void delay_ms(unsigned char x)
    {
        char var1;
        for(var1=0;var1<x;var1++)
        {              
            Delay1KTCYx(2);
        }
    }

    void delay_us(unsigned char x)
    {
        char var1;
        for(var1=0;var1<x;var1++)
        {              
            //Delay10TCYx(2);
            Delay1TCY();
            Delay1TCY();
        }
    }
     
    EDIT:
    Why when i plug in the mem card i get like a error asking me that its not formated. Where should my data go then?
     
    Last edited: Aug 22, 2008
  8. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    Use your code. Init the SD and then read sector 0. Then use MPLAB's watch window to view your read buffers. Change the properties for them from ASCII to hex so they make sense.

    Once I get my editor ready enough for public viewing, you can have a copy of that and use it to look with. It works now, but it's not real nice to use yet.
     
    Last edited: Aug 22, 2008
  9. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    No idea what you mean by "far apart" and "closer". :confused:
    EDIT: Is it possible that you don't know about ASCII? This link is one of my favorite ASCII tables. See how numbers 0 to 32 are all control characters and stuff beyond 127 is other strange stuff? They often don't print anything onscreen (or print gibberish), so you see gaps.

    I thought you had already done asynchronous serial comm (RS232)? If you had, you'd already know about ASCII.

    Anyway, if you're going to use my hex editor you'll have to learn to do async serial (it's EASY!), because I use a VT100 terminal program (Tera Term Pro) as a display and the PC's keyboard as input.

    Again, no idea what you mean by that. You can write anywhere you want on the card.

    You mean when you plug it into the camera after writing to it with the PIC? If so, that's because you're probably wiping out part of a FAT or directory, or a file, when you write to the card. No worries. That's gonna happen when you write to random places on the card. Reformat it and continue.

    Eventually you'll either get a card for raw use, meaning for PIC writing only, or you'll build code to write in proper FAT16 ways that won't trash the directory/FAT structure on the card.
     
    Last edited: Aug 23, 2008
  10. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    Here's an advance look at my hex editor for SD cards and 18F4620. It displays one 512-byte sector at a time. Left side is hex and right side is ASCII.

    This is a what a typical sector 0 (MBR - Master Boot Record) looks like. Look at the 16 bytes starting at $1be for the partition table for the card.

    Don't forget that FAT was invented on PC, so all multi-byte values in here are little-endian (LSB first). Get used to it.

    [​IMG]


    Here's the Boot Record (first sector of the partition) for this little 32MB card. Even though it's labeled as FAT12, it's actually FAT16.

    [​IMG]


    And here is the root directory

    [​IMG]
     
    Last edited: Aug 25, 2008
  11. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    I know about ASCII um.. let me make the question clearer.

    Since im writing 512 bytes back to back arent they supposed to be lined up closer?

    Example if i write 256 A's 2 times (to make 512) is it not supposed to be like back to back?

    AAAAAAAA...
    AAAAAAAA...

    and not like

    AAAAAAAA...
    jhfondljnfdln094u....(garbage)
    AAAAAAAA...

    Thats what it seems like.

    EDIT: SORRY! i can be so stupid sometimes lol. Probable am tired. Um I see it does write back to back just that some characters are unknown by me lol. I checked the actual hex values and all line up! Im going to try and write some useful information on it later after work. and see if i can get the info out. Like small bmp hexs and stuff. If i can then ill try that Fat16 Stuff so i can load actual BMPs on the card and try to get the hex info out.
     
    Last edited: Aug 23, 2008
  12. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    More like unknown to ASCII. They're unprintable. Anything beyond 127 is not "official" ASCII, as they're more than 7-bits. And the first 32 aren't printable either. They're control codes/escape codes/etc.
     
  13. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    Why not write some ASCII text to the card. A few paragraphs (512 characters) of some easily recognizable text. Then when you look at the buffers in your watch window in the default ASCII mode you can tell at a glance if it got written and read back properly. Be sure you're looking at the read buffer (not the write buffer). :p

    EDIT: Here's 256 bytes of text:
    and another 256 bytes:
     
    Last edited: Aug 24, 2008
  14. Eurisko

    Eurisko New Member

    Joined:
    Aug 19, 2008
    Messages:
    7
    Likes:
    1
    I'm toying with this idea. Don't laugh.

    Have some spoken messages (error waves, prompt waves) on the SD card, then the PIC can just TELL me what the problem is with my code. I'll have to teach it how to say numbers, 0 - 255 to start.

    Would be nice if it had a feminine voice, I'd hate to have to listen to mine.

    Maybe even a talking alarm clock. Gets louder and angrier the longer I stay in bed. I think I've just described my wife...
     
  15. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    heh that would be nice. Sorry i have been working too much this week and havent had time to actually do this. But i will have time on friday. Also i am going to order those 4620's today... when they come in the real fun begins.
     
  16. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    Oh i almost forgot Futz... can i just store the whole thing (512) in eeprom and then send it from eeprom to the card? Seems like i can but want to make sure as not to waste valuable time.
     
  17. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    Most on-chip EEPROMs are 256 bytes. You could store half of it and use it twice.
     
  18. Mike - K8LH

    Mike - K8LH Well-Known Member

    Joined:
    Jan 22, 2005
    Messages:
    3,637
    Likes:
    109
    Location:
    Michigan, USA
    The '2620/'4620 has 1024 bytes internal EEPROM...
     
  19. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    Do they really?!! I hadn't looked. Very generous.

    Anyway, it's only a temporary fix for Atomsoft. EEPROM isn't very well suited to use as a buffer.

    Like it or not, if you're going to do FAT16 file operations (or even a lot of raw use) on an SD card, you pretty much need a chip with as much RAM as possible.
     
    Last edited: Aug 26, 2008
  20. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    BORED: i know this probably has been thought of but since you can run code in eeprom why not make like a parser? true it will be a tad slower but in cases where it doesnt matter wouldnt that be cool?

    By parser i mean like code to read eeprom and then determin what command and arguments to give out to that.

    Reply:
    Um thats cool. I just need it to test if im sending ok nothing more. Once its a fact that i can read/write 100% -/+ 10% lol i will be happy enough to get into the Fat16 and other FileSystems. Anyone know where i can get info on file structures?

    Like BMP Files and JPG, GIF, TXT and so on... (BMP , JPEG & TXT are of most importance)
     
    Last edited: Aug 26, 2008
  21. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    Since the 4620 is out till sep 16 cant i get the "PIC18F4525"
     

Share This Page