CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Best configuration for the SPI to work with a SD card

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
nacho72



Joined: 20 Oct 2014
Posts: 13

View user's profile Send private message

Best configuration for the SPI to work with a SD card
PostPosted: Thu Nov 27, 2014 11:55 am     Reply with quote

Hi!

I'm having problems to set properly the configuration of the SPI port to communicate with a SD card. I have read lot's of post in this forum about the topic, and I tried several of the recommendations but without success.

I dove inside the mmcsd API until I reached the function that sends a single byte to the SPI bus (spi_write() or spi_xfer()). I used an oscilloscope to see the waveform of the CS, SDI, SDO and SCK and following the figures of the following link, I came up to the conclusion I'm setting something wrong.

https://www.diolan.com/dln_doc/spi-transfer-modes.html

I read somewhere that the best configuration of the SPI to work with SD cards is Mode = 3. As I understand, both SCK and CS must be at 1 until there is a command. In that moment SCK will send some cycles and CS will change to low level.

So with that idea in mind, I started trying different configurations of the SPI port, with the only goal of sending the CMD 0 and read the response from the SD card.

FIRST CONFIGURATION

I used the standard library of ccs, (mmcsd.c) -I don't include it all due to its length, so I will just mention the small changes I did to configure it based on my needs-

Code:
#ifndef MMCSD_SPI_XFER
   #if defined(MMCSD_SPI_HW)
      #use spi(MASTER, MMCSD_SPI_HW, BITS=8, MSB_FIRST, MODE=0, baud=400000, stream=mmcsd_spi)
   #else
      #ifndef MMCSD_PIN_SCL
         #define MMCSD_PIN_SCL     PIN_C3 //o
         #define MMCSD_PIN_SDI     PIN_C4 //i
         #define MMCSD_PIN_SDO     PIN_C5 //o
         #define MMCSD_PIN_SELECT  PIN_C2 //o
      #endif
   
      #use spi(MASTER, DI=MMCSD_PIN_SDI, DO=MMCSD_PIN_SDO, CLK=MMCSD_PIN_SCL, BITS=8, MSB_FIRST, MODE=0, baud=19200, stream=mmcsd_spi)
   #endif
   
   #define MMCSD_SPI_XFER(x)  spi_xfer(mmcsd_spi, x)
#endif


The rest of the code is exactly as it appears in the interface folder of the PCW (version 5)

Code:
the main.c

#include <18F27J13.h>
#device PASS_STRINGS = IN_RAM
#fuses NOWDT, HS, NOPROTECT
#use delay(clock=20000000)

#use rs232(baud=9600, UART1, errors)

#include <stdlib.h> // for atoi32

//media library, a compatable media library is required for FAT.
#use fast_io(c)
#define MMCSD_PIN_SCL     PIN_C3 //o
#define MMCSD_PIN_SDI     PIN_C4 //i
#define MMCSD_PIN_SDO     PIN_C5 //o
#define MMCSD_PIN_SELECT  PIN_C2 //o

#include "mmcsd.c"

//FAT library.
#include "fat.c"

void main(void)
{

   
   // main loop
   while(TRUE)
   {

      mmcsd_init();
      //mmcsd_go_idle_state();
      
      delay_ms(500);

    }

}


It is just a loop to initialize the mmcsd.


Result for this configuration

The CS goes from up to down just before the CMD0 is sent in the line of SDI -as the schema of the above links says-.

Problems: The clock is continuously working, instead of stay high after and before the command.

Big problem: There is not command back from the SD card.



SECOND CONFIGURATION

I decided to just send a byte, not even a whole command. As I read doing this the SD card should make an echo (I'm not sure if it is right because I just read it in one single post).

So I used the following code:

main.c

Code:
#include <main.h>
#include <math.h>
#include <stdlib.h>         // biblioteca estandar para lenguaje C

#define CMD0   0x40
#define CMD8   0x48
#define CMD9   0x49
#define CMD17  0x51
#define CMD24  0x58
#define CMD55  0x77
#define CMD41  0x69
#define CMD16  0x50

//#byte TRISB=0xF93
#byte TRISC=0xF94

   int i;
   long j=0;
   long k=0;
   int dato=1;
    int resp1=0xFF;
   int resp2=0xFF;
    int resp3=0xFF;
   int resp4=0xFF;
   int resp5=0xFF;
   int resp6=0xFF;
    int respuesta[512];

void espera_ciclos(int n)
{
  int i;
  for(i=0; i<n; i++)
   {
[b]   resp1=spi_write(0xAC); //0xFF[/b]
   resp2;

   }
}
void envia_comando(int cmd,long long arg,int crc)
{
   spi_write(cmd);
   spi_write(arg>>24);
   spi_write(arg>>16);
   spi_write(arg>>8);
   spi_write(arg);
   spi_write(crc);
}
void inicializa_SD()
{
   output_high(PIN_C2); // desactivo la tarjeta CS=1
    espera_ciclos(20);   // envía n ciclos de reloj para inicializar
   output_low(PIN_C2);  // activo la tarjeta CS=0
    espera_ciclos(20);   // envía n ciclos de reloj para inicializar

    envia_comando(CMD0,0x00000000,0x95); // reset de la tarjeta e inicia en modo SPI

   resp1=spi_write(0xFF);
   resp1=spi_write(0xFF);
   resp1=spi_write(0xFF);   

    espera_ciclos(5);    // envía n ciclos de reloj para que la tarjeta pueda responder
    output_low(PIN_C2);  // re-activo la tarjeta CS=0 por si acaso
    espera_ciclos(10);   // envía n ciclos de reloj despues de activar la tarjeta
   
    envia_comando(CMD8,0x000001AA,0x87); // se envia a la tarjeta la tension de trabajo (comando OBLIGATORIO)

    espera_ciclos(10);   // envía n ciclos de reloj para que la tarjeta pueda responder
   
    // inicio un bucle para mandar el comando ACMD41 (CMD55 + CMD41) e inicializar la tarjeta
    // este bucle es necesario porque se necesitan varios intentos para que acepte la orden
    for(i=0;i<100;i++)
    {
         output_low(PIN_C2);   // re-activo la tarjeta CS=0 por si acaso
      espera_ciclos(2);     // envía n ciclos de reloj despues de activar la tarjeta
       
      envia_comando(CMD55,0x00000000,0xFF); // avisa a la tarjeta de que interprete el siguiente comando como ¨application-specific¨
        
      espera_ciclos(5);     // envía n ciclos de reloj para que la tarjeta pueda responder
      output_high(PIN_C2);  // desactivo la tarjeta CS=1
        espera_ciclos(2);     // envía n ciclos de reloj de basura
         output_low(PIN_C2);   // activo la tarjeta CS=0
          espera_ciclos(10);   // envía n ciclos de reloj despues de activar la tarjeta
   
      envia_comando(CMD41,0x00000000,0xFF); // inicia la tarjeta despertandola del modo IDLE
 
         for(j=0;j<10;j++)
         {
          resp1=spi_read2(0xFF);
          if(resp1==0x00)
         {
               break;
         }
   
        }
        if(resp1==0x00)
        break;
 
   }

   espera_ciclos(5);     // envía n ciclos de reloj para que la tarjeta pueda responder
   output_high(PIN_C2);  // desactivo la tarjeta CS=1
   espera_ciclos(2);     // envía n ciclos de reloj de basura
   output_low(PIN_C2);   // activo la tarjeta CS=0
   espera_ciclos(10);   // envía n ciclos de reloj despues de activar la tarjeta

   envia_comando(CMD16,0x00000200,0xFF); // configura el bloque de esctritura a 512 bytes
 
   espera_ciclos(5);     // envía n ciclos de reloj para que la tarjeta pueda responder
   output_high(PIN_C2);  // desactivo la tarjeta CS=1

   

   espera_ciclos(2);     // envía n ciclos de reloj de basura
    //setup_spi2 (SPI_MASTER | SPI_H_TO_L| SPI_XMIT_L_TO_H |SPI_CLK_DIV_4 );
   output_low(PIN_C2);   // activo la tarjeta CS=0
 
 espera_ciclos(1000);   // envía n ciclos de reloj despues de activar la tarjeta
}
void main()
{


   TRISC=0b00010000;  //

   setup_spi (SPI_MASTER | SPI_H_TO_L| SPI_XMIT_L_TO_H |SPI_CLK_DIV_64 );


    inicializa_SD();
   
   envia_comando(CMD24,0x0004CE00,0xFF);    
     for(j=0;j<10;j++)
         {
          resp1=spi_read2(0xFF);
          if(resp1==0x00)
         {
            spi_write(0xFE);
            for(j=0;j<512;j++)
               {
                spi_write(0xCC);
              }
            spi_write(0xFF);
            spi_write(0xFF);
            resp2=spi_read2(0xFF);
         
               break;
         }
   
        }

   espera_ciclos(5);     // envía n ciclos de reloj para que la tarjeta pueda responder
   output_high(PIN_C2);  // desactivo la tarjeta CS=1
   espera_ciclos(2);     // envía n ciclos de reloj de basura
   output_low(PIN_C2);   // activo la tarjeta CS=0
   espera_ciclos(10);   // envía n ciclos de reloj despues de activar la tarjeta




   envia_comando(CMD24,0x0004D000,0xFF);
     for(j=0;j<10;j++)
         {
          resp1=spi_read2(0xFF);
          if(resp1==0x00)
         {
            spi_write(0xFE);
            for(j=0;j<512;j++)
               {
                spi_write(0xDD);
              }
            spi_write(0xFF);
            spi_write(0xFF);
            resp2=spi_read2(0xFF);
         
               break;
         }
   
        }

   espera_ciclos(5);     // envía n ciclos de reloj para que la tarjeta pueda responder
   output_high(PIN_C2);  // desactivo la tarjeta CS=1
   espera_ciclos(2);     // envía n ciclos de reloj de basura
   output_low(PIN_C2);   // activo la tarjeta CS=0
   espera_ciclos(10);   // envía n ciclos de reloj despues de activar la tarjeta
/*   
   envia_comando(CMD17,0x00011400,0xFF);
   for(j=0;j<10;j++)
         {
          resp1=spi_read2(0xFF);
          if(resp1==0x00)
         {
            for(i=0;i<100;i++)
            {
               resp3=spi_read2(0xFF);
               if(resp3==0xFE)
               {
                for(k=0;k<512;k++)
                {
                  respuesta[k]=spi_read2(0xFF);
                }
                break;
               }
            }
               break;
         }
   
        }
*/
while(TRUE)
   {
      dato=spi_read(0xff);
      i++;
   }

}

main.h

Code:
#include <18F27J13.h>
#device adc=16
#use delay(clock=20000000)



#FUSES NOWDT, HS // WDT128, NOXINST, HS


#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
//#use i2c(Master,Fast,sda=PIN_C4,scl=PIN_C3)

#define MMCSD_PIN_SCL     PIN_C3 //o
#define MMCSD_PIN_SDI     PIN_C4 //i
#define MMCSD_PIN_SDO     PIN_C5 //o
#define MMCSD_PIN_SELECT  PIN_C2 //o


/
#use spi(MASTER, DI=MMCSD_PIN_SDI, DO=MMCSD_PIN_SDO, CLK=MMCSD_PIN_SCL, BITS=8, MSB_FIRST, MODE=3, baud=19200, stream=mmcsd_spi)




Note: Most of the code is irrelevant, because I just focused my watch on the line.

Code:
resp1=spi_write(0xAC);


RESULT

With this configuration, The CS completely ignore where the command is sent, it stays always high, but the SCK signal is activated just at the same time as the 0xAC byte is sent, after and before that line stays at high level.

Problem: once again there is not reply -as I mentioned before I was expecting an echo-


After all I read, only way to make the SD reply is when:

-CS is low when the command/byte is sent from the PIC to the SD card
-The SCK signal is always high but when the command/byte is sent from the PIC to the SD card (in that moment it send cycles)

But I can't get both things working together in the same configuration. Has anyone any idea about what am I mixing/missing?


Further information:

Compiler: PCW 5
PIC: 18F27J13
Xtal= 20Mhz

-I'm working with 3.3V in both PIC and SD card
-The schema is correct -I don't know how to upload a picture- Just mention there are 4 10k pull up resistors in the four lines: CS, SCK, SDI and SDO.
-fat.c and mmcsd.c directly taken from the interface folder of the PCW 5


Thank you very much for your time and effort, if you need more information please let me know.
Ttelmah



Joined: 11 Mar 2010
Posts: 19552

View user's profile Send private message

PostPosted: Thu Nov 27, 2014 12:14 pm     Reply with quote

Have you got the pull up resistors on the lines?.

These are required (not optional). One in particular is necessary to ensure the line is high when the card powers up. Nothing to do with SPI modes. Before the chip starts.

The CCS supplied code will work (needs a few tweaks to make it work 'right', but it will function), if the hardware is right. There are tweaks for the problems in the code library.

Other thing is the supplied code will only support SD, not SDHC. Look in the code library for an SDHC tweak.
nacho72



Joined: 20 Oct 2014
Posts: 13

View user's profile Send private message

PostPosted: Thu Nov 27, 2014 12:25 pm     Reply with quote

Hi, yes there are 4 pull up resistors, one in each line.

The first configuration is basically the codes given CCS. That is:

ex_fat.c
fat.c
mmcsd.c

I tested to write/read a file, and it didn't work, so I was debugging the code for a few days, until I reach the lowes level of the code, where it comunicates directly with the SPI bus, and there is well I found out that nothing works, because the SD is not sending response to the PIC.

Firstly I though it may be a problem with the connections, but everything is correct -I compared it with other working circuits- So then I watched the information of the link I mentioned and I understood the problem could be the timming with SDI, SCK and CS. As I mentioned I can't make them work correctly at the same time.

That is why I think I'm configurating the SPI wrong.
Ttelmah



Joined: 11 Mar 2010
Posts: 19552

View user's profile Send private message

PostPosted: Thu Nov 27, 2014 12:27 pm     Reply with quote

What is the capacity of the card?.
nacho72



Joined: 20 Oct 2014
Posts: 13

View user's profile Send private message

PostPosted: Thu Nov 27, 2014 12:38 pm     Reply with quote

I tested both codes with a 2Gb and a 8Gb
Ttelmah



Joined: 11 Mar 2010
Posts: 19552

View user's profile Send private message

PostPosted: Thu Nov 27, 2014 3:08 pm     Reply with quote

8GB, will be SDHC.
Some makes of card are 'known problematical'. It might be your 2GB card is one of these...
asmallri



Joined: 12 Aug 2004
Posts: 1635
Location: Perth, Australia

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Thu Nov 27, 2014 4:39 pm     Reply with quote

It sounds like you have a hardware fault. Most likely you have not wired up the SD interface the way you think you have or the way you think it should be wired up is wrong.

SD cards operate correctly in 2 of the 4 SPI modes.

Here is a link to a user manual for my file system. Around page 20 you will find hardware troubleshooting tips.

http://www.brushelectronics.com/download/BE_SD-MMC_FAT_File_System_User_Manual.pdf

There are some hardware reference designs, ones that actually work, on our projects page here http://www.brushelectronics.com/index.php?page=projects
_________________
Regards, Andrew

http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!!
nacho72



Joined: 20 Oct 2014
Posts: 13

View user's profile Send private message

PostPosted: Fri Nov 28, 2014 7:03 am     Reply with quote

Thanks for your answers, but besides there may exist a problem with the SD card, I think I'm programming something badly.

It is the first time I work with mmcsd, but as I read, the pic regardless of the proper connection with the SD card or not, should be able to:

-Activate chip select just before the command is sent
-Hold SCK high until the command is sent
-Send command and send SCK signal at the same time
-Once the comand is sent hold again the SCK high

Of course if there is a bad connection with the SD card, the PIC will never get and answer, but I think at least SCK, CS and SDI should work properly together.

That is why I think the problem is when I set the parameters for the SPI.

And yes, once I see the timing between SCK,CS and SDI are like in the link I include in the question, and I still don't get response from the SD card, I will start considering the problem is with the memory card.
Ttelmah



Joined: 11 Mar 2010
Posts: 19552

View user's profile Send private message

PostPosted: Fri Nov 28, 2014 8:40 am     Reply with quote

I couldn't wade through your code. You seem to be starting completely the wrong way.

Start with the CCS code.

Don't change it at all. Just set the pins to be used for SPI.

Call this. Nothing else.

The mmc_init code will loop clocking for ever, if mmc_go_idle_state returns 1.

Though an SD card can theoretically be clocked to DC, the initialisation sequence is meant to be clocked between 100kHz, and 400kHz. There is a timeout if you go much too slow. You may be hitting this....

As a separate comment, mode 0, is the _defined_ correct mode for SD. mode 3 will also work in most cases, but mode 0 is the correct mode.
nacho72



Joined: 20 Oct 2014
Posts: 13

View user's profile Send private message

PostPosted: Sat Nov 29, 2014 11:50 am     Reply with quote

Thank you, so I will set mode 0.

Some questions about what you just said in your last comment.

The mmc_init code will loop clocking for ever, if mmc_go_idle_state returns 1.

I though we expect when the mmc_go_idle command is called, the sd card reply with 0x01, but at the same time we don't want a loop clocking, just clocking signal when a command is sent and high level the rest of the time.

¿Isn't it?

And about: the initialisation sequence is meant to be clocked between 100kHz, and 400kHz. There is a timeout if you go much too slow

Does it means I have to change the baudrate once the initialization is over, so

Code:
#use spi(MASTER, MMCSD_SPI_HW, BITS=8, MSB_FIRST, MODE=0, baud=400000, stream=mmcsd_spi)


And then move it to a slower speed?
Ttelmah



Joined: 11 Mar 2010
Posts: 19552

View user's profile Send private message

PostPosted: Sat Nov 29, 2014 11:56 am     Reply with quote

In your modified code, you show the clock rate set to 19200bps....

#use spi(MASTER, DI=MMCSD_PIN_SDI, DO=MMCSD_PIN_SDO, CLK=MMCSD_PIN_SCL, BITS=8, MSB_FIRST, MODE=0, baud=19200, stream=mmcsd_spi)
nacho72



Joined: 20 Oct 2014
Posts: 13

View user's profile Send private message

PostPosted: Sat Nov 29, 2014 12:56 pm     Reply with quote

I know I know, that is why I asked you if I have to change to 400000

The thing is, I have read before that the proper way to do the initialization was by slowing down the baudrate -so exactly the opposite that you recommend me- And they gave this instruction to call at the beginning of the main():

Code:
setup_spi (SPI_MASTER | SPI_H_TO_L| SPI_XMIT_L_TO_H |SPI_CLK_DIV_64 );


But since you recommend exactly the opposite I just asked to confirm where should I change the baudrate. Because I didn't know if it should be change in the #use spi(...) or directly in the main() with a setup_spi() similar to the one I linked above.

And once the initialization is over, and I start reading/writing from/the sd card, should I modify somehow the baudrate to a different speed or just 400000 all time?
asmallri



Joined: 12 Aug 2004
Posts: 1635
Location: Perth, Australia

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Sat Nov 29, 2014 11:58 pm     Reply with quote

nacho72 wrote:
I know I know, that is why I asked you if I have to change to 400000

The thing is, I have read before that the proper way to do the initialization was by slowing down the baudrate -so exactly the opposite that you recommend me- And they gave this instruction to call at the beginning of the main():

Code:
setup_spi (SPI_MASTER | SPI_H_TO_L| SPI_XMIT_L_TO_H |SPI_CLK_DIV_64 );


But since you recommend exactly the opposite I just asked to confirm where should I change the baudrate. Because I didn't know if it should be change in the #use spi(...) or directly in the main() with a setup_spi() similar to the one I linked above.

And once the initialization is over, and I start reading/writing from/the sd card, should I modify somehow the baudrate to a different speed or just 400000 all time?


Ttelmah gave you the correct information - you just interpreted it wrong.

The SD card "should" be initially configured between 100Kbps and 400Kbps as defined in the standard. Once you have put the card into SPI mode then you can increase the SPI bus speed. Depending on your hardware implementation (crosstalk etc) you could increase the speed up to 20MHz or higher. However a more realistic speed for a general implementation would be a 10MHz SPI clock.

There are two possible things going on here:
1. You, for reasons know only to you, have not bothered reading the supplied MMC code from CCS. If you had, there would be no reason for you to ask the questions you are asking because the answers are evident in the source code. It could be that you are blind (and therefore cannot read the source code - but have the amazing ability in this state to submit posts) or you are lazy and hope other people will tell you how to do it so you will not have to read the source code.

2. You have wired up the hardware yourself and you have done it wrong. No one on this thread has a crystal ball. You have not provided the schematic for your implementation and you are hoping someone can guess where your error is.
_________________
Regards, Andrew

http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!!
nacho72



Joined: 20 Oct 2014
Posts: 13

View user's profile Send private message

PostPosted: Sun Nov 30, 2014 2:46 pm     Reply with quote

Oh I already solved it the other day, sorry for making you waste your time with your answer asmallri hahaha I hope you didn't have anything better to do, because the question I asked was quite long and actually the solution was really easy and nothing to do with what you mention above

Very Happy
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group