#include "W25Q128_Ins.h" 
#include "MYSPI.h"
#include "SysTick.h"
#include "USART.h"
u16 W25QXX_TYPE=W25Q128; //型号W25Q128，4K字节为一个扇区，16个扇区为1个块，容量为16M字节,共128个块,4096个扇区//               
//初始化SPI FLASH的IO口//
void W25QXX_Init(void)
{ 
 GPIO_InitTypeDef GPIO_InitStructure;
 RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );//PORTB时钟使能 
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;             // PB12，对应该闪存的CS引脚// 
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;       //推挽输出//
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOA, &GPIO_InitStructure);
 GPIO_SetBits(GPIOA,GPIO_Pin_4);                       //PB12置高电平//
 W25QXX_CS=1;                                           //SPI FLASH不选中，其实跟上一行代码实现功能一样//
 SPI1_Init();                                           //初始化SPI//
 SPI1_SetSpeed(SPI_BaudRatePrescaler_2);                //重新设置波特率为18M时钟,高速模式//
 W25QXX_TYPE=W25QXX_ReadID();                           //读取FLASH ID//  
}  
//编写读取W25QXX的状态寄存器函数，这里只读取S0-S7位的值//
u8 W25QXX_ReadSR(void)   
{  
 u8 byte=0;   
 W25QXX_CS=0;                            //使能器件//   
 SPI1_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器指令，W25X_ReadStatusReg=0x05，可忽略接收值//     
 byte=SPI1_ReadWriteByte(0xff);          //读取一个字节，发送0xff，读取回来的值传至byte//  
 W25QXX_CS=1;                            //取消片选     
 return byte;   
} 
//编写写入W25QXX状态寄存器函数，这里只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写？？？//
void W25QXX_Write_SR(u8 sr)   
{   
 W25QXX_CS=0;                            //使能器件//   
 SPI1_ReadWriteByte(W25X_WriteStatusReg);//发送写入状态寄存器指令，W25X_WriteStatusReg=0x01，忽略接收值//     
 SPI1_ReadWriteByte(sr);                 //写入一个字节，忽略接收值//  
 W25QXX_CS=1;                            //取消片选//            
}   
//W25QXX写入使能，将WEL置1//   
void W25QXX_Write_Enable(void)   
{
 W25QXX_CS=0;                             //W25QXX_CS对应PBout(12)，即PB12置0//    
 SPI1_ReadWriteByte(W25X_WriteEnable);    //写入使能，W25X_WriteEnable的指令为0x06//  
 W25QXX_CS=1;                             //取消片选，即PB12置1//            
} 
//W25QXX写入禁止，将WEL清零，即置0//  
void W25QXX_Write_Disable(void)   
{  
 W25QXX_CS=0;                              //使能器件//   
 SPI1_ReadWriteByte(W25X_WriteDisable);    //发送写入禁止指令，W25X_WriteDisable为0x04//     
 W25QXX_CS=1;                              //取消片选//            
}   
//读取芯片ID，本开发版返回值0XEF17,表示芯片型号为W25Q128//    
u16 W25QXX_ReadID(void)
{
 u16 Temp = 0;   
 W25QXX_CS=0;        
 SPI1_ReadWriteByte(0x90);           //发送读取ID命令，即0x90//     
 SPI1_ReadWriteByte(0x00);           //后面跟一个24位0x000000地址//
 SPI1_ReadWriteByte(0x00);      
 SPI1_ReadWriteByte(0x00);         
 Temp|=SPI1_ReadWriteByte(0xFF)<<8;  //读取Manufacturer ID号，即0xEF//
 Temp|=SPI1_ReadWriteByte(0xFF);     //读取Device ID号，即0x17//
 W25QXX_CS=1;        
 return Temp;
}         
//pBuffer:数据存储区，ReadAddr:开始读取的地址(24bit)，NumByteToRead:要读取的字节数(最大65535)//
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   
{ 
  u16 i;                 
  W25QXX_CS=0;                                //使能器件//   
  SPI1_ReadWriteByte(W25X_ReadData);          //发送读取指令,W25X_ReadData为0x03//   
  SPI1_ReadWriteByte((u8)((ReadAddr)>>16));   //发送24bit地址，最低的8位//    
  SPI1_ReadWriteByte((u8)((ReadAddr)>>8));    //发送24bit地址，次低的8位//  
  SPI1_ReadWriteByte((u8)ReadAddr);           //发送24bit地址，高8位// 
  for(i=0;i<NumByteToRead;i++)
 { 
    pBuffer[i]=SPI1_ReadWriteByte(0XFF);      //循环读数，每次读1个字节//  
   }
 W25QXX_CS=1;                 
}  
//SPI在一页(0~65535个字节)内写入少于256个字节的数据//
//pBuffer:数据存储区，WriteAddr:开始写入的地址(24bit)，NumByteToWrite:要写入的字节数(最大256),不应超过该页的剩余字节数//  
void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
  u16 i;  
  W25QXX_Write_Enable();                      //SET WEL// 
  W25QXX_CS=0;                                //使能器件//   
  SPI1_ReadWriteByte(W25X_PageProgram);       //发送写页命令，W25X_PageProgram为0x02//    
  SPI1_ReadWriteByte((u8)((WriteAddr)>>16));  //发送24bit地址，最低的8位//    
  SPI1_ReadWriteByte((u8)((WriteAddr)>>8));   //发送24bit地址，次低的8位//
  SPI1_ReadWriteByte((u8)WriteAddr);          //发送24bit地址，高8位//
  for(i=0;i<NumByteToWrite;i++)
   SPI1_ReadWriteByte(pBuffer[i]);            //循环写数,每次写1个字节//  
  W25QXX_CS=1;                                //取消片选// 
  W25QXX_Wait_Busy();                         //等待写入结束//
} 
//无检验写入SPI FLASH，确保所写地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败//
//具有自动换页功能，在指定地址开始写入指定长度的数据,但是要确保地址不越界//
//pBuffer:数据存储区，WriteAddr:开始写入的地址(24bit)，NumByteToWrite:要写入的字节数(最大65535)//
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
{        
 u16 pageremain;   
 pageremain=256-WriteAddr%256;                      //单页剩余的字节数//        
 if(NumByteToWrite<=pageremain)                     //判断要写入的字节数是否小于单页剩余的字节数//
  pageremain=NumByteToWrite;                        //如果小于，就将NumByteToWrite值赋给pageremain//             
 while(1)
 {    
  W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);  //一次写入少于256个字节的数据//
  if(NumByteToWrite==pageremain)break;              //如果写入字节数小于剩余字节数，则写完//
   else 
  {
   pBuffer+=pageremain;                           
   WriteAddr+=pageremain; 
   NumByteToWrite-=pageremain;                      //减去已经写入了的字节数//
   if(NumByteToWrite>256)pageremain=256;            //一次可以写入256个字节//
   else pageremain=NumByteToWrite;                  //不够256个字节//
  }
 };     
} 
//写SPI FLASH，在指定地址开始写入指定长度的数据，该函数带擦除操作//
//pBuffer:数据存储区，WriteAddr:开始写入的地址(24bit)，NumByteToWrite:要写入的字节数(最大65535)//   
u8 W25QXX_BUFFER[4096];                            //4096个字节为一个扇区，扇区是最小的擦除单位//  
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
{ 
 u32 secpos;
 u16 secoff;
 u16 secremain;    
 u16 i;    
 u8  *W25QXX_BUF;   
 W25QXX_BUF=W25QXX_BUFFER;      
 secpos=WriteAddr/4096;                                //扇区起始地址//  
 secoff=WriteAddr%4096;                                //在该扇区内的偏移量//
 secremain=4096-secoff;                                //扇区剩余空间大小//   
 if(NumByteToWrite<=secremain)
  secremain=NumByteToWrite;                            //不大于4096个字节//
 while(1) 
 {
  W25QXX_Read(W25QXX_BUF,secpos*4096,4096);            //读出整个扇区的内容，保存在W25QXX_BUF里//
  for(i=0;i<secremain;i++)                             //校验数据
  {
   if(W25QXX_BUF[secoff+i]!=0XFF)break;                //判断是否需要擦除//     
  }
  if(i<secremain)                                     
  {
   W25QXX_Erase_Sector(secpos);                        //擦除这个扇区//
   for(i=0;i<secremain;i++)                      
   {
    W25QXX_BUF[i+secoff]=pBuffer[i];                   //把要写的数据写到W25QXX_BUF缓存中//
    }
   W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);  //一次性吧缓存W25QXX_BUFFER的数据写到对应的sector// 
  }
  else 
    W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain); //写已经擦除了的,直接写入扇区剩余区间//        
  if(NumByteToWrite==secremain)break;                  //写入结束了//
  else                                                 //写入未结束//
  {
   secpos++;                                           //扇区地址增1//
   secoff=0;                                           //偏移位置为0//   
   pBuffer+=secremain;                                 //指针偏移//
   WriteAddr+=secremain;                               //写地址偏移//    
   NumByteToWrite-=secremain;                          //字节数递减//
   if(NumByteToWrite>4096)
    secremain=4096;                                    //下一个扇区还是写不完//
   else 
    secremain=NumByteToWrite;                          //下一个扇区可以写完//
  }  
 };  
}
//擦除整个芯片，等待时间超长//
void W25QXX_Erase_Chip(void)   
{                                   
  W25QXX_Write_Enable();                      //SET WEL// 
  W25QXX_Wait_Busy();   
  W25QXX_CS=0;                                //使能器件//   
  SPI1_ReadWriteByte(W25X_ChipErase);         //发送片擦除命令,W25X_ChipErase为0xC7//  
  W25QXX_CS=1;                                //取消片选// 
  W25QXX_Wait_Busy();                         //等待芯片擦除结束//
}   
//擦除一个扇区，Dst_Addr:扇区地址 根据实际容量设置，擦除一个扇区的最少时间:150ms//
void W25QXX_Erase_Sector(u32 Dst_Addr)   
{    
  Dst_Addr*=4096;
  W25QXX_Write_Enable();                      //SET WEL//   
  W25QXX_Wait_Busy();   
  W25QXX_CS=0;                                //使能器件//   
  SPI1_ReadWriteByte(W25X_SectorErase);       //发送扇区擦除指令,W25X_SectorErase为0x20// 
  SPI1_ReadWriteByte((u8)((Dst_Addr)>>16));   //发送24bit地址//    
  SPI1_ReadWriteByte((u8)((Dst_Addr)>>8));   
  SPI1_ReadWriteByte((u8)Dst_Addr);  
  W25QXX_CS=1;                                //取消片选//            
  W25QXX_Wait_Busy();                         //等待擦除完成//
}  
//等待空闲函数，只有当状态寄存器的S0位（BUSY）置0时，循环结束//
void W25QXX_Wait_Busy(void)   
{   
 while((W25QXX_ReadSR()&0x01)==0x01);    
 }  
//进入掉电模式//
void W25QXX_PowerDown(void)   
{ 
  W25QXX_CS=0;                                //使能器件//   
  SPI1_ReadWriteByte(W25X_PowerDown);         //发送掉电命令，W25X_PowerDown为0xB9//  
  W25QXX_CS=1;                                //取消片选//            
  delay_us(3);                                //等待tDP//  
}   
//唤醒，释放掉电模式//
void W25QXX_WAKEUP(void)   
{  
  W25QXX_CS=0;                               //使能器件//   
  SPI1_ReadWriteByte(W25X_ReleasePowerDown); //发生释放掉电指令，ReleasePowerDown为0xAB//    
  W25QXX_CS=1;                               //取消片选//            
  delay_us(3);                               //等待TRES1//
}  

