
// W25Q128FV 16 MByte-os FLASH memoria kezelése szoftveres SPI -vel.
//  ( részletek )


//COMMANDS
#define W_EN 	    0x06	//write enable
#define W_DE	    0x04	//write disable
#define R_SR1       0x05	//read status reg 1
#define R_SR2	    0x35	//read status reg 2
#define R_SR3	    0x15	//read status reg 3
#define W_SR1	    0x01	//write status reg 1
#define W_SR2	    0x31	//write status reg 2
#define W_SR3	    0x11	//write status reg 3
#define PAGE_PGM	0x02	//page program
#define QPAGE_PGM	0x32	//quad input page program
#define BLK_E_64K	0xD8	//block erase 64KB
#define BLK_E_32K	0x52	//block erase 32KB
#define SECTOR_E	0x20	//sector erase 4KB
#define CHIP_ERASE	0xc7	//chip erase
#define CHIP_ERASE2	0x60	//=CHIP_ERASE
#define E_SUSPEND	0x75	//erase suspend
#define E_RESUME	0x7a	//erase resume
#define PDWN		0xb9	//power down
#define HIGH_PERF_M	0xa3	//high performance mode
#define CONT_R_RST	0xff	//continuous read mode reset
#define RELEASE		0xab	//release power down or HPM/Dev ID (deprecated)
#define R_MANUF_ID	0x90	//read Manufacturer and Dev ID (deprecated)
#define R_UNIQUE_ID	0x4b	//read unique ID (suggested)
#define R_JEDEC_ID	0x9f	//read JEDEC ID = Manuf+ID (suggested)
#define READ		0x03
#define FAST_READ	0x0b
#define GLOBAL_UNLOCK 0x98  //Global Block/Sector Unlock
#define GLOBAL_LOCK   0x7E  //Global Block/Sector Lock

#define SR1_BUSY_MASK	0x01
#define SR1_WEN_MASK	0x02

#define WINBOND_MANUF	0xef

#define DEFAULT_TIMEOUT 200 //  

#define select()    CS = 0
#define deselect()  CS = 1



void EFlashTest(void){
  unsigned int i;
  unsigned char bf[256];
  unsigned char x;
  unsigned int w;
  unsigned long int ww;
  //
  wake_up();
  Nop();
  ww = readSR();
  Nop();
  SetCMPState(0);
  ww = readSR();
  Nop();
  Nop();
  setWEL(W_EN);
  SetCMPState(0);
  ww = readSR();
  Nop();
  Nop();
  ww = readSR();
  Nop();
  Nop();
  w = readPartID();
  Nop();
  Nop();
  x = readManufacturer();
  //
  if(!SetFlashProtect(FLASH_UNPROTECT)) return;   // error
  Nop();
  Nop();
  ww = readSR();
  Nop();
  Nop();
  //eraseSector(0,4096);
  GlobalUnlock();
  Nop();
  //SetCMPState(1);
  Nop();
  ww = readSR();
  Nop();
  Nop();
  //
  for(i=0;i<=255;i++)
    bf[i] = i;
  w = write(512,(uint8_t *)&bf[0],10);   // WRITE
  Nop();
  Nop();
  Nop();
  Nop();
  Nop();
  Nop();
  ww = readSR();
  Nop();
  Nop();
  //
  for(i=0;i<=255;i++)            // törlés
    bf[i] = 0;
  
  w = read (512,(uint8_t *)&bf[0],10);   // READF
  Nop();
  Nop();
  ww = readSR();
  Nop();
  Nop();
  Nop();
  power_down(); 
}



// A FLASH a CLK LH élnél olvassa be a biteket,
// és a HL élnél küldi ki a biteket.
// SCK = 0 alaphelyzet !!
// KÜLDÉS  : 7.bit elöször
// OLVASÁS : 7.bit jön elöször
/* INPUT :
     x     - ezt küldi ki
   OUTPUT :
           - beolvasott byte
 */
static unsigned char transfer(unsigned char x){
  unsigned char cnt, i, iv, b;
  //
  iv = 1;
  SCK = 0;
  cnt = 8;
  while(cnt--){
    // vátel
    b = SDI;
    // ADÁS
    if(x & 128) SDO = 1;   // 7.bit kiküldi
    else SDO = 0;
    SCK = 1;               // LH él, FLASH beolvassa a bitet
    x <<= 1;
    // 
    //for(i=0;i<iv;i++);     // várakozás
    x |= b; 
    SCK = 0;               // HL él, FLASH küldi a bitet
  }
  return(x);  
} 



//
/*  
    WPS  CMP  SEC  TB  BP2  BP1  BP0  PROTECTED
     0    0    x   x    0    0    0     NONE
     0    0    x   x    1    1    1     16MB
     0    1    x   x    0    0    0     16MB
     0    1    x   x    1    1    1     NONE
 
 INPUT :
 st     - állapot  (1= PROTECT ON)
 */
unsigned char SetFlashProtect(unsigned char st){
  unsigned long int ww;
  unsigned char r2;
  //
  if(st>1) return(0);       // hibás állapot
  // LB1..3 = 0 beállitása
  ww = readSR();
  r2 = (ww>>8);
  r2 &= 0b11000111;         // 3..5 bitek = 0 lesz
  //
  setWEL(W_EN);
  select();
  transfer(W_SR2);
  transfer(r2);
  deselect();
  // PROTECT ON
  SetWPSState(0);
  SetCMPState(st);           // állapot beállitás
  //
  return(1);
}



// STATUS reg READ
/*
 OUTPUT :
          - word, Lo=STreg1, Hi=STreg2, HiHi=STreg3
*/
uint32_t readSR(void)
{
  uint8_t r1,r2,r3;
  uint32_t ww;
  //
  select();
  transfer(R_SR1);
  r1 = transfer(0xff);
  deselect();
  //
  deselect();//some delay
  select();
  transfer(R_SR2);
  r2 = transfer(0xff);
  deselect();
  //
  deselect();//some delay
  select();
  transfer(R_SR3);
  r3 = transfer(0xff);
  deselect();
  ww = r3;
  ww <<= 8;
  ww += r2;
  ww <<= 8;
  ww += r1;
  return (ww);
}


void SetCMPState(uint8_t state){
  uint8_t r2;
  // Beolvassuk
  select();
  transfer(R_SR2);
  r2 = transfer(0xFF);
  deselect();
  //
  setWEL(W_EN);
  r2 &= 0b10111111;       // 6.bit = 0 lesz
  if(state) r2 |= 64;      // SET, ha kell
  //
  select();
  transfer(W_SR2);
  transfer(r2);
  deselect();
}



// Müvelet befejezésére vár megadott ideig, a BUSY-t nézi...
/* INPUT :
     tmo    - time out ms-ban
 
 */
uint8_t   sync(uint16_t tmo)
{
  uint8_t c;
  //
  WaitmsAsync(tmo);

  select();
  transfer(R_SR1);
  do {
    c = transfer(0x00);
    if(!(c & SR1_BUSY_MASK)) //not busy
    {
      deselect();
      return(1);
    }
    for(c=0;c<100;c++);        // várakozás 
  }while(GetEndAsyncWait());   // kilép ha lejárt a tmo
  deselect();
  return false;
}



// Write Enable Latch
/* INPUT:
    pk   - W_EN/W_DE
 */
void  setWEL(uint8_t pk)
{
  unsigned long int ww;
  w_cmd = pk;        // 
  select();
  transfer(w_cmd);
  deselect();
  ww = readSR();
  Nop();
  Nop();
}


// READ
/* INPUT :
     addr   - 3 byte-os cím
     *buf   - mutato a beolvasott adatok mentési cimére
     n      - méret
   OUTPUT :
            - beolvasott byteszám
 */
uint16_t  read (uint32_t addr,uint8_t *buf,uint16_t n)
{
  uint16_t i;
  //
  if(!sync(50))
    return(0);
  //  
  select();
  transfer(READ);
  transfer(addr>>16);
  transfer(addr>>8);
  transfer(addr);
  for(i=0;i<n;i++)
  {
    *buf++ = transfer(0xFF);
  }
  deselect();
  //
  return(n);
}



// WRITE
// Akármennyi byte-ot beir !
/* INPUT :
     addr   - 3 byte-os cím
     *bufp  - mutato a beirando adatokra
     n      - méret
   OUTPUT :
            - beirt byteszám
 */
uint16_t  write(uint32_t addr,uint8_t *bufp,uint16_t n)
{
  uint16_t i;
  uint8_t k;
  uint8_t a;
  uint16_t start_page;
  uint16_t end_page;
  uint16_t j;
  uint32_t ww;
  //
  i = 0;
  if(addr & 0x000000ff)       // nem LAP határon van
  {
    // TÖREDÉK LAP BEIRÁSA
    k = 0xff - (uint8_t)addr + 1;
    //remaining bytes in current page
    if(!sync(50))
      return 0;
    setWEL(W_EN);
    //write min(n,k) bytes
    a = n > k ? k : n;        // beirhato byteszám a lapra
    select();
    transfer(PAGE_PGM);
    transfer((uint8_t)(addr>>16));
    transfer((uint8_t)(addr>>8));
    transfer((uint8_t)addr);
    for(i=0;i<a;i++)
    {
      transfer(bufp[i]);
    }
    deselect();
    if(n<=k)                  //all data written
      return i;
    //  
    n -= k;                   // a beirtakat levonjuk
    addr = (addr & 0x00ffff00) + 256;
  }
  // több LAP beirása
  end_page = (addr + (uint32_t)n) >> 8;
  for(start_page=addr>>8;start_page<end_page;start_page++)
  {
    if(!sync(50))
      return i;
    setWEL(W_EN);
    select();
    transfer(start_page>>8);
    transfer(start_page);
    transfer(0x00);
    //bufp += i;
    for(j=0;j<=255;j++)
      transfer(bufp[i+j]);
    i+=256;
    deselect();
  }
  // MARADÉK byte-ok beirása
  if(i < n)
  {
    if(!sync(50))
      return i;
    setWEL(W_EN);
    ww = readSR();
    select();
    transfer(end_page>>8);
    transfer(end_page);
    transfer(0x00);
    //bufp += i;
    for(j=0;j<=n-i;j++)
      transfer(bufp[i+j]);
    deselect();
  }
  return n;
}


/* INPUT :
     addr_start   - szektor cime
     n            - szektor mérete
   OUTPUT :
                  - =1, ha sikeres volt
 */
uint8_t   eraseSector(uint32_t addr_start,uint32_t n)
{
  if(n==0)
    return false;
  if(n%4096)                  //test if n is 12bit-aligned
    return false;
  if(addr_start & 0x00000fff) //test if addr_start is 12bit-aligned
    return false;
  //  
  setWEL(W_EN);
  select();
  transfer(SECTOR_E);
  transfer(addr_start>>16);
  transfer(addr_start>>8);
  transfer(addr_start);
  deselect();
  //
  return(1);  
}


void GlobalUnlock(void){
  if(!sync(2))
    return;
  setWEL(W_EN);
  select();
  transfer(GLOBAL_UNLOCK);
  deselect();
}