zoukankan      html  css  js  c++  java
  • STM32——SPI驱动SD卡

    文末有工程链接(FATFS文件系统也移植了)
    VCC:5V
    GND:GND
    MISO:PA6
    MOSI:PA7
    SCK:PA5
    CS:PA4

    
    /*外设驱动*/
    /*.c*/
    /*以下是SPI1口初始化模块的初始化代码,访问SD Card这里针是对SPI1的初始化*/
    
    void SPI1_Init(void)
    {
     	GPIO_InitTypeDef GPIO_InitStructure;
      SPI_InitTypeDef  SPI_InitStructure;
    
    	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE );//PORTA与SPI1时钟使能 	
     
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA
    
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    	
    	SD_CS=1;
    	
     	GPIO_SetBits(GPIOA,GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7); 
    
    	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
    	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//设置SPI工作模式:设置为主SPI
    	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//设置SPI的数据大小:SPI发送接收8位帧结构
    	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;		//串行同步时钟的空闲状态为高电平
    	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;	//串行同步时钟的第二个跳变沿(上升或下降)数据被采样
    	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
    	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;		//定义波特率预分频的值:波特率预分频值为256
    	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
    	SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRC值计算的多项式
    	SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
     
    	SPI_Cmd(SPI1, ENABLE); //使能SPI外设
    	
    	SPI1_ReadWriteByte(0xff);//启动传输		 
     
    
    }   
    //SPI 速度设置函数
    //SpeedSet:
    //SPI_BaudRatePrescaler_2   2分频   
    //SPI_BaudRatePrescaler_8   8分频   
    //SPI_BaudRatePrescaler_16  16分频  
    //SPI_BaudRatePrescaler_256 256分频 
      
    void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)
    {
      assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
    	SPI1->CR1&=0XFFC7;
    	SPI1->CR1|=SPI_BaudRatePrescaler;	//设置SPI1速度 
    	SPI_Cmd(SPI1,ENABLE); 
    
    } 
    
    //SPIx 读写一个字节
    //TxData:要写入的字节
    //返回值:读取到的字节
    u8 SPI1_ReadWriteByte(u8 TxData)
    {		
    	u8 retry=0;				 	
    	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
    		{
    		retry++;
    		if(retry>200)return 0;
    		}			  
    	SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个数据
    	retry=0;
    
    	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位
    		{
    		retry++;
    		if(retry>200)return 0;
    		}	  						    
    	return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据					    
    }
    
    /*.h*/
    #define SD_CS  PAout(4)
     				  	    													  
    void SPI1_Init(void);			 //初始化SPI口
    void SPI1_SetSpeed(u8 SpeedSet); //设置SPI速度   
    u8 SPI1_ReadWriteByte(u8 TxData);//SPI总线读写一个字节
    
    /*SD卡驱动*/
    
    /*.c*/
    //SD卡的类型 
    u8  SD_Type=0;
    
    //等待卡准备好
    //返回值:0,准备好了;其他,错误代码
    u8 SD_WaitReady(void)
    {
    	u32 t=0;
    	u8 reg;
    	for(t=0;t<0xffff;t++)
    	{
    		reg=SPI1_ReadWriteByte(0XFF);//获取返回值
    		if(reg==0XFF)
    			break; 	
    	}
    	if(t<0xffffff)
    		return 0;
    	else
    		return 1;
    }
    
    
    
    //取消选择,释放SPI总线
    void SD_DisSelect(void)
    {
    	SD_CS=1;
     	SPI1_ReadWriteByte(0xff);//提供额外的8个时钟
    }
    
    
    //选择sd卡,并且等待卡准备OK
    //返回值:0,成功;1,失败;
    u8 SD_Select(void)
    {
    	SD_CS=0;
    	if(SD_WaitReady()==0)	//等待成功
    		return 0;
    	SD_DisSelect();	//等待失败
    	return 1;
    }
    
    
    //向SD卡发送一个命令
    //输入: u8 cmd   命令 
    //      u32 arg  命令参数
    //      u8 crc   crc校验值	   
    //返回值:SD卡返回的响应															  
    u8 SD_SendCmd(u8 cmd, u32 arg, u8 crc)
    {
      u8 r1=0;	
    	u8 Retry=0; 
    	SD_DisSelect();	//取消上次片选
    
    	if(SD_Select())	//片选失效 
    	{
    		return 0XFF;
    	}
    	//发送
    	//分别写入命令
      SPI1_ReadWriteByte(cmd | 0x40);
      SPI1_ReadWriteByte(arg >> 24);
      SPI1_ReadWriteByte(arg >> 16);
      SPI1_ReadWriteByte(arg >> 8);
      SPI1_ReadWriteByte(arg);	  
      SPI1_ReadWriteByte(crc); 
    	if(cmd==CMD12)SPI1_ReadWriteByte(0xff);
      //等待响应,或超时退出
    	Retry=0X1F;
    	do
    	{
    		r1=SPI1_ReadWriteByte(0xFF);
    	}
    	while((r1&0X80) && Retry--);	 
    	//返回状态值
      return r1;
    }	
    //初始化SD卡
    u8 SD_Init(void)
    {
    	u8 r1=0;      	// 存放SD卡的返回值
      u16 retry;	// 用来进行超时计数
      u8 buf[4];
    	u16 i;
    	
    
    	SPI1_Init();	//初始化SPI1
    
    	SPI1_SetSpeed(SPI_BaudRatePrescaler_256);	//配置为低速度模式
    	for(i=0;i<10;i++)	//发送至少74个脉冲
    	{
    		SPI1_ReadWriteByte(0xff);
    	}
    	retry=20;
    	do
    	{
    		//进入IDLE状态
    		r1=SD_SendCmd(CMD0,0,0x95);
    	}	
    	while((r1!=0X01) && (retry--));
    	//默认无卡
    	SD_Type=0;
    	//识别卡类型
    	if(r1==0X01)
    	{
    		//SD V2.0
    		if(SD_SendCmd(CMD8,0x1AA,0x87)==1)
    		{
    			//Get trailing return value of R7 resp
    			for(i=0;i<4;i++)buf[i]=SPI1_ReadWriteByte(0XFF);	
    			//卡是否支持2.7~3.6V
    			if(buf[2]==0X01&&buf[3]==0XAA)
    			{
    				retry=0XFFFE;
    				do
    				{
    					//发送CMD55
    					SD_SendCmd(CMD55,0,0X01);	
    					//发送CMD41
    					r1=SD_SendCmd(CMD41,0x40000000,0X01);
    				}
    				while(r1&&retry--);
    				//鉴别SD2.0卡版本开始
    				if(retry&&SD_SendCmd(CMD58,0,0X01)==0)
    				{
    					//得到OCR值
    					for(i=0;i<4;i++)buf[i]=SPI1_ReadWriteByte(0XFF);
    					//检查CCS
    					if(buf[0]&0x40)
    					{
    						SD_Type=SD_TYPE_V2HC;   
    					}						
    					else
    					{
    						SD_Type=SD_TYPE_V2; 
    					}						
    				}
    			}
    		}
    	}
    	//SD V1.x/ MMC	V3
    	else
    	{
    		//发送CMD55
    		SD_SendCmd(CMD55,0,0X01);		
    		//发送CMD41
    		r1=SD_SendCmd(CMD41,0,0X01);	
    		if(r1<=1)
    		{		
    			SD_Type=SD_TYPE_V1;
    			retry=0XFFFE;
    			//等待退出IDLE模式
    			do 
    			{
    				//发送CMD55
    				SD_SendCmd(CMD55,0,0X01);	
    				//发送CMD41
    				r1=SD_SendCmd(CMD41,0,0X01);
    			}while(r1&&retry--);
    		}
    		//MMC卡不支持CMD55+CMD41识别
    		else
    		{
    			//MMC V3
    			SD_Type=SD_TYPE_MMC;
    			retry=0XFFFE;
    			//等待退出IDLE模式
    			do 
    			{											    
    				//发送CMD1
    				r1=SD_SendCmd(CMD1,0,0X01);
    			}while(r1&&retry--);  
    		}
    		//错误的卡
    		if(retry==0||SD_SendCmd(CMD16,512,0X01)!=0)
    		{
    			SD_Type=SD_TYPE_ERR;
    		}
    	}
    	//取消片选
    	SD_DisSelect();
    	//配置为高速度模式
    	SPI1_SetSpeed(SPI_BaudRatePrescaler_4);
    	if(SD_Type)
    	{
    		return 0;
    	}
    	else if(r1)
    	{
    		return r1; 
    	}		
    	//其他错误
    	return 0xaa;
    }
    //等待SD卡回应
    //Response:要得到的回应值
    //返回值:0,成功得到了该回应值
    //    其他,得到回应值失败
    u8 SD_GetResponse(u8 Response)
    {
    	//等待次数	 
    	u16 Count=0xFFFF;  						  
    	//等待得到准确的回应  	
    	while ((SPI1_ReadWriteByte(0XFF)!=Response)&&Count)
    	{
    		Count--;  
    	}
    	if (Count==0)
    	{
    		//得到回应失败 
    		return MSD_RESPONSE_FAILURE;  
    	}
    	else
    	{
    		//正确回应
    		return MSD_RESPONSE_NO_ERROR;
    	}
    }
    
    
    
    
    //从sd卡读取一个数据包的内容
    //buf:数据缓存区
    //len:要读取的数据长度.
    //返回值:0,成功;其他,失败;	
    u8 SD_RecvData(u8*buf,u16 len)
    {			  	  
    	//等待SD卡发回数据起始令牌0xFE
    	if(SD_GetResponse(0xFE))
    	{
    		return 1;
    	}
    	//开始接收数据
      while(len--)
      {
        *buf=SPI1_ReadWriteByte(0xFF);
        buf++;
      }
      //下面是2个伪CRC(dummy CRC)
      SPI1_ReadWriteByte(0xFF);
      SPI1_ReadWriteByte(0xFF);		
      //读取成功							  					    
      return 0;
    }
    
    
    
    
    //获取SD卡的CSD信息,包括容量和速度信息
    //输入:u8 *cid_data(存放CID的内存,至少16Byte)	    
    //返回值:0:NO_ERR
    //		 1:错误														   
    u8 SD_GetCSD(u8 *csd_data)
    {
      u8 r1;	 
    	//发CMD9命令,读CSD
      r1=SD_SendCmd(CMD9,0,0x01);
      if(r1==0)
    	{
    		//接收16个字节的数据 
        r1=SD_RecvData(csd_data, 16);
      }
    	//取消片选
    	SD_DisSelect();
    	if(r1)
    	{
    		return 1;
    	}
    	else
    	{
    		return 0;
    	}
    }  
    
    
    
    
    //获取SD卡的总扇区数(扇区数)   
    //返回值:0: 取容量出错 
    //       其他:SD卡的容量(扇区数/512字节)
    //每扇区的字节数必为512,因为如果不是512,则初始化不能通过.														  
    u32 SD_GetSectorCount(void)
    {
        u8 csd[16];
        u32 Capacity;  
        u8 n;
    		u16 csize;  					    
    		//取CSD信息,如果期间出错,返回0
        if(SD_GetCSD(csd)!=0)
    		{
    			return 0;	    
    		}
        //如果为SDHC卡,按照下面方式计算
    		//V2.00的卡
        if((csd[0]&0xC0)==0x40)	 
        {	
    			csize = csd[9] + ((u16)csd[8] << 8) + 1;
    			//得到扇区数	
    			Capacity = (u32)csize << 10; 		   
        }
    		//V1.XX的卡
    		else
        {	
    			n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
    			csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;
    			//得到扇区数 
    			Capacity= (u32)csize << (n - 9);  
        }
        return Capacity;
    }
    
    
    
    
    
    u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt)
    {
    	u8 r1;
    	//转换为字节地址
    	if(SD_Type!=SD_TYPE_V2HC)
    	{
    		sector <<= 9;
    	}
    	if(cnt==1)
    	{
    		//读命令
    		r1=SD_SendCmd(CMD17,sector,0X01);
    		//指令发送成功
    		if(r1==0)
    		{
    			//接收512个字节	 
    			r1=SD_RecvData(buf,512);  
    		}
    	}
    	else
    	{
    		//连续读命令
    		r1=SD_SendCmd(CMD18,sector,0X01);
    		do
    		{
    			//接收512个字节	 
    			r1=SD_RecvData(buf,512);
    			buf+=512;  
    		}
    		while(--cnt && r1==0); 	
    		//发送停止命令
    		SD_SendCmd(CMD12,0,0X01);	
    	}   
    	//取消片选
    	SD_DisSelect();
    	return r1;
    }
    
    
    
    
    //读取SD卡的指定扇区的内容,并通过串口1输出
    //sec:扇区物理地址编号
    void SD_Read_Sectorx(u32 sec)
    {
    	//存储扇区数据
    	u8 buf[512];
    	u16 i;
    	
    	//读取0扇区的内容
    	if(SD_ReadDisk(buf,sec,1)==0)	
    	{	
    		LCD_ShowString(60,190,200,16,16,"USART1 Sending Data...");
    		printf("SECTOR 0 DATA:
    ");
    		//打印sec扇区数据  
    		for(i=0;i<512;i++)printf("%x ",buf[i]);  	   
    		printf("
    DATA ENDED
    ");
    		LCD_ShowString(60,190,200,16,16,"USART1 Send Data Over!");
    	}
    }
    
    
    
    
    //获取SD卡的CID信息,包括制造商信息
    //输入: u8 *cid_data(存放CID的内存,至少16Byte)	  
    //返回值:0:NO_ERR
    //		 1:错误														   
    u8 SD_GetCID(u8 *cid_data)
    {
      u8 r1;	   
      //发CMD10命令,读CID
      r1=SD_SendCmd(CMD10,0,0x01);
      if(r1==0x00)
    	{
    		//接收16个字节的数据	 
    		r1=SD_RecvData(cid_data,16);
      }
    	//取消片选
    	SD_DisSelect();
    	if(r1)
    		return 1;
    	else
    		return 0;
    }
    
    
    
    
    //向sd卡写入一个数据包的内容 512字节
    //buf:数据缓存区
    //cmd:指令
    //返回值:0,成功;其他,失败;	
    u8 SD_SendBlock(u8*buf,u8 cmd)
    {	
    	u16 t;		  	  
    	//等待准备失效
    	if(SD_WaitReady())
    	{
    		return 1;
    	}
    	SPI1_ReadWriteByte(cmd);
    	//不是结束指令
    	if(cmd!=0XFD)
    	{
    		//提高速度,减少函数传参时间
    		for(t=0;t<512;t++)
    		{
    			SPI1_ReadWriteByte(buf[t]);
    		}
    		//忽略crc
    	  SPI1_ReadWriteByte(0xFF);
    	  SPI1_ReadWriteByte(0xFF);
    		//接收响应
    		t=SPI1_ReadWriteByte(0xFF);
    		if((t&0x1F)!=0x05)
    		{
    			//响应错误		
    			return 2;		
    		}			
    	}						 		
    	//写入成功							  					    
      return 0;
    }
    
    
    
    
    
    //写SD卡
    //buf:数据缓存区
    //sector:起始扇区
    //cnt:扇区数
    //返回值:0,ok;其他,失败.
    u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt)
    {
    	u8 r1;
    	//转换为字节地址
    	if(SD_Type!=SD_TYPE_V2HC)
    	{
    		sector *= 512;
    	}
    	if(cnt==1)
    	{
    		//读命令
    		r1=SD_SendCmd(CMD24,sector,0X01);
    		//指令发送成功
    		if(r1==0)
    		{
    			//写512个字节	   
    			r1=SD_SendBlock(buf,0xFE);
    		}
    	}
    	else
    	{
    		if(SD_Type!=SD_TYPE_MMC)
    		{
    			SD_SendCmd(CMD55,0,0X01);	
    			//发送指令	
    			SD_SendCmd(CMD23,cnt,0X01);
    		}
    		//连续读命令
     		r1=SD_SendCmd(CMD25,sector,0X01);
    		if(r1==0)
    		{
    			do
    			{
    				//接收512个字节	 
    				r1=SD_SendBlock(buf,0xFC);
    				buf+=512;  
    			}
    			while(--cnt && r1==0);
    			//接收512个字节 
    			r1=SD_SendBlock(0,0xFD);
    		}
    	}   
    	//取消片选
    	SD_DisSelect();
    	return r1;
    }	
    
    
    /*.h*/
    // SD卡类型定义  
    #define SD_TYPE_ERR     0X00
    #define SD_TYPE_MMC     0X01
    #define SD_TYPE_V1      0X02
    #define SD_TYPE_V2      0X04
    #define SD_TYPE_V2HC    0X06
    
    // SD卡指令表  	   
    #define CMD0    0       //卡复位
    #define CMD1    1
    #define CMD8    8       //命令8 ,SEND_IF_COND
    #define CMD9    9       //命令9 ,读CSD数据
    #define CMD10   10      //命令10,读CID数据
    #define CMD12   12      //命令12,停止数据传输
    #define CMD16   16      //命令16,设置SectorSize 应返回0x00
    #define CMD17   17      //命令17,读sector
    #define CMD18   18      //命令18,读Multi sector
    #define CMD23   23      //命令23,设置多sector写入前预先擦除N个block
    #define CMD24   24      //命令24,写sector
    #define CMD25   25      //命令25,写Multi sector
    #define CMD41   41      //命令41,应返回0x00
    #define CMD55   55      //命令55,应返回0x01
    #define CMD58   58      //命令58,读OCR信息
    #define CMD59   59      //命令59,使能/禁止CRC,应返回0x00
    
    //数据写入回应字意义
    #define MSD_DATA_OK                0x05
    #define MSD_DATA_CRC_ERROR         0x0B
    #define MSD_DATA_WRITE_ERROR       0x0D
    #define MSD_DATA_OTHER_ERROR       0xFF
    
    //SD卡回应标记字
    #define MSD_RESPONSE_NO_ERROR      0x00
    #define MSD_IN_IDLE_STATE          0x01
    #define MSD_ERASE_RESET            0x02
    #define MSD_ILLEGAL_COMMAND        0x04
    #define MSD_COM_CRC_ERROR          0x08
    #define MSD_ERASE_SEQUENCE_ERROR   0x10
    #define MSD_ADDRESS_ERROR          0x20
    #define MSD_PARAMETER_ERROR        0x40
    #define MSD_RESPONSE_FAILURE       0xFF
    
    void SD_Read_Sectorx(u32 sec);
    u8 SD_Init(void);
    u8 SD_WaitReady(void);
    u8 SD_GetResponse(u8 Response);
    u32 SD_GetSectorCount(void);
    u8 SD_GetCID(u8 *cid_data);
    u8 SD_GetCSD(u8 *csd_data);
    u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt);
    u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt);
    
    
    

    Projrct源码

  • 相关阅读:
    C 语言学习 --3
    C 语言学习 --2
    C 语言学习 -1
    C 语言学习 说明
    神经网络5:循环神经网络1
    神经网络4:卷积神经网络学习 2
    神经网络3:神经网络学习 1
    神经网络2:卷积神经网络学习 1
    神经网络 1 : 卷积神经网络CNN 、 循环神经网络 RNN
    轮播swiper配置选项
  • 原文地址:https://www.cnblogs.com/hhsxy/p/14018442.html
Copyright © 2011-2022 走看看