以下是自己寫的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;
}