⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sdcard.c

📁 PDIUSB12+51做 U盘程序,可以将自己的系统单做U盘跟 电脑通信
💻 C
字号:
//创建日期:2007/5/5
//作者:龚志


//
#include "SDCard.h"

//int1 HaveActSd = 0;

void delay8ms()
{
	unsigned char i,j;
	for(i=0;i<100;i++)
	for(j=0;j<30;j++)
	;
}


//函数说明
//参数
//返回
void Assert_SPI_CS(char value)
{
	SD_CS = value?1:0;	
}



//函数说明     写一个字节到spi
//参数         写数据
//返回         成功返回NO_ERROR 失败返回超时
char WriteToSPI(unsigned char databyte)
{ 
 unsigned char i;
 unsigned char temp;
 temp = databyte;
 for(i=0;i<8;i++)
 {
  SD_SCK = 0;                 //SCK为低的时候准备传输的数据,为高的时候发送数据
  if((temp & 0x80) != 0)  
	SD_MOSI = 1;
  else
    SD_MOSI = 0;
  temp = temp << 1;  
  SD_SCK = 1;
 }
 SD_MOSI = 1;
 return NO_ERROR;
  
}

//函数说明  从spi读一个数据
//参数      接受数据的指针
//返回       成功返回NO_ERROR 失败返回超时
char RdFromSPI(unsigned char *pdatabyte)
{
 int i;
 bit bt;
 unsigned char temp = 0;
 for(i=0;i<8;i++)
 {
  SD_MISO = 1;	            //上跳沿读数据
  SD_SCK = 0;
  SD_SCK = 1;
  temp = temp << 1;  
  bt = SD_MISO;
  if(bt == 1)  temp = temp | 0x1;
  else  temp = temp & 0xfe;
 }
 *pdatabyte = temp;
 return NO_ERROR;
}



void ConvertInt4ToChar4(unsigned long length,unsigned char *para)
{
	*para	=	(unsigned char)length;
	*(para+1)	=	(unsigned char)(length>>8);	
	*(para+2)	=	(unsigned char)(length>>16);
	*(para+3)	=	(unsigned char)(length>>24);	
}

//函数说明   设置块长度
//参数       块长度
//返回       统一
char SetBlockLen(unsigned int length)
{
  char ret; 
  unsigned char para[4];
  unsigned char respond;
  SD_CS = 0	;                            //	 置CS低
   ConvertInt4ToChar4(length,para);	     //把长度转化为四字节参数
  ret = SendCmd(16,para,R1,&respond);    //发送CMD16即设置块长度命令 
  if(ret != NO_ERROR) 					 //出错处理 
  {  
   	SD_CS = 1;
    return ERROR;
  }
  if(respond != 0)   {SD_CS = 1;return ERROR;}   //命令响应不为0表示有错误
  SD_CS = 1;
  return NO_ERROR;
}




//函数说明 读数据块
//参数
//返回值正确返回NO_ERROR ,不正确返回ERROR
char RDBlock(unsigned long addr, unsigned char *rbuf)
{
  char ret;
  unsigned char  temp;

  int i;
  char respond, para[4];
  Assert_SPI_CS(0); 
  ConvertInt4ToChar4(addr,para);     					//参数转化
  ret = SendCmd(17,para,R1,&respond);					//发送读命令
  if(ret != NO_ERROR) {Assert_SPI_CS(1);return ERROR;} //出错处理
  if(respond != 0)  {Assert_SPI_CS(1);return ERROR;}    //响应错误 
  ret = RdFromSPI(&temp);   							//从spi读同步字节
  while(temp == 0xff)   //为FF表示SD忙
  {
    RdFromSPI(&temp);
  }
  if(temp != 0xfe)   //同步字节错误后就重启SD卡
  {
	  delay8ms();
	  Assert_SPI_CS(1);

	  InitialSD();
	  return ERROR;
  }
  for(i=0; i<=512-1; i++)          //读512字节数据,数据出来的顺序  注意数据的顺序(应该是先低位后高位)
  {
    RdFromSPI(&rbuf[i]);
  }
   RdFromSPI(&temp);	            //读两字节的校验
   RdFromSPI(&temp);
	Assert_SPI_CS(1);
  return NO_ERROR;

}














//函数说明  写一512字节到sd卡
//返回值    统一
//参数说明  addr地址  wbuf数据指针
char WriteBlock(unsigned long  addr, unsigned char *wbuf)    
{
  char  temp;
  unsigned int  j=0 ;
  char ret;
  int i;
  char respond;
  unsigned char para[4];
  //print("addr:");
 // printhex(addr);
  //print("\r\n");
  delay8ms();
  Assert_SPI_CS(0);
  ConvertInt4ToChar4(addr,para);   //参数转化
  ret = SendCmd(24,para,R1,&respond);  //发送写命令   
  if(ret != NO_ERROR) {delay8ms();delay8ms();Assert_SPI_CS(1);InitialSD();return ERROR;}  //注意:这两个时间延迟是需要的
  if(respond != 0x0 ) {Assert_SPI_CS(1);InitialSD();return ERROR;}

  WriteToSPI(0xff);        //写8个高脉冲 

  WriteToSPI(0xfe);            //写同步字节
  
  for(i=0; i<512; i++)   //写512字节数据    注意写数据的顺序(应该是先低后高)
  {
    ret = WriteToSPI(wbuf[i]);

  }
  ret = WriteToSPI(0xff);                        
  ret = WriteToSPI(0xff);
  
  RdFromSPI(&temp);	 //数据回应正确值为OX1F
                    
   
	if((temp & 0x1f) == 0xb) //后面都为错处处理
	{
	  delay8ms();
	  WriteToSPI(0xff);	  
	  Assert_SPI_CS(1);
	  InitialSD();
	  return ERROR;
	}

	else if((temp & 0x1f) == 0xd)
	{
	  delay8ms();
	  WriteToSPI(0xff);
	  Assert_SPI_CS(1);
	  InitialSD();
	  return ERROR;
	}

	else if((temp & 0x1f) != 0x5)
	{
	   delay8ms(); 
	   WriteToSPI(0xff);

	   Assert_SPI_CS(1); 
	   InitialSD();
	   return ERROR;
	}
	   j = 0;
	   do
	   {
	   		RdFromSPI(&temp);  //读忙标志 为OXFF表示SD卡忙
			j++;
	   }
	   while((j<1000) && (temp == 0xff))	;
	   Assert_SPI_CS(1);
	   if((j >= 1000) || (temp == 0xff))	{/*print("over time\r\n");*/return ERROR;}
	   else {/*print("wait:");printhex(j);print("\r\n");*/return NO_ERROR;}
}
	


// 函数说明  向sd卡发送一个命令
//参数:cmdNo命令码,para命令参数,paralen参数长度,respondtype响应类型,respond响应内容指针
//返回值:无错的情况下返回NO_ERROR 有错的情况下有两种返回值
char SendCmd(unsigned char cmdNo, unsigned char *para, unsigned char respondtype,unsigned char *respond)
{                                                              
  char i, respondlength, ret;
  unsigned char temp;  
  int retry=0;
  //print("in cmd\r\n");
  ret = WriteToSPI((cmdNo & 0x3F) | 0x40);    //发送命令字节 置命令码的最高两位为“01”
  for(i=3;i>=0;i--)              //发送参数 先发高字节     4字节参数每个命令必须有
  {
    ret = WriteToSPI(para[i]);
  }
  temp = GenerateCRC((cmdNo & 0x3F) | 0x40,para);
  ret = WriteToSPI(temp);                    //发送校验字节
    switch (respondtype)                 /* 根据不同的命令,得到不同的响应长度 */
    {                       
      case R1:  
      case R1b: respondlength = 1;   break;            
      case R2:  respondlength = 2;   break;            
      case R3:  respondlength = 5;   break;            
      default:  respondlength = 0;   break;  /* 返回命令响应类型错误 return error of command response type */          
    }
  if(respondlength == 0)  
  { 
    return ERROR;    //命令参数中返回类型给错    
  }
  RdFromSPI(&temp);
  while((temp & 0x80) != 0)// 等待命令响应,响应的开始位为0
  {
    RdFromSPI(&temp);
    retry++;
    if(retry >= 200) break;
  }
  if((retry >= 200) && ((temp & 0x80) != 0))  
  {
	  //print("over 200\r\n");
      return ERROR;
    
  }	
  
  respond[0] = temp;
  return NO_ERROR;
}










//函数说明    激活sd卡
//参数
//返回       返回值统一
char Activate_SD()
{
  char ret;
  unsigned char temp, para[4];
  unsigned char i;
  for(i=0;i<4;i++)
  {
      para[i] = 0;
  }
  Assert_SPI_CS(0);
  do
  {
    ret = SendCmd(1, para, R1,&temp);   //发送CMD1
    
    if((ret == NO_ERROR) && (temp == 0x00))  //出错处理
	 {Assert_SPI_CS(1);return NO_ERROR;}
    i++;
  } 
  while(i<200);

	Assert_SPI_CS(1);
	return ERROR;   

}

//函数说明	允许校验与否
//参数
//返回


char Enable_CRC(unsigned char bEnable)
{
  unsigned char ret, temp, para[4];
 // print("in enable crc\r\n");
  if (bEnable == 1)
    para[0] = 1;                     /* 使能crc enable crc */
  else
    para[1] = 0;
    Assert_SPI_CS(0);
  ret = SendCmd(59, para, R1,&temp);   //发送CMD59
  //printhex(temp);
  if(ret == ERROR) {Assert_SPI_CS(1);return ERROR;}
  if(temp != 0x00) {Assert_SPI_CS(1);return ERROR;}
  Assert_SPI_CS(1);
  return NO_ERROR;
}




//函数说明	计算命令的校验码
//参数
//返回

char GenerateCRC(unsigned char cmdNo, unsigned char *para)
{

  unsigned char i, j;
  unsigned char reg = 0;
  unsigned char array[5];
  
  array[0] = cmdNo;
  for (i = 1; i < 5; i++)            //将参数的顺序重新排列 
    array[i] = para[4 - i];
  
  for (i = 0; i < 5; i++)           ///          计算5个字节的CRC7 
  {
      for (j = 0; j < 8; j++)
      {
      reg <<= 1;
      reg ^= ((((array[i] << j) ^ reg) & 0x80) ? 0x9 : 0);
      }
  }  
  return ((reg << 1) + 0x01) ;      //        计算结果的CRC7左移一位,并将最低位置1 
}	

//函数说明   复位sd卡
//参数
//返回     返回值统一
char Reset_SD()
{
  char ret;
  int i;
  unsigned char para[4];
  char respond;
  Assert_SPI_CS(0);
  for(i=0;i<4;i++)
  {
      para[i] = 0;
  }
  for(i=0;i<100;i++)
  {
    ret = SendCmd(0,&para[0],R1,&respond);		//发送CMD0命令
    if((ret == NO_ERROR) && (respond == 0x01)) 
	{Assert_SPI_CS(1);return NO_ERROR;}			 //出错处理
  }
  Assert_SPI_CS(1);
  return ERROR;

}


//初始化SD卡为SPI模式
char InitSDToSpi()
{ 
  char ret;
  unsigned char x; 
  Assert_SPI_CS(1);   //置SD卡的CS线为高
  for(x=0;x<10;x++)	  //写80个正脉冲到SD卡 ,要求发送74个
  WriteToSPI(0xff); 
  Assert_SPI_CS(0);   //CS=0;
  ret = Reset_SD();			//复位SD卡,此时CS = 0,因此进入SPI模式
  if(ret != NO_ERROR) {Assert_SPI_CS(1);return ERROR;}
  else  {Assert_SPI_CS(1);return NO_ERROR;}	
}

//初始化SD卡
char InitialSD()
{
	while(1) 								
	{
		if(InitSDToSpi() == NO_ERROR)	break;	   //初始化为SPI模式
	}
	while(1)
	{
		if(Activate_SD() == NO_ERROR) break;      //激活SD卡
	}
	while(1)
	{
  		if( Enable_CRC(0) == NO_ERROR)	break;    //不使用校验

 	 }
	while(1)
	{
  		if( SetBlockLen(512) == NO_ERROR)	break;  //设置读写块长度为512字节

 	 }
	 return NO_ERROR;
}



⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -