Attached is some code for driving an SD card via an SPI port. This
was written specifically for a SiLabs C8051F320 but should adapt
with little trouble to any other SPI interface.
The code has been widely tested on a variety of different brand SD
cards - some have little quirks.
Clyde
On Wed, Apr 12, 2006 at 07:16:32PM -0000, darcy_will wrote:
> Thanks to everyone that has offered their help with this topic! I
> honestly didn't expect such a fantastic response in such a short
> period of time.
>
> Cheers all
> D.
>
>
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
--
Clyde Stubbs | HI-TECH Software
Email: clyde@... | Phone Fax
WWW: http://www.htsoft.com/ | USA: (408) 490 2885 (408) 490 2885
PGP: finger clyde@... | AUS: +61 7 3722 7777 +61 7 3722 7778
---------------------------------------------------------------------------
HI-TECH C: compiling the real world.
----------
/* SD card interface. Copyright (C) 2005 HI-TECH Software Pty. Ltd. */
#include <8051.h>
#define FALSE 0
#define TRUE 1
// Error codes for the SD
enum {
RESET_FAIL=1, READ_FAIL, CSD_FAIL, WRITE_FAIL, STATUS_FAIL, ACMD_FAIL, ERASE_FAIL
};
// card present states
enum {
CARD_MISSING = 0,
CARD_PRESENT,
CARD_READY,
CARD_FAIL,
CARD_EJECTED
} card_state;
static unsigned char * xptr; // data pointer
static unsigned char error_code; // code d'error
static unsigned long card_size; // card size in blocks
static unsigned long part_offset;// offset to start of first partition
static unsigned char file_format; // partition layout
#define V33 (1<< 6) // bit identifying 3.3V
#define SD_BLKLEN 512 // block size
#define CSD_SIZE 16 // CSD size in bytes
#define CID_SIZE 16 // CID size in bytes
unsigned char blkbuf[SD_BLKLEN];
#define ACMD 55 // command following is application specific
#define CARD_SELECT (P1_BITS.B3) // this bit should drive the card select
#define SPIBUSY 0x80 // spi busy bit
#define CRC 0x95 // all purpose CRC
static bit fat32; // set if we have a fat32 card
static bit fat_check; // we need to check the fat again
static unsigned char timeout; // timeout value in ticks
// this must be implemented in an timer interrupt. If timeout is non-zero, the
// interrupt routine should decrement it once per timer tick.
// adjust the calculation in SET_TIMEOUT so that the argument represents milliseconds.
#define SET_TIMEOUT(x) (timeout = (((x)*128UL))/1000+1)
#define TIMEDOUT() (timeout == 0)
// fetch a long value from the buffer
static unsigned long
get_long(unsigned char * ptr)
{
unsigned long value;
value = *ptr++;
value += *ptr++ << 8;
value += (unsigned long)*ptr++ << 16;
value += (unsigned long)*ptr++ << 24;
return value;
}
static void
SPI_init(void)
{
// setup the SPI for the SD card.
// Data latched on rising edge, input valid in middle of clock,
// idle state is high.
// initial clock for the SD card must be very slow - 200kHz
// due to the open-drain connection
SPI0CKR = 0x30; // start SPI real slow
SPI0CFG = 0B01000000;
SPI0CN = 1;
}
// Send one byte to the SPI
static void
SPI_send(unsigned char databyte)
{
while(TXBMT == 0)
continue;
SPI0DAT = databyte;
}
// read one byte from the SPI
static unsigned char
SPI_read(void)
{
SPIF = 0;
SPI0DAT = 0xFF; // clock out 1s while clocking in data
while(!SPIF)
continue;
return SPI0DAT;
}
// Send idle (0xFF) a specified number of bytes
static void
SD_idle(unsigned char cnt)
{
while(cnt--)
SPI_send(0xFF);
}
// Send a command and wait for a response
static unsigned char
SD_send(unsigned char cmd, unsigned long arg)
{
unsigned char tok;
CARD_SELECT = 0; // select the card
SD_idle(3); // lead-in
SPI_send(cmd|0x40); // send the command
SPI_send(arg >> 24);
SPI_send(arg >> 16);
SPI_send(arg >> 8);
SPI_send(arg);
SPI_send(CRC);
cmd = 16;
do
tok = SPI_read(); // wait for response - up to 16 times
while(tok & 0x80 && --cmd != 0);
return tok;
}
static bit
SD_reset(void)
{
SPI_init();
SD_idle(100);
CARD_SELECT = 0; // select the card
SD_idle(100); // send numerous clocks to initialize
CARD_SELECT = 1;
SD_idle(100);
SD_send(0, 0); // send initialization command
SET_TIMEOUT(1000); // initialization should take no more than 1 second
do {
if(TIMEDOUT()) {
error_code = RESET_FAIL;
return FALSE;
}
SD_send(55, 0); // app-specific command follows
} while(SD_send(41, 0) & 1);
SPI0CKR = 0; // set SPI to maximum speed - 12 MHz
return TRUE;
}
static unsigned char
SD_cmd(unsigned char cmd, unsigned long arg)
{
if(SD_send(cmd, arg) == 0)
return 0;
// card did not respond in a reasonable time - might be a PNY!
// hard reset the card and retry the command, but only once.
_delay(24000000/4); // wait 240 ms to ensure buffers flushed
SD_reset();
return SD_send(cmd, arg);
}
// read the status register
static unsigned
SD_readstatus(void)
{
unsigned char i;
i = SD_cmd(13, 0);
if(i & 0x80) {
error_code = STATUS_FAIL;
return FALSE;
}
return (i << 8) + SPI_read();
}
// read the CSD - 16 bytes
static bit
SD_readcsd(void)
{
unsigned char token;
unsigned char i;
unsigned int len;
if(SD_cmd(9, 0)) {
error_code = CSD_FAIL;
return FALSE;
}
SET_TIMEOUT(200);
do
token = SPI_read();
while(token != 0xFE && !TIMEDOUT());
if(token & 1) {
error_code = CSD_FAIL;
return FALSE;
}
i = 0;
do
blkbuf[i] = SPI_read();
while(++i != CSD_SIZE);
SPI_read();
SPI_read();
i = (blkbuf[15-6] & 3) << 1; // bits 48-49
i += blkbuf[15-5] >> 7; // bit 47
i += 2;
len = blkbuf[15-8] << 2;
len += blkbuf[15-7] >> 6;
len += (blkbuf[15-9] & 3) << 10;
len++;
card_size = (1L << i) * len - 1; // set to number of last block
file_format = (blkbuf[15-1] >> 2) & 3;
return TRUE;
}
static void
SD_stopwrite(void)
{
SPI_send(0xFD);
SET_TIMEOUT(500);
SPI_send(0xFF);
while(!TIMEDOUT())
if(SPI_read() == 0xFF)
break;
}
// commence a read operation
static bit
SD_startread(unsigned long blockno)
{
if(SD_cmd(17, blockno << 9)) {
error_code = READ_FAIL;
return FALSE;
}
return TRUE;
}
static bit
SD_waitread(void)
{
unsigned char token;
unsigned int i;
SET_TIMEOUT(200);
do
token = SPI_read();
while(token != 0xFE && !TIMEDOUT());
if(token & 1) {
error_code = READ_FAIL;
return FALSE;
}
return TRUE;
}
// commence a write operation
static bit
SD_startwrite(unsigned long blockno)
{
unsigned char token;
unsigned int i;
if(SD_cmd(24, blockno << 9)) {
error_code = WRITE_FAIL;
return FALSE;
}
SPI_send(0xFF);
SPI_send(0xFE); // start of write
return TRUE;
}
static bit
SD_writeblock(unsigned long blockno, unsigned char * buf)
{
unsigned int i;
unsigned char tok;
if(!SD_startwrite(blockno))
return FALSE;
i = SD_BLKLEN;
do
SPI_send(*buf++);
while(--i != 0);
do
tok = SPI_read();
while(tok == 0xFF);
tok &= 0x1F;
if(tok != 5) {
error_code = WRITE_FAIL;
return FALSE;
}
// wait for completion
while(SPI_read() != 0xFF)
continue;
return TRUE;
}
// read one BLKLEN block from the card.
static bit
SD_readblock(unsigned long blockno, unsigned char * buf)
{
unsigned int i;
if(!SD_startread(blockno))
return FALSE;
if(!SD_waitread())
return FALSE;
i = 0;
do
*buf++ = SPI_read();
while(++i != SD_BLKLEN);
SPI_read();
SPI_read(); // flush unwanted CRC
return TRUE;
}
// init the SD card
static bit
SD_init(void)
{
unsigned char i;
unsigned long sectors;
part_offset = 0;
card_size = 0;
if(!SD_reset() || !SD_readcsd())
return FALSE;
if(file_format == 0) {
if(!SD_readblock(0, blkbuf))
return FALSE;
if(blkbuf[SD_BLKLEN-1] == 0xAA && blkbuf[SD_BLKLEN-2] == 0x55) {
xptr = blkbuf+0x1BE; // point to start of partition table
i = 4;
do {
if(xptr[0] == 0 &&
(xptr[4] == 1 || xptr[4] == 4 || xptr[4] == 6 || xptr[4] == 14)) {
part_offset = get_long(xptr+8);
card_size -= part_offset;
break;
}
} while(--i != 0);
}
}
return TRUE;
}
// check the filesystem type - set a flag if it is FAT32
static void
SD_fatchick(void)
{
unsigned char i;
unsigned long sectors;
fat32 = 0;
fat_check = 0;
if(!SD_readblock(part_offset, blkbuf))
return;
if(blkbuf[0x13] != 0 || blkbuf[0x14] != 0)
return; // must be <= 32MB, quite safe
sectors = get_long(blkbuf+0x20); // get number of sectors
i = blkbuf[0x0D]; // sectors per cluster
while((i >>= 1) != 0)
sectors >>= 1;
if(sectors >= 65525)
fat32 = 1;
}
[Non-text portions of this message have been removed]Message
Re: [lpc2000] SD/SPI driver code
2006-04-12 by Clyde Stubbs
Attachments
- No local attachments were found for this message.