////////////////////////////////////////////////////////////////////////////////

/// flash底层驱动
/// 
///
/// fatfs文件系统用的flsh底层驱动，支持w25qxx系列spiflash
/// @file    fatfs_flash_spi.c 
/// @author  gkl 
/// @date    2017-04-25
/// @version v0.1

////////////////////////////////////////////////////////////////////////////////
  
#include "fatfs_flash_spi.h"
#include "w25q128.h"
/// 扇区大小
#define FLASH_SECTOR_SIZE		4096
/// 扇区数量

#ifdef FLASH_W25Q128
/// 16M 16*1024/4096
#define FLASH_SECTOR_COUNT		4096
#endif

#ifdef FLASH_W25Q256
/// 32M 32*1024/4096
#define FLASH_SECTOR_COUNT		8192
#endif

//#define FLASH_BLOCK_SIZE		65536
/// 同时擦除扇区个数
#define FLASH_BLOCK_SIZE		1

static volatile u_int32_t  SPITimeout = SPIT_LONG_TIMEOUT;   
static volatile DSTATUS TM_FATFS_FLASH_SPI_Stat = STA_NOINIT;	
/// 超时函数
static u_int16_t SPI_TIMEOUT_UserCallback(void);
/// 申明函数
static void SPI_FLASH_SendByte(u_int8_t DataBuffer);
static void SPI_FLASH_WaitForWriteEnd(void);
static void SPI_FLASH_WriteEnable(void);
static u_int8_t SPI_FLASH_ReadByte();
static void SPI_FLASH_BufferRead(u_int8_t* pBuffer, u_int32_t ReadAddr, u_int32_t NumByteToRead);
static void SPI_FLASH_BufferWrite(u_int8_t* pBuffer, u_int32_t WriteAddr, u_int16_t WriteBytesNum);
static u_int32_t SPI_FLASH_ReadID();
static void SPI_FLASH_PageWrite(u_int8_t* pBuffer, u_int32_t WriteAddr, u_int16_t NumByteToWrite);
static void SPI_FLASH_SectorErase(u_int32_t SectorAddr);
static void SPI_Flash_GPIO_Init(void);

/// 初始化flsh芯片
/// 初始化单片机IO控制管脚，读取JEDEC ID，判断芯片是否工作正常
/// 
/// @param  none 
/// @param  none 
/// @return none  
/// @note   修改日志
///         gkl与2017-04-25创建
DSTATUS TM_FATFS_FLASH_SPI_disk_initialize(void)
{
    // 配置IO管脚，初始化flsh芯片
    SPI_Flash_GPIO_Init();
#ifdef FLASH_W25Q256
    Enter4ByteAddrMode();      //new add    进入4Byte模式
#endif
    
#ifdef FLASH_W25Q128    
      Exit4ByteAddrMode();     //new add    退出4Byte模式
#endif
    // 读取FLASH出厂唯一ID，检测FLASH是否正常功能
	if(sFLASH_ID == SPI_FLASH_ReadID()) { 			
		// 正常,清除没有初始化标志
	    FLASH_INFO("FLASH read JEDEC ID:%02x",sFLASH_ID);
		return TM_FATFS_FLASH_SPI_Stat &= ~STA_NOINIT;	
	} else {
	    FLASH_ERROR("FLASH read JEDEC ID failed! %02x",sFLASH_ID); 
		return TM_FATFS_FLASH_SPI_Stat |= STA_NOINIT;
	}
    
}

/// 检测flash状态是否正常是否正常工作 
/// 通过读取flash ID来判断芯片是否工作正常，每种芯片的该ID号相同
/// 
/// @param  none    
/// @param  none 
/// @return none 
/// @note   修改日志
///         gkl与2017-04-25创建
DSTATUS TM_FATFS_FLASH_SPI_disk_status(void)
{
	FLASH_DEBUG_FUNC();
	
	if(sFLASH_ID == SPI_FLASH_ReadID()) {		
		return TM_FATFS_FLASH_SPI_Stat &= ~STA_NOINIT;	
	} else {
		return TM_FATFS_FLASH_SPI_Stat |= STA_NOINIT;
	}
}

/// 获取flash相关信息 
/// 
/// @param  cmd 命名类型   
/// @param  *buff 获取到的数据返回值 
/// @return 固定返回RES_OK状态
/// @note   修改日志
///         gkl与2017-04-25创建
DRESULT TM_FATFS_FLASH_SPI_disk_ioctl(BYTE cmd, char *buff)
{
	FLASH_DEBUG_FUNC();

	switch (cmd) {
	// 扇区大小
	case GET_SECTOR_SIZE :     
		*(WORD * )buff = FLASH_SECTOR_SIZE;		
		break;
	// 同时擦除扇区个数
	case GET_BLOCK_SIZE :
		*(DWORD * )buff = FLASH_BLOCK_SIZE;	
		break;
	// 扇区数量	
	case GET_SECTOR_COUNT:
		*(DWORD * )buff = FLASH_SECTOR_COUNT;		
		break;
	case CTRL_SYNC :
		break;
	default:
		break;
	}
	return RES_OK;
}

/// 磁盘读取数据 
/// 
/// @param	*buff 存放读取数据的buff的地址     
/// @param  sector 读取sector的地址，取值（0，1，2……）
/// @param  count sector的个数，取值（0，1，2……） 
/// @return RES_OK 成功 RES_NOTRDY 芯片工作不正常  
/// @note   修改日志
///         gkl与2017-04-25创建
DRESULT TM_FATFS_FLASH_SPI_disk_read(BYTE *buff, DWORD sector, UINT count)
{
	FLASH_DEBUG_FUNC();
	if ((TM_FATFS_FLASH_SPI_Stat & STA_NOINIT)) {
	    FLASH_ERROR("TM_FATFS_FLASH_SPI_disk_read"); 
		return RES_NOTRDY;
	}
	// 左移12位，将sector数转化为芯片内部存储空间的地址 
	SPI_FLASH_BufferRead(buff, sector<<12, count<<12);

	return RES_OK;
}

/// 磁盘写数据 
///
/// @param	*buff 写数据buff的地址     
/// @param  sector 写数据开始sector地址，取值（0，1，2……） 
/// @param  count 写的sector个数 
/// @return 固定返回RES_OK
/// @note   修改日志
///         gkl与2017-04-25创建
DRESULT TM_FATFS_FLASH_SPI_disk_write(BYTE *buff, DWORD sector, UINT count)
{
	u_int32_t write_addr;  
	u_int32_t erase_addr;
	FLASH_DEBUG_FUNC();
 //   sector += 512; //new add 前面预留512个sector(512 * 4KB = 2MB)用作他用，2M以后的空间用作文件系统
	write_addr = sector<<12;    
	for (u_int8_t i=0; i < count; i++) {
		erase_addr = sector<<12;    
		SPI_FLASH_SectorErase(erase_addr);
		sector++;
	}
	SPI_FLASH_BufferWrite(buff,write_addr,count<<12);   //一个sector=4K
	return RES_OK;
}

/// 擦除一块sector 
///
/// @param  SectorAddr sector的块地址，取值（0，1，2……）   
/// @return none 
/// @note   修改日志
///         gkl与2017-04-25创建
static void SPI_FLASH_SectorErase(u_int32_t SectorAddr)
{
	SPI_FLASH_WriteEnable();
	SPI_FLASH_CS_ENABLE;
	SPI_FLASH_SendByte(W25X_Flash_SecErase_CMD);
#ifdef FLASH_W25Q256
    SPI_FLASH_SendByte((u_int8_t)((SectorAddr&0xff000000)>>24));  //new add
	SPI_FLASH_SendByte((u_int8_t)((SectorAddr&0x00ff0000)>>16));
	SPI_FLASH_SendByte((u_int8_t)((SectorAddr&0x0000ff00)>>8));
	SPI_FLASH_SendByte((u_int8_t)SectorAddr);
#endif 
    
#ifdef FLASH_W25Q128
	SPI_FLASH_SendByte((u_int8_t)((SectorAddr&0x00ff0000)>>16));
	SPI_FLASH_SendByte((u_int8_t)((SectorAddr&0x0000ff00)>>8));
	SPI_FLASH_SendByte((u_int8_t)SectorAddr);
#endif    
    
    
	SPI_FLASH_CS_DISABLE;
	SPI_FLASH_WaitForWriteEnd();
}

/// 擦除整块芯片 
/// 手册上说擦除整块需要1s-w25q128
/// @param  none   
/// @param  none 
/// @return none  
/// @note   修改日志
///         gkl与2017-04-26创建
///         测试会超时，没有通过
#if 0
static void SPI_FLASH_BulkErase()
{
	SPI_FLASH_WriteEnable();
	SPI_FLASH_WaitForWriteEnd();
	SPI_FLASH_CS_ENABLE;
	SPI_FLASH_SendByte(W25X_Flash_ChipErase_CMD);
	// 发送完指令后，cs必须拉高，否则指令不执行
	SPI_FLASH_CS_DISABLE;
	// The BUSY bit is a 1 during the Chip Erase cycle and becomes a 0 when 
	// finished and the device is ready to accept
    // other instructions again. 
	SPI_FLASH_WaitForWriteEnd();
}
#endif

/// flash底层驱动-页写 
/// 最大支持一页（256字节）数据写入 
/// 
/// @param  pBuffer  指针，指向存储写数据的buffer   
/// @param  WriteAddr 向flash内部写数据的地址 
/// @param  NumByteToRead 要写入的数据长度 
/// @return none 
/// @note   修改日志
///         gkl与2017-04-25创建 
static void SPI_FLASH_PageWrite(u_int8_t* pBuffer, u_int32_t WriteAddr, u_int16_t NumByteToWrite)
{
  	if (NumByteToWrite > W25X_Flash_PAGEBYTE_LENGTH) {	
     	NumByteToWrite = W25X_Flash_PAGEBYTE_LENGTH;
     	FLASH_ERROR("SPI_FLASH_PageWrite too large!");
  	}
	SPI_FLASH_WriteEnable();
	SPI_FLASH_CS_ENABLE;
	SPI_FLASH_SendByte(W25X_Flash_PageProgram_CMD);
#ifdef FLASH_W25Q256    
    SPI_FLASH_SendByte((u_int8_t)((WriteAddr&0xff000000)>>24));  //new add
	SPI_FLASH_SendByte((u_int8_t)((WriteAddr&0x00ff0000)>>16));
	SPI_FLASH_SendByte((u_int8_t)((WriteAddr&0x0000ff00)>>8));
	SPI_FLASH_SendByte((u_int8_t)WriteAddr);
#endif
    
#ifdef FLASH_W25Q128    
	SPI_FLASH_SendByte((u_int8_t)((WriteAddr&0x00ff0000)>>16));
	SPI_FLASH_SendByte((u_int8_t)((WriteAddr&0x0000ff00)>>8));
	SPI_FLASH_SendByte((u_int8_t)WriteAddr);
#endif    
    
	// while there is data to be written on the FLASH 
	while (NumByteToWrite--) {
		SPI_FLASH_SendByte(*pBuffer);
		pBuffer++;
	}
	SPI_FLASH_CS_DISABLE;
    // 每次写入一定数量的数据都要延时等待写入结束
  	SPI_FLASH_WaitForWriteEnd();
}

/// 连续向芯片写数据 
///
/// @param  pBuffer  指针，指向存储写数据的buffer   
/// @param  WriteAddr 向flash内部写数据的地址 
/// @param  NumByteToRead 要写入的数据长度 
/// @return none 
/// @note   修改日志
///         gkl与2017-04-25创建
static void SPI_FLASH_BufferWrite(u_int8_t* pBuffer, u_int32_t WriteAddr, u_int16_t NumByteToWrite)
{
    u_int16_t PageByteRemain = 0;
     
    PageByteRemain = SPI_FLASH_PageSize - WriteAddr%SPI_FLASH_PageSize;
    if(NumByteToWrite <= PageByteRemain) {
        PageByteRemain = NumByteToWrite;
    }
    while(1) {
        SPI_FLASH_PageWrite(pBuffer,WriteAddr,PageByteRemain);
        if(NumByteToWrite == PageByteRemain) {
            break;
        } else {
            pBuffer       += PageByteRemain;
            WriteAddr     += PageByteRemain;
            NumByteToWrite -= PageByteRemain;
            if (NumByteToWrite > SPI_FLASH_PageSize) {
                PageByteRemain = SPI_FLASH_PageSize;
            } else {
                PageByteRemain = NumByteToWrite;
            }
        }
    }
}


/// 连续从芯片读取数据 
///
/// @param  pBuffer  指针，指向存储接收数据的buffer   
/// @param  ReadAddr 从flash内部读取数据的地址 
/// @param  NumByteToRead 要读取的数据长度 
/// @return none 
/// @note   修改日志
///         gkl与2017-04-25创建
static void SPI_FLASH_BufferRead(u_int8_t* pBuffer, u_int32_t ReadAddr, u_int32_t NumByteToRead)
{
	SPI_FLASH_CS_ENABLE; 
#ifdef FLASH_W25Q256    
	SPI_FLASH_SendByte(W25X_Flash_ReadData_CMD);
    SPI_FLASH_SendByte((u_int8_t)((ReadAddr&0xff000000)>>24));  //new add
	SPI_FLASH_SendByte((u_int8_t)((ReadAddr&0x00ff0000)>>16));
	SPI_FLASH_SendByte((u_int8_t)((ReadAddr&0x0000ff00)>>8));
	SPI_FLASH_SendByte((u_int8_t)ReadAddr);
#endif
    
#ifdef FLASH_W25Q128
    SPI_FLASH_SendByte(W25X_Flash_ReadData_CMD);
	SPI_FLASH_SendByte((u_int8_t)((ReadAddr&0x00ff0000)>>16));
	SPI_FLASH_SendByte((u_int8_t)((ReadAddr&0x0000ff00)>>8));
	SPI_FLASH_SendByte((u_int8_t)ReadAddr);
#endif    
    
	// while there is data to be read 
	while (NumByteToRead--) { 
		*pBuffer = SPI_FLASH_ReadByte();
		// Point to the next location where the byte read will be saved 
		pBuffer++;
	}
	SPI_FLASH_CS_DISABLE;
}

/// 读取FLASH出厂唯一ID
///
/// @param  NULL   
/// @param  NULL
/// @return 返回u32型ID值
/// @note   修改日志
///         gkl与2017-04-25创建
static u_int32_t SPI_FLASH_ReadID()
{   
    int nID = 0;
	
    SPI_FLASH_CS_ENABLE; 
	SPI_FLASH_SendByte(W25X_Flash_JedecDeviceID_CMD);
	nID = SPI_FLASH_ReadByte();
	nID <<= 8;
	nID |= SPI_FLASH_ReadByte();
	nID <<= 8;
	nID |= SPI_FLASH_ReadByte();
	SPI_FLASH_CS_DISABLE;
	
  return nID;
}
	
/// 读取一个字节数据
///
/// @param  none   
/// @param	none 
/// @return 读取到的值 
/// @note   修改日志
///         gkl与2017-04-25创建
static u_int8_t SPI_FLASH_ReadByte()
{
 	u_int8_t retValue = 0;
	// 时钟线拉高,恢复时钟线为高电平
    SPI_FLASH_CLK_HIGH;            
    for (u_int8_t i= 0; i < 8; i++) {
        retValue <<= 1;
        SPI_FLASH_CLK_HIGH;           
        if (SPI_FLASH_DO_READ) {
            retValue |= 0x01;
        } else {
            retValue &= 0xFE;
        }
		// 时钟线拉低,产生下降沿读出数据
        SPI_FLASH_CLK_LOW;            
    }
    SPI_FLASH_CLK_HIGH;

    return retValue;
}

/// 写一个字节数据
///
/// @param  byte 需写到flash中的数   
/// @param	none 
/// @return 没有用返回值 
/// @note   修改日志
///         gkl与2017-04-25创建
static void SPI_FLASH_SendByte(u_int8_t DataBuffer)
{
 	// 时钟线拉低，恢复时钟线为低电平
	SPI_FLASH_CLK_LOW;         
	for (u_int8_t i = 0; i < 8; i++) {
		SPI_FLASH_CLK_LOW;         
		if (DataBuffer & 0x80) {
			SPI_FLASH_DI_HIGH;
		} else {
			SPI_FLASH_DI_LOW;
		}
		DataBuffer <<= 1;
		//	时钟线拉高，产生上升沿写入数据
		SPI_FLASH_CLK_HIGH; 
	}
	SPI_FLASH_CLK_LOW;
	//	一字节数据传送完毕，MOSI数据线置高表示空闲状态
	SPI_FLASH_DI_HIGH; 
}

/// 写使能 
///
/// @param  none   
/// @param	none 
/// @return none 
/// @note   修改日志
///         gkl与2017-04-25创建
static void SPI_FLASH_WriteEnable(void)
{
	SPI_FLASH_CS_ENABLE;
	SPI_FLASH_SendByte(W25X_Flash_WriteEnable_CMD);
	SPI_FLASH_CS_DISABLE;
}

/// 等待芯片操作完成
/// 通过读取芯片状态寄存器，判断busy来判断芯片上一指令是否操作完成，是否可以进行下一指令操作
/// 如果芯片处于busy状态，则一直等待，超时打印错误信息
///
/// @param  none   
/// @param  none
/// @return none
/// @note   修改日志
///         gkl与2017-04-25创建
static void SPI_FLASH_WaitForWriteEnd(void)
{
  u_int8_t FLASH_Status = 0xFF;

  SPITimeout = SPIT_LONG_TIMEOUT;
  
  // Loop as long as the memory is busy with a write cycle 
  do {
  	SPI_FLASH_CS_ENABLE;
  	SPI_FLASH_SendByte(W25X_Flash_ReadSR_CMD);
    FLASH_Status = SPI_FLASH_ReadByte();
	SPI_FLASH_CS_DISABLE;
	
	if ((SPITimeout--) == 0) { 
		SPI_TIMEOUT_UserCallback();
		return;
	}
  } while ((FLASH_Status & W25X_FLASH_WRITE_BUSYBIT) == 0x01); 
  FLASH_INFO("time:%d",SPITimeout); 
}

/// 出错函数 
///
/// @param  none   
/// @param  none
/// @return none
/// @note   修改日志
///         gkl与2017-04-25创建
static uint16_t SPI_TIMEOUT_UserCallback(void)
{
  // Block communication and all processes 
  FLASH_ERROR("SPI Timeout error!");
  return 0;
}
 
/// 初始化flsh gpio管脚 
///
/// @param  none   
/// @param  none
/// @return none
/// @note   修改日志
///         gkl与2017-04-26创建
static void SPI_Flash_GPIO_Init(void)
{   
    drv_gpio_open(PORTB, 4,  OUTPUT, 1);//WP
    drv_gpio_open(PORTB, 3,  OUTPUT, 1);//DI
    drv_gpio_open(PORTB, 2,  OUTPUT, 1);//CLK
    drv_gpio_open(PORTA, 29, OUTPUT, 1);//HOLD
    drv_gpio_open(PORTA, 28, OUTPUT, 1);//CS
    drv_gpio_open(PORTA, 27, INPUT, 1); //DO 输入 内部上拉
   
    SPI_FLASH_CS_DISABLE;
    SPI_FLASH_HOLD_HIGH;
    SPI_FLASH_CLK_HIGH;
    SPI_FLASH_WP_ENABLE;
    SPI_FLASH_DI_LOW;
}

/// 
///
/// @param  none   
/// @param  none
/// @return none
/// @note   修改日志
///         gkl与2017-04-26创建
int GetGBKCode_from_EXFlash(unsigned char* pBuffer,const unsigned char * c)
{ 
    unsigned char High8bit,Low8bit;
    unsigned int pos;
    High8bit=*c;     
    Low8bit=*(c+1);  
	
    pos = ((High8bit-0xa0-16)*94+Low8bit-0xa0-1)*2*16;
		SPI_FLASH_BufferRead(pBuffer,4096+pos,32);
		return 0;  
     
}
