Recorder design based on STM32 (STM32F103+VS1053B)

Posted by *Lynette on Wed, 12 Jan 2022 05:52:32 +0100

1, Environment introduction

MCU: STM32F103C8T6

Development software: Keil5

Audio module: VS1053B

Recording file storage device: SD card, driven by SPI protocol

Display: 0.96 inch OLED with SPI interface

Code style: register programming is adopted, with simple code, high execution efficiency, in place annotation and convenient transplantation.

Download address of complete source code of the project (you can compile and run the test after downloading): https://download.csdn.net/download/xiaolong1126626497/19520781

2, Function introduction

This is a recorder function designed based on STM32F103C8T6. The supported functions are as follows:

1. Press key 1 to start automatic recording. The default is recording once every 5 seconds. After recording, it will be automatically saved in the directory specified by SD. The file name shall be named at the current time; The audio file format is stored in WAV format.

2. Press key 2 to start manual recording. After pressing the key, start recording, and press again to end recording. After recording, the file is also saved in the SD card.

3. SD card file system adopts FAT32 format. STM32 transplants FATFS open source file system to read and write SD card.

4. OLED display screen is used to display the status of the current recorder: idle, recording, playback, etc.

5. Press key 3 to start the automatic playback function. Automatically scan the directory and play the recording files in order.

Technical introduction:

1. The SD card is driven by SPI protocol. Because there is no high requirement for speed, SPI protocol has been fully met; If you want higher speed, you can use SDIO protocol.

2. The audio module adopts VS1053B, which supports IIS and SPI protocols. I use SPI protocol driver here. SPI is relatively simple, and the code is easy to transplant. It can be easily transplanted to other MCU. VS1053 is powerful and supports recording, decoding and playback.

3. The file system adopts FATFS file system, which has perfect functions and is free of charge, and supports FAT16, FAT32 and other formats. The bottom layer is also better adapted for transplantation. At present, in addition to FATFS, there are many other embedded file systems to choose from, and the transplantation is similar.

4. The OLED display is 0.96 inch. Driven by SPI protocol, it mainly displays some states. SPI swipes the screen faster. This OLED also supports IIC interface.

5. There is no speaker device on the vs1053 module, which can be used to listen to the playback recording with headphones or speakers.

Wiring description between hardware and STM32:

OLED display: D0----SCK-----PB14 D1----MOSI----PB13 RES - reset (active at low level) - PB12 DC --- data and command control pin - PB1 CS --- chip selection pin ----- PA7

VS1053: #define VS1053_ Dreq pain (11) / / dreq data request #define VS1053_ Reset paout (12) / / RST hardware reset - active low level #define VS1053_ XCS paout (13) / / XCS chip selection - active low level #define VS1053_ Xdcs paout (14) / / xdcs is used for data slice selection and byte synchronization #define VS1053_SCLK PAout(15) #define VS1053_OUTPUT PBout(3) #define VS1053_INPUT PBin(4)

SD card interface: 5V----5V GND---GND SPI1_MOSI---PA7 SPI1_MISO---PA6 SPI1_CS---PA4 SPI1_SCK--PA5

3, Relevant hardware used

STM32F103C8T6 system board:

OLED display:

VS1053:

SD card slot:

4, Operating instructions

The development board has a reset key and a K0 key.

Program download:

The program supports three modes:

Because the development board has only one K0 key, the three modes are switched through one key.

One key is switched by pressing the counting mode. The switching sequence is automatic recording mode, manual recording mode and playback mode.

(1) Automatic recording mode: press the key once to enter the automatic recording mode. In the automatic recording mode, the recording will exit automatically after 5 seconds. After exiting, the playback state will be started automatically, that is, to play the audio recorded for just 5 seconds. During playback, press the key to exit the playback state.

(2) Manual recording mode: after pressing the K0 key for the second time, enter the manual recording mode. In the manual recording mode, you can record for a long time. If you want to end the recording, press the K0 key to end it; Automatically start the playback state after the end, that is, play the audio just recorded. During playback, press the key to exit the playback state.

(3) Playback mode: after pressing the K0 key for the third time, enter the playback mode, automatically scan the wav directory and play audio files in sequence.

During playback, you can press K0 key to exit playback mode. The files recorded each time are stored in the wav directory under the root directory of the SD card. Each status is displayed on the OLED display It will also be printed to the serial port debugging assistant terminal through the serial port.

5, Files stored on SD card

There are two directories on the SD card: font directory and wav directory. Font: 16x16 font library files are stored in the directory. wav directory to store recorded audio files.

6, Partial source code

6.1 VS1053.c this is the driver code of vs1053

#include "vs1053b.h"	

/*
Function function: porting interface -- SPI sequential reading and writing a byte
 Function parameter: data: data to be written
 Return value: read 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; 
}


/*
Function: initialize the IO port of VS1053	 
*/
void VS1053_Init(void)
{
	 RCC->APB2ENR|=1<<0;
	 AFIO->MAPR&=~(0x7<<24);  //Release 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 function: soft reset
*/
void VS1053_SoftReset(void)
{	 
	u8 retry=0;  				   
	while(VS1053_DREQ==0); 							//Wait for software reset to end	   
	VS1053_SPI_ReadWriteByte(0Xff);			//Start transmission
	retry=0;
	while(VS1053_ReadReg(SPI_MODE)!=0x0800)	// Software reset, new mode  
	{
		VS1053_WriteCmd(SPI_MODE,0x0804);		// Software reset, new mode	    
		DelayMs(2);//Wait at least 1.35ms 
		if(retry++>100)break; 	  
	}	
	while(VS1053_DREQ==0);//Wait for software reset to end	 
	retry=0;
	while(VS1053_ReadReg(SPI_CLOCKF)!=0X9800)//Set the clock of VS10XX, triple frequency, 1.5xADD 
	{
		VS1053_WriteCmd(SPI_CLOCKF,0X9800);	//Set the clock of VS10XX, triple frequency, 1.5xADD
		if(retry++>100)break; 	    
	}	 
	DelayMs(20);
} 


/*
MP3 function: hard reset
 Function return value: 1: reset failed; 0: reset succeeded	
*/
u8 VS1053_Reset(void)
{
	u8 retry=0;
	VS1053_RESET=0;
	DelayMs(20);
	VS1053_XDCS=1;//Cancel data transfer
	VS1053_XCS=1; //Cancel data transfer
	VS1053_RESET=1;	   
	while(VS1053_DREQ==0&&retry<200)//Wait for DREQ to be high
	{
		retry++;
		DelayUs(50);
	}
	DelayMs(20);	
	if(retry>=200)return 1;
	else return 0;	    		 
}


/*
Function: write command to VS10XX
 Function parameters:
				address:Command address
				data   :Command data
*/
void VS1053_WriteCmd(u8 address,u16 data)
{  
	while(VS1053_DREQ==0);	//Waiting idle		   	   
	VS1053_XDCS=1; 	 
	VS1053_XCS=0; 	 
	VS1053_SPI_ReadWriteByte(VS_WRITE_COMMAND);//Send VS10XX write command
	VS1053_SPI_ReadWriteByte(address); 	//address
	VS1053_SPI_ReadWriteByte(data>>8); 	//Send high eight bits
	VS1053_SPI_ReadWriteByte(data);	 		//Eighth place
	VS1053_XCS=1;            
} 


/*
Function parameter: write data to VS1053
 Function parameter: data: data to be written
*/
void VS1053_WriteData(u8 data)
{
	VS1053_XDCS=0;   
	VS1053_SPI_ReadWriteByte(data);
	VS1053_XDCS=1;      
}


/*
Function: read VS1053 register 
Function parameter: Address: register address
 Return value: read value
*/
u16 VS1053_ReadReg(u8 address)
{ 
	u16 temp=0;   	
  while(VS1053_DREQ==0);//Non waiting idle state   	
	VS1053_XDCS=1;       
	VS1053_XCS=0;        
	VS1053_SPI_ReadWriteByte(VS_READ_COMMAND);//Send VS10XX read command
	VS1053_SPI_ReadWriteByte(address);       	//address
	temp=VS1053_SPI_ReadWriteByte(0xff); 		  //Read high byte
	temp=temp<<8;
	temp+=VS1053_SPI_ReadWriteByte(0xff); 		//Read low byte
	VS1053_XCS=1;      
   return temp; 
}  


/*
Function: read the RAM of VS1053
 Function parameter: addr: RAM address
 Return value: read value
*/
u16 VS1053_ReadRAM(u16 addr) 
{ 
	u16 res;			   	  
 	VS1053_WriteCmd(SPI_WRAMADDR, addr); 
	res=VS1053_ReadReg(SPI_WRAM);  
 	return res;
} 


/*
Function: write VS1053 RAM
 Function parameters:
				addr: RAM address
				val:Value to write 
*/
void VS1053_WriteRAM(u16 addr,u16 val) 
{  		   	  
 	VS1053_WriteCmd(SPI_WRAMADDR,addr);	//Write RAM address 
	while(VS1053_DREQ==0); 							//Waiting idle	   
	VS1053_WriteCmd(SPI_WRAM,val); 			//Write RAM value 
} 


/*
Function parameter: send audio data once, fixed to 32 bytes
 Return value: 0, sent successfully
				  1,The data was not sent successfully   
*/ 
u8 VS1053_SendMusicData(u8* buf)
{
	u8 n;
	if(VS1053_DREQ!=0)  //Send data to VS10XX
	{			   	 
		VS1053_XDCS=0;  
    for(n=0;n<32;n++)
		{
			VS1053_SPI_ReadWriteByte(buf[n]);	 			
		}
		VS1053_XDCS=1;     				   
	}else return 1;
	return 0;//Successfully sent
}


/*
Function parameter: send audio data once, fixed to 32 bytes
 Return value: 0, sent successfully
				  1,The data was not sent successfully   
*/ 
void VS1053_SendMusicByte(u8 data)
{
	u8 n;
	while(VS1053_DREQ==0){}	   	 
	VS1053_XDCS=0;  
	VS1053_SPI_ReadWriteByte(data);	 			
	VS1053_XDCS=1;     				   
}


/*
Function: set the volume of VS1053 playback
 Function parameter: volx: Volume (0 ~ 254)
*/
void VS1053_SetVol(u8 volx)
{
    u16 volt=0; 			      //Temporary volume value
    volt=254-volx;			    //Take the inverse to get the maximum value, which represents the maximum representation 
	  volt<<=8;
    volt+=254-volx;					//Get the volume after setting
    VS1053_WriteCmd(SPI_VOL,volt);//Set volume 
}


/*--------------------------------------Recording function-----------------------------------------------------*/


/*
Function: vs10xx load patch
 Function parameters:
				patch: patch First address
				len  : patch length
*/
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); 
			} 
		} 
	} 	
}


/*
Function parameter: initialize WAV header
*/
void VS1053_RecoderWavInit(__WaveHeader* wavhead) //Initialize WAV header			   
{
	wavhead->riff.ChunkID=0X46464952;	//"RIFF"
	wavhead->riff.ChunkSize=0;				//It has not been determined yet. Finally, it needs to be calculated
	wavhead->riff.Format=0X45564157; 	//"WAVE"
	wavhead->fmt.ChunkID=0X20746D66; 	//"fmt "
	wavhead->fmt.ChunkSize=16; 				//The size is 16 bytes
	wavhead->fmt.AudioFormat=0X01; 		//0X01, indicating PCM;0X01 indicates IMA ADPCM
 	wavhead->fmt.NumOfChannels=1;			//Mono
 	wavhead->fmt.SampleRate=8000;			//8Khz sampling rate
 	wavhead->fmt.ByteRate=wavhead->fmt.SampleRate*2;//16 bits, i.e. 2 bytes
 	wavhead->fmt.BlockAlign=2;				//Block size, 2 bytes as a block
 	wavhead->fmt.BitsPerSample=16;		//16 bit PCM
  wavhead->data.ChunkID=0X61746164;	//"data"
 	wavhead->data.ChunkSize=0;				//The data size also needs to be calculated  
}

//There is a bug in the WAV recording of VS1053. This plugin can correct this problem 							    
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,  
}; 


/*
Function: activate PCM recording mode
 Function parameters:
				agc:0,automatic gain 
        1024 Equivalent to 1 time
        512 Equivalent to 0.5x
        Maximum 65535 = 64 times		  
*/
void VS1053_RecoderInit(u16 agc)
{
	//In case of IMA ADPCM, the sampling rate is calculated as follows:
 	//Sampling rate = CLKI/256*d;	
	//Assuming d=0 and frequency doubling, the external crystal oscillator is 12.288M Then Fc=(2*12288000)/256*6=16Khz
	//In the case of linear PCM, the sampling rate directly writes the sampling value 
  VS1053_WriteCmd(SPI_BASS,0x0000);    
 	VS1053_WriteCmd(SPI_AICTRL0,8000);	//Set the sampling rate to 8Khz
 	VS1053_WriteCmd(SPI_AICTRL1,agc);		//Set gain, 0, automatic gain. 1024 is equivalent to 1 times, 512 is equivalent to 0.5 times, and the maximum value is 65535 = 64 times	
 	VS1053_WriteCmd(SPI_AICTRL2,0);		  //Set the maximum gain value, 0, representing the maximum value 65536=64X
 	VS1053_WriteCmd(SPI_AICTRL3,6);		  //Left channel (MIC mono input)
	VS1053_WriteCmd(SPI_CLOCKF,0X2000);	//Set the clock of VS10XX, mult: double frequency; ADD: not allowed; CLK:12.288Mhz
	VS1053_WriteCmd(SPI_MODE,0x1804);		//MIC, recording activation    
 	DelayMs(5);					//Wait at least 1.35ms 
 	VS1053_LoadPatch((u16*)VS1053_WavPlugin,40);//patch is required for WAV recording of VS1053
}

6.2 SD.c this is the SD card driver code

#include "sdcard.h"			   
static u8  SD_Type=0;  //Type of SD card

/*
Function function: the SD card bottom interface reads and writes a byte to the SD card through SPI timing
 Function parameter: data is the data to be written
 Return value: read data
*/
u8 SDCardReadWriteOneByte(u8 DataTx)
{		 
    u16 cnt=0;				 
    while((SPI1->SR&1<<1)==0)		 //The waiting to send buffer is empty -- the waiting to send buffer is empty	
    {
      cnt++;
      if(cnt>=65530)return 0; 	  //Timeout exit u16=2 bytes
    }	
    SPI1->DR=DataTx;	 	  		      //Send a byte 
    cnt=0;
    while((SPI1->SR&1<<0)==0) 		//Wait for a byte to be received   
    {
      cnt++;
      if(cnt>=65530)return 0;	   //Timeout exit
    }	  						    
    return SPI1->DR;          		//Return received data
}


/*
Function function: initialization of underlying SD card interface
SPI1 Interface -- SD card wiring principle
5V----5V
GND---GND
SPI1_MOSI---PA7
SPI1_MISO---PA6
SPI1_CS---PA4
SPI1_SCK--PA5
*/
void SDCardSpiInit(void)
{
  /*1. Turn on the clock*/
 	RCC->APB2ENR|=1<<2;		    //Enable PORTA clock
  
  /*2. Configure GPIO port mode*/
  GPIOA->CRL&=0x0000FFFF;
  GPIOA->CRL|=0xB8B30000;
  
  /*3. Pull up*/
  GPIOA->ODR|=1<<4;
	
		/*SPI1 Basic configuration*/
	RCC->APB2ENR|=1<<12;    //Turn on SPI1 clock
	RCC->APB2RSTR|=1<<12;
	RCC->APB2RSTR&=~(1<<12);
	
	SPI1->CR1=0X0; 		//Clear register
	SPI1->CR1|=0<<15; //Select double line bidirectional mode
	SPI1->CR1|=0<<11; //Transmit / receive using 8-bit data frame format;
	SPI1->CR1|=0<<10; //Full duplex (transmit and receive);
	SPI1->CR1|=1<<9;  //Enable software slave device management
	SPI1->CR1|=1<<8;  //NSS
	SPI1->CR1|=0<<7;  //Frame format, send the high bit first
	SPI1->CR1|=0x0<<3;//When the bus frequency is 36MHZ, the SPI speed is 18MHZ, high speed.
	SPI1->CR1|=1<<2;  //Configure master device
	SPI1->CR1|=1<<1;  //When idle, SCK remains high.
	SPI1->CR1|=1<<0;  //Data sampling starts at the edge of the second clock.
	SPI1->CR1|=1<<6;  //Turn on the SPI device.
}

/*
Function function: deselect and release SPI bus
*/
void SDCardCancelCS(void)
{
	SDCARD_CS=1;
 	SDCardReadWriteOneByte(0xff);//Provide additional 8 clocks
}


/*
Function function: select sd card and wait for the card to be ready for OK
 Function return value: 0, successful; 1. Failure;
*/
u8 SDCardSelectCS(void)
{
	SDCARD_CS=0;
	if(SDCardWaitBusy()==0)return 0;//Wait for success
	SDCardCancelCS();
	return 1;//Wait failed
}


/*
Function: wait for the card to be ready
 Function return value: 0, ready; Other, error code
*/
u8 SDCardWaitBusy(void)
{
	u32 t=0;
	do
	{
		if(SDCardReadWriteOneByte(0XFF)==0XFF)return 0;//OK
		t++;		  
	}while(t<0xFFFFFF);//wait for 
	return 1;
}


/*
Function function: wait for SD card response
 Function parameters:
					Response:Response value to get
 Return value:
					0,The response value was obtained successfully
					Other, failed to get response value
*/
u8 SDCardGetAck(u8 Response)
{
	u16 Count=0xFFFF;//Waiting times	   						  
	while((SDCardReadWriteOneByte(0XFF)!=Response)&&Count)Count--;//Waiting for an accurate response  	  
	if(Count==0)return SDCard_RESPONSE_FAILURE;//Failed to get response   
	else return SDCard_RESPONSE_NO_ERROR;//Correct response
}


/*
Function: read the contents of a data packet from sd card
 Function parameters:
				buf:Data buffer
				len:The length of data to be read
 Return value:
			0,success; Others, failure;	
*/
u8 SDCardRecvData(u8*buf,u16 len)
{			  	  
	if(SDCardGetAck(0xFE))return 1;//Wait for the SD card to send back the data start token 0xFE
    while(len--)//Start receiving data
    {
        *buf=SDCardReadWriteOneByte(0xFF);
        buf++;
    }
    //Here are 2 dummy CRC s
    SDCardReadWriteOneByte(0xFF);
    SDCardReadWriteOneByte(0xFF);									  					    
    return 0;//Read successful
}


/*
Function: write 512 bytes of a data packet to sd card
 Function parameters:
					buf Data buffer
					cmd instructions
 Return value: 0 indicates success; Other values indicate failure;
*/
u8 SDCardSendData(u8*buf,u8 cmd)
{	
	u16 t;		  	  
	if(SDCardWaitBusy())return 1;  //Waiting for preparation to fail
	SDCardReadWriteOneByte(cmd);
	if(cmd!=0XFD)//Not an end instruction
	{
		for(t=0;t<512;t++)SDCardReadWriteOneByte(buf[t]);//Improve speed and reduce function parameter transfer time
	    SDCardReadWriteOneByte(0xFF); //Ignore crc
	    SDCardReadWriteOneByte(0xFF);
		  t=SDCardReadWriteOneByte(0xFF); //Receive response
		if((t&0x1F)!=0x05)return 2;   //Response error									  					    
	}						 									  					    
    return 0;//Write successful
}



/*
Function function: Send a command to SD card
 Function parameters:
				u8 cmd   command 
				u32 arg  Command parameters
				u8 crc   crc Check value	
Return value: the response returned by the SD card
*/												  
u8 SendSDCardCmd(u8 cmd, u32 arg, u8 crc)
{
	u8 r1;	
	u8 Retry=0; 
		
	SDCardCancelCS();               //Cancel last selection
	if(SDCardSelectCS())return 0XFF;//Film selection failure 
	//send data
	SDCardReadWriteOneByte(cmd | 0x40);//Write command separately
	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--);	  //Wait for a response, or exit timeout
   return r1;	//Return status value
}	



/*
Function: obtain CID information of SD card, including manufacturer information
 Function parameter: U8 * CID_ Data (memory for storing CID, at least 16Byte)	  
Return value:
					0: Success, 1: error				
*/
u8 GetSDCardCISDCardOutnfo(u8 *cid_data)
{
    u8 r1;	   
    //Send SDCard_CMD10 command, read CID
    r1=SendSDCardCmd(SDCard_CMD10,0,0x01);
    if(r1==0x00)
	  {
			r1=SDCardRecvData(cid_data,16);//Receive 16 bytes of data	 
    }
	SDCardCancelCS();//Cancel film selection
	if(r1)return 1;
	else return 0;
}	


/*
Function Description:
					Obtain CSD information of SD card, including capacity and speed information
 Function parameters:
					u8 *cid_data(Memory for storing CID (at least 16Byte)	    
Return value:
					0: Success, 1: error	
*/
u8 GetSDCardCSSDCardOutnfo(u8 *csd_data)
{
	u8 r1;	 
	r1=SendSDCardCmd(SDCard_CMD9,0,0x01);    //Send SDCard_CMD9 command, read CSD
	if(r1==0)
	{
		r1=SDCardRecvData(csd_data, 16);//Receive 16 bytes of data 
	}
	SDCardCancelCS();//Cancel film selection
	if(r1)return 1;
	else return 0;
}  


/*
Function function: get the total number of sectors of SD card (number of sectors)   
Return value:
				0 Indicates an error in capacity detection. Other values indicate the capacity of the SD card (number of sectors / 512 bytes)
explain:
				The number of bytes per sector must be 512 bytes. If it is not 512 bytes, initialization cannot pass	
*/
u32 GetSDCardSectorCount(void)
{
    u8 csd[16];
    u32 Capacity;  
	  u16 csize;  					    
    if(GetSDCardCSSDCardOutnfo(csd)!=0) return 0;	//Get CSD information. If there is an error during the, return 0
    if((csd[0]&0xC0)==0x40)  //SDHC card, calculated as follows
    {	
			csize = csd[9] + ((u16)csd[8] << 8) + 1;
			Capacity = (u32)csize << 10;//Get the number of sectors	 		   
    }
    return Capacity;
}



/*
Function: initialize SD card
 Return value: non-0 indicates initialization failure!
*/
u8 SDCardDeviceInit(void)
{
  u8 r1;      // Store the return value of SD card
  u16 retry;  // Used for timeout counting
  u8 buf[4];  
	u16 i;
	SDCardSpiInit();		//Initialize the underlying IO port
	
 	for(i=0;i<10;i++)SDCardReadWriteOneByte(0XFF); //Send a minimum of 74 pulses
	retry=20;
	do
	{
		r1=SendSDCardCmd(SDCard_CMD0,0,0x95);//Enter IDLE state
	}while((r1!=0X01) && retry--);
 	SD_Type=0;   //Default no card
	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)    //Does the card support 2.7~3.6V
			{
				retry=0XFFFE;
				do
				{
					SendSDCardCmd(SDCard_CMD55,0,0X01);	    //Send SDCard_CMD55
					r1=SendSDCardCmd(SDCard_CMD41,0x40000000,0X01);//Send SDCard_CMD41
				}while(r1&&retry--);
				if(retry&&SendSDCardCmd(SDCard_CMD58,0,0X01)==0)//Identify SD2 0 card version start
				{
					for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF);//Get OCR value
					if(buf[0]&0x40)SD_Type=SDCard_TYPE_V2HC;    //Check CCS
					else SD_Type=SDCard_TYPE_V2;   
				}
			}
		}
	}
	SDCardCancelCS();       //Cancel film selection
	if(SD_Type)return 0;  //0 returned after successful initialization
	else if(r1)return r1; //Return value error value	   
	return 0xaa;          //Other errors
}


/*
Function: read SD card
 Function parameters:
				buf:Data buffer
				sector:a sector
				cnt:sector number
 Return value:
				0,ok;Others, failed
 explain:
				SD The size of one sector of the card is 512 bytes
*/
u8 SDCardReadData(u8*buf,u32 sector,u32 cnt)
{
	u8 r1;
	if(SD_Type!=SDCard_TYPE_V2HC)sector<<=9;//Convert to byte address
	if(cnt==1)
	{
		r1=SendSDCardCmd(SDCard_CMD17,sector,0X01);//Read command
		if(r1==0)												  //Command sent successfully
		{
			r1=SDCardRecvData(buf,512);			//Receive 512 bytes	   
		}
	}else
	{
		r1=SendSDCardCmd(SDCard_CMD18,sector,0X01);//Continuous read command
		do
		{
			r1=SDCardRecvData(buf,512);//Receive 512 bytes	 
			buf+=512;  
		}while(--cnt && r1==0); 	
		SendSDCardCmd(SDCard_CMD12,0,0X01);	//Send stop command
	}   
	SDCardCancelCS();//Cancel film selection
	return r1;//
}

/*
Function: write data to SD card
 Function parameters:
				buf:Data buffer
				sector:starting sector 
				cnt:sector number
 Return value:
				0,ok;Others, failed
 explain:
				SD The size of one sector of the card is 512 bytes
*/
u8 SDCardWriteData(u8*buf,u32 sector,u32 cnt)
{
	u8 r1;
	if(SD_Type!=SDCard_TYPE_V2HC)sector *= 512;//Convert to byte address
	if(cnt==1)
	{
		r1=SendSDCardCmd(SDCard_CMD24,sector,0X01);//Read command
		if(r1==0)//Command sent successfully
		{
			r1=SDCardSendData(buf,0xFE);//Write 512 bytes	   
		}
	}
	else
	{
		if(SD_Type!=SDCard_TYPE_MMC)
		{
			SendSDCardCmd(SDCard_CMD55,0,0X01);	
			SendSDCardCmd(SDCard_CMD23,cnt,0X01);//Send instructions	
		}
 		r1=SendSDCardCmd(SDCard_CMD25,sector,0X01);//Continuous read command
		if(r1==0)
		{
			do
			{
				r1=SDCardSendData(buf,0xFC);//Receive 512 bytes	 
				buf+=512;  
			}while(--cnt && r1==0);
			r1=SDCardSendData(0,0xFD);//Receive 512 bytes 
		}
	}   
	SDCardCancelCS();//Cancel film selection
	return r1;//
}