Yahoo Groups archive

Lpc2000

Index last updated: 2026-04-28 23:31 UTC

Message

Re: [lpc2000] SD/SPI driver code

2006-04-12 by Clyde Stubbs

Ok, seems the attachment was stripped. It's here:

http://www.htsoft.com/resources/sd.c

Clyde

On Thu, Apr 13, 2006 at 05:51:01AM +1000, Clyde Stubbs wrote:
> 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]
> 
> 
> 
>  
> 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.

Attachments

Move to quarantaine

This moves the raw source file on disk only. The archive index is not changed automatically, so you still need to run a manual refresh afterward.