51单片机驱动SD卡

单片机: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);
	
}





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值