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. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    Hey all while im waiting for payday to finish my charger project. i thought id start on my other project. SD Cards. I recently bought a few things from futulec:

    SD/MMC Mini Board

    Logic Converter Mini Board

    I wanted these for my glcd mostly but plan to do TFT LCDs (color) Soon! So i though heh memory is needed.

    i hope this kind of thread is ok to post. Im hoping to get help on the simpler things here like SPI and other things.

    At the end i of learning this i will of course share my whole project including schematics, code and thoughts. I will be sure to keep everything neat and commented as much as possible. So if there is anyone who wished to help me along the journey.. I say thank you.

    Here are some PDFs i collected along the way:
    PICmicro Mid-Range MCU Family Reference Manual
    SanDisk SD-ROM Product Manual
    PIC - MMC (Multi Media Card) Flash Memory Extension


    Any links or datasheets or anything that can help would be great. Also any advice or experience is great too!
     
  2. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    I got working on mine again this weekend. I'm having some strangeness and haven't yet got it working. I would like to compare code with what you come up with. :p My code is mostly borrowed from the book "Learning To Fly The PIC24", but modified to suit the 18F4620 I'm using.

    The 32MB cards I have don't seem to work in SPI mode. They return illegal command codes every time. After thinking it was my code for quite a while I finally grabbed the 4GB card out of the camera and it works (almost) properly. So I'm shopping for another SD card just for experimenting. Think I'll get a 2GB. They're around $11.

    I'll find my list and post it again later today or tonight. There are some very good refs out there, along with a lot of useless crap.
     
    Last edited: Aug 11, 2008
  3. Mike - K8LH

    Mike - K8LH Well-Known Member

    Joined:
    Jan 22, 2005
    Messages:
    3,637
    Likes:
    109
    Location:
    Michigan, USA
    Well how do you expect me to help you if neither one of you guys bought a set of boards for me (grin)?

    Just joking. I sincerely wish I had the budget to buy some of these things. I'm always going to be a year behind you guys on these fun projects.

    Mike
     
  4. dave

    Dave New Member

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


     
  5. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.

    SD sockets can be made out of an old ribbon cable end or purchased for a couple bucks. The bought ones you just solder some pins on and plug into your breadboard. Cheap.

    SD cards are also dirt cheap these days. Like I said, 2GB for around $11. Look around and you'll likely find better deals than that.

    The rest is just wiring and some pullup resistors. Very simple. The logic level converter isn't really necessary. Use voltage dividers for the wires that need em.
     
    Last edited: Aug 11, 2008
  6. AtomSoft

    AtomSoft Well-Known Member

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

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    Even SparkFun sells sockets for only $4. I've seen them elsewhere for around $2.

    And Futurlec's SD/MMC Mini Board at $7 is a great deal. For what you get, it's well worth a meager $7, if you're doing an order anyway.
     
    Last edited: Aug 11, 2008
  8. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    I was suprised to get it from futurlec in such short time. I always hear bad things. But i didnt know it comes from thailand. If i knew that i would have expected but i thought it was in US or UK so i was expecting it sooner. But when you add time for customs then its ok.
     
  9. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    Last edited: Aug 11, 2008
  10. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    thx. just got out of work and my feet are killing me. Im going to try something tonigh on my 18F448
     
  11. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    So I got a 2GB card and played with the code for a long while. I think I almost have it working. But now I plug the 32MB card in and it works normally (meaning almost working). I don't know what was going on before. So buying the 2GB wasn't necessary. Ah well... It'll get used, I guess.

    Now I have code that seems to init the card to SPI mode, but won't write a sector. According to Lucio Di Jasio, when I send the write_single command it's supposed to return zero, meaning "command accepted". It never does. It's always one, meaning "idle".

    Here's my current, broken code for 18F4620:
    Code (text):
    #include "sdcard.h"

    char data1[256];
    char data2[256];
    char buffer1[256];
    char buffer2[256];

    void main(void){
        LBA addr;
        int i,r;
        trisd=0;
        trisc=0b00010000;
        CSel=1;                     //init chip select (active low)
        LED=0;                      //LED off
        osccon=0x72;                    //8MHz clock
        osctune.6=0;                    //disable PLL
        while(!osccon.IOFS);                //wait for osc stable
        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)
        sspcon1.SSPEN=1;                //SSPEN(5)=1 enable SPI
        for(i=0;i<256;i++){             //fill the buffers
            data1[i]=i;
            data2[i]=i;
        }
        r=initMedia();
        if(r){                      //card init failed - 1 blink
            while(1){
                LED=1;
                delay_ms(75);
                LED=0;
                delay_s(1);
            }
        }
        else{                       //write card
            addr = 10000;
            for(i=0;i<1000;i++){
                if(!writeSector(addr+i,data1,data2)){
                    while(1){       //write failed - 2 blinks
                        LED=1;
                        delay_ms(75);
                        LED=0;
                        delay_ms(75);
                        LED=1;
                        delay_ms(75);
                        LED=0;
                        delay_s(1);
                    }
                }
            }
            addr=10000;             //verify write
            for(i=0;i<1000;i++){
                if(!readSector(addr+i,buffer1,buffer2)){
                    while(1){       //verify failed - 3 blinks
                        LED=1;
                        delay_ms(75);
                        LED=0;
                        delay_ms(75);
                        LED=1;
                        delay_ms(75);
                        LED=0;
                        delay_ms(75);
                        LED=1;
                        delay_ms(75);
                        LED=0;
                        delay_s(1);
                    }
                }
                if(memcmp(data1,buffer1,256)||memcmp(data2,buffer2,256)){   //verify
                    while(1){       //mismatch - 4 blinks
                        LED=1;
                        delay_ms(75);
                        LED=0;
                        delay_ms(75);
                        LED=1;
                        delay_ms(75);
                        LED=0;
                        delay_ms(75);
                        LED=1;
                        delay_ms(75);
                        LED=0;
                        delay_ms(75);
                        LED=1;
                        delay_ms(75);
                        LED=0;
                        delay_s(1);
                    }
                }
            }
        }
        while(1){                   //success!
            LED=1;
            delay_s(1);
            LED=0;
            delay_s(1);
        }
    }

    int writeSector(LBA a,char *p1,char *p2)
    {
        unsigned r,i;
        LED=1;                      //turn on write LED
        r=sendSDCmd(24,(a<<9));
        if(r==0){
            writeSPI(0xfe);             //send Data Start byte
            for(i=0;i<256;i++)          //first 256 bytes
                writeSPI(*p1++);
            for(i=0;i<256;i++)          //second 256 bytes
                writeSPI(*p2++);
            writeSPI(0xff);             //send dummy CRC
            writeSPI(0xff);
            if((r=writeSPI(0xff) & 0x0f) == 0x05){  //check if data accepted
                for(i=10000;i>0;i--){
                    if(r=writeSPI(0xff))
                        break;
                }
            }
            else
                r=0;                //fail
        }
        CSel=1;writeSPI(0xff);              //disable SD
        LED = 0;                    //LED off
        return(r);
    }

    int readSector(LBA a, char *p1, char *p2)
    {
        int r,i;
        LED = 1;                    //turn on read LED
        r = sendSDCmd(17,(a<<9));
        if(r==0){                   //check if command was accepted
            i=10000;                //wait for a response
            do{
                r = writeSPI(0xff);
                if(r==0xfe)
                    break;
            }while(--i > 0);
            if(i){                  //if no timeout, read 512 byte sector
                for(i=0;i<256;i++)
                    *p1++ = writeSPI(0xff);
                for(i=0;i<256;i++)
                    *p2++ = writeSPI(0xff);
                writeSPI(0xff);         //ignore CRC
                writeSPI(0xff);
            }
        }
        CSel=1;writeSPI(0xff);              //disable SD
        LED = 0;                    //read LED off
        return(r == 0xfe);
    }

    int initMedia(void)
    {
        int i,r;
        CSel=1;                 //while card is not selected
        for(i=0;i<16;i++)           //send 80 clock cycles to start up
            writeSPI(0xff);
        CSel=0;                 //then select the card
        r = sendSDCmd(0,0);         //send reset command to enter SPI mode
        CSel=1;                 //disable SD
        writeSPI(0xff);
        if(r != 1)              //error check - need 1
            return 0x84;
        i = 10000;              //send init for up to 0.3s
        CSel=0;
        do{
            r = sendSDCmd(1,0);     //send init command
            CSel=1;             //disable SD
            writeSPI(0xff);
            if(r) break;
        }while(--i > 0);
        if(i==0)                //time out error 0x85
            return 0x85;
        sspcon1 = 0b00010000;           //speed up spi clock
        sspcon1.SSPEN=1;
        return 0;
    }

    int sendSDCmd(unsigned char c,LBA a)
    {
        int i,r;
        CSel=0;                     //send command packet (6 bytes)
        writeSPI(c|0x40);               //send command & frame bit
        writeSPI(a>>24);                //send 32-bit address
        writeSPI(a>>16);
        writeSPI(a>>8);
        writeSPI(a);
        writeSPI(0x95);                 //send CRC
        i=9;                        //wait for response
        do{
            r=writeSPI(0xff);           //check if ready
            if(r != 0xff)
                break;
        }while(--i > 0);
        return(r);
    }

    unsigned char writeSPI(unsigned char send)
    {
        sspbuf=send;
        while(!sspstat.BF);
        return sspbuf;
    }  
     
    and the header file:
    Code (text):
    #include <system.h>
    #include <memory.h>

    #pragma CLOCK_FREQ  8000000
    #pragma DATA    _CONFIG1H, _OSC_INTIO67_1H
    #pragma DATA    _CONFIG2H, _WDT_OFF_2H
    #pragma DATA    _CONFIG3H, _MCLRE_ON_3H
    #pragma DATA    _CONFIG4L, _LVP_OFF_4L & _XINST_OFF_4L

    #define CSel    latd.2          //chip select line
    #define LED     latd.1

    typedef unsigned long LBA;

    int writeSector(LBA,char *,char *);
    int readSector(LBA, char *,char *);
    int initMedia(void);
    int sendSDCmd(unsigned char,LBA);
    unsigned char writeSPI(unsigned char);
     
    Last edited: Aug 12, 2008
  12. Someone Electro

    Someone Electro New Member

    Joined:
    Sep 10, 2003
    Messages:
    2,579
    Likes:
    2
    Location:
    A boring village in Europe (slovene)
    Well i have one word for you MDDFS Lib. Its a library made officially by microchip and it dosent only handle the communication to the card or hard drive it also handles the FAT12 FAT16 or FAT32 file system. It supports everything from directory's to file search to attributes and time stamps.Only thing it dosent support yet are long filenames.

    Also it comes with code ready to interface with SD cards, CF cards and IDE harddrives. Its also very easy to get working.
     
  13. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    Thanks Electro, but I make it a policy to always do it myself first, even if it's with mostly someone else's code. I don't care how long it takes. I like to learn how it works. Using libs teaches me nothing.

    For me getting there is all the fun. :D
     
  14. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    This is so confusing. I have not seen not 1 flow chart type info to explain how to initialize the sd card.

    Im using MCC18 (C18) because i dont have full version of boostC (liscenece wont handle this)

    OpenSPI(SPI_FOSC_64, MODE_00, SMPEND);
    I use the above like to set to 312khz

    SPI Master mode, clock = FOSC/64 (my crystal is 20mhz)
    Setting for SPI bus Mode 0,0
    Input data sample at end of data out

    I tried to convert futz code well upto the init part, this is what i have but the LED blinks stating that the init failed:

    Code (text):
    #include <p18cxxx.h>
    #include <stdio.h>
    #include <delays.h>
    #include <spi.h>

    #pragma config WDT = OFF, OSC = HS, LVP = OFF

    // FUNCTION Prototypes
    void main(void);
    void set_wren(void);
    int initMedia(void);
    int sendSDCmd(unsigned char c, unsigned long a);
    void delay_s(unsigned char x);
    void delay_ms(unsigned char x);
    void delay_us(unsigned char x);

    unsigned char var;

    #define CSel LATDbits.LATD0
    #define LED LATDbits.LATD1

    //*******************************************


    void main(void)
    {
        char i,r ;
        LED = 0;
        TRISDbits.TRISD0 = 0;
        TRISDbits.TRISD1 = 0;
        CSel = 1;                 //init chip select (active low)
       
        OpenSPI(SPI_FOSC_64, MODE_00, SMPEND);

        r=initMedia();
        if(r){                      //card init failed - 1 blink
            while(1){
                LED=1;
                delay_ms(75);
                LED=0;
                delay_s(1);
            }

        }

        while(1);
    }


    int initMedia(void)
    {
        char i,r;

        CSel=1;                 //while card is not selected
        for(i=0;i<16;i++)       //send 80 clock cycles to start up
            putcSPI(0xff);
        CSel=0;                 //then select the card

        r = sendSDCmd(0,0);     //send reset command to enter SPI mode

        CSel=1;                 //disable SD
        WriteSPI(0xff);

        if(r != 1)              //error check - need 1
            return 0x84;

        i = 10000;              //send init for up to 0.3s
        CSel=0;

        do{
            r = sendSDCmd(1,0); //send init command
            CSel=1;             //disable SD
            putcSPI(0xff);
            if(r) break;
        }while(--i > 0);

        if(i==0)                //time out error 0x85
            return 0x85;

        return 0;
    }

    int sendSDCmd(unsigned char c, unsigned long a)
    {
        int i,r;
        CSel=0;                     //send command packet (6 bytes)
        putcSPI(c|0x40);                //send command & frame bit
        putcSPI(a>>24);             //send 32-bit address
        putcSPI(a>>16);
        putcSPI(a>>8);
        putcSPI(a);
        putcSPI(0x95);                  //send CRC
        i=9;   
                        //wait for response
        do{
            r=putcSPI(0xff);            //check if ready
            if(r != 0xff)
                break;
        }while(--i > 0);

        return(r);
    }


    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();
        }
    }
     
    Last edited: Aug 12, 2008
  15. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    I've seen a couple, but they're never quite detailed enough. :p

    I think you should be sampling in middle, not end.

    Then you're semi close to where I am. Spending tons of time in the debugger, setting breakpoints all over the place to see what r is at different points.

    I changed my LED blink delays to 150ms so I could see and count them better. May even go to 200 or more.

    Oh ya, the code I posted had the trisc set for all outs. Trisc.4 (the SDI pin) must be a 1. I've edited the post, but you may have screen-scraped the source before I did that.

    The thing seems to initialize fine, but still returns 1 instead of 0 when I send it a write_single command (24), and therefore doesn't write anything. :confused:
     
    Last edited: Aug 12, 2008
  16. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    Im trying to understand how send the reset command.
    I know that data goes and comes in packets:
    (to send)

    [​IMG]

    "SD commands are listed in the form CMDXX" How do i send that out?

    Should i make a array like:
    unsigned char pData[5];

    and fill it with the bytes for the packet?

    1. 01 is like start bits
    2. How would i turn CMD0 into 6 bits?
    3. What would be the args?
    4. CRC i see is a constant because not really used in SPI (0x95) only for fist to set SPI.
    5. 1 is the stop bit.

    Thats my main concern right now. I kinda can follow the app notes but cant until i understand the above 2,3 and how to send it all.

    Well sending isnt going to be hard. I can write each byte out 1 after the other no issue im sure. Its just preparing them.

    EXAMPLE:
    Code (text):
        pData[0]= 0x40;
        pData[1]= 0x00;
        pData[2]= 0x00;
        pData[3]= 0x00;
        pData[4]= 0x00;
        pData[5]= 0x95;
     

    Attached Files:

    • cmd.jpg
      cmd.jpg
      File size:
      15.2 KB
      Views:
      1,213
    Last edited: Aug 12, 2008
  17. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    ok i think i finally got it to initialize with your code :D
     
  18. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    my issue is memory. Would it be ok (might be slower) to read and write to rom on the uC instead of program mem.
     
  19. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    OK i havent got it to write but compiles fine! Um in the WriteSector function r returns
    r=sendSDCmd(24,(a<<9)); == (0x80)

    Not sure but it receives it as 2 bytes. Does the byte length refer to like R2 (Response type R2) If so then the error is "Out of Range, CSD Overwrite"

    Any thoughst?
     
  20. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    A write_single command gets an R1 response, which is one byte. It's getting stored into an int, so it shows up in the debugger as two bytes of which the msbyte will always be zero. It could just as well be stored in an unsigned char, but I just typed in that part of the book code as is.

    Six byte packets:
    Byte 1. The command (OR'd with 0x40 to set frame bit). Commands are just numbers and will never exceed 6 bits. You don't have to turn them into 6 bits. :p The args in my (Julio's) function are the command (unsigned char) and the address (unsigned long (32-bits) - uses LBA alias).

    Bytes 2,3,4 & 5. The 32-bit address if needed. Four zero bytes otherwise.

    Byte 6. The CRC (ignored after init)

    CMD0 means command 0. You send a 0 byte. CMD24 means command 24. You send 24 decimal.
     
    Last edited: Aug 12, 2008
  21. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    Got it working! :D:D:D I went to the book's web-site and found the problem in the errata. It wasn't initializing correctly before. Here's the corrected initMedia() function:
    Code (text):
    int initMedia(void)
    {
        int i,r;
        CSel=1;writeSPI(0xff);          //while card is not selected
        for(i=0;i<16;i++)               //send 80 clock cycles to start up
            writeSPI(0xff);
        CSel=0;                         //then select the card
        r = sendSDCmd(0,0);             //send reset command to enter SPI mode
        CSel=1;writeSPI(0xff);          //disable SD
        if(r != 1)                      //error check - need 1
            return 0x84;
        i = 10000;                      //send init for up to 0.3s
        CSel=0;
        do{
            r = sendSDCmd(1,0);         //send init command
            CSel=1;writeSPI(0xff);      //disable SD
            if([COLOR="Red"]!r[/COLOR]) break;
        }while(--i > 0);
        if(i==0)                        //time out error 0x85
            return 0x85;
        sspcon1 = 0b00010000;           //speed up spi clock
        sspcon1.SSPEN=1;
        return 0;
    }
    The change is where it's waiting for the response to the init command. It was checking if r was true. That's wrong. It was supposed to be checking for false (0). So it never got properly initialized and was stuck forever in idle mode.
     
    Last edited: Aug 12, 2008

Share This Page