
#include "debug.h"
#include "sd_routines.h"

#define 	SdTimerFlag 	(!(TIM_GetFlagStatus(TIM3, TIM_FLAG_Update))) 
//#define 	SPITimerFlag 	(!(TIM_GetFlagStatus(TIM6, TIM_FLAG_Update))) 
#define 	SdInitTimerFlag (!(TIM_GetFlagStatus(TIM11, TIM_FLAG_Update))) 

volatile TimeDate td;

//===================   INTERNAL GLOBAL VARIABLES  ================================ 
DSTATUS Stat = STA_NOINIT;							/* Disk status */
BYTE SDHC_flag, MMC_flag = 0;					    /* Flag it is SDHC or MMC card or not */
BYTE ocr[4], csd[16], cid[16];						/* Buffers for cards internal registers */


/*******************************************************************************
* Function Name  : SD_sendCommand
* Description    : Send command to SD or MMC card
* Input          : command, argument of command
* Output         : Card response
* Return         : BYTE
*******************************************************************************/
BYTE SD_sendCommand(BYTE cmd, DWORD arg) {
	BYTE crc_7 = 0;
	BYTE crc_buf[5];
	BYTE response = 0;
	BYTE n = 0;
	BYTE token = 0;

	memset(crc_buf, 0x00, sizeof(crc_buf));

	//SD card accepts byte address while SDHC accepts block address in multiples of 512
	//so, if it's SD card we need to convert block address into corresponding byte address by 
	//multipying it with 512. which is equivalent to shifting it left 9 times
	//following 'if' loop does that

	if(SDHC_flag == 0) {		
		if(cmd == READ_SINGLE_BLOCK || cmd == READ_MULTIPLE_BLOCKS || cmd == WRITE_SINGLE_BLOCK || cmd == WRITE_MULTIPLE_BLOCKS || cmd == ERASE_BLOCK_START_ADDR || cmd == ERASE_BLOCK_END_ADDR ) {
    		arg = arg << 9;
   		}	   
	}

	SD_CS_ASSERT;

	SPI_transmit(crc_buf[0] = cmd | 0x40); 			//send command, two ms-bits always '01'
	SPI_transmit(crc_buf[1] = arg>>24);
	SPI_transmit(crc_buf[2] = arg>>16);
	SPI_transmit(crc_buf[3] = arg>>8);
	SPI_transmit(crc_buf[4] = arg);

	crc_7 = crc7(crc_buf);						  	//Calculate crc

	SPI_transmit(crc_7);			 				//Transmit CRC7 for command

	SetSdTimer(500);					  			//wait for R1 response
	do {
	   response = SPI_transmit(0xFF);
	}
	while(response == 0xFF && SdTimerFlag);     
	if(response == 0xFF){
		SD_CS_DEASSERT; 
		return 1;						   			//return if time-out
	} 

	if(response == 0x00 && cmd == 58) {  			//checking response of CMD58
		for (n = 0; n < 4; n++) {
			ocr[n] = SPI_transmit(0xFF);
		}
	} 

    if(response == 0x00 && cmd == 9) {				// Wait for data packet in timeout of 250ms  
		SetSdTimer(250);					  	
		do {
	   		token = SPI_transmit(0xFF);
		}
		while(token == 0xFF && SdTimerFlag);     
		if(token == 0xFE) {
			n = 0;							
			do {
				csd[n] = SPI_transmit(0xFF);		// Receive the data block into buffer  
				n++;
			} while (n < 16);
			
			crc_7 = crc7_calc(&csd[0], 15);
			if (crc_7 == csd[15]) {
				response = 1;					    // Return with success  	
			} else {
				response = 0;						// If not valid data token, retutn with error 
			}
									
		} else {									
			response = 0;
		}
	}	
	
	if(response == 0x00 && cmd == 10) {
		SetSdTimer(250);					  		// Wait for data packet in timeout of 250ms 
		do {
	   		token = SPI_transmit(0xFF);
		}
		while(token == 0xFF && SdTimerFlag); 
		if(token == 0xFE) {
			n = 0;							
			do {
				cid[n] = SPI_transmit(0xFF);		// Receive the data block into buffer 
				n++;
			} while (n < 16);

			crc_7 = crc7_calc(cid, 15);
			if (crc_7 == cid[15]) {
				response = 1;						// Return with success 
			} else {
				response = 0;						// If not valid data token, retutn with error 
			}
									 	
		} else {									
			response = 0;
		}
	}
							
    if(response == 0x01 && cmd == 8) {  			//checking response of CMD8
		for (n = 0; n < 4; n++) {
			ocr[n] = SPI_transmit(0xFF);
		}
	} 

	if(response == 0x00 && cmd == 13) {  			//checking response of CMD8
		response = SPI_transmit(0xFF); 
	}
	SPI_transmit(0xFF); 							//extra 8 CLK
	SD_CS_DEASSERT;
	return response; 								//return state
}
/******************************************************************************/   


/*******************************************************************************
* Function Name  : SD_readSingleBlock
* Description    : Read single blocks from SD card
* Input          : start block, buffer to store reading data
* Output         : '0' - if Ok, '1' - if error was accident
* Return         : BYTE
*******************************************************************************/
BYTE SD_readSingleBlock(DWORD startBlock, BYTE *buff) {
	BYTE response = 0;
	DWORD i = 0;
	BYTE crc_low = 0;
	BYTE crc_high = 0;
	WORD crc_16 = 0;

 	response = SD_sendCommand(READ_SINGLE_BLOCK, startBlock); 	//read a Block command

 	if(response != 0x00) {
		return response; 					//check for SD status: 0x00 - OK (No flags set)
	}

	SD_CS_ASSERT;

	SetSdTimer(500);
	do {
	   response = SPI_transmit(0xFF);
	} 
	while(response != 0xFE && SdTimerFlag);     //wait for start block token 0xfe (0x11111110) 	
	if(response != 0xFE){
		SD_CS_DEASSERT; 
		return 1;						   	//return if time-out
	} 
	
	for(i=0; i<512; i++) { 		//read 512 bytes
  		buff[i] = SPI_transmit(0xFF);
		//sendchar(buff[i]);
	}

	crc_high = SPI_transmit(0xFF); 			//receive incoming CRC (16-bit), CRC is ignored here
	crc_low = SPI_transmit(0xFF);
	crc_16 = crc16(buff, 512);

	SPI_transmit(0xFF); 					//extra 8 clock pulses
	SD_CS_DEASSERT;

	if (((((WORD)(crc_high << 8)) & 0xFF00) | crc_low) == crc_16) {
		return 0;
	}
	return 1;	   
}
/******************************************************************************/ 


/*******************************************************************************
* Function Name  : SD_writeSingleBlock
* Description    : Write single block to SD card
* Input          : start block, buffer to store writing data
* Output         : '0' - if Ok, '1' - if error was accident
* Return         : BYTE
*******************************************************************************/
BYTE SD_writeSingleBlock(DWORD startBlock, BYTE *buff) {
	BYTE response = 0;
	DWORD i = 0;
	BYTE crc_low = 0;
	BYTE crc_high = 0;
	WORD crc_16 = 0;

	if (MMC_flag != 1) {
		SetSdTimer(500);
		do {
			if ((SD_sendCommand(APP_CMD, EMPTY) <= 1) && (SD_sendCommand(SET_WR_BLK_ERASE_COUNT, 1) == 0)) break;	/* ACMD23 */
		} while (SdTimerFlag);
	
		if (!SdTimerFlag) {
		   	SD_CS_DEASSERT;             	
	        return 1;
		}
	}
 	response = SD_sendCommand(WRITE_SINGLE_BLOCK, startBlock); //write a Block command
  
 	if(response != 0x00) {
		return response; 				//check for SD status: 0x00 - OK (No flags set)
	}
	SD_CS_ASSERT;

	SPI_transmit(0xFE);     			//Send start block token 0xfe (0x11111110)

	for(i=0; i<512; i++) {   			//send 512 bytes data
  		SPI_transmit(buff[i]);
	}
	crc_16 = crc16(buff, 512);
	crc_high = (BYTE)((crc_16 >> 8) & 0x00FF);
	crc_low  = (BYTE)(crc_16 & 0x00FF);
	SPI_transmit(crc_high);     			//transmit dummy CRC (16-bit), CRC is ignored here
	SPI_transmit(crc_low);

	response = SPI_transmit(0xFF);

	if( (response & 0x0F) != 0x05){ 	//response= 0xXXX0AAA1 ; AAA='010' - data accepted
        SD_CS_DEASSERT;             	//AAA='101'-data rejected due to CRC error
        return response;        		//AAA='110'-data rejected due to write error 
	}

	SetSdTimer(500);
	do {
		response = SPI_transmit(0xFF);
	} 
	while (response != 0xFF && SdTimerFlag);
	if (response == 0x00) {
		SD_CS_DEASSERT;
		return 1;
	}


	SD_CS_DEASSERT;

	SPI_transmit(0xFF);   					//just spend 8 clock cycle delay before reasserting the CS line

	SD_CS_ASSERT;         					//re-asserting the CS line to verify if card is still busy

	 			
	SetSdTimer(500);						//wait for SD card to complete writing and get idle
	do {
		response = SPI_transmit(0xFF);
	} 
	while (response != 0xFF && SdTimerFlag);
	if (response == 0x00) {
		SD_CS_DEASSERT;
		return 1;
	}

	SD_CS_DEASSERT;	
	return 0;
}
/******************************************************************************/


/*******************************************************************************
* Function Name  : SD_readMultipleBlock
* Description    : Read data  (multiple blocks) from SD card
* Input          : start block, number of block, buffer to store reading data
* Output         : '0' - if Ok, '1' - if error was accident
* Return         : BYTE
*******************************************************************************/
BYTE SD_readMultipleBlock (DWORD startBlock, DWORD totalBlocks, BYTE *buff) {
	BYTE response = 0;
	DWORD i = 0;
	BYTE crc_low = 0; 
	BYTE crc_high = 0;
	WORD crc_16 = 0;
	WORD byteNum = 0;
	BYTE crc_buff[512];

	response = SD_sendCommand(READ_MULTIPLE_BLOCKS, startBlock); //write a Block command

	if(response != 0x00) {
		return response; 						//check for SD status: 0x00 - OK (No flags set)
	}
	SD_CS_ASSERT;

	while(totalBlocks) {
		SetSdTimer(500);
		do {
			response = SPI_transmit(0xFF);
		} 
		while (response != 0xFE && SdTimerFlag);
		if (response != 0xFE) {
			SD_CS_DEASSERT;
			return 1;
		}

  		for(i = 0; i < 512; i++, byteNum++) { //read 512 bytes
    		crc_buff[i] = buff[byteNum] = SPI_transmit(0xFF);		
	    }

		crc_high = SPI_transmit(0xFF); 			//receive incoming CRC (16-bit), CRC is ignored here
		crc_low = SPI_transmit(0xFF);
		crc_16 = crc16(crc_buff, 512);
		if (((((WORD)(crc_high << 8)) & 0xFF00) | crc_low) != crc_16) {
			return 1;
		}

  		SPI_transmit(0xFF); 					//extra 8 cycles
  		/*for(i = 0; i < 512; i++) { 			//send the block to UART
    		dprintf_param ("0x%X ",buff[i]);
  		}*/
  		totalBlocks--;
	}

	SD_sendCommand(STOP_TRANSMISSION, EMPTY); 	//command to stop transmission	

	SD_CS_DEASSERT;	
	SPI_transmit(0xFF); 						//extra 8 clock pulses
	return 0;
}

/******************************************************************************/


/*******************************************************************************
* Function Name  : SD_writeMultipleBlock
* Description    : Write data to multiple blocks of SD card
* Input          : start block, number of block, buffer to store writing data
* Output         : '0' - if Ok, '1' - if error was accident
* Return         : BYTE
*******************************************************************************/
BYTE SD_writeMultipleBlock(DWORD startBlock, DWORD totalBlocks, BYTE *buff) {
	BYTE response = 0;
	DWORD i = 0;
	DWORD blockCounter = 0;
	BYTE crc_low = 0; 
	BYTE crc_high = 0;
	WORD crc_16 = 0;
	WORD byteNum = 0;
	BYTE crc_buff[512];

	if (MMC_flag != 1) {
		SetSdTimer(500);
		do {
			if ((SD_sendCommand(APP_CMD, EMPTY) <= 1) && (SD_sendCommand(SET_WR_BLK_ERASE_COUNT, totalBlocks) == 0)) break;	/* ACMD23 */
		} while (SdTimerFlag);
	
		if (!SdTimerFlag) {
		   	SD_CS_DEASSERT;             	
	        return 1;
		}
	}
	response = SD_sendCommand(WRITE_MULTIPLE_BLOCKS, startBlock); //write a Block command

	if(response != 0x00) {
		return response; 							//check for SD status: 0x00 - OK (No flags set)
	}

	SD_CS_ASSERT;
	while( blockCounter < totalBlocks ) {

   		SPI_transmit(0xFC); 						//Send start block token 0xFC (0x11111100)

   		for(i = 0; i < 512; i++, byteNum++) {		//send 512 bytes data
			crc_buff[i] = buff[byteNum];
			SPI_transmit(buff[byteNum]);
		}

   		crc_16 = crc16(crc_buff, 512);
		crc_high = (BYTE)((crc_16 >> 8) & 0x00FF);
		crc_low  = (BYTE)(crc_16 & 0x00FF);
		SPI_transmit(crc_high);     				//transmit dummy CRC (16-bit), CRC is ignored here
		SPI_transmit(crc_low);

   		response = SPI_transmit(0xFF);

   		if( (response & 0x0F) != 0x05) {			//response= 0xXXX0AAA1 ; AAA='010' - data accepted 
			SPI_transmit(0xFD); 					//send 'stop transmission token'
      		SD_CS_DEASSERT;                 		
        	return response;           				   
   		}

		SetSdTimer(500);
		do {
			response = SPI_transmit(0xFF);
		} 
		while (response != 0xFF && SdTimerFlag);
		if (response == 0x00) {
			SD_CS_DEASSERT;
			return 1;
		}

   		SPI_transmit(0xFF); 						//extra 8 bits
   		blockCounter++;
	}
	
	SPI_transmit(0xFD); 							//send 'stop transmission token'

	SetSdTimer(500);
	do {
		response = SPI_transmit(0xFF);
	} 
	while (response != 0xFF && SdTimerFlag);
	if (response == 0x00) {
		SD_CS_DEASSERT;
		return 1;
	}	

	SD_CS_DEASSERT;	 

	SD_CS_ASSERT; 									//re assertion of the CS signal is required to verify if card is still busy			
	SetSdTimer(500);								//wait for SD card to complete writing and get idle
	do {
		response = SPI_transmit(0xFF);
	} 
	while (response != 0xFF && SdTimerFlag);
	if (response == 0x00) {
		SD_CS_DEASSERT;
		return 1;
	}

	SD_CS_DEASSERT;		
	return 0;
}
/******************************************************************************/


/*******************************************************************************
* Function Name  : SPI_transmit
* Description    : Sends a byte through the SPI interface and return the byte
*                  received from the SPI bus.
* Input          : byte : byte to send.
* Output         : None
* Return         : The value of the received byte.
*******************************************************************************/
BYTE SPI_transmit(BYTE data) {
	/* Loop while DR register in not emplty */
  	//while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);

	//SetSPITimer(200);
  	/* Send byte through the SPI2 peripheral */
  	SPI_I2S_SendData(SPI2, data);

  	/* Wait to receive a byte */
  	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET /*&& SPITimerFlag*/);

  	/* Return the byte read from the SPI bus */
  	return SPI_I2S_ReceiveData(SPI2);					
}
/******************************************************************************/


/*******************************************************************************
* Function Name  : disk_initialize
* Description    : Initialize SD or MMC card
* Input          : Physical drive nmuber
* Output         : Status of disk
* Return         : DSTATUS
*******************************************************************************/ 
DSTATUS disk_initialize (BYTE drv) { 
	 
	BYTE n = 0;
	BYTE f = 0; 
	BYTE result = 0;
	
	TurnOn_SD;													// подача питания на SD-карту
	
	if (check_SD_insertion () == TRUE) {						// Check there is card presence?
		if (drv == 0) {
			dprintf_text(" Start Init Drv 0 ");
		}
	
		SDHC_flag = 0;
		MMC_flag = 0;
													 		
		SD_SpiInit(_500kHz);		   							// Slowly init SD or MMC
										  																																			
		for (n = 10; n; n--) {
			SPI_transmit(0xFF);									// 80 dummy clocks 
		}
		SD_CS_ASSERT;											// CS = L
		f = 0;
		do {
			n ++;
			result = SD_sendCommand(GO_IDLE_STATE, 0);			// Go Idle state
			if (n > 0xFFFE) break;
		} while (result != 0x01);
		if (SD_sendCommand(GO_IDLE_STATE, 0) == 1) {							// Enter Idle state 
			SetInitSdTimer(1000);												// Initialization timeout of 1000 msec 
			if (SD_sendCommand(SEND_IF_COND, 0x1AA) == 1) {						// SDC Ver2+ 
				if (ocr[2] == 0x01 && ocr[3] == 0xAA) {							// The card can work at vdd range of 2.7-3.6V 
					do {
						if ((SD_sendCommand(APP_CMD, EMPTY) <= 1) && SD_sendCommand(SD_SEND_OP_COND, 1 << 30) == 0) break;	
					} while (SdInitTimerFlag);
					if (SdInitTimerFlag && SD_sendCommand(READ_OCR, EMPTY) == 0) {	// Check CCS bit 
						f = (ocr[0] & 0x40) ? 3 : 1;
					}
				}	
				if (f == 1) {
					dprintf_text("Standard Capacity Card (Ver 2.x) Detected!");
				} else if (f == 3) {
					dprintf_text("High Capacity Card Detected!");
				}	
	
				if (f == 3) SDHC_flag = 1;
			} else {															// SDC Ver1 or MMC 
				if (SD_sendCommand(APP_CMD, 0) <= 1 && SD_sendCommand(SD_SEND_OP_COND, 0) <= 1) f = 4;	// SDC? 			
				do {
					if (!f && SD_sendCommand(SEND_OP_COND, 0) == 0) {			// CMD1 for MMC 			   						
						MMC_flag = 1;												 
						dprintf_text("Multi Media Card Detected!");	
						break;								
					}
					if ( f && SD_sendCommand(APP_CMD, 0) <= 1 && SD_sendCommand(SD_SEND_OP_COND, 0) == 0) {	// ACMD41 	
						dprintf_text("Standard Capacity Card (Ver 1.x) Detected!");	
						break;	
					}
				} while (SdInitTimerFlag);
				if (SdInitTimerFlag) f = 1;
			}
	
			if (f == 1 && SD_sendCommand(SET_BLOCK_LEN, 512) != 0) {				// Select R/W block length 
				f = 0;
			}
	
			if (SD_sendCommand(CRC_ON_OFF, 0x01) == 0) {							// TURN ON CRC 
				dprintf_text(" CRC ENABLED! ");
			}
	
		}
		SD_CS_DEASSERT;							// CS = H 
		SPI_transmit(0xFF);						
	
		if (f & 1) {							
			/* Initialization succeded */
			dprintf_text(" Init Succeded ");
			Stat &= ~STA_NOINIT;				// Clear STA_NOINIT
		} else {
			/* Initialization failed */
			dprintf_text(" Init Failed ");
		}
	
		SD_SpiInit(_16MHz);
		return Stat;
	}
	return STA_NODISK;

}

/*******************************************************************************
* Function Name  : disk_ioctl
* Description    : Getting cstatus of disk
* Input          : Physical drive nmuber
* Output         : Status of disk
* Return         : DSTATUS
*******************************************************************************/ 
DSTATUS disk_status (
	BYTE drv		/* Physical drive nmuber (0) */
)
{
	if (drv) return STA_NOINIT;		/* Supports only single drive */
	return Stat;
}

/******************************************************************************/



/*******************************************************************************
* Function Name  : disk_ioctl
* Description    : Send/receive control data of card
* Input          : Physical drive nmuber, Control code, Buffer to send/receive control data
* Output         : RES_PARERR or RES_OK
* Return         : DRESULT
*******************************************************************************/ 
DRESULT disk_ioctl (
	BYTE drv,		/* Physical drive nmuber (0) */
	BYTE ctrl,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{
	DRESULT res;
 	BYTE response;
	BYTE n; 
	WORD csize;

	if (drv) return RES_PARERR;

	res = RES_ERROR;

	if (Stat & STA_NOINIT) return RES_NOTRDY;

	SD_CS_ASSERT;		/* CS = L */

	switch (ctrl) {
	case GET_SECTOR_COUNT :	/* Get number of sectors on the disk (DWORD) */
		if (SD_sendCommand(SEND_CSD, 0) == 1) {
			n = csd[0] >> 6;
			switch (n) {
			case 0 :	/* CSD ver 1.XX */
				n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
				csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
				*(DWORD*)buff = (DWORD)csize << (n - 9);
				res = RES_OK;
				break;
			case 1 :	/* CSD ver 2.00 */
				csize = csd[9] + ((WORD)csd[8] << 8) + 1;
				*(DWORD*)buff = (DWORD)csize << 10;
				res = RES_OK;
				break;	
			case 2 :	/* MMC */
				n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
				csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
				*(DWORD*)buff = (DWORD)csize << (n - 9);
				res = RES_OK;
				break;
			}
		}
		break;

	case GET_SECTOR_SIZE :						
		*(WORD*)buff = 512;
		res = RES_OK;
		break;

	case GET_BLOCK_SIZE:
        *(DWORD*)buff = 1;
        break;

	case CTRL_SYNC :							/* Make sure that data has been written */
		SD_CS_ASSERT;
		SetSdTimer(500);						//wait for SD card to complete writing and get idle
		do {
			response = SPI_transmit(0xFF);
		} 
		while (response != 0xFF && SdTimerFlag);
		if (response == 0xFF) {
			res = RES_OK;
		}	
		break;
	default:
		res = RES_PARERR;
	}
	

	SD_CS_ASSERT;
	SetSdTimer(500);							//wait for SD card to complete writing and get idle
	do {
		response = SPI_transmit(0xFF);
	} 
	while (response != 0xFF && SdTimerFlag);
	if (response == 0xFF) {
		res = RES_OK;
	}
	SD_CS_DEASSERT;								/* CS = H */

	SPI_transmit(0xFF);							/* Idle (Release DO) */

	return res;
}
/******************************************************************************/


/*******************************************************************************
* Function Name  : disk_read
* Description    : Wrapper of SD_readSingleBlock and SD_readMultipleBlock
* Input          : Physical drive nmuber, Pointer to the data to store read data, Start sector number (LBA), Sector count (1..255)
* Output         : RES_ERROR or RES_OK
* Return         : DRESULT
*******************************************************************************/ 
DRESULT disk_read (
	BYTE drv,			/* Physical drive nmuber (0) */
	BYTE *buff,			/* Pointer to the data buffer to store read data */
	DWORD sector,		/* Start sector number (LBA) */
	BYTE count			/* Sector count (1..255) */
)
{
	BYTE result;

	if (drv || !count) return RES_PARERR;
	if (Stat & STA_NOINIT) return RES_NOTRDY;

	if (count == 1) {
		result = SD_readSingleBlock(sector, buff); 
	} else {
		result = SD_readMultipleBlock (sector, count, buff); 	
	}
	return result ? RES_ERROR : RES_OK;
}
/******************************************************************************/


/*******************************************************************************
* Function Name  : disk_write
* Description    : Wrapper of SD_writeSingleBlock and SD_writeMultipleBlock
* Input          : Physical drive nmuber, pointer to the data to be written, 
* 				 : start sector number (LBA), Sector count (1..255)
* Output         : RES_ERROR or RES_OK
* Return         : DRESULT
*******************************************************************************/ 

#if _READONLY == 0
DRESULT disk_write (
	BYTE drv,			/* Physical drive nmuber (0) */
	BYTE *buff,			/* Pointer to the data to be written */
	DWORD sector,		/* Start sector number (LBA) */
	BYTE count			/* Sector count (1..255) */
)
{
	BYTE result;	

	if (drv || !count) return RES_PARERR;
	if (Stat & STA_NOINIT) return RES_NOTRDY;
	if (Stat & STA_PROTECT) return RES_WRPRT;

	if (count == 1) {
		result = SD_writeSingleBlock (sector, buff);
	} else { 	
		result = SD_writeMultipleBlock (sector, count, buff);
	} 
	return result ? RES_ERROR : RES_OK;
}
#endif 



/******************************************************************************/


/*******************************************************************************
* Function Name  : check_SD_write_permission
* Description    : Cheking is card writing protect or not
* Input          : None
* Output         : '1' if card is lock, '0' if it is unlock
* Return         : BYTE
*******************************************************************************/ 
BYTE check_SD_write_permission() {
	BYTE count = 0;
	BYTE i = 0;
	for (i = 0; i < 30; i++) {
		if (GPIO_ReadInputDataBit(GPIO_LOCK, WP_LOCKER)) {
			count ++;
		}
	}
	if (count > 20) {
		dprintf_text(" Writing and Formatting Is Not Enable ");
		return FALSE;
	}
	return TRUE;
}
/******************************************************************************/


/*******************************************************************************
* Function Name  : check_SD_insertion
* Description    : Cheking is card presence in socket
* Input          : None
* Output         : '1' if there is no card, '0' if it is presence or not
* Return         : BYTE
*******************************************************************************/ 
BYTE check_SD_insertion(void) {
	BYTE count = 0;
	BYTE i = 0;
	for (i = 0; i < 30; i++) {
		if (GPIO_ReadInputDataBit(GPIO_LOCK, CP_LOCKER)) {
			count ++;
		}
	}
	if (count > 20) {
		dprintf_text(" Card Is Not Insert ");
		return FALSE;
	}
	return TRUE;
}
/******************************************************************************/


/*******************************************************************************
* Function Name  : get_fattime
* Description    : Read RTC register of microcontroller and fat_time calculation 
* Input          : None
* Output         : System time
* Return         : DWORD
*******************************************************************************/
DWORD get_fattime (void) {	
	return GetSystemTime();
}
/******************************************************************************/


/*******************************************************************************
* Function Name  : scan_files
* Description    : Read files name of given directory 
* Input          : Pointer to reading directory, pointer to the path name working buffer
* Output         : Last (higher) file name
* Return         : FRESULT
*******************************************************************************/
FRESULT scan_files (
	char* path,						/* Pointer to reading directory */
	char* name_files				/* Pointer to the path name working buffer */
)
{
	char modify_name[20];		    // Buffer for keeping first part of file name (mantissa)
	char extension[4];				// Buffer for keeping extension of file 
	WORD intName = 0; 				// Integer state of file name 
	BYTE digN, extN;                // Counts of name and extension symbols
	WORD result = 0;
	DIR dirs;
	FRESULT res;
	FILINFO Finfo;		
	char *fn;

	if ((res = f_opendir(&dirs, path)) == FR_OK) {
		while (((res = f_readdir(&dirs, &Finfo)) == FR_OK) && Finfo.fname[0]) {
			if (_FS_RPATH && Finfo.fname[0] == '.') continue;
			fn = Finfo.fname;
			dprintf_text (fn);										   // Sequential out of root directories entry
			memset(modify_name, 0x00, sizeof(modify_name));		   // Clear buffers for mantissa end extension
			memset(extension, 0x00, sizeof(extension));
			for (digN = 0; fn[digN] != 0; digN++) {				   // Cickle until end of file name (\0) 
				if (fn[digN] >= 0x30 && fn[digN] <= 0x39) {		   // If name consist not only digit (except '.') - skip it
					modify_name[digN] = fn[digN]; 				   // Copy file name
				} else if (fn[digN] == '.') {
					for (extN = 0; extN < 3; extN++) { 
						 extension[extN] = fn[++digN];			   // Copy file extension
					}
					if ((strcmp(extension,"HDS\0") == 0) && ((Finfo.fattrib & AM_DIR) == 0)) {// If extension not HDS - skip file, and go far 
						//dprintf_param (" MANTISSA: %s ", modify_name);	
						//dprintf_param (" EXTENSION: %s ", extension);
						result = atoi(modify_name);				   // Convert file name into digit state and icrement it... 
						if (result >= intName) {
							intName = result;
							result++;
							itoa(result, name_files);			   //... reconvert digit state into new file name
						}
					} else {
						break;
					}
				} else {
				 	break;
				}
			}
		}
	}
	return res;
}
/******************************************************************************/

/*******************************************************************************
* Function Name  : SD_erase
* Description    : Erase specified number of blocks, received throughout argument
* Input          : byte : start (and number) erased block
* Output         : None 
* Return         : result of operation
*******************************************************************************/

BYTE SD_erase (DWORD start_block, DWORD total_blocks) {
	BYTE response = 0;

	if(MMC_flag == 1) {	 // If it is MMC-card
		response = SD_sendCommand(ERASE_GROUP_START_ADDR, start_block); 	//send starting block address
		if(response != 0x00) 												//check for SD status: 0x00 - OK (No flags set)
	  	return response;
	
		response = SD_sendCommand(ERASE_GROUP_END_ADDR, ((start_block + total_blocks) - 1)); //send end block address
		if(response != 0x00)
	  	return response;
	} else {			 // If we work with SD-card
		response = SD_sendCommand(ERASE_BLOCK_START_ADDR, start_block); 	//send starting block address
		if(response != 0x00) 												//check for SD status: 0x00 - OK (No flags set)
	  	return response;
	
		response = SD_sendCommand(ERASE_BLOCK_END_ADDR, ((start_block + total_blocks) - 1)); //send end block address
		if(response != 0x00)
	  	return response;	  
	}
	response = SD_sendCommand(ERASE_SELECTED_BLOCKS, 0); 			//erase all selected blocks
	if(response != 0x00)
  	return response;
	
	if(response == 0x00) {  					//wait 100 sec until erase complete	
		SD_CS_ASSERT; 
		SetSdTimer(100000);
		do {
			response = SPI_transmit(0xFF);
		}
		while(response != 0xFF && SdTimerFlag);     	
		if(response != 0xFF){
		  	SD_CS_DEASSERT; 
			return 1;						   	//return if time-out
		} 
	} 
	SD_CS_DEASSERT;

	response = SD_sendCommand(SEND_STATUS, 0);
	return response; 	// return status

}
/*******************************************************************************/

/*******************************************************************************
* Function Name  : GetSystemTime
* Description    : Return system time
* Input          : None
* Output         : System time int32
* Return         :	bit31:25
    				Year from 1980 (0..127)
					bit24:21
    				Month (1..12)
					bit20:16
    				Day in month(1..31)
					bit15:11
    				Hour (0..23)
					bit10:5
    				Minute (0..59)
					bit4:0
    				Second / 2 (0..29) 	
*******************************************************************************/
DWORD GetSystemTime (void) {
	//return getTimeDS1338();
	DWORD time = 0;

//	DWORD sec = 0;
//	DWORD min = 0;
//	DWORD hour = 0;
//	DWORD day = 0;
//	DWORD month = 0;
//	DWORD year = 0;

	RTC_DateTypeDef   RTC_DateStructure;
	RTC_TimeTypeDef   RTC_TimeStructure;

	RTC_GetDate(RTC_Format_BIN, &RTC_DateStructure);	    // Get the current Date 
	RTC_GetTime(RTC_Format_BIN, &RTC_TimeStructure);	    // Get the current Time 
												
	//dprintf_param(" RTC: %u", time);
	td.year =  ((BYTE)RTC_DateStructure.RTC_Year); 
	//dprintf_param(" Year: %u", year);
	td.month = ((BYTE) RTC_DateStructure.RTC_Month); 
	//dprintf_param(" Month: %u", month);
	td.day =   ((BYTE)RTC_DateStructure.RTC_Date); 
	//dprintf_param(" Day: %u", day);
	td.hour =  ((BYTE)RTC_TimeStructure.RTC_Hours); 
	//dprintf_param(" Hour: %u", hour);
	td.min =   ((BYTE)RTC_TimeStructure.RTC_Minutes); 
	//dprintf_param(" Min: %u", min);
	td.sec =   ((BYTE)((RTC_TimeStructure.RTC_Seconds)/2));
	//dprintf_param(" Sec: %u", sec);
	time =  (((20 + td.year) << 25) & 0xFE000000) | ((td.month << 21) & 0x01E00000) | ((td.day << 16) & 0x001F0000) | ((td.hour << 11) & 0x0000F800) | ((td.min << 5) & 0x000007E0) | (td.sec & 0x0000001F);	
	//dprintf_param (" System time now is: %u", time);
	return time;
	
}
/*******************************************************************************/

/*******************************************************************************
* Function Name  : SetSdTimer
* Description    : Init te Sd timing timer
* Input          : dword : time in ms
* Output         : None
* Return         : None
*******************************************************************************/
void SetSdTimer (DWORD timeout) {	
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_DeInit(TIM3);
	/* TIM3 clock enable */
  	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);		
	/* ---------------------------------------------------------------------------
			TIM_Period * TIM_Prescaler	
	Time = ---------------------------
				32 000 000
	----------------------------------------------------------------------------*/
  	/* Compute the prescaler value */
  	/* Time base configuration 	   */
	TIM_TimeBaseStructure.TIM_Period = timeout;	// значение до которого досчитывает таймер
  	TIM_TimeBaseStructure.TIM_Prescaler = 32000 - 1;
  	TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
  	/* TIM3 enable counter */
	TIM_ARRPreloadConfig(TIM3, ENABLE);			// разрешаем автоперезагрузку 
	TIM_ClearFlag(TIM3, TIM_FLAG_Update);	   	// Сбрасываем флаг перезагрузки
	TIM_Cmd(TIM3, ENABLE);						// включаем таймер и контролируем флаг перезагрузки  
}
/******************************************************************************/

///*******************************************************************************
//* Function Name  : SetSdTimer
//* Description    : Init te Sd timing timer
//* Input          : dword : time in ms
//* Output         : None
//* Return         : None
//*******************************************************************************/
//void SetSPITimer (DWORD timeout) {	
//	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
//	TIM_DeInit(TIM6);
//	/* TIM6 clock enable */
//  	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);		
//	/* ---------------------------------------------------------------------------
//			TIM_Period * TIM_Prescaler	
//	Time = ---------------------------
//				32 000 000
//	----------------------------------------------------------------------------*/
//  	/* Compute the prescaler value */
//  	/* Time base configuration 	   */
//	TIM_TimeBaseStructure.TIM_Period = timeout;	// значение до которого досчитывает таймер
//  	TIM_TimeBaseStructure.TIM_Prescaler = 32000 - 1;
//  	TIM_TimeBaseStructure.TIM_ClockDivision = 0;
//  	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
//  	TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
//  	/* TIM6 enable counter */
//	TIM_ARRPreloadConfig(TIM6, ENABLE);			// разрешаем автоперезагрузку 
//	TIM_ClearFlag(TIM6, TIM_FLAG_Update);	   	// Сбрасываем флаг перезагрузки
//	TIM_Cmd(TIM6, ENABLE);						// включаем таймер и контролируем флаг перезагрузки  
//}
///******************************************************************************/

/*******************************************************************************
* Function Name  : SetInitSdTimer
* Description    : Init the Sd init timing timer
* Input          : dword : time in ms
* Output         : None
* Return         : None
*******************************************************************************/
void SetInitSdTimer (DWORD timeout) {	
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_DeInit(TIM11);
	/* TIM11 clock enable */
  	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM11, ENABLE);		
	/* ---------------------------------------------------------------------------
			TIM_Period * TIM_Prescaler	
	Time = ---------------------------
				32 000 000
	----------------------------------------------------------------------------*/
  	/* Compute the prescaler value */
  	/* Time base configuration 	   */
	TIM_TimeBaseStructure.TIM_Period = timeout;	// значение до которого досчитывает таймер
  	TIM_TimeBaseStructure.TIM_Prescaler = 32000 - 1;
  	TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  	TIM_TimeBaseInit(TIM11, &TIM_TimeBaseStructure);
  	/* TIM11 enable counter */
	TIM_ARRPreloadConfig(TIM11, ENABLE);			// разрешаем автоперезагрузку 
	TIM_ClearFlag(TIM11, TIM_FLAG_Update);	   	// Сбрасываем флаг перезагрузки
	TIM_Cmd(TIM11, ENABLE);						// включаем таймер и контролируем флаг перезагрузки  
}
/******************************************************************************/

/*******************************************************************************
* Function Name  : SD_SpiInit
* Description    : Configures the SPI2.
* Input          : Value of frequency prescalers divider   
* Output         : None
* Return         : None
*******************************************************************************/
void SD_SpiInit(WORD prescaler) {
  	SPI_InitTypeDef  SPI_InitStructure;
  	GPIO_InitTypeDef GPIO_InitStructure;
  	/* Enable SPI2 and GPIO clocks */
  	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
  	/* Configure SPI2 pins: SCK, MISO and MOSI */
  	GPIO_InitStructure.GPIO_Pin = SCK | MISO | MOSI;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  	GPIO_Init(SD_PORT, &GPIO_InitStructure);
    /* Configure I/O for SD Chip select */
  	GPIO_InitStructure.GPIO_Pin = CS_SD;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
   	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  	GPIO_Init(GPIOB, &GPIO_InitStructure); 	
  	SD_CS_DEASSERT;								/* Deselect the FLASH: Chip Select high */
  	/* SPI2 configuration */
	SPI_DeInit(SPI2);
  	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
  	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	switch (prescaler) {
	case 2:
	  	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
		break;
	case 4:
	  	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
		break;
	case 8:
	  	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
		break;
	case 16:
	  	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
		break;
	case 32:
	  	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;
		break;
	case 64:
	  	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;
		break;
	case 128:
	  	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
		break;
	case 256:
	  	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
		break;
	default:
	  	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
		break;
	}
  	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  	SPI_InitStructure.SPI_CRCPolynomial = 7;
  	SPI_Init(SPI2, &SPI_InitStructure);	

	GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_SPI2);
  	GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_SPI2);
  	GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2);

  	SPI_Cmd(SPI2, ENABLE);					   /* Enable SPI2  */
}
/*******************************************************************************/



