基于STM32的录音机设计(STM32F103+VS1053B)

一、环境介绍

MCU:  STM32F103C8T6

开发软件:  Keil5

音频模块:  VS1053B

录音文件存储设备:  SD卡,采用SPI协议驱动

显示屏:  SPI接口的0.96寸OLED

代码风格:  采用寄存器编程,代码简洁、执行效率高、注释到位、移植方便。

项目完整源代码下载地址(下载即可编译运行测试):  https://download.csdn.net/download/xiaolong1126626497/19520781

二、功能介绍

这是基于STM32F103C8T6设计的录音机功能,支持的功能如下:

1.  按下按键1启动自动录音,默认为5秒录音一次,录音完毕自动保存在SD指定目录下。文件名称采用当前时间命名;音频文件格式采用WAV格式存储。

2.  按下按键2启动手动录音,按键按下之后开始录音,再次按下结束录音,录音完毕之后,文件也是一样的保存在SD卡里。

3. SD卡文件系统采用FAT32格式,STM32移植了FATFS开源文件系统对SD卡进行读写操作。

4. OLED显示屏用于显示当前录音机的状态:  空闲、录音、回放等状态。

5. 按下按键3,启动自动回放功能。自动扫描目录,按顺序播放录音文件。

技术介绍:

1.  SD卡采用SPI协议驱动,因为对速度没有很高要求,SPI协议已经完全满足;如果要更高的速度,可以采用SDIO协议。

2.  音频模块采用VS1053B,这个芯片支持IIS和SPI协议。我这里采用的是SPI协议驱动,SPI比较简单,代码也好移植,可以很方便的移植到其他单片机上运行。VS1053功能比较强大,支持录音、解码播放。

3.  文件系统采用的是FATFS文件系统,这个文件系统功能比较完善,使用免费,支持FAT16、FAT32等格式。底层也比较好适配移植。当前,除了FATFS以外,还有很多其他的嵌入式文件系统可以选择,移植都大同小异。

4. OLED显示屏是0.96寸的。采用SPI协议驱动,主要是显示一些状态,SPI刷屏比较快,这款OLED也支持IIC接口。

5. VS1053模块上没有喇叭设备,可以适应耳机或者音箱听回放的录音。

 

硬件与STM32的接线说明:

OLED显示屏:
D0----SCK-----PB14
D1----MOSI----PB13
RES—复位(低电平有效)—PB12
DC---数据和命令控制管脚—PB1
CS---片选引脚-----PA7

VS1053:
#define VS1053_DREQ     PAin(11)      //DREQ  数据请求
#define VS1053_RESET    PAout(12)   //RST   硬件复位--低电平有效
#define VS1053_XCS      PAout(13)      //XCS   片选--低电平有效
#define VS1053_XDCS     PAout(14)      //XDCS  用于数据片选、字节同步
#define VS1053_SCLK     PAout(15)
#define VS1053_OUTPUT   PBout(3)
#define VS1053_INPUT    PBin(4)

SD卡接口:
5V----5V
GND---GND
SPI1_MOSI---PA7
SPI1_MISO---PA6
SPI1_CS---PA4
SPI1_SCK--PA5
 

三、使用的相关硬件

STM32F103C8T6系统板: 

OLED显示屏: 

VS1053: 

SD卡卡槽: 

 

 

四、操作说明

开发板有一个复位键和一个K0按键。

程序下载:

 

程序支持三种模式:

因为开发板只有一个K0按键,所以三种模式都是通过一个按键进行切换的。

一个按键是通过按下的计数方式进行切换的,切换的顺序是自动录音模式、手动录音模式、回放模式。

(1)自动录音模式:按下一次按键后,进入自动录音模式,自动录音模式下,录音5秒自动退出,退出后自动启动播放状态,就是播放刚才5秒录制的音频,播放过程中按下按键可以退出播放状态。

(2)手动录音模式:第二次按下K0按键后,进入手动录音模式,手动录音模式下,可以长时间录音,如果要结束录音,按下K0按键即可结束;结束后自动启动播放状态,就是播放刚才录制的音频,播放过程中按下按键可以退出播放状态。

(3)回放模式:第三次按下K0按键后,进入回放模式,自动扫描wav目录,进行顺序播放音频文件。

    播放过程中可以按下K0按键退出回放模式。
每次录音后的文件是存放在SD卡根目录下的wav目录下。
每个状态都会在OLED显示屏上显示
也会同时通过串口打印到串口调试助手终端。

五、SD卡上存放的文件

SD卡上有两个目录:font目录和wav目录。
font目录下存放16x16字库文件。
wav目录下存放录音的音频文件。
 

六、部分源码

6.1 VS1053.c   这是VS1053的驱动代码

#include "vs1053b.h"	

/*
函数功能:移植接口--SPI时序读写一个字节
函数参数:data:要写入的数据
返 回 值:读到的数据
*/
u8 VS1053_SPI_ReadWriteByte(u8 tx_data)
{			  	 
	u8 rx_data=0;				 
  u8 i;
  for(i=0;i<8;i++)
	{
		VS1053_SCLK=0;  
		if(tx_data&0x80){VS1053_OUTPUT=1;}
		else {VS1053_OUTPUT=0;}
		tx_data<<=1;	
		VS1053_SCLK=1;
		rx_data<<=1;
		if(VS1053_INPUT)rx_data|=0x01;
	}
	return rx_data; 
}


/*
函数功能:初始化VS1053的IO口	 
*/
void VS1053_Init(void)
{
	 RCC->APB2ENR|=1<<0;
	 AFIO->MAPR&=~(0x7<<24);  //释放PA13/14/15
	 AFIO->MAPR|=0x4<<24;
	
	 RCC->APB2ENR|=1<<2;
	 RCC->APB2ENR|=1<<3;
	 
	 GPIOA->CRH&=0x00000FFF;
	 GPIOA->CRH|=0x33338000;
	  
	 GPIOB->CRL&=0xFFF00FFF;
	 GPIOB->CRL|=0x00083000;
	
	 VS1053_SCLK=1;
	 VS1053_XCS=1;
     VS1053_RESET=1;

}	


/*
函数功能:软复位VS10XX
*/
void VS1053_SoftReset(void)
{	 
	u8 retry=0;  				   
	while(VS1053_DREQ==0); 							//等待软件复位结束	   
	VS1053_SPI_ReadWriteByte(0Xff);			//启动传输
	retry=0;
	while(VS1053_ReadReg(SPI_MODE)!=0x0800)	// 软件复位,新模式  
	{
		VS1053_WriteCmd(SPI_MODE,0x0804);		// 软件复位,新模式	    
		DelayMs(2);//等待至少1.35ms 
		if(retry++>100)break; 	  
	}	
	while(VS1053_DREQ==0);//等待软件复位结束	 
	retry=0;
	while(VS1053_ReadReg(SPI_CLOCKF)!=0X9800)//设置VS10XX的时钟,3倍频 ,1.5xADD 
	{
		VS1053_WriteCmd(SPI_CLOCKF,0X9800);	//设置VS10XX的时钟,3倍频 ,1.5xADD
		if(retry++>100)break; 	    
	}	 
	DelayMs(20);
} 


/*
函数 功 能:硬复位MP3
函数返回值:1:复位失败;0:复位成功	
*/
u8 VS1053_Reset(void)
{
	u8 retry=0;
	VS1053_RESET=0;
	DelayMs(20);
	VS1053_XDCS=1;//取消数据传输
	VS1053_XCS=1; //取消数据传输
	VS1053_RESET=1;	   
	while(VS1053_DREQ==0&&retry<200)//等待DREQ为高
	{
		retry++;
		DelayUs(50);
	}
	DelayMs(20);	
	if(retry>=200)return 1;
	else return 0;	    		 
}


/*
函数功能:向VS10XX写命令
函数参数:
				address:命令地址
				data   :命令数据
*/
void VS1053_WriteCmd(u8 address,u16 data)
{  
	while(VS1053_DREQ==0);	//等待空闲		   	   
	VS1053_XDCS=1; 	 
	VS1053_XCS=0; 	 
	VS1053_SPI_ReadWriteByte(VS_WRITE_COMMAND);//发送VS10XX的写命令
	VS1053_SPI_ReadWriteByte(address); 	//地址
	VS1053_SPI_ReadWriteByte(data>>8); 	//发送高八位
	VS1053_SPI_ReadWriteByte(data);	 		//第八位
	VS1053_XCS=1;            
} 


/*
函数参数:向VS1053写数据
函数参数:data:要写入的数据
*/
void VS1053_WriteData(u8 data)
{
	VS1053_XDCS=0;   
	VS1053_SPI_ReadWriteByte(data);
	VS1053_XDCS=1;      
}


/*
函数功能:读VS1053的寄存器 
函数参数:address:寄存器地址
返回值:读到的值
*/
u16 VS1053_ReadReg(u8 address)
{ 
	u16 temp=0;   	
  while(VS1053_DREQ==0);//非等待空闲状态   	
	VS1053_XDCS=1;       
	VS1053_XCS=0;        
	VS1053_SPI_ReadWriteByte(VS_READ_COMMAND);//发送VS10XX的读命令
	VS1053_SPI_ReadWriteByte(address);       	//地址
	temp=VS1053_SPI_ReadWriteByte(0xff); 		  //读取高字节
	temp=temp<<8;
	temp+=VS1053_SPI_ReadWriteByte(0xff); 		//读取低字节
	VS1053_XCS=1;      
   return temp; 
}  


/*
函数功能:读取VS1053的RAM
函数参数:addr:RAM地址
返 回 值:读到的值
*/
u16 VS1053_ReadRAM(u16 addr) 
{ 
	u16 res;			   	  
 	VS1053_WriteCmd(SPI_WRAMADDR, addr); 
	res=VS1053_ReadReg(SPI_WRAM);  
 	return res;
} 


/*
函数功能:写VS1053的RAM
函数参数:
				addr:RAM地址
				val:要写入的值 
*/
void VS1053_WriteRAM(u16 addr,u16 val) 
{  		   	  
 	VS1053_WriteCmd(SPI_WRAMADDR,addr);	//写RAM地址 
	while(VS1053_DREQ==0); 							//等待空闲	   
	VS1053_WriteCmd(SPI_WRAM,val); 			//写RAM值 
} 


/*
函数参数:发送一次音频数据,固定为32字节
返 回 值:0,发送成功
				  1,本次数据未成功发送   
*/ 
u8 VS1053_SendMusicData(u8* buf)
{
	u8 n;
	if(VS1053_DREQ!=0)  //送数据给VS10XX
	{			   	 
		VS1053_XDCS=0;  
    for(n=0;n<32;n++)
		{
			VS1053_SPI_ReadWriteByte(buf[n]);	 			
		}
		VS1053_XDCS=1;     				   
	}else return 1;
	return 0;//成功发送了
}


/*
函数参数:发送一次音频数据,固定为32字节
返 回 值:0,发送成功
				  1,本次数据未成功发送   
*/ 
void VS1053_SendMusicByte(u8 data)
{
	u8 n;
	while(VS1053_DREQ==0){}	   	 
	VS1053_XDCS=0;  
	VS1053_SPI_ReadWriteByte(data);	 			
	VS1053_XDCS=1;     				   
}


/*
函数功能:设定VS1053播放的音量
函数参数:volx:音量大小(0~254)
*/
void VS1053_SetVol(u8 volx)
{
    u16 volt=0; 			      //暂存音量值
    volt=254-volx;			    //取反一下,得到最大值,表示最大的表示 
	  volt<<=8;
    volt+=254-volx;					//得到音量设置后大小
    VS1053_WriteCmd(SPI_VOL,volt);//设音量 
}


/*--------------------------------------录音功能-----------------------------------------------------*/


/*
函数功能:vs10xx装载patch
函数参数:
				patch:patch首地址
				len  :patch长度
*/
void VS1053_LoadPatch(u16 *patch,u16 len) 
{
	u16 i; 
	u16 addr, n, val; 	  			   
	for(i=0;i<len;) 
	{ 
		addr = patch[i++]; 
		n    = patch[i++]; 
		if(n & 0x8000U) //RLE run, replicate n samples 
		{ 
			n  &= 0x7FFF; 
			val = patch[i++]; 
			while(n--)VS1053_WriteCmd(addr, val);  
		}
		else //copy run, copy n sample 
		{ 
			while(n--) 
			{ 
				val = patch[i++]; 
				VS1053_WriteCmd(addr, val); 
			} 
		} 
	} 	
}


/*
函数参数:初始化WAV头
*/
void VS1053_RecoderWavInit(__WaveHeader* wavhead) //初始化WAV头			   
{
	wavhead->riff.ChunkID=0X46464952;	//"RIFF"
	wavhead->riff.ChunkSize=0;				//还未确定,最后需要计算
	wavhead->riff.Format=0X45564157; 	//"WAVE"
	wavhead->fmt.ChunkID=0X20746D66; 	//"fmt "
	wavhead->fmt.ChunkSize=16; 				//大小为16个字节
	wavhead->fmt.AudioFormat=0X01; 		//0X01,表示PCM;0X01,表示IMA ADPCM
 	wavhead->fmt.NumOfChannels=1;			//单声道
 	wavhead->fmt.SampleRate=8000;			//8Khz采样率 采样速率
 	wavhead->fmt.ByteRate=wavhead->fmt.SampleRate*2;//16位,即2个字节
 	wavhead->fmt.BlockAlign=2;				//块大小,2个字节为一个块
 	wavhead->fmt.BitsPerSample=16;		//16位PCM
  wavhead->data.ChunkID=0X61746164;	//"data"
 	wavhead->data.ChunkSize=0;				//数据大小,还需要计算  
}

//VS1053的WAV录音有bug,这个plugin可以修正这个问题 							    
const u16 VS1053_WavPlugin[40]=/* Compressed plugin */ 
{ 
		0x0007, 0x0001, 0x8010, 0x0006, 0x001c, 0x3e12, 0xb817, 0x3e14, /* 0 */ 
		0xf812, 0x3e01, 0xb811, 0x0007, 0x9717, 0x0020, 0xffd2, 0x0030, /* 8 */ 
		0x11d1, 0x3111, 0x8024, 0x3704, 0xc024, 0x3b81, 0x8024, 0x3101, /* 10 */ 
		0x8024, 0x3b81, 0x8024, 0x3f04, 0xc024, 0x2808, 0x4800, 0x36f1, /* 18 */ 
		0x9811, 0x0007, 0x0001, 0x8028, 0x0006, 0x0002, 0x2a00, 0x040e,  
}; 


/*
函数功能:激活PCM 录音模式
函数参数:
				agc:0,自动增益
        1024相当于1倍
        512相当于0.5倍
        最大值65535=64倍		  
*/
void VS1053_RecoderInit(u16 agc)
{
	//如果是IMA ADPCM,采样率计算公式如下:
 	//采样率=CLKI/256*d;	
	//假设d=0,并2倍频,外部晶振为12.288M.那么Fc=(2*12288000)/256*6=16Khz
	//如果是线性PCM,采样率直接就写采样值 
  VS1053_WriteCmd(SPI_BASS,0x0000);    
 	VS1053_WriteCmd(SPI_AICTRL0,8000);	//设置采样率,设置为8Khz
 	VS1053_WriteCmd(SPI_AICTRL1,agc);		//设置增益,0,自动增益.1024相当于1倍,512相当于0.5倍,最大值65535=64倍	
 	VS1053_WriteCmd(SPI_AICTRL2,0);		  //设置增益最大值,0,代表最大值65536=64X
 	VS1053_WriteCmd(SPI_AICTRL3,6);		  //左通道(MIC单声道输入)
	VS1053_WriteCmd(SPI_CLOCKF,0X2000);	//设置VS10XX的时钟,MULT:2倍频;ADD:不允许;CLK:12.288Mhz
	VS1053_WriteCmd(SPI_MODE,0x1804);		//MIC,录音激活    
 	DelayMs(5);					//等待至少1.35ms 
 	VS1053_LoadPatch((u16*)VS1053_WavPlugin,40);//VS1053的WAV录音需要patch
}

 

6.2   SD.c  这是SD卡的驱动代码

#include "sdcard.h"			   
static u8  SD_Type=0;  //存放SD卡的类型

/*
函数功能:SD卡底层接口,通过SPI时序向SD卡读写一个字节
函数参数:data是要写入的数据
返 回 值:读到的数据
*/
u8 SDCardReadWriteOneByte(u8 DataTx)
{		 
    u16 cnt=0;				 
    while((SPI1->SR&1<<1)==0)		 //等待发送区空--等待发送缓冲为空	
    {
      cnt++;
      if(cnt>=65530)return 0; 	  //超时退出  u16=2个字节
    }	
    SPI1->DR=DataTx;	 	  		      //发送一个byte 
    cnt=0;
    while((SPI1->SR&1<<0)==0) 		//等待接收完一个byte   
    {
      cnt++;
      if(cnt>=65530)return 0;	   //超时退出
    }	  						    
    return SPI1->DR;          		//返回收到的数据
}


/*
函数功能:底层SD卡接口初始化
SPI1接口---SD卡接线原理
5V----5V
GND---GND
SPI1_MOSI---PA7
SPI1_MISO---PA6
SPI1_CS---PA4
SPI1_SCK--PA5
*/
void SDCardSpiInit(void)
{
  /*1. 开启时钟*/
 	RCC->APB2ENR|=1<<2;		    //使能PORTA时钟
  
  /*2. 配置GPIO口模式*/
  GPIOA->CRL&=0x0000FFFF;
  GPIOA->CRL|=0xB8B30000;
  
  /*3. 上拉*/
  GPIOA->ODR|=1<<4;
	
		/*SPI1基本配置*/
	RCC->APB2ENR|=1<<12;    //开启SPI1时钟
	RCC->APB2RSTR|=1<<12;
	RCC->APB2RSTR&=~(1<<12);
	
	SPI1->CR1=0X0; 		//清空寄存器
	SPI1->CR1|=0<<15; //选择“双线双向”模式
	SPI1->CR1|=0<<11; //使用8位数据帧格式进行发送/接收;
	SPI1->CR1|=0<<10; //全双工(发送和接收);
	SPI1->CR1|=1<<9;  //启用软件从设备管理
	SPI1->CR1|=1<<8;  //NSS
	SPI1->CR1|=0<<7;  //帧格式,先发送高位
	SPI1->CR1|=0x0<<3;//当总线频率为36MHZ时,SPI速度为18MHZ,高速。
	SPI1->CR1|=1<<2;  //配置为主设备
	SPI1->CR1|=1<<1;  //空闲状态时, SCK保持高电平。
	SPI1->CR1|=1<<0;  //数据采样从第二个时钟边沿开始。
	SPI1->CR1|=1<<6;  //开启SPI设备。
}

/*
函数功能:取消选择,释放SPI总线
*/
void SDCardCancelCS(void)
{
	SDCARD_CS=1;
 	SDCardReadWriteOneByte(0xff);//提供额外的8个时钟
}


/*
函数 功 能:选择sd卡,并且等待卡准备OK
函数返回值:0,成功;1,失败;
*/
u8 SDCardSelectCS(void)
{
	SDCARD_CS=0;
	if(SDCardWaitBusy()==0)return 0;//等待成功
	SDCardCancelCS();
	return 1;//等待失败
}


/*
函数 功 能:等待卡准备好
函数返回值:0,准备好了;其他,错误代码
*/
u8 SDCardWaitBusy(void)
{
	u32 t=0;
	do
	{
		if(SDCardReadWriteOneByte(0XFF)==0XFF)return 0;//OK
		t++;		  
	}while(t<0xFFFFFF);//等待 
	return 1;
}


/*
函数功能:等待SD卡回应
函数参数:
					Response:要得到的回应值
返 回 值:
					0,成功得到了该回应值
					其他,得到回应值失败
*/
u8 SDCardGetAck(u8 Response)
{
	u16 Count=0xFFFF;//等待次数	   						  
	while((SDCardReadWriteOneByte(0XFF)!=Response)&&Count)Count--;//等待得到准确的回应  	  
	if(Count==0)return SDCard_RESPONSE_FAILURE;//得到回应失败   
	else return SDCard_RESPONSE_NO_ERROR;//正确回应
}


/*
函数功能:从sd卡读取一个数据包的内容
函数参数:
				buf:数据缓存区
				len:要读取的数据长度.
返回值:
			0,成功;其他,失败;	
*/
u8 SDCardRecvData(u8*buf,u16 len)
{			  	  
	if(SDCardGetAck(0xFE))return 1;//等待SD卡发回数据起始令牌0xFE
    while(len--)//开始接收数据
    {
        *buf=SDCardReadWriteOneByte(0xFF);
        buf++;
    }
    //下面是2个伪CRC(dummy CRC)
    SDCardReadWriteOneByte(0xFF);
    SDCardReadWriteOneByte(0xFF);									  					    
    return 0;//读取成功
}


/*
函数功能:向sd卡写入一个数据包的内容 512字节
函数参数:
					buf 数据缓存区
					cmd 指令
返 回 值:0表示成功;其他值表示失败;
*/
u8 SDCardSendData(u8*buf,u8 cmd)
{	
	u16 t;		  	  
	if(SDCardWaitBusy())return 1;  //等待准备失效
	SDCardReadWriteOneByte(cmd);
	if(cmd!=0XFD)//不是结束指令
	{
		for(t=0;t<512;t++)SDCardReadWriteOneByte(buf[t]);//提高速度,减少函数传参时间
	    SDCardReadWriteOneByte(0xFF); //忽略crc
	    SDCardReadWriteOneByte(0xFF);
		  t=SDCardReadWriteOneByte(0xFF); //接收响应
		if((t&0x1F)!=0x05)return 2;   //响应错误									  					    
	}						 									  					    
    return 0;//写入成功
}



/*
函数功能:向SD卡发送一个命令
函数参数:
				u8 cmd   命令 
				u32 arg  命令参数
				u8 crc   crc校验值	
返回值:SD卡返回的响应
*/												  
u8 SendSDCardCmd(u8 cmd, u32 arg, u8 crc)
{
	u8 r1;	
	u8 Retry=0; 
		
	SDCardCancelCS();               //取消上次片选
	if(SDCardSelectCS())return 0XFF;//片选失效 
	//发送数据
	SDCardReadWriteOneByte(cmd | 0x40);//分别写入命令
	SDCardReadWriteOneByte(arg >> 24);
	SDCardReadWriteOneByte(arg >> 16);
	SDCardReadWriteOneByte(arg >> 8);
	SDCardReadWriteOneByte(arg);	  
	SDCardReadWriteOneByte(crc); 
	if(cmd==SDCard_CMD12)SDCardReadWriteOneByte(0xff);//Skip a stuff byte when stop reading
	Retry=0X1F;
	do
	{
		r1=SDCardReadWriteOneByte(0xFF);
	}while((r1&0X80) && Retry--);	  //等待响应,或超时退出
   return r1;	//返回状态值
}	



/*
函数功能:获取SD卡的CID信息,包括制造商信息
函数参数:u8 *cid_data(存放CID的内存,至少16Byte)	  
返 回 值:
					0:成功,1:错误				
*/
u8 GetSDCardCISDCardOutnfo(u8 *cid_data)
{
    u8 r1;	   
    //发SDCard_CMD10命令,读CID
    r1=SendSDCardCmd(SDCard_CMD10,0,0x01);
    if(r1==0x00)
	  {
			r1=SDCardRecvData(cid_data,16);//接收16个字节的数据	 
    }
	SDCardCancelCS();//取消片选
	if(r1)return 1;
	else return 0;
}	


/*
函数说明:
					获取SD卡的CSD信息,包括容量和速度信息
函数参数:
					u8 *cid_data(存放CID的内存,至少16Byte)	    
返 回 值:
					0:成功,1:错误	
*/
u8 GetSDCardCSSDCardOutnfo(u8 *csd_data)
{
	u8 r1;	 
	r1=SendSDCardCmd(SDCard_CMD9,0,0x01);    //发SDCard_CMD9命令,读CSD
	if(r1==0)
	{
		r1=SDCardRecvData(csd_data, 16);//接收16个字节的数据 
	}
	SDCardCancelCS();//取消片选
	if(r1)return 1;
	else return 0;
}  


/*
函数功能:获取SD卡的总扇区数(扇区数)   
返 回 值:
				0表示容量检测出错,其他值表示SD卡的容量(扇区数/512字节)
说   明:
				每扇区的字节数必为512字节,如果不是512字节,则初始化不能通过.	
*/
u32 GetSDCardSectorCount(void)
{
    u8 csd[16];
    u32 Capacity;  
	  u16 csize;  					    
    if(GetSDCardCSSDCardOutnfo(csd)!=0) return 0;	//取CSD信息,如果期间出错,返回0
    if((csd[0]&0xC0)==0x40)  //SDHC卡,按照下面方式计算
    {	
			csize = csd[9] + ((u16)csd[8] << 8) + 1;
			Capacity = (u32)csize << 10;//得到扇区数	 		   
    }
    return Capacity;
}



/*
函数功能: 初始化SD卡
返 回 值: 非0表示初始化失败!
*/
u8 SDCardDeviceInit(void)
{
  u8 r1;      // 存放SD卡的返回值
  u16 retry;  // 用来进行超时计数
  u8 buf[4];  
	u16 i;
	SDCardSpiInit();		//初始化底层IO口
	
 	for(i=0;i<10;i++)SDCardReadWriteOneByte(0XFF); //发送最少74个脉冲
	retry=20;
	do
	{
		r1=SendSDCardCmd(SDCard_CMD0,0,0x95);//进入IDLE状态 闲置
	}while((r1!=0X01) && retry--);
 	SD_Type=0;   //默认无卡
	if(r1==0X01)
	{
		if(SendSDCardCmd(SDCard_CMD8,0x1AA,0x87)==1)  //SD V2.0
		{
			for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF);
			if(buf[2]==0X01&&buf[3]==0XAA)    //卡是否支持2.7~3.6V
			{
				retry=0XFFFE;
				do
				{
					SendSDCardCmd(SDCard_CMD55,0,0X01);	    //发送SDCard_CMD55
					r1=SendSDCardCmd(SDCard_CMD41,0x40000000,0X01);//发送SDCard_CMD41
				}while(r1&&retry--);
				if(retry&&SendSDCardCmd(SDCard_CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始
				{
					for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF);//得到OCR值
					if(buf[0]&0x40)SD_Type=SDCard_TYPE_V2HC;    //检查CCS
					else SD_Type=SDCard_TYPE_V2;   
				}
			}
		}
	}
	SDCardCancelCS();       //取消片选
	if(SD_Type)return 0;  //初始化成功返回0
	else if(r1)return r1; //返回值错误值	   
	return 0xaa;          //其他错误
}


/*
函数功能:读SD卡
函数参数:
				buf:数据缓存区
				sector:扇区
				cnt:扇区数
返回值:
				0,ok;其他,失败.
说  明:
				SD卡一个扇区大小512字节
*/
u8 SDCardReadData(u8*buf,u32 sector,u32 cnt)
{
	u8 r1;
	if(SD_Type!=SDCard_TYPE_V2HC)sector<<=9;//转换为字节地址
	if(cnt==1)
	{
		r1=SendSDCardCmd(SDCard_CMD17,sector,0X01);//读命令
		if(r1==0)												  //指令发送成功
		{
			r1=SDCardRecvData(buf,512);			//接收512个字节	   
		}
	}else
	{
		r1=SendSDCardCmd(SDCard_CMD18,sector,0X01);//连续读命令
		do
		{
			r1=SDCardRecvData(buf,512);//接收512个字节	 
			buf+=512;  
		}while(--cnt && r1==0); 	
		SendSDCardCmd(SDCard_CMD12,0,0X01);	//发送停止命令
	}   
	SDCardCancelCS();//取消片选
	return r1;//
}

/*
函数功能:向SD卡写数据
函数参数:
				buf:数据缓存区
				sector:起始扇区
				cnt:扇区数
返回值:
				0,ok;其他,失败.
说  明:
				SD卡一个扇区大小512字节
*/
u8 SDCardWriteData(u8*buf,u32 sector,u32 cnt)
{
	u8 r1;
	if(SD_Type!=SDCard_TYPE_V2HC)sector *= 512;//转换为字节地址
	if(cnt==1)
	{
		r1=SendSDCardCmd(SDCard_CMD24,sector,0X01);//读命令
		if(r1==0)//指令发送成功
		{
			r1=SDCardSendData(buf,0xFE);//写512个字节	   
		}
	}
	else
	{
		if(SD_Type!=SDCard_TYPE_MMC)
		{
			SendSDCardCmd(SDCard_CMD55,0,0X01);	
			SendSDCardCmd(SDCard_CMD23,cnt,0X01);//发送指令	
		}
 		r1=SendSDCardCmd(SDCard_CMD25,sector,0X01);//连续读命令
		if(r1==0)
		{
			do
			{
				r1=SDCardSendData(buf,0xFC);//接收512个字节	 
				buf+=512;  
			}while(--cnt && r1==0);
			r1=SDCardSendData(0,0xFD);//接收512个字节 
		}
	}   
	SDCardCancelCS();//取消片选
	return r1;//
}	

 

 

相关推荐
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:马嘣嘣 返回首页