Issue Reading/Writing to SD card

Thread Starter

newbieEm

Joined Jun 8, 2010
7
I have written code to read/write 512 bytes to SD/card. I can reset the card (send reset command), initialize but not write 512 bytes. I have a question, When I do sectorWrite(), do I start writing from sector 0? my mmc card is 2g, fat16 formatted. I am using PIC18F4550. here is my entire code. Can you guys see any problem. Thanks

Rich (BB code):
=======================================================
#include "PicStar1000.h"          /* PicStar library header file            */
#include "p18cxxx.h"
#include <spi.h>

typedef unsigned long _ulong;

#define SIZE 32

#define SD_CS PORTBbits.RB2
#define SD_CS_TRIS TRISBbits.TRISB2
#define SD_CD PORTAbits.RA0
#define SD_CD_TRIS TRISAbits.TRISA0

#define GO_IDLE_STATE 0 //CMD0
#define SEND_OP_COND 1 //CMD1
#define SETBLOCKLEN 16 //CMD16
#define BLOCKLEN_512 512
#define CRC_ON_OFF 59 //CMD59
#define WRITE_SINGLE_BLOCK 24 //CMD24
#define READ_SINGLE_BLOCK 17 //CMD17
#define DATA_START_BYTE 0xFE
#define DATA_ACCEPTED 0x05

//Globals
long lDelay = 48000000;
char data[SIZE];
char buffer[SIZE];


/*
	TRISCbits.TRISC7 = 0;
	//SCK (Master mode) must have TRISB<1>bit cleared
	TRISBbits.TRISB1 = 0;
	//SCK (SLave mode) must have TRISB<1>bit set (ignored)
	//SS must have TRISA<5>bit set
	TRISAbits.TRISA5 = 1;

	//0 = idle state for clock is a low level
	SSPCON1bits.CKP = 1; 
	//bit 3 - 0 0010 = SPI Master mode, clock = F0sc/64
	SSPCON1bits.SSPM0 = 0;
	SSPCON1bits.SSPM1 = 0;
	SSPCON1bits.SSPM2 = 1;
	SSPCON1bits.SSPM3 = 0;

	//SSPCON1 bit 5 SSPEN set 1 - Enables serial port, configures SCK, SDO, SDI and SS as serial port pins
	SSPCON1bits.SSPEN=1;		//SSPEN(5)=1 enable SPI
*/

void initSpi(void)
{
	SD_CS = 1; //Initialize SPI chip select line
	//CS pin set it to output
	SD_CS_TRIS = 0;

	OpenSPI(SPI_FOSC_64, MODE_11, SMPMID);
}

unsigned char writeSPI(unsigned char buffer)
{
	SSPBUF = buffer;
	while(!SSPSTATbits.BF);
	return SSPBUF;	
}

int sendCmd(unsigned char c, _ulong a )
{
	int i,r;
	//All th SD Card commands are 6 bytes long and transmitted MSB first
	//After programming operation is completed, host must check the results of the programming using SEND_STATUS (CMD13)

	//Commands
	// CMD1 (SEND_OP_COND)
	// ACMD41 (SD_SEND_OP_COND)
	// CMD59 (CRC_ON_OFF)
	// CMD58 (READ_OCR)

	SD_CS = 0; //Starting an operation by Chip select active low

	writeSPI(c|0x40);
	writeSPI(a >> 24);
	writeSPI(a >> 16);
	writeSPI(a>>8);
	writeSPI(a);

	writeSPI(0x95); //Send CRC

	//Response back from card
	while((r = ReadSPI()) == 0xFF);
	
	/*
	i = 9;
	do
	{
		//r = ReadSPI();
		r = writeSPI(0xFF);
		if( r!= 0xFF) break;
	} while(--i > 0 );
	*/
	return r;

}

int initMedia(void)
{
	int i,r;

	SD_CS = 1; //It will enter SPI mode if the CS signal is asserted(negative)
	writeSPI(0xFF);

	//Send 80 clock cycles to start up
	for(i = 0; i < 10; i++)
	{
		writeSPI(0xFF);
	}

	SD_CS  = 0;

	
	//Reset command CMD0 40 00 00 00 00 95. All SD commands are 6 bytes long.
	r = sendCmd( GO_IDLE_STATE, 0x0);

	//Disable SD it may be busy
	SD_CS  = 1;
	writeSPI(0xFF);

	/*	
	if( r != 1 ) 
	{
	
		return 0x84;
	}
	*/
	i = 10000;

	//Init command
	SD_CS = 0;
	do
	{
		r = sendCmd(SEND_OP_COND,0);
		
		//Disable SD
		SD_CS  = 0;
		writeSPI(0xFF);
		
		if( !r) break;
	} while(--i > 0 );

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

	OpenSPI(SPI_FOSC_4, MODE_11, SMPMID);//Increase clock speed
	sendCmd(CRC_ON_OFF,0x0);  //Turn off CRC if possible
	sendCmd(SETBLOCKLEN, BLOCKLEN_512);//Set block length 512

	return 0;

}

//Valid write command CMD24
//Valid block length is 512
// DataIn Command - Response - Start block token (1 byte)|Data block - Response/busy - Next Command
int writeSector(_ulong a,char *p)
{
	unsigned r,i;
	int index;

	r = sendCmd( WRITE_SINGLE_BLOCK ,(a<<9));
	
	if(r==0)
	{

		writeSPI( DATA_START_BYTE );			//send Data Start byte
		/*
		for(i=0;i<SIZE;i++)		//first 256 bytes
			writeSPI(*p1++);
		for(i=0;i<SIZE;i++)		//second 256 bytes
			writeSPI(*p2++);
		*/

		//Instead of 2 256 byes send 16 * 32 = 512 bytes 
		for( i = 0; i < 512; i++)
		{
			//writeSPI(*p);
			writeSPI(1);
		}		

		writeSPI(0xFF);			//send 2 bytes of dummy CRC
		writeSPI(0xFF);


		if((r = writeSPI(0xFF) & 0x0F) == DATA_ACCEPTED)
		{	
			index = 0;
		
			do
			{
				r = writeSPI(0xFF);
				index++;
			}while( (r == 0x00) && (index != 0));

			if(index == 0 )
			{
				r = 0; //timeout
			}

			writeSPI(0xFF);			//Clk cycles ...wait
		
		}
		else
		{
			r=0;			//fail
		}
	}
	
	SD_CS = 1; //Chip select
	
	return(r);
}

//Valid read command CMD17
//DataIn (Host to card) --Command --- DataOut -- Response -- - DataBlock|CRC - Next Command
int readSector(_ulong a, char *p)
{
	int r,i,j, index;
	r = sendCmd( READ_SINGLE_BLOCK,(a<<9));
	
	if(r==0)
	{				
		i=10000;	
		do{
			r = writeSPI(0xFF);
			if(r==0xFE)
			{
				break;
			}
		
		} while(--i > 0);
		
		if(i)
		{	//if no timeout, read 512 byte sector
			/*
			for(i=0;i<SIZE;i++)
				*p1++ = writeSPI(0xff);
			for(i=0;i<SIZE;i++)
				*p2++ = writeSPI(0xff);
			*/

			for( i = 0; i < 16; i++)
			{
				for(j=0;j < 32;j++)		
				{
					*p = ReadSPI();//writeSPI(0xFF);
				}
			}	
			
			writeSPI(0xFF);		//ignore CRC
			writeSPI(0xFF);
		}
	}

	SD_CS = 1; //Chip select
	writeSPI(0xFF);			//disable SD
	return(r == 0xFE);
}


void main()
{
	int i,r;
	_ulong addr;

	PsInitHw(0);

	initSpi();

	DelClks(lDelay/10);//Delay for 100 ms
	r = initMedia();

	if( r )
	{
		//Error card init failed
		return;
	}

	//Fill send buffer and receive buffer
	for(i=0;i<SIZE;i++)
	{	//fill the send buffers
		data=i;
		buffer=0;		
	}

	//Write card
	i = 0;
	addr = 10000;
	//for(i=0;i<1000;i++)
	//{
		if(!writeSector(addr+i,data))
		{
			/*
			while(1)
			{	//write failed - 2 blinks
				DelClks(lDelay );	
			}*/
		}
	//}

	
	addr=10000;	

	//Delay
	DelClks(lDelay/5); //200 ms delay		

	//Read
	//for(i=0;i<1000;i++)
	//{
		if(!readSector(addr+i,buffer))
		{
			/*
			while(1)
			{	//verify failed - 3 blinks
				DelClks(lDelay );
			}*/
		}
	//}

	//Delay
	DelClks(lDelay);		

	

	CloseSPI();

	KeyWait();
}

 
Last edited by a moderator:

Jotto

Joined Apr 1, 2011
151
I have written code to read/write 512 bytes to SD/card. I can reset the card (send reset command), initialize but not write 512 bytes. I have a question, When I do sectorWrite(), do I start writing from sector 0? my mmc card is 2g, fat16 formatted. I am using PIC18F4550. here is my entire code. Can you guys see any problem. Thanks
Are you using Ruby code? I also noticed you are checking CRC. This is such a big change from basic coding that I am used to. I mostly used fortran, C+ and C++. I have seen a lot of scripting with what I call Ruby, but I am not sure that is the correct name for it. Its a whole new ball game with this new coding. I like that quit a script an before you quit you can tell it to go to another script. So it just keeps going without a pause.
 

kubeek

Joined Sep 20, 2005
5,794
Are you using Ruby code?
How did you find that? It seems like pretty standard C to me..

newbieEm: try looking up the SD protocol specification. That should clarify all your questions and you should be able to check if you issue the right commands in the right order.
 

Jotto

Joined Apr 1, 2011
151
We program controllers for progressives, they said it was C+, and others were C++, but we never programmed symbols like you use here, so I guess the unit took care of that after we entered the data required. We wouldn't see the finished product, so I thought this was a new type of program.

The Ruby comes in because a lot of scripting that is done in another place I spend time, they call it Ruby and have the symbols that were being used here. I have never seen those symbols used. That is why I asked, the scripting that I have done uses "goto", "waitfor". Colan is used but not some of those other ones, {}. I can't make sense of it.
 

ErnieM

Joined Apr 24, 2011
8,377
Is there any particular reason you are not using the free SD controller/file system library for this device that Microchip provides for free?

And if there is a reason, then why don't you see how they do it and do the same?
 

THE_RB

Joined Feb 11, 2008
5,438
I think he's not sure about how a FAT system works?

To newbieEM- writing to any sector on the card as a 512 byte sector is very easy and does not need a FAT formatted card. Some people use that system for its simplicity and just write data to the card that only the PIC can read. This can be common in dataloggers etc.

If that is what you want, just a simple way for the PIC to read and write that card just put the data in whatever sector you choose.

But if you want to write data to a file on the card (so that file can be read on your PIC in Windows etc), then you need to work with the FAT. The FAT tells what all the filenames are and where each file's data is stored, etc.

Wiki has a very good page on FAT16. And like ErnieM said if you need FAT16 support you can get libraries from Microchip, or your compiler might even have FAT16 libraries built in or freely available (as MikroC compiler does).
 

spinnaker

Joined Oct 29, 2009
7,830
Is there any particular reason you are not using the free SD controller/file system library for this device that Microchip provides for free?

And if there is a reason, then why don't you see how they do it and do the same?

I have looked at that library. Unless I am missing something, it is very hard to setup unless you happen to have one of the chips to which it is targeted.
 

ErnieM

Joined Apr 24, 2011
8,377
I have looked at that library. Unless I am missing something, it is very hard to setup unless you happen to have one of the chips to which it is targeted.
Nah, it's pretty straightforward to set one of these up for any PIC you want to use. It's just not documented anywhere exactly HOW to do that.

ELM has an excellent file system too, just that I have never used it. I have used the MC one and find it useful.

One example of the file system for the PIC18 is at "...\Microchip Solutions v2011-10-18\MDD File System-SD Card"

The Microchip libraries all typically depend on a file named "hardwareprofile.h" to be part of your project. This describes your specific PIC and how it is connected to the libraries. Figuring out what should go there is worth your effort; MC crams the definitions for so many PICs and platforms into these they are very hard to understand, but you can start simply. For a SD card I would just start with the clock, the SPI pins, and the other SD card pins.

For the clock, you need something like this (adjusted to your real crystal frequency):
Rich (BB code):
    #define GetSystemClock()        48000000                        // System clock frequency (Hz)
    #define GetPeripheralClock()    GetSystemClock()                // Peripheral clock freq.
    #define GetInstructionClock()   (GetSystemClock()/4)            // Instruction clock freq.
Next is the "hard part" but taken line by line you can adjust all the #defines to match your platform:
Rich (BB code):
    #define USE_PIC18
    #define USE_SD_INTERFACE_WITH_SPI
    
    #define INPUT_PIN           1
    #define OUTPUT_PIN          0
    
    // Chip Select Signal
    #define SD_CS               PORTBbits.RB3
    #define SD_CS_TRIS          TRISBbits.TRISB3
    
    // Card detect signal
    #define SD_CD               PORTBbits.RB4
    #define SD_CD_TRIS          TRISBbits.TRISB4
    
    // Write protect signal
    #define SD_WE               PORTAbits.RA4
    #define SD_WE_TRIS          TRISAbits.TRISA4
    
    // Registers for the SPI module you want to use
    #define SPICON1             SSP1CON1
    #define SPISTAT             SSP1STAT
    #define SPIBUF              SSP1BUF
    #define SPISTAT_RBF         SSP1STATbits.BF
    #define SPICON1bits         SSP1CON1bits
    #define SPISTATbits         SSP1STATbits
    
    #define SPI_INTERRUPT_FLAG  PIR1bits.SSPIF   
    
    // Defines for the HPC Explorer board
    #define SPICLOCK            TRISCbits.TRISC3
    #define SPIIN               TRISCbits.TRISC4
    #define SPIOUT              TRISCbits.TRISC5
    
    // Latch pins for SCK/SDI/SDO lines
    #define SPICLOCKLAT         LATCbits.LATC3
    #define SPIINLAT            LATCbits.LATC4
    #define SPIOUTLAT           LATCbits.LATC5
    
    // Port pins for SCK/SDI/SDO lines
    #define SPICLOCKPORT        PORTCbits.RC3
    #define SPIINPORT           PORTCbits.RC4
    #define SPIOUTPORT          PORTCbits.RC5
    
    #define SPIENABLE           SSPCON1bits.SSPEN
    
    // Will generate an error if the clock speed is too low to interface to the card
    #if (GetSystemClock() < 400000)
        #error System clock speed must exceed 400 kHz
    #endif
So those two areas should be the starting point of your customized hardwareprofile.h.

There is also a file called FSconfig.h you need to look thru, it provides all the options your need to make concerning using the file system. You use it to add or remove features. Find a copy of it most anywhere and move it to your project folder, and edit that version.

You add FSIO.c and SD-SPI as part of your project. Adjust your path (if need be) to see your hardwareprofile.h and FSconfig.h. And that's it. Try building it and then track down the errors starting top down.
 

spinnaker

Joined Oct 29, 2009
7,830
Nah, it's pretty straightforward to set one of these up for any PIC you want to use. It's just not documented anywhere exactly HOW to do that.

ELM has an excellent file system too, just that I have never used it. I have used the MC one and find it useful.

One example of the file system for the PIC18 is at "...\Microchip Solutions v2011-10-18\MDD File System-SD Card"

The Microchip libraries all typically depend on a file named "hardwareprofile.h" to be part of your project. This describes your specific PIC and how it is connected to the libraries. Figuring out what should go there is worth your effort; MC crams the definitions for so many PICs and platforms into these they are very hard to understand, but you can start simply. For a SD card I would just start with the clock, the SPI pins, and the other SD card pins.

For the clock, you need something like this (adjusted to your real crystal frequency):
Rich (BB code):
    #define GetSystemClock()        48000000                        // System clock frequency (Hz)
    #define GetPeripheralClock()    GetSystemClock()                // Peripheral clock freq.
    #define GetInstructionClock()   (GetSystemClock()/4)            // Instruction clock freq.
Next is the "hard part" but taken line by line you can adjust all the #defines to match your platform:
Rich (BB code):
    #define USE_PIC18
    #define USE_SD_INTERFACE_WITH_SPI
    
    #define INPUT_PIN           1
    #define OUTPUT_PIN          0
    
    // Chip Select Signal
    #define SD_CS               PORTBbits.RB3
    #define SD_CS_TRIS          TRISBbits.TRISB3
    
    // Card detect signal
    #define SD_CD               PORTBbits.RB4
    #define SD_CD_TRIS          TRISBbits.TRISB4
    
    // Write protect signal
    #define SD_WE               PORTAbits.RA4
    #define SD_WE_TRIS          TRISAbits.TRISA4
    
    // Registers for the SPI module you want to use
    #define SPICON1             SSP1CON1
    #define SPISTAT             SSP1STAT
    #define SPIBUF              SSP1BUF
    #define SPISTAT_RBF         SSP1STATbits.BF
    #define SPICON1bits         SSP1CON1bits
    #define SPISTATbits         SSP1STATbits
    
    #define SPI_INTERRUPT_FLAG  PIR1bits.SSPIF   
    
    // Defines for the HPC Explorer board
    #define SPICLOCK            TRISCbits.TRISC3
    #define SPIIN               TRISCbits.TRISC4
    #define SPIOUT              TRISCbits.TRISC5
    
    // Latch pins for SCK/SDI/SDO lines
    #define SPICLOCKLAT         LATCbits.LATC3
    #define SPIINLAT            LATCbits.LATC4
    #define SPIOUTLAT           LATCbits.LATC5
    
    // Port pins for SCK/SDI/SDO lines
    #define SPICLOCKPORT        PORTCbits.RC3
    #define SPIINPORT           PORTCbits.RC4
    #define SPIOUTPORT          PORTCbits.RC5
    
    #define SPIENABLE           SSPCON1bits.SSPEN
    
    // Will generate an error if the clock speed is too low to interface to the card
    #if (GetSystemClock() < 400000)
        #error System clock speed must exceed 400 kHz
    #endif
So those two areas should be the starting point of your customized hardwareprofile.h.

There is also a file called FSconfig.h you need to look thru, it provides all the options your need to make concerning using the file system. You use it to add or remove features. Find a copy of it most anywhere and move it to your project folder, and edit that version.

You add FSIO.c and SD-SPI as part of your project. Adjust your path (if need be) to see your hardwareprofile.h and FSconfig.h. And that's it. Try building it and then track down the errors starting top down.
Thanks

I will give it another look. Hope the OP follows your advice too.

One thing that I remember is you have to play with memory blocks to get it to compile. You have to define your own block or something. Am I right?
 

spinnaker

Joined Oct 29, 2009
7,830
I am going to start my own thread on the Microchip SD library as to not hijack the OP's thread. I will post it in the embedded forum please look there.
 

THE_RB

Joined Feb 11, 2008
5,438
The MikroC compilers have built in FAT16 support, with functions for all the main stuff;
* create a file
* open a file
* delete a file
* write a byte to a file
* read a byte from a file

For the more complex directory management and FAT32 stuff MikroC have recently released a FAT32 library that loads straight into the compiler, it can be found on their library site;
www.libstock.com
 
Top