單片機:STC12C5A60S2 晶振:24MHZ
51單片機讀sd卡資料:http://download.csdn.net/detail/sparkstrike/7982195
sd卡引腳
一.IO接口
sbit CLK = P3^4;//同步時鐘
sbit DI = P3^5;//Cmd/DataIn
sbit DO = P3^6;//DataOut
sbit CS = P3^7;//片選
二.模拟SPI
注:spi相關見http://blog.csdn.net/sparkstrike/article/details/39609235
//***********模拟spi寫函數
void SPI_W(unsigned char Data){
unsigned char i;
for(i = 0; i<8; i++){
Data <<= 1;
CLK = 0;
DI = CY;
CLK = 1;
};
DI = 1;
}
//***********模拟spi讀函數
unsigned char SPI_R(){
unsigned char Data,i;
DO = 1;//設定DO接口為輸入狀态
for(i = 0; i<8; i++){
Data <<= 1;
CLK = 0;
CLK = 1;
Data |= DO;
};
return Data;
}
三.等待SD卡的回應(在寫入一些指令後,sd卡會回應一些東西)
//**************讀sd卡回應
unsigned char SD_Response(){
unsigned char i,Response;
for(i = 0; i<10; i++){
Response = SPI_R();
if(Response == 0x00)
break;
if(Response == 0x01)
break;
};
return Response;
}
四.向sd卡寫入指令
Cmd為指令,指令有複位指令、讀指令、寫指令等,4位的Arguement為位址,CRC為校驗碼
//***************向SD發指令
void SD_Cmd(unsigned char Cmd, unsigned long Argument, unsigned char CRC){
unsigned char arg[4];
arg[0] = (unsigned char)Argument;
arg[1] = (unsigned char)(Argument >> 8);
arg[2] = (unsigned char)(Argument >> 16);
arg[3] = (unsigned char)(Argument >> 24);
SPI_W(Cmd | 0x40);
SPI_W(arg[3]);
SPI_W(arg[2]);
SPI_W(arg[1]);
SPI_W(arg[0]);
SPI_W(CRC);
}
五.初始化SD卡
SD卡有兩種讀寫模式:SD模式和SPI模式,預設的讀寫模式為SD模式,單片機用SPI模式比較友善,要使用SPI模式需要在SD卡上電是對它寫入CMD0指令和CMD1指令
/*************SD卡初始化,設定SPI模式
unsigned char SD_Init(){
unsigned int delay = 0;
unsigned char i;
unsigned char Response = 0xff;
CS = 1;
for(i = 0; i<10; i++){
SPI_W(0xff);//上電後給74個以上的時間脈沖
};
CS = 0;
SD_Cmd(0x00, 0, 0x95);//指令CMD0,複位SD卡
//等待複位成功
i = 0;
while(SD_Response() != 0x01){//等待SD卡回應信号
i++;
if(i > 100){
return 0;//失敗傳回0
};
};
CS = 1;
SPI_W(0xff);//關片選後寫8個空脈沖,SD卡複位完畢
//設定SPI
i = 0;
CS = 0;
while(Response != 0x00){//循環等待成功回應,若成功,回應信号為0x00
SD_Cmd(0x01, 0, 0xff);//CMD1,将SD卡設定為SPI模式,無需CRC校驗,填入0xff
Response = SD_Response();
if(i > 100){
return 0;//嘗試100次,失敗傳回0
};
};
CS = 1;
SPI_W(0xff);//給8個空脈沖
return 1;
}
六.SD卡讀寫資料
因為sd卡的讀寫都是以扇區為機關的,是以這裡定義一個全局變量,512表示一個扇區
unsigned int const len = 512;//扇區大小
1.寫入資料:
adress為開始寫的位址,一定要為512的整數倍,block為一個512位元組的數組
//***************SD卡寫入資料塊
unsigned char SD_Block_W(unsigned char* block, unsigned long address){
unsigned int i;
unsigned char Response_Write;
CS =0;
SD_Cmd(0x18, address, 0xff);//CMD18,塊寫入指令
while(SD_Response() != 0x00);//循環等待指令回應0x00
for(i = 0; i<10; i++){
SPI_W(0xff);//寫入一定量空脈沖
};
SPI_W(0xfe);//0xfe為塊頭部,後面跟512b位元組,+2bCRC(0xff,0xff)
for(i=0; i<len; i++){
SPI_W(block[i]);//寫入512b位元組
};
SPI_W(0xff);
SPI_W(0xff);
Response_Write = SPI_R()&0x0f;//寫入CRC碼後SD卡會回應一個xxx0,1001
while(SPI_R() == 0);//等待SD卡回應
CS = 1;
SPI_W(0xff);//寫入8個空脈沖
if(Response_Write == 0x05){
return 1;
}else{
return 0;
};
}
2.讀資料:
adress為開始讀的位址,一定要為512的整數倍,block為一個512位元組的數組
//****************從sd卡讀資料塊
void SD_Block_R(unsigned char* block, unsigned long address){
unsigned int i;
CS = 0;
SD_Cmd(0x11, address, 0xff);//CMD11,資料塊讀寫指令,
while(SD_Response()!=0x00);//循環等待指令回應0x00
while(SPI_R() != 0xfe); //0xfe為塊讀出的頭, 後面緊跟512位元組的資料塊+2位元組的CRC
for(i=0; i<len ; i++){
block[i] = SPI_R();//讀資料
};
SPI_R();
SPI_R();//兩個位元組的CRC。舍棄
CS =1;
SPI_R();//8個空脈沖
}
附:測試程式(程式從SD卡512000處寫入512位元組的資料,并讀出)
/*********************************************************************************************/
#include <STC12C5A60S2.H> //單片機頭檔案
sbit CLK = P3^4;//同步時鐘
sbit DI = P3^5;//Cmd/DataIn
sbit DO = P3^6;//DataOut
sbit CS = P3^7;//片選
unsigned int const len = 512;//扇區大小
void DELAY_MS (unsigned int a){
unsigned int i;
while( --a != 0){
for(i = 0; i < 600; i++);
}
}
//***********模拟spi寫函數
void SPI_W(unsigned char Data){
unsigned char i;
for(i = 0; i<8; i++){
Data <<= 1;
CLK = 0;
DI = CY;
CLK = 1;
};
DI = 1;
}
//***********模拟spi讀函數
unsigned char SPI_R(){
unsigned char Data,i;
DO = 1;//設定DO接口為輸入狀态
for(i = 0; i<8; i++){
Data <<= 1;
CLK = 0;
CLK = 1;
Data |= DO;
};
return Data;
}
//**************讀sd卡回應
unsigned char SD_Response(){
unsigned char i,Response;
for(i = 0; i<10; i++){
Response = SPI_R();
if(Response == 0x00)
break;
if(Response == 0x01)
break;
};
return Response;
}
//***************向SD發指令
void SD_Cmd(unsigned char Cmd, unsigned long Argument, unsigned char CRC){
unsigned char arg[4];
arg[0] = (unsigned char)Argument;
arg[1] = (unsigned char)(Argument >> 8);
arg[2] = (unsigned char)(Argument >> 16);
arg[3] = (unsigned char)(Argument >> 24);
SPI_W(Cmd | 0x40);
SPI_W(arg[3]);
SPI_W(arg[2]);
SPI_W(arg[1]);
SPI_W(arg[0]);
SPI_W(CRC);
}
//*************SD卡初始化
unsigned char SD_Init(){
unsigned int delay = 0;
unsigned char i;
unsigned char Response = 0xff;
CS = 1;
for(i = 0; i<10; i++){
SPI_W(0xff);//上電後給74個以上的時間脈沖
};
CS = 0;
SD_Cmd(0x00, 0, 0x95);//指令CMD0,複位SD卡
//等待複位成功
i = 0;
while(SD_Response() != 0x01){//等待SD卡回應信号
i++;
if(i > 100){
return 0;//失敗傳回0
};
};
CS = 1;
SPI_W(0xff);//關片選後寫8個空脈沖,SD卡複位完畢
//設定SPI
i = 0;
CS = 0;
while(Response != 0x00){//循環等待成功回應,若成功,回應信号為0x00
SD_Cmd(0x01, 0, 0xff);//CMD1,将SD卡設定為SPI模式,無需CRC校驗,填入0xff
Response = SD_Response();
if(i > 100){
return 0;//嘗試100次,失敗傳回0
};
};
CS = 1;
SPI_W(0xff);//給8個空脈沖
return 1;
}
//***************SD卡寫入資料塊
unsigned char SD_Block_W(unsigned char* block, unsigned long address){
unsigned int i;
unsigned char Response_Write;
CS =0;
SD_Cmd(0x18, address, 0xff);//CMD18,塊寫入指令
while(SD_Response() != 0x00);//循環等待指令回應0x00
for(i = 0; i<10; i++){
SPI_W(0xff);//寫入一定量空脈沖
};
SPI_W(0xfe);//0xfe為塊頭部,後面跟512b位元組,+2bCRC(0xff,0xff)
for(i=0; i<len; i++){
SPI_W(block[i]);//寫入512b位元組
};
SPI_W(0xff);
SPI_W(0xff);
Response_Write = SPI_R()&0x0f;//寫入CRC碼後SD卡會回應一個xxx0,1001
while(SPI_R() == 0);//等待SD卡回應
CS = 1;
SPI_W(0xff);//寫入8個空脈沖
if(Response_Write == 0x05){
return 1;
}else{
return 0;
};
}
//****************從sd卡讀資料塊
void SD_Block_R(unsigned char* block, unsigned long address){
unsigned int i;
CS = 0;
SD_Cmd(0x11, address, 0xff);//CMD11,資料塊讀寫指令,
while(SD_Response()!=0x00);//循環等待指令回應0x00
while(SPI_R() != 0xfe); //0xfe為塊讀出的頭, 後面緊跟512位元組的資料塊+2位元組的CRC
for(i=0; i<len ; i++){
block[i] = SPI_R();//讀資料
};
SPI_R();
SPI_R();//兩個位元組的CRC。舍棄
CS =1;
SPI_R();//8個空脈沖
}
void main (void){
unsigned char xdata block[len];
int i;
//初始化SD卡
SD_Init();
for(i=0;i<512;i++){
block[i]=0xaa;
};
//從512000處寫sd卡
SD_Block_W(block, len*1000);
//從512000處讀sd卡
SD_Block_R(block, len*1000);
}