天天看點

51單片機驅動SD卡

單片機:STC12C5A60S2 晶振:24MHZ

51單片機讀sd卡資料:http://download.csdn.net/detail/sparkstrike/7982195

sd卡引腳

51單片機驅動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);
	
}
           

繼續閱讀