Code for Interfacing an SD Card to a PIC16F690 in SPI mode to retrieve files stored in FAT16 Format


Addresses of Key Data Elements
Data ElementAddress (dec)Size (in bytes - Little Endian)
Start of first Partition Entry [A]4544
Bytes Per Sector [B][A]*512+112
Reserved Sectors [C][A]*512+142
Number of FATs [D][A]*512+161
Sectors Per FAT [E][A]*512+222
Root[A]*512+[B]*[C]+[D]*[E]*[B]Variable


This code is written for the PIC16F690 in Hi-Tech C.

//RC7 = SDO - transmit data to memory chip
//RB6 = SCK - clock for transmitting data to memory chip
//RB7 = Chip select for memory chip
//RB4= data in for memory chip


#include <pic.h>


__CONFIG(INTCLK & WDTDIS & PWRTDIS & MCLRDIS & BORDIS); // Setup the configuration word

void init_Device(void) //Initializes the device, setting the oscialltor speed, pin direction, and pull-ups
{
   OSCCON=0b01110111; //Oscillator 8 Mhz
   TRISA=0; // all Port A output
   TRISB=0; // all Port B output
   TRISB5=1; // make serial port reception pin input
   TRISB4=1; //make SDI reception pin input
   TRISC=0; // all Port C output
   TRISC3=1; // Make RC3 input
   PEIE=1; //enable interrupts for RCIF
   ANSEL=0; //All inputs digital
   ANSELH=0; //All inputs digital
   SBOREN=0; //disable brown out reset
}

void init_SPI(void)
{
   CKP=0; //0=idle clock state is low
   CKE=1; //0=Data transmitted on falling edge of SCK
   SMP=1; //1=SPI mode-Input data sampled at end of data output time
   SSPM3=0; //FOSC/64
   SSPM2=0; //FOSC/64
   SSPM1=0; //FOSC/4 - "1" for /64
   SSPM0=0; //FOSC/64
}

void Delay(long x)
{
   while (x>0){x--;}
}

void WriteSPI(int x) //writes a bit to the memory chip through the SPI module
{
   SSPEN=1; //enable SPI
   SSPBUF=x;
   while (BF==0)
   {;}
   SSPEN=0; //disable SPI
}


void InitializeSPIMode(void)
{
   char x; //used for "while" loops
   unsigned long Address=0;
   unsigned short BytesPerSector=0;
   char ReservedSectors=0;
   char FATCount=0;
   unsigned short SectorsPerFat=0;
   unsigned long FatSpace=0;

   char AddressL=0; //Low byte of address
   char AddressM=0; //Mid byte of address
   char AddressH=0; //High byte of address

   Delay(25000); //Wait one millisecond

/////////////////////////////////// Slow Oscillator/SPI down for initialization ////////////////////////////////////

OSCCON=0b01000111; //Oscillator 8 Mhz 0b01110111 for 8mhz
SSPM1=1; //SPI - FOSC/64

////////////////////////////////// Initialize SPI Mode /////////////////////////////////////////////////////////////

WriteSPI(0xFF);WriteSPI(0xFF);WriteSPI(0xFF);WriteSPI(0xFF);WriteSPI(0xFF);WriteSPI(0xFF);WriteSPI(0xFF);WriteSPI(0xFF);WriteSPI(0xFF);WriteSPI(0xFF); //Wake up the card (80 clock pulses)

RB7=0; Delay(5);//Assert CS

WriteSPI(0x40);WriteSPI(0x00);WriteSPI(0x00);WriteSPI(0x00);WriteSPI(0x00);WriteSPI(0x95); //CMD0
WriteSPI(0);
WriteSPI(0);

WriteSPI(0x48);WriteSPI(0x00);WriteSPI(0x00);WriteSPI(0x01);WriteSPI(0b10101010);WriteSPI(0x87); //CMD8
WriteSPI(0);
WriteSPI(0);

while(SSPBUF!=0) //ACMD 41 to initialize SD Card - keep doing until you get 0 meaning it's not busy
{
   RB7=1; //deassert CS
   Delay(5);
   RB7=0; //assert CS

   WriteSPI(0xFF);

   WriteSPI(0b0111011);WriteSPI(0x00);WriteSPI(0x00);WriteSPI(0x00);WriteSPI(0x00);WriteSPI(0xFF); //CMD55
   WriteSPI(0xFF);
   WriteSPI(0xFF);

   RB7=1; //deassert CS
   RB7=0; //assert CS

   WriteSPI(0xFF);
   WriteSPI(0b01101001);WriteSPI(0b00000000);WriteSPI(0b00000000);WriteSPI(0x00);WriteSPI(0x00);WriteSPI(0xFF); //ACMD41
   WriteSPI(0xFF);
   WriteSPI(0xFF);
}

////////////////////////////////// Speed Oscillator and SPI back up after initialization /////////////////////////////

OSCCON=0b01110111; //Oscillator 8 Mhz 0b01110111 for 8mhz
SSPM1=0;

////////////////////////////////// Find Address of FAT16 Partition Entry //////////////////////////////////////////////

RB7=1; //deassert CS
RB7=0; //assert CS

WriteSPI(0xFF);

WriteSPI(0b01010000);WriteSPI(0);WriteSPI(0);WriteSPI(0);WriteSPI(4);WriteSPI(0xFF); //CMD16 4 byte block - want a return of "0"
WriteSPI(0xFF);
WriteSPI(0xFF);
WriteSPI(0xFF);

WriteSPI(0b01010001);WriteSPI(0x00);WriteSPI(0x00);WriteSPI(0x01);WriteSPI(0xC6);WriteSPI(0xFF); //CMD17 (Read Single Block) - want a response of XFE signaling that data is coming

while(SSPBUF!=0xFE) //Keep clocking until we get valid info (Follows 0xFE)
{
   WriteSPI(0xFF);
}

WriteSPI(0xFF);
Address=SSPBUF; //Loads LSB of address
WriteSPI(0xFF);
Address=Address+SSPBUF*256; //loads second bytes into address
WriteSPI(0xFF);
AddressH=Address+SSPBUF*65536; //loads third bytes into address
WriteSPI(0xFF);
WriteSPI(0xFF); //two dummy blocks at end to get through check sums
WriteSPI(0xFF);

////////////////////////////////// Find Address of Root //////////////////////////////////////////////

RB7=1; //deassert CS
Delay(5);
RB7=0; //assert CS

Address=Address*2; //Since address is in sectors, multiply by 2 then shift a byte over
Address=Address<<8;
AddressL=Address;
AddressM=Address>>8;
AddressH=Address>>16;

WriteSPI(0b01010000);WriteSPI(0);WriteSPI(0);WriteSPI(0);WriteSPI(13);WriteSPI(0xFF); //CMD16 13 byte block - want a return of "0"
WriteSPI(0xFF);
WriteSPI(0xFF);
WriteSPI(0xFF);

WriteSPI(0b01010001);WriteSPI(0);WriteSPI(AddressH);WriteSPI(AddressM);WriteSPI(11);WriteSPI(0xFF); //CMD17 (Read single Block) - want a response of XFE signaling that data is coming

while(SSPBUF!=0xFE) //Keep clocking until we get valid info (Follows 0xFE)
{
   WriteSPI(0xFF);
}

WriteSPI(0xFF);
BytesPerSector=SSPBUF; //LSB of Bytes per Sector
WriteSPI(0xFF);
BytesPerSector=BytesPerSector+SSPBUF*256; //MSB of Bytes per sector
WriteSPI(0xFF); //sectors per cluster, unused
WriteSPI(0xFF);
ReservedSectors=SSPBUF; //LSB of reserved sectors
WriteSPI(0xFF); //MSB of reserved sectors (ignore)
WriteSPI(0xFF);
FATCount=SSPBUF; //number of FATs
WriteSPI(0xFF); //number of root entries, unused
WriteSPI(0xFF); //number of root entries, unused
WriteSPI(0xFF); //small sectors, unused
WriteSPI(0xFF); //small sectors, unused
WriteSPI(0xFF); //Media Type, unused
WriteSPI(0xFF);
SectorsPerFat=SSPBUF; //LSB of sectors per FAT
WriteSPI(0xFF);
SectorsPerFat=SectorsPerFat+SSPBUF*256; //MSB of sectors per FAT
WriteSPI(0xFF); //Two dummy bytes to clear the CRC
WriteSPI(0xFF);

Address=Address+BytesPerSector*ReservedSectors; //Adds the number of reserved sectors to the starting address
FatSpace=FATCount;
FatSpace=FatSpace*BytesPerSector;
FatSpace=FatSpace*SectorsPerFat;

Address=Address+FatSpace; //add FAT data space to the address to get to the root directory

//////////////////////////////////// Retrieve info from Root Section ////////////////////////////////////////

RB7=1; //deassert CS
Delay(5);
RB7=0; //assert CS

AddressL=Address;
AddressM=Address>>8;
AddressH=Address>>16;

WriteSPI(0b01010000);WriteSPI(0);WriteSPI(0);WriteSPI(0);WriteSPI(32);WriteSPI(0xFF); //CMD16 32 byte block - want a return of "0"
WriteSPI(0xFF);
WriteSPI(0xFF);
WriteSPI(0xFF);

WriteSPI(0b01010010);WriteSPI(0);WriteSPI(AddressH);WriteSPI(AddressM);WriteSPI(0);WriteSPI(0xFF); //CMD18 (Read Multiple Block) - want a response of XFE signaling that data is coming

while(SSPBUF!=0xFE) //Keep clocking until we get valid info (Follows 0xFE)
{
   WriteSPI(0xFF);
}

while(1)
{
   WriteSPI(0xFF);
}

}

void main(void){

init_Device(); //initialize device
init_SPI(); //initialize SPO module
PORTA=0;
PORTB=0;
PORTC=0;

RB6=0; //Set clock to low
RB7=1; //set CS to high

InitializeSPIMode();

for(;;){

}
}




Make sure you check out my projects:

USB Serial Communication using the Microchip MCP2200 - Interfacing microcontrollers with a computer using this cheap chip.

Interfacing a PCM1725 to a PIC16F690 - Adding audio to a project by interfacing a PCM1725 DAC to a PIC16F690.

Using the Avago ADNS-2610 Sensor - Programming an optical sensor from an optical mouse.

USB Communications using the PIC18F4550 - Programming a microcontroller to communicate with a computer via the USB port

Interfacing to Microcontrollers to SD Cards - Accessing files stored on SD Cards in FAT16 format via the SPI module in a microcontroller

Building Electronic Drums - How to construct electronic drums using piezo sensors

Circuit Archive - If you are looking for other sample circuits for various uses and components, this is the place for you.

Component List - If you are looking for suppliers for different project components, I've compiled some good ones here.