1

I am trying to interface a 32 GB microSD card with PIC32MX795F512L using SPI communication. SPI communication is working fine as I have tested it by shorting the Data Input and Output line and receiving back the data which I sent. I am following the examples provided in Lucio de Jasio's book, Programming 32-bit Microcontrollers in C.

Following are the tasks I am performing:

1. Initializing the SPI Communication in initSPI(), setting the baudrate=76 (250 kHz)
2. Initializing the SD card
   -> CS = 1
   -> Sending 80 clock cycles to wake the card
   -> CS = 0
   -> Sending CMD0 command (0x40, 0x00, 0x00, 0x00, 0x00, 0x95)

But the problem is I am not receiving the R1 response from the card, instead I am receiving the 0xFF. Following is the code:

#define FCY 77000000UL
#define FPB (FCY/2)
#define BAUDRATE    9600
#pragma config POSCMOD=HS,FNOSC=PRIPLL
#pragma config FPLLIDIV=DIV_3, FPLLMUL=MUL_21, FPLLODIV=DIV_1
#pragma config FPBDIV=DIV_2, FWDTEN=OFF

#include <stdio.h>
#include <stdlib.h>
#include <plib.h>
#include <stdbool.h>

#define SDI  _RC4
#define SDCS _RA9
#define enableSD()    SDCS = 0
#define disableSD()   SDCS = 1
#define readSPI() writeSPI(0xFF)

int main(int argc, char** argv)
{
    TRISAbits.TRISA9 = 0;   // CS as output
    TRISCbits.TRISC4 = 1;   // SDI as input
    TRISDbits.TRISD0 = 0;   // SDO as output
    TRISDbits.TRISD10 = 0;  // SCK as output
    AD1PCFG = 0xFFFF;
    DDPCONbits.JTAGEN = 0;
    OpenUART1( UART_EN | UART_NO_PAR_8BIT | UART_1STOPBIT, UART_RX_ENABLE | UART_TX_ENABLE, (FPB/16/BAUDRATE)-1 );
    disableSD(); //CS PIN INITIALLY HIGH TO DISABLE CARD
    initSPI();
    initSD();

    while(1)
    {

    }
    return (EXIT_SUCCESS);
}

void initSPI( void)
{
    SPI1BRG = 76; // FPB/154=250KHZ; (76+1)*2 = 154, so brg = 76
    SPI1CONbits.MSTEN = 1; // MasterEnable
    SPI1CONbits.CKE = 1;   // CKE on
    SPI1CONbits.ON = 1;    // SPI Module ON
}

unsigned char writeSPI(unsigned int b)
{
    SPI1BUF=b; // Write to buffer for TX
    while( !SPI1STATbits.SPIRBF)
        ; // Wait transfer complete
    return(SPI1BUF);
}

int initSD(void)
{
    int i,r,rx;
    int data;

    // Step1: Disable SD card
    disableSD();

    // Step2: Send 80 clock cycles to wake up the card
    for(i=0; i<=9; i++)
    {
        data = writeSPI(0xFF);
    }

    // Step3: Enable SD card
    enableSD();

    // Step4: Send CMD0 command to RESET
    r = sendCMD0();
    disableSD();
    if(r!=1)
    {
        putsUART1("CMD reject\n");
    }
}

int sendCMD0()
{
    int i,r;
    enableSD();

    writeSPI(0x40);
    writeSPI(0x00);
    writeSPI(0x00);
    writeSPI(0x00);
    writeSPI(0x00);
    writeSPI(0x95); //CMD0 Command

    for( i=0; i<100; i++)
    {
        r = readSPI();
        if ((r & 0x80) == 0)
            break;
    }
    return ( r);
}

Here is the schematic:

The schematics

I am not receiving the R1 response. It is stuck at step4 in initSD().

Is there a way I can know if the card has woken up or not after sending 80 clock pulses? After 80 clock pulses, the response from the card is 0xFF.

Do I need to reduce the clock frequency? Currently it's 250 kHz.

Why is the card not initializing. How can I fix it?

Peter Mortensen
  • 1,676
  • 3
  • 17
  • 23
user007
  • 409
  • 1
  • 8
  • 18
  • You could compare to my library on Github ( [header](https://github.com/edeca/Electronics/blob/master/Include/sd_spi.h), [code](https://github.com/edeca/Electronics/blob/master/Include/sd_spi.c) ) and see what the differences are. I've tested on a decent number of cards to work out edge cases. – David Jan 20 '16 at 21:02
  • I'll look for the reference (didn't see it yesterday) , but from what I remember SdCards need a hefty decoupling cap - 10uF//100nF close to the holder. Google the following "decoupling capacitor for SdCard" You probably don't need to go further than the google page :-) – ChrisR Jan 21 '16 at 09:45
  • you might want to look at which has code, in C, for accessing the chip in SPI mode. It is way too long to post as an answer, Or I would post it as an answer. – user3629249 Jan 21 '16 at 22:03
  • @user3629249 can you please share the link again. It says internal server error – user007 Jan 22 '16 at 05:06
  • http://www.microchip.com/forums/m530149.aspx – user3629249 Jan 22 '16 at 15:36
  • Have u tried another card? Also get rid of all the ints. Use unsigned char or uint8_t everywhere. Also make the UART print the values of r returned so we can see whats going on. – SoreDakeNoKoto Feb 05 '16 at 19:03

2 Answers2

1

Looks on the surface like an electrical problem

  • Try a slower speed to start - you may have excessive capacitance in your lines or a slow speed sdCard.
  • Are you polling until the card comes ready (see below)
  • Can you put a scope on it to check timings, these are critical?
  • Try a different SD card, or same card in a working device
  • Can you transmit and receive to a different port on same device?
  • Can you transmit and receive to a different board using same PIC device?
  • Are you using microchip drivers / software libraries?
  • Have you got MISO/MOSI correctly wired?
  • Are the power requirements met - decoupling caps etc? 10uF+//100nF (some people use 100uF!!)
  • Are your output and input pins configured correctly?

At a later stage you may need to check the following - no particular order.

  • Check the 'endianness' of your SPI
  • Check your CRC
  • Double check your protocol

https://www.sdcard.org/downloads/pls/simplified_specs/archive/part1_110.pdf

pg 47 says that you need to poll the device until the card comes ready Are you doing this?

ChrisR
  • 851
  • 4
  • 9
  • I am using 250KHZ, will definitely try with slower speed. My Micro SD card is 32gb class 10 and is in good working state. PIC32 is also working fine, I have done many other programs on it. I am not using microchip libraries and yes MISO MOSI lines wired correctly. I think we only have to set CS line manually and rest of the lines are set automatically. I have also uploaded the schematic. Please have a look at it. I didnt get `Check the 'endianness'`, can you please explain a bit – user007 Jan 20 '16 at 10:48
  • endianness is the jargon for 'do you send MSB first , or LSB first'. Perhaps check your hardware /pin init code, with that given in the microchip libs. Most of my work is with ARM so I unfortunately cant help with PIC specifics for pin speed, pullup/pulldown etc. Try a delay after setting the CS line. – ChrisR Jan 20 '16 at 11:13
  • You mean to say I should add delay after step1 in initSD() – user007 Jan 20 '16 at 11:22
  • @user007 yes, and poll it until it is ready at <50ms intervals. see my edits – ChrisR Jan 20 '16 at 11:36
  • I am not polling instead was waiting for response allowing upto 8 bytes delay. I will poll it until its response is 0x01. Thanks – user007 Jan 20 '16 at 11:41
  • Is there any response from the card after sending 80 clock pulses – user007 Jan 20 '16 at 12:03
  • The driver I use puts a 1mS wait after power_on (chip select) then send CMD8, CMD55, CMD41. It recommend using < 24MHz unless you do a 'high speed switch'. This is SDIO )4 bit) mode. Polling is in the CMDs – ChrisR Jan 20 '16 at 12:35
1

I don't see any code for making the MISO pin an input pin or for making the SCK pin output. Since you aren't using the SPI library in plib, you have to do all the pin initialization yourself. Also, after sending CMD0, increase the number of iterations (in your for loop) from 8 to 20 (I use 100), to provide enough leeway. Also use this comparison instead:

if ((r & 0x80) == 0) 
   break;
return r;

Also make r an unsigned char everywhere.

SoreDakeNoKoto
  • 1,635
  • 1
  • 13
  • 22
  • Ohh thankyou for suggesting that. I thought after initializing the SPI, all the SDO SDI sck are automatically configured. Now I'll make SDO & SCK output and SDI as input. – user007 Jan 21 '16 at 04:54
  • Why you are using this if condition: `if ((r & 0x80) == 0)` . Can you explain a bit. – user007 Jan 21 '16 at 05:02
  • The first bit of the sd cards response is always a zero. So one must check the first bit of any received bytes for a zero. Your original code does roughly the same thing as mine, but i've been using mine for a while with PICs so i figure mine is more reliable :) – SoreDakeNoKoto Jan 21 '16 at 06:50
  • `uint8_t` would be better than `unsigned char`. – David Jan 21 '16 at 07:22
  • I used your if condition but the card is not responding 0x01. Is there any error in my functions for transmitting data.? Can you pls have a look at it. Is there any other command I can send before CMD0 to check any other things.? – user007 Jan 21 '16 at 07:35
  • Can you update the code in your question? After making all the pin inits? – SoreDakeNoKoto Jan 21 '16 at 08:19
  • @TisteAndii I have updated my code. – user007 Jan 21 '16 at 09:33
  • Change all your ints to uint8_t. Explicitly make SPICON1bits.CKP = 0. – SoreDakeNoKoto Jan 21 '16 at 09:49
  • Also put SPI1CON = 0 at the top of the init_spi(). And then try another SD card. No other ideas here :( – SoreDakeNoKoto Jan 21 '16 at 13:17
  • Okay I'll put SPI1CON =0 and will try with that. Thanks – user007 Jan 22 '16 at 05:07
  • And make the type changes and the CKP assignment – SoreDakeNoKoto Jan 22 '16 at 07:30
  • @TisteAndii I am not that much expert in SD Card programming but when we initialize SPI module, all the relative pins are configured automatically.! – Aircraft Jan 23 '16 at 06:31
  • Only if you are using the peripheral libraries. Those arent being used here. Inspect open_spi.c in the plib and you will see the same pin assignments there. Here, plib isnt being used for SPI – SoreDakeNoKoto Jan 23 '16 at 09:13