#include <p18f2550.h>
#include <stdio.h>
#include <delays.h>
#pragma config WDT = OFF, FOSC = HS, LVP = OFF
typedef unsigned LBA; // logic block address, 32 bit wide
//Funtions
void main(void);
void initSD( void);
void delay_s(unsigned char);
void delay_ms(unsigned char);
void delay_us(unsigned char);
unsigned char writeSPI( unsigned char b);
int sendSDCmd( unsigned char c, unsigned a);
int initMedia( void);
int writeSECTOR( LBA a, char *p, char *p2);
int readSECTOR( LBA a, char *p, char *p2);
// I/O definitions
#define SDCS LATCbits.LATC6
#define SDCS_T TRISCbits.TRISC6
#define SDCD PORTCbits.RC0
#define SDCD_T TRISCbits.TRISC0
#define LED_T TRISBbits.TRISB2
#define LED LATBbits.LATB2
// Macros
#define readSPI() writeSPI( 0xFF)
#define clockSPI() writeSPI( 0xFF)
#define disableSD() SDCS = 1; clockSPI()
#define enableSD() SDCS = 0
// SD card commands
#define RESET 0 // a.k.a. GO_IDLE (CMD0)
#define INIT 1 // a.k.a. SEND_OP_COND (CMD1)
#define READ_SINGLE 17
#define WRITE_SINGLE 24
#define I_TIMEOUT 10000
#define RI_TIMEOUT 10000
#define R_TIMEOUT 25000
#define W_TIMEOUT 250000
#define FALSE 0
#define FAIL FALSE
#define E_COMMAND_ACK 0x80
#define E_INIT_TIMEOUT 0x81
#define DATA_START 0xFE
#define DATA_ACCEPT 0x05
#pragma udata data1
char data1[256];
#pragma udata buffer1
char buffer1[256];
int writeSECTOR( LBA a, char *p, char *p2)
// a LBA of sector requested
// p pointer to sector buffer
// returns TRUE if successful
{
unsigned r, i;
// 1. send WRITE command
r = sendSDCmd( WRITE_SINGLE, ( a << 9));
if ( r == 0) // check if command was accepted
{
// 2. send data
writeSPI( DATA_START);
// send 512 bytes of data
for( i=0; i<256; i++)
writeSPI( *p++);
for( i=0; i<256; i++)
writeSPI( *p2++);
// 3. send dummy CRC
clockSPI();
clockSPI();
// 4. check if data accepted
r = readSPI();
if ( (r & 0xf) == DATA_ACCEPT)
{
// 5. wait for write completion
for( i=0; i<W_TIMEOUT; i++)
{
r = readSPI();
if ( r != 0 )
break;
}
} // accepted
else
r = FAIL;
} // command accepted
// 6. remember to disable the card
disableSD();
return ( r); // return TRUE if successful
}
int readSECTOR( LBA a, char *p, char *p2)
// a LBA of sector requested
// p pointer to sector buffer
// returns TRUE if successful
{
int r, i;
// 1. send READ command
r = sendSDCmd( READ_SINGLE, ( a << 9));
if ( r == 0) // check if command was accepted
{
// 2. wait for a response
for( i=0; i<R_TIMEOUT; i++)
{
r = readSPI();
if ( r == DATA_START)
break;
}
// 3. if it did not timeout, read 512 byte of data
if ( i != R_TIMEOUT)
{
i = 256;
do{
*p++ = readSPI();
} while (--i>0);
do{
*p2++ = readSPI();
} while (--i>0);
// 4. ignore CRC
readSPI();
readSPI();
} // data arrived
} // command accepted
// 5. remember to disable the card
disableSD();
return ( r == DATA_START); // return TRUE if successful
}
void initSD( void)
{
ADCON1 = 0x0F;
TRISB = 0x01;
TRISC = 0;
LED_T = 0;
SDCD_T = 1;
SDCS = 1; // initially keep the SD card disabled
SDCS_T = 0; // make Card select an output pin
// init the SPI2 module for a slow (safe) clock speed first
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)
SSPCON1bits.SSPEN=1; //SSPEN(5)=1 enable SPI
}
unsigned char writeSPI( unsigned char b)
{
// send one byte of data and receive one back at the same time
SSPBUF=b; // write to buffer for TX
while( !SSPSTATbits.BF); // wait transfer complete
return SSPBUF; // read the received value
}
int sendSDCmd( unsigned char c, unsigned a)
// c command code
// a byte address of data block
{
int i, r;
// enable SD card
enableSD();
// send a comand packet (6 bytes)
writeSPI( c | 0x40); // send command
writeSPI( a>>24); // msb of the address
writeSPI( a>>16);
writeSPI( a>>8);
writeSPI( a); // lsb
writeSPI( 0x95); // send CMD0 CRC
// now wait for a response, allow for up to 8 bytes delay
for( i=0; i<8; i++)
{
r=readSPI();
if ( r != 0xFF)
break;
}
return ( r);
// NOTE CSCD is still low!
}
int initMedia( void)
// returns 0 if successful
// E_COMMAND_ACK failed to acknowledge reset command
// E_INIT_TIMEOUT failed to initialize
{
int i, r;
// 1. with the card NOT selected
disableSD();
// 2. send 80 clock cycles start up
for ( i=0; i<10; i++)
clockSPI();
// 3. now select the card
enableSD();
// 4. send a single RESET command
r = sendSDCmd( RESET, 0); disableSD();
if ( r != 1) // must return Idle
return E_COMMAND_ACK; // comand rejected
// 5. send repeatedly INIT until Idle terminates
for (i=0; i<I_TIMEOUT; i++)
{
r = sendSDCmd( INIT, 0); disableSD();
if ( !r)
break;
}
if ( i == RI_TIMEOUT)
return E_INIT_TIMEOUT; // init timed out
// 6. increase speed
SSPCON1bits.SSPEN=0; // disable the SPI module
SSPCON1 = 0x30;
SSPCON1bits.SSPEN=1; // enable SPI
return 0;
}
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
void main(void)
{
int result,i,addr;
initSD();
while(SDCD);
delay_ms(100);
result = initMedia();
while(result != 0) result = initMedia();
for(i=0;i<256;i++)
data1[i] = i;
retryW:
delay_ms(100);
addr = 10000;
for(i=0;i<1000;i++){
if(!writeSECTOR(addr+i,data1,data1))
goto retryW;
}
LED = 1;
retryV:
delay_ms(100);
addr=10000; //verify write
for(i=0;i<1000;i++){
if(!readSECTOR(addr+i,buffer1,buffer1))
goto retryV;
}
if(memcmp(data1,buffer1,256)){ //verify
while(1); //mismatch - 4 blinks
}
while(1);
}
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
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();
}
}