以下是自己寫的sd card driver,雖然沒有整理成一個檔案,但有心要寫driver的人一定可以改的出來,因為關鍵點都列出來了!其中也有查表式的軟體CRC(這部份抄來的),SD 1.1的spec需要自己在網路上找,這個driver幾乎適用所有的cpu,只要有io腳都可以,8051也不例外,當然pic、TIDSP也可以,如果有硬體的SPI就更好了,只要加個FAT,就可以存取檔案了。
除了SDHC,大部分的SD CARD幾乎都試過,時間有限,就不逐行解釋了。
#include "sd.h"
#include "uart.h"
#include "tmr_ctrl.h"
#include "sdcmdset.h"
#include "sddef.h"
#define SD_DUMMY_WR() CPLD_B.sd_wp = 0
/*
READ BLOCK:
DATA IN *-CMD(1)-*
DATA OUT *-R1(1)-**-START TOKEN(1)-**-DATA BLOCK(512)-**-CRC16(2)-*
READ BLOCK ERROR:
DATA IN *-CMD(1)-*
DATA OUT *-R1(1)-**-ERR TOKEN(1)-*
WRITE BLOCK:
DATA IN *-CMD(1)-* *-START TOKEN(1)-**-DATA BLOCK(512)-**-CRC16(2)-*
DATA OUT *-R1(1)-* *-DATA RESP(1)-*--BUSY--
CSD REG:PAGE 63
SPI MODE:PAGE 86
CMD: PAGE 92
*/
#define SD_OP_COND_RETRY 100
#define SD_WR_TIMEOUT 10000 //max:65535
#define SD_TIMEOUT 255 // ms
#define SD_RETRY_TIME 3
#define SD_RESET_RETRY 1
#define SPI_DELAY 0
#define SD_CRC_DISP 0
#define SD_QRY_WR_STATUS 0
// ----------------- variable -------------------
U32 SD_TotalKB, SD_TotalSectors;
BOOL SDActive = 0, SDWrLock = 0;
#define CSD_SIZE (128/8)
static const U8 SDRstCmd[] = { 0xff, 0x40, 0, 0, 0, 0, 0x95, 0xff };
static const U8 SDOpCondCmd[] = { 0xff, CMD1_OP_COND, 0, 0, 0, 0, 0xff, 0xff };
_EXTERNAL_MEMORY_
static char PTmp[40];
static U8 CSD[CSD_SIZE] = {0};
#if SD_DBG_MSG_ON
static const char *SDRespMsg[] = {
// 16bit:0~7
"IDLE",
"Erase Reset",
"Illegal Command",
"CRC err.",
"Erase Seq Err",
"Addr OOB",
"Arg OOB",
"Unknown error",
// ---- 16bit:8~15
"Card is Locked",
"WP Erase Skip",
"General error",
"Card controller error",
"Card ECC Error",
"Write protect violation",
"An invalid erase",
"CSD_Overwrite"
};
#endif
// ----------------- PROTOTYPE -------------------
static void Calc_CRC7 (U8 *buf, U16 len);
static U16 Calc_CRC16(U8 *ptr, U16 len) ;
static void SPI_Wr(U8 arg);
static U8 SPI_Rd(void);
static U8 SD_RstCmd(void);
static U8 SD_InitCmd(void);
static U8 WaitSDResp8State(void);
static U8 WaitSDResp16State(void);
static void _SDCmdOut(U8 cmd, U32 arg);
static U8 SD_Reset(void);
static U8 SDCmd200K(U8 *cmd, U8 len, U8 resp);
static U8 SDCmdRst400K(void);
#if SD_DBG_MSG_ON
static void SD_ParseRespMsg(U16 val);
#endif
static void CalcSDSize(void);
static U8 SDCommand(U8 cmd, U32 arg);
static U8 SD_RdCSD(void);
static U8 QrySDStatus(void);
static void SD_OutputFF(U16 cnt);
// ----------------- function -------------------
#ifndef SYS_DELAY_TICK_US
#define SpiDelay() { U8 dly = SPI_DELAY; while(dly--); }
#else
#define SpiDelay() SysDelay(SYS_DELAY_TICK_US)
#endif
#define SpiDelay() { U8 dly = SPI_DELAY; while(dly--); }
/**************** SD INIT *****************/
void SD_Init(void)
{
SD_IOInit();
CPLD_B.sd_w = 0xff;
SD_CSB(1);
}
static DISK_STATUS SD_Reset(void)
{
U8 i;
for(i = 0; i < SD_RESET_RETRY; i++)
{
if( !IsSDInsert() )
return (fatDiskStatus = DISK_REMOVE);
#if SD_DBG_MSG_ON
DBG_PRINT("RST\xD\xA");
#endif
if( SD_RstCmd() != DISK_ACCESS_OK )
continue;
SD_DELAY(100);
#if SD_DBG_MSG_ON
DBG_PRINT("Init\xD\xA");
#endif
if( SD_InitCmd() != DISK_ACCESS_OK )
continue;
break;
}
return (fatDiskStatus = (i == SD_RESET_RETRY? DISK_ACCESS_ERROR:DISK_ACCESS_OK));
}
U8 SDCardCmd(U8 cmd, U32 arg)
{
U8 resp;
resp = SDCommand(cmd, arg);
return resp;
}
/*****************************************************************************/
BOOL SDReadOnly(void)
{
return SDWrLock;
}
DISK_STATUS SDRdSector (U32 lba, U8 *buf )
{
U8 retry;
if(lba < SD_TotalSectors)
{
#if SD_SIMPLE_MSG_ON
DBG_PRINT("R");
#endif
for(retry = 0; retry < SD_RETRY_TIME; retry++)
{
if( (fatDiskStatus = SD_RdBlock( lba, buf )) == DISK_ACCESS_OK )
{
#if SD_DBG_MSG_ON
DBG_PRINT("SDRdSector:%lu\xD\xA", lba);
#endif
break;
}
if( IsSDInsert() )
{
#if SD_DBG_MSG_ON
DBG_PRINT("SDRdSector Retry:%d\xD\xA", retry);
#endif
SD_Insert();
}
else
{
return (fatDiskStatus = DISK_REMOVE);
}
}
}
else
{
fatDiskStatus = DISK_ACCESS_ERROR;
}
return fatDiskStatus;
}
DISK_STATUS SDWrSector (U32 lba, U8 *buf )
{
U8 retry;
#if SD_MBR_PROTECT
if(lba == 0)
{
#if SD_DBG_MSG_ON
DBG_PRINT("SDWrSector:0\xD\xA");
#endif
return (fatDiskStatus = DISK_ACCESS_ERROR);
}
#endif
#if SD_DBG_MSG_ON
DBG_PRINT("SDWrSector:%lu\xD\xA", lba);
#endif
if(lba < SD_TotalSectors)
{
#if SD_SIMPLE_MSG_ON
DBG_PRINT("W");
#endif
for(retry = 0; retry < SD_RETRY_TIME; retry++)
{
if( (fatDiskStatus = SD_WrBlock( lba, buf )) == DISK_ACCESS_OK )
break;
if( IsSDInsert() )
{
#if SD_DBG_MSG_ON
DBG_PRINT("SDWrSector Retry:%d\xD\xA", retry);
#endif
SD_Insert();
}
else
{
return (fatDiskStatus = DISK_REMOVE);
}
}
}
else
{
fatDiskStatus = DISK_ACCESS_ERROR;
}
return fatDiskStatus;
}
U8 SD_RdBlock(U32 blk, U8 *buf)
{
U8 resp, retry;
U16 i, crc16;
blk *= SECTOR_SIZE;
for(retry = 0; retry < SD_RETRY_TIME; retry++)
{
if( !IsSDInsert() )
return DISK_REMOVE;
if( (resp = SDCommand(CMD17_RD_ONE_BLK, blk )) != 0 )
{
#if SD_DBG_MSG_ON
DBG_PRINT("SD_RdBlock retry:%d\xD\xA", retry);
#endif
SD_DELAY(100);
continue;
}
for(i = 0; i<1000; i++)
{
if( SPI_Rd() == SINGLE_START_TOKEN )
break;
}
for(i = 0; i<(SECTOR_SIZE); i++)
{
buf[i] = SPI_Rd();
}
// verify crc here if required.
i = (U16)SPI_Rd() << 8; // 16bit CRC.
i |= SPI_Rd();
crc16 = Calc_CRC16(buf, 512);
if(i != crc16)
{
#if SD_CRC_DISP
DBG_PRINT("CRC16 ERROR:%x\xD\xA", i);
#endif
return DISK_ACCESS_ERROR;
}
break;
}
return retry == SD_RETRY_TIME? DISK_ACCESS_ERROR:DISK_ACCESS_OK;
}
U8 SD_WrBlock(U32 blk, U8 *buf)
{
U16 i, crc16;
U8 resp, retry;
blk *= SECTOR_SIZE;
for(retry = 0; retry < SD_RETRY_TIME; retry++)
{
if( !IsSDInsert() )
return DISK_REMOVE;
if( (resp = SDCommand(CMD24_WR_ONE_BLK, blk)) != 0 )
{
#if SD_DBG_MSG_ON
DBG_PRINT("\xD\xA:w retry:%d\xD\xA", retry);
#endif
SD_DELAY(100);
continue;
}
SPI_Wr( SINGLE_START_TOKEN );
for(i = 0; i<SECTOR_SIZE; i++)
SPI_Wr( buf[i] );
// --- send crc here if required ---- //
crc16 = Calc_CRC16(buf, 512);
SPI_Wr( (U8)(crc16>>8) );
SPI_Wr( (U8)crc16 );
// ============= wait response ============= //
for(i = 0; i<SD_WR_TIMEOUT; i++)
{
if( (resp = SPI_Rd()) != 0xff )
break;
}
if( i == SD_WR_TIMEOUT )
{
#if SD_DBG_MSG_ON
DBG_PRINT("SD Timeout1:\xD\xA");
#endif
return DISK_TIMEOUT;
}
// ========================================= //
// ============= wait bus idle ============= //
for(i = 0; i<SD_WR_TIMEOUT; i++)
{
if( SPI_Rd() == 0xff )
break;
}
if( i == SD_WR_TIMEOUT )
{
#if SD_DBG_MSG_ON
DBG_PRINT("SD Timeout2:\xD\xA");
#endif
return DISK_TIMEOUT;
}
// ============= parse response ============ //
if( (resp & 0x5) != 0x5 )
{
#if SD_DBG_MSG_ON
DBG_PRINT("\xD\xAWRespErr:%x\xD\xA", resp);
#endif
continue;
}
break;
}
#if SD_QRY_WR_STATUS
if(retry < SD_RETRY_TIME)
{
QrySDStatus();
}
#endif
return retry >= SD_RETRY_TIME? DISK_ACCESS_ERROR:DISK_ACCESS_OK;
}
// ----------------------------- local api --------------------------
static U8 SD_RstCmd(void)
{
U16 i, ii;
SD_PWR(PWR_OFF);
SD_DELAY(2000);
SD_PWR(PWR_ON);
SD_DELAY(1000);
CPLD_B.sd_w = 0xff;
SD_OutputFF(10);
SD_CSB(0);
if( SDCmd200K(SDRstCmd, sizeof(SDRstCmd), 1) != 1 )
{
#if SD_DBG_MSG_ON
DBG_PRINT("SD_RstCmd Err.\xD\xA");
#endif
return DISK_ACCESS_ERROR;
}
return DISK_ACCESS_OK;
}
static void SDOClk200K_H(void)
{
SD_CLK(1);
SD_DUMMY_WR();
SD_DUMMY_WR();
SD_DUMMY_WR();
}
static void SDOClk200K_L(void)
{
SD_CLK(0);
SD_DUMMY_WR();
}
static void SDIClk200K_H(void)
{
SD_CLK(1);
SD_DUMMY_WR();
}
static void SDIClk200K_L(void)
{
SD_CLK(0);
SD_DUMMY_WR();
SD_DUMMY_WR();
}
static U8 SDCmd200K(U8 *cmd, U8 len, U8 resp)
{
register U8 val;
register U16 retry = 50000;
while(len--)
{
val = *cmd;
cmd++;
CPLD_B.sd_w = val;
SDOClk200K_H(); SDOClk200K_L();
SDOClk200K_H(); SDOClk200K_L();
SDOClk200K_H(); SDOClk200K_L();
SDOClk200K_H(); SDOClk200K_L();
SDOClk200K_H(); SDOClk200K_L();
SDOClk200K_H(); SDOClk200K_L();
SDOClk200K_H(); SDOClk200K_L();
SDOClk200K_H(); SD_CLK(0);
}
CPLD_B.sd_w = val = 0xff;
while( val != resp )
{
if( !IsSDInsert() )
return 0xff;
SDIClk200K_H(); SDIClk200K_L();
SDIClk200K_H(); SDIClk200K_L();
SDIClk200K_H(); SDIClk200K_L();
SDIClk200K_H(); SDIClk200K_L();
SDIClk200K_H(); SDIClk200K_L();
SDIClk200K_H(); SDIClk200K_L();
SDIClk200K_H(); SDIClk200K_L();
SDIClk200K_H(); SDIClk200K_L();
val = CPLD_B.sd_r;
if( !(retry--) )
{
SD_OutputFF(1);
#if SD_DBG_MSG_ON
DBG_PRINT("--400K TimeOut:%x\xD\xA", val);
#endif
return 0xff;
}
}
SD_OutputFF(1);
return val;
}
static U8 SD_InitCmd(void)
{
U8 i;
for(i = 0; i < SD_OP_COND_RETRY; i++)
{
if( !IsSDInsert() )
return DISK_REMOVE;
if( SDCmd200K(SDOpCondCmd, sizeof(SDOpCondCmd), 0) == 0 )
break;
SD_DELAY(15);
}
if(i == SD_OP_COND_RETRY)
{
#if SD_DBG_MSG_ON
DBG_PRINT("SDOpCond Err.\xD\xA");
#endif
return DISK_ACCESS_ERROR;
}
SDCardCmd(CMD59_CRC_ONOFF, 1);
if( SDCardCmd(CMD16_SET_BLKLEN, 512) != 0 )
{
#if SD_DBG_MSG_ON
DBG_PRINT("SDBlkLen Err.\xD\xA");
#endif
return DISK_ACCESS_ERROR;
}
return DISK_ACCESS_OK;
}
static void _SDCmdOut(U8 cmd, U32 arg)
{
U8 *ptr = (U8*)&arg;
U8 d[7];
U8 i;
d[0+1] = cmd;
#ifdef B_ENDIAN
d[1+1] = ptr[0]; d[2+1] = ptr[1]; d[3+1] = ptr[2]; d[4+1] = ptr[3];
#else
d[1+1] = ptr[3]; d[2+1] = ptr[2]; d[3+1] = ptr[1]; d[4+1] = ptr[0];
#endif
//d[5+1] = 0xFF; // null crc.
Calc_CRC7(d, 6);
SPI_Wr( 0xff );
for(i = 1; i<7; i++)
{
SPI_Wr( d[i] );
}
SPI_Wr( 0xff );
}
static U8 SDCommand(U8 cmd, U32 arg)
{
_SDCmdOut(cmd, arg);
return WaitSDResp8State();
}
static void CalcSDSize(void)
{
U32 mult, bk_size;
U32 block_nr;
bk_size = 1 << (RdBitPosiVal(CSD, CSD_RD_BL_LEN_LOC, CSD_RD_BL_LEN_BITS) - 9) ; // unit:0.5kb.
mult = 1 << (RdBitPosiVal(CSD, CSD_C_SIZE_MULT_LOC, CSD_C_SIZE_MULT_BITS) + 2 );
block_nr = ((U32)RdBitPosiVal(CSD, CSD_C_SIZE_LOC, CSD_C_SIZE_BITS) + 1) * mult;
SD_TotalSectors = block_nr * bk_size;
SD_TotalKB = SD_TotalSectors / 2; // unit:1kb
#if SD_DBG_MSG_ON
DBG_PRINT("SD:%ldKB, %ldMB.\xD\xA", SD_TotalKB, SD_TotalKB>>10);
#endif
}
static U8 SD_RdCSD(void)
{
U8 resp;
U16 i;
if( (resp = SDCommand(CMD9_SEND_CSD, 0)) != 0 )
{
return resp;
}
for(i = 0; i<0x100; i++)
{
if( SPI_Rd() == SINGLE_START_TOKEN )
break;
}
for(i = 0; i<CSD_SIZE; i++)
{
CSD[i] = SPI_Rd();
}
// verify crc here if required.
i = SPI_Rd() << 8; // 16bit CRC.
i |= SPI_Rd();
#if SD_CRC_DISP
DBG_PRINT("CRC16:%x\xD\xA", i);
#endif
return SD_READ_OK;
}
static U8 WaitSDResp8State(void)
{
U8 resp;
SetTimeOut(SD_TIMEOUT); // ms.
while( (resp = SPI_Rd()) == 0xff )
{
if( IsTimeOut() )
{
#if SD_DBG_MSG_ON
DBG_PRINT("--Cmd TimeOut\xD\xA");
#endif
break;
}
}
#if SD_DBG_MSG_ON
SD_ParseRespMsg(resp);
#endif
return resp;
}
#if SD_QRY_WR_STATUS
static U8 QrySDStatus(void)
{
_SDCmdOut(CMD13_SEND_STATUS, 0);
return WaitSDResp16State();
}
static U8 WaitSDResp16State(void)
{
U16 resp;
SetTimeOut(SD_TIMEOUT); // ms.
while( (resp = SPI_Rd()) == 0xff )
{
if( IsTimeOut() )
{
#if SD_DBG_MSG_ON
DBG_PRINT("--Cmd TimeOut\xD\xA");
#endif
return 0xff;
}
}
resp <<= 8;
resp |= SPI_Rd();
#if SD_DBG_MSG_ON
SD_ParseRespMsg(resp);
#endif
return resp;
}
#endif
#if SD_DBG_MSG_ON
static void SD_ParseRespMsg(U16 val)
{
U8 i;
U16 sft;
for(i = 0, sft = 1; i < 16;
i++ , sft<<=1)
{
if(val & sft) DBG_PRINT("Resp:%s\xD\xA", SDRespMsg[i]);
}
}
#endif
void SD_Eject(void)
{
SD_PWR(PWR_OFF);
SD_IOInit();
SDActive = 0;
#if SD_DBG_MSG_ON
DBG_PRINT("\xD\xA##SD Extract##\xD\xA");
#endif
}
// FALSE: ERR, TRUE:OK.
BOOL SD_Insert(void)
{
SDActive = FALSE;
if( !IsSDInsert() )
return FALSE;
SD_Init();
if( SD_Reset() != DISK_ACCESS_OK )
{
#if SD_DBG_MSG_ON
DBG_PRINT("SD_Reset Err\xD\xA");
#endif
return FALSE;
}
#if SD_DBG_MSG_ON
else
DBG_PRINT("\xD\xA**SD Ready**\xD\xA");
#endif
SD_RdCSD();
CalcSDSize();
SDWrLock = IsSDLock();
SDActive = TRUE;
#if SD_DBG_MSG_ON
if(SDWrLock)
DBG_PRINT("SD RO\xD\xA");
else
DBG_PRINT("SD RW\xD\xA");
#endif
return TRUE;
}
// --------------------------- SPI BUS --------------------------- //
static U8 SPI_Rd(void)
{
register U8 val = 0;
U8 i;
CPLD_B.sd_w = 0xff;
for(i = 0; i<8; i++)
{
val<<=1;
SpiDelay();
SD_CLK(1);
SpiDelay();
val |= SD_SDO();
SD_CLK(0);
}
return val;
}
static void SPI_Wr(U8 v)
{
register U8 val;
U8 i;
val = v;
for(i = 0; i<8; i++, val<<=1)
{
SD_SDI( (val & 0x80) );
SpiDelay();
SD_CLK(1);
SpiDelay();
SD_CLK(0);
}
}
static void SD_OutputFF(U16 cnt)
{
U8 ii;
SD_SDI(1);
while(cnt--)
{
for(ii = 0; ii<8; ii++)
{
SYS_TINY_DELAY_INIT();
SD_CLK(1);
SYS_TINY_DELAY_WAIT();
SD_CLK(0);
}
}
}
static const U8 CRC7_Table[256] = {
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0x00 , 0x09 , 0x12 , 0x1B , 0x24 , 0x2D , 0x36 , 0x3F , 0x48 , 0x41 , 0x5A , 0x53 , 0x6C , 0x65 , 0x7E , 0x77 ,
0x19 , 0x10 , 0x0B , 0x02 , 0x3D , 0x34 , 0x2F , 0x26 , 0x51 , 0x58 , 0x43 , 0x4A , 0x75 , 0x7C , 0x67 , 0x6E ,
0x32 , 0x3B , 0x20 , 0x29 , 0x16 , 0x1F , 0x04 , 0x0D , 0x7A , 0x73 , 0x68 , 0x61 , 0x5E , 0x57 , 0x4C , 0x45 ,
0x2B , 0x22 , 0x39 , 0x30 , 0x0F , 0x06 , 0x1D , 0x14 , 0x63 , 0x6A , 0x71 , 0x78 , 0x47 , 0x4E , 0x55 , 0x5C ,
0x64 , 0x6D , 0x76 , 0x7F , 0x40 , 0x49 , 0x52 , 0x5B , 0x2C , 0x25 , 0x3E , 0x37 , 0x08 , 0x01 , 0x1A , 0x13 ,
0x7D , 0x74 , 0x6F , 0x66 , 0x59 , 0x50 , 0x4B , 0x42 , 0x35 , 0x3C , 0x27 , 0x2E , 0x11 , 0x18 , 0x03 , 0x0A ,
0x56 , 0x5F , 0x44 , 0x4D , 0x72 , 0x7B , 0x60 , 0x69 , 0x1E , 0x17 , 0x0C , 0x05 , 0x3A , 0x33 , 0x28 , 0x21 ,
0x4F , 0x46 , 0x5D , 0x54 , 0x6B , 0x62 , 0x79 , 0x70 , 0x07 , 0x0E , 0x15 , 0x1C , 0x23 , 0x2A , 0x31 , 0x38 ,
0x41 , 0x48 , 0x53 , 0x5A , 0x65 , 0x6C , 0x77 , 0x7E , 0x09 , 0x00 , 0x1B , 0x12 , 0x2D , 0x24 , 0x3F , 0x36 ,
0x58 , 0x51 , 0x4A , 0x43 , 0x7C , 0x75 , 0x6E , 0x67 , 0x10 , 0x19 , 0x02 , 0x0B , 0x34 , 0x3D , 0x26 , 0x2F ,
0x73 , 0x7A , 0x61 , 0x68 , 0x57 , 0x5E , 0x45 , 0x4C , 0x3B , 0x32 , 0x29 , 0x20 , 0x1F , 0x16 , 0x0D , 0x04 ,
0x6A , 0x63 , 0x78 , 0x71 , 0x4E , 0x47 , 0x5C , 0x55 , 0x22 , 0x2B , 0x30 , 0x39 , 0x06 , 0x0F , 0x14 , 0x1D ,
0x25 , 0x2C , 0x37 , 0x3E , 0x01 , 0x08 , 0x13 , 0x1A , 0x6D , 0x64 , 0x7F , 0x76 , 0x49 , 0x40 , 0x5B , 0x52 ,
0x3C , 0x35 , 0x2E , 0x27 , 0x18 , 0x11 , 0x0A , 0x03 , 0x74 , 0x7D , 0x66 , 0x6F , 0x50 , 0x59 , 0x42 , 0x4B ,
0x17 , 0x1E , 0x05 , 0x0C , 0x33 , 0x3A , 0x21 , 0x28 , 0x5F , 0x56 , 0x4D , 0x44 , 0x7B , 0x72 , 0x69 , 0x60 ,
0x0E , 0x07 , 0x1C , 0x15 , 0x2A , 0x23 , 0x38 , 0x31 , 0x46 , 0x4F , 0x54 , 0x5D , 0x62 , 0x6B , 0x70 , 0x79
};
static const U16 CRC16_Table[256]={
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
static void Calc_CRC7 (U8 *buf, U16 len)
{
register U8 acc = 0;
register U16 i;
buf[0] = 0;
for (i = 0; i < len; ++i)
{
acc = CRC7_Table[ (acc << 1) ^ buf[i] ];
}
buf[6] = ((buf[0] ^ acc) << 1) | 0x1;
}
static U16 Calc_CRC16(U8 *ptr, U16 len)
{
register U16 crc;
U8 d;
crc = 0;
while(len-- != 0)
{
d = (U8) (crc >> 8);
crc <<= 8;
crc ^= CRC16_Table[d ^ *ptr];
ptr++;
}
return crc;
}
- 11樓. KJ2011/05/03 09:32Header File for SD Card Driver
您好,因為最近有測試治具記錄資料的需求,所以想採用SD Card來做量測資料的記錄,也因此爬文爬到貴寶地,目前程式環境是採用Keil C進行程式的撰寫,在爬了一下之後,也跟其他人有相同的問題,就是您程式中Header File的部份,不知是否可以分享給小弟呢?
小弟的E-mail是 kjung.tw@yahoo.com.tw
感謝您了.
之前C語言只是寫一些測試用的東西而已,對於這些較進階的部份比較不懂,所以如果問了一些阿里不達的問題時,請多多包涵,謝謝.
真的有缺嗎?可能是我沒有檢察清楚,我不是很清楚缺了哪個,可以告訴我嗎? 彼得d 於 2011/05/03 17:32回覆 - 10樓. 彼得d2011/04/06 13:43程式碼
skydrive不知原因的掛了,我把程式碼放在hinet上,應該沒問題了
http://petercake.myweb.hinet.net/download/fat.zip
裡面的fat_new是我從以前做的案子裡刮出來的,應該是穩定的,只不過要花點時間移植。
mcu_8051是更久以前使用8051平台做出的,會動,配合前面的fat_new來修改才會比較正常動作。
使用單晶片來移植要很注意熱插拔造成的SD CARD ESD毀損的問題,一般硬體工程師通常不會注意這個嚴重性,尤其這個問題加上脈衝電流會更嚴重,要使用可以floating的腳位才會比較安全,等確定插上後,再來設定io方向,8051可以使用P0=0xFF使port變成open drain
sd card電源最好設計成BJT電流源控制,控制在幾百mA以下,不然至少也要加個電感,來避免sd card插上時脈衝電流灌入io port導致sd card毀損。
- 9樓. zero2010/12/01 19:53請問這份程式可否拿來做SPI
您好,
我目前正在試著用SPI來把CPU的資料寫入SD CARD,
所用的CPU是32bit的,而CPU本身有內建SPI的功能,
想請問一下,這份程式是否可應用到上面?
如果可套用上去,那我需要先完成哪些設定?
因為問題有點多,
所以如果可以的話,是否可以跟您要您的信箱,以便請教您呢?
麻煩您了,謝謝
可以,把spi設定好就可以了,把我程式內spi R/W改成硬體就可以了,硬體spi更好用。
只不過我的程式只支援sd1.1,2.0存取模式稍稍不同,我沒有支援。
彼得d 於 2010/12/06 10:45回覆 - 8樓. 急思考keil c 的人2010/02/11 13:48疑問
如果是用來讀出usb內的資料內容,是否是一樣用這一個程式碼??那個zip檔只有sd card driver,fat部分是可共用的,usb要自己弄了。
usb1.1, 2.0的等級和SD類似,但是複雜一些,和pc對連更要面臨WDM的問題,這裡建議直接使用大廠的solution,如果要發展自己的solution,建議用mess storage就好,搭配網路上找到的usb open source就可以了。
windows程式要穩定就不是我辦的到的,一定要去問專門寫WDM的人,microsoft的東西很麻煩,規格很常變,windows 7也不知道吃不吃WDM,windows部分真的無能為力。
彼得d 於 2010/02/13 08:59回覆 - 7樓. ahkun2010/01/17 06:18感謝您的回覆...^^
說實在的, 小弟的 C 還真的很肉腳!
最近在摸 mp3+sd+ide 的開發板,
為了 函式參數傳遞 造成動作異常的問題, 抓了老半天
目前雖然動作已正常, 可是問題還是不知出在那裡, 於是就將就用了...^^"
也因此, 怕 linux c 與 keil c 語法會差很多,
才會厚這臉皮, 問看看有沒有 keil c 的源碼...^^"
不過這樣也好, 這樣就沒有理由摸魚打混了...^^"
同時也可看看 linux c 與 keil c 的差異性.
很少在網路上發文, 若有不得體之處, 還請見諒!
最後要再次感謝您提供的資料, 也謝謝撥空回覆.
謝謝! ^_^這類的問題我倒幫得上忙,我會的很少,只能提供一點小小意見。
linux c與keil c、乃至於windows C, ARM C, x86 C的差異到底在哪裡呢?其實許多人都會問類似的問題。
c語言是幾十年前以那個時代的計算機想像出來的解決方案,某個實驗室認為只要用c語言的模式,就是int, for, while void func(xxx)等等的語法,同時包含翻譯到組合語言的介面規格,這個模式就能夠以計算機來解決問題,同時這個解決方法可以在不同的計算機都能夠用。
所以不管哪種語言,乃至於VB、Java等等,終究要回歸到個機器的組合語言,組合語言更要回歸到機械碼,機械碼便是啟動cpu與各類硬體周邊的開關。
Keil只是公司,他們公司有出產各種cpu的compiler,乃至於RTOS都有,同公司的tool有一個好處,就是思考模式類似,文件類似,tool產生的資訊如map檔案都類似,如果我們用同一種公司的tool,便會省去許多學習與猜測的時間。
ARM/x86/8051/PIC等等cpu,是由ARM/INTEL/MICORCHIP等等公司的專利,他們發明了各種結構來解決某類的問題,例如ARM7沒有除法指令,x86卻有,學過邏輯設計就知道,除法電路是比較龐大與複雜的電路,而一般的問題並不太需要太多的複雜除法,所以是否需要在一顆cpu內加入除法電路?可否用較慢的軟體動作來達到除法的功能?這便要看解決的問題是哪種類型,ARM7與X86的精神是不同的,產品只要做到可解決便可,複雜的CPU意味著複雜的學習與製造,solution適當性牽涉PM對資源的了解與否。
Linux與windows差別呢?windows是微軟認為如何以計算機來解決事情,它牽涉到各類資源的統合,螢幕顯示有大有小,cpu有老有舊,程式也有老舊,會新增刪除,各種奇怪的硬體,使用者有笨的聰明的,有工程師也有玩電動的,當然也有看A片的,還有相容性問題等等,是個非常龐大的solution,微軟使用了許多方法,windows API、DLL、Active X、WDM等等方法,所以用這類的角度來看linux等等其他作業系統,就會明白其實並無好壞,只是單純的適用性的問題,硬要拿linux的強項來比windows的弱項是不公平的,windows笑linux是4隻不同的企鵝也不太好,每種solution都有他的適用極限。
所以在linux與windows內發展軟體的精神是不同的,就算都是linux,PC與embedded system也是不同的,就算都是c語言,ARM/x86操作手法也是不同的,人類的精力很有限,所以要慎選範圍,有些學習僅供吃飯用,有些學習是人生大事,要花多少力氣去搞,要好好想想了。
彼得d 於 2010/01/21 13:35回覆 - 6樓. ahkun2010/01/10 18:47關於 - 比較完整的程式碼
小弟剛接觸 sd card, 也是在網路上爬文爬到這兒來.
不知您那 "比較完整的程式碼" 是什麼環境的?
如是 linux source code, 小弟無 linux 的環境
在前文有看到您先前就是用keil c發展的,
請問可否寄一份 keil c 版的,
給小弟我(ak@ahkun.com)參考嗎?
如 "比較完整的程式碼" 是 keil C source code, 就以上略過.
在此感謝您無私的分享此技術! ^^那份keil C版的已經因為幾年前的電腦浩劫消失了,實在是莫可奈何。
"比較完整的程式碼"是說這份原始碼已經包含FAT16/32+sd card driver,其中fat16/32也有api可以使用,只是需要花精神移植。
這份程式碼是我從我幫某家公司做的案子中抽出的,做了好久才完成,後來發現其他人也有需求,覺得這種程式碼實在不需要所有人都走一次,才決定把他公布出來。
MCU絕對可用,最早期是用AT89C52外掛ROM/RAM發展完成,後來移植到某些32bit的cpu,要移植先做到以下動作:
1. 想盡辦法compiler完成。
2. 做出rs232,連結到pc的hyper terminal,這樣發展測試會很快
基本上c語言熟悉的人,應該幾天就可以移植完成。
彼得d 於 2010/01/13 10:42回覆 - 5樓. 彼得d2009/11/30 09:21比較完整的程式碼
移植這些東西要花些力氣來增修程式,不過內容看過是完整的,受限於時間,可能幫不上忙,除非有必要,記得把CRC關閉,這非常耗CPU時間,記得也要叫SD CARD關閉CRC。
fat部分原本是從source forge取得,但因為內容不完整被我重寫了,可支援fat32/16,不支援長檔名,同時也被我改成catch模式,這樣比較快,但要耗記憶體。
sdcmdset.h因為懶的key,所以是從網路上的某處抄來的,年代已非常久遠,也忘了在哪找到的!
sd.c部分裡面有需要200kHz來initial的命令,我發現台灣的sd card似乎不需要這樣做,反倒是有些國際大廠有這個限制,如果有硬體SPI的還好解決,沒有的就需要示波器來幫忙了。
程式碼有無問題,我也不敢保證,畢竟這是4年前寫的,寫的也不見得好,有問題就改掉吧!
這裡不建議把這些東西改的太完整,例如長檔名,就算你寫出來了也會發現沒甚麼機會用,但是這些東西會花掉你非常多的時間,也沒甚麼研究價值,就是規格罷了。
特殊應用的同志們,例如會長時間存取,千萬注意儲存裝置的讀寫特性,這部分solution的提供者不一定會幫你做好,但是這部分牽涉到儲存裝置的壽命、相容性與存取效率,差異非常的大,要搞這部分記得要把microsoft fat規格書讀熟。
搞檔案系統要小心有些存取錯誤是導因於硬體,如果傻傻的一直在程式碼裡尋找,小心會找死掉。
- 4樓. KENI LIN2009/11/18 13:14謝謝您的回覆...
感謝您的回覆!!
用8051控制SD card讀寫,有詢問過Slilcon代理商FAE,但他們是把SD Card當作一般flash讀寫用,可是我是要做成FAT16格式存成 TXT 檔,再從8051去 load TXT 檔裡面的文字.
在網路爬文找了好久,才找到您的分享,如果有進一步消息再麻煩您指點我,謝謝!
My mail : keni_lin@hotmail.com
謝謝!
keni 2009.11.18
不知道你有沒有去keil的網站下載uvision 2的driver,這樣就可以使用keil的ide來發展,比用silicon lab的ide要好用多了。
silicon lab的cpu還滿快的,也有硬體spi,spi把它拼到極限應該沒有關係,只要把sd card 的crc檢查關閉就可以非常快速的存取,所以我的程式你還要再修改一下。
程式碼的部分您可能還要再等一下,我最近事情太多了~~~~
彼得d 於 2009/11/19 10:38回覆 - 3樓. KENI_LIN2009/11/10 16:07請教一下程式....
請問這篇文章的h檔是您定義的嗎? 還是可以在哪裡可以下載到?
#include "sd.h"
#include "uart.h"
#include "tmr_ctrl.h"
#include "sdcmdset.h"
#include "sddef.h"如果都是您這邊自己定義,可以寄給我(keni_lin@hotmail.com)參考嗎? 最近我在研究如何用8051讀SD Card裡面的資料,爬文的時候發現您的這篇文章,希望可以跟您交流一下心得!
另外我是用Keil C編輯程式,不知到你的程式有沒有支援Keil C的平台.
對啊,都是我自己定義的,其實我很想把這些東西放在網路硬碟上,讓有需要的人自行取用,但我目前發現的網路硬碟都有使用時間,時間一到就自動刪除,超麻煩。
但是sd規格書好像有保密,是不能隨意傳遞的,程式碼是我自己寫的,應該沒關係吧!
fat的程式碼是在source forge找到的,但是那版的程式fat32沒做完,存取效率也不行,我把它全部改寫了,本來想放回source forge的,但是不知道怎麼做,想想就算了。
linux source code太高級了,不適用8051。
keil C可以用,我當初就是用keil c發展的,後來才移植到其他32bit平台,最近比較忙,我整理整理再寄給你參考一下。
只不過8051是8bit,fat32會使用到大量的unsigned long變數,運算速度會非常慢,尤其是上面的CRC打開後,會慢到嚇死人,如果cpu沒有SD interface但又很需要SD的話,建議增加一顆CPLD來做CRC+SPI,CPLD又可以設定成open drain,一舉數得,只是麻煩點。
彼得d 於 2009/11/17 09:51回覆 - 2樓. qqaf91952009/09/09 00:19請教一下
我看不懂...請教一下這個程式如何使用???
可以留信箱給我嗎??我希望可以請教有關這方面的問題...謝謝
目前許多多媒體solution的cpu都有內建sd card的處理線路,就會用不到我提供的程式,許多solution應該都有os在內,存檔機制都很完整,就更不需要了。
但是有許多工控設備並沒有這些東西,如果他們要sd card怎麼辦,sd card非常方便,可以存很多東西,一般都喜歡用內建eeprom來解決這事,許多工控cpu都是16/32bit,處理fat是很容易的事,8bit會比較慢,如果這些cpu可以再移植一個小型os就更漂亮了。
這個是最底層的driver,連最原始的8051都可以直接使用,sd card本身就是SPI介面。
cchaha@ms35.hinet.net
彼得d 於 2009/09/09 10:51回覆