/************************************************/
/*Flash Flash Memory Configuration Code         */
/*SST39VF040    4 Mbit                          */
/*PIC32MX795F512L-80I/PF                        */
/*PIC32MX695F512L-80I/PF                        */
/************************************************/

#include "xc.h"
#include "hardware.h"

uint8_t data_flash;
unsigned long address_flash;
int Flash_MID;
int Flash_DID;
int flash_size;
unsigned long Bytes_used;

void Flash_Init(void)
{
    int dly = 100;
    //RB8 = A16
    AD1PCFGbits.PCFG8 = 1;
    TRISBbits.TRISB8 = 0;
    PORTBbits.RB8 = 0;
    
    //RA5 = A17
    TRISAbits.TRISA5 = 0;
    PORTAbits.RA5 = 0;
    
    //RA3 = A18
    TRISAbits.TRISA3 = 0;
    PORTAbits.RA3 = 0;
        
    //RF12 = /CS2
    TRISFbits.TRISF12 = 0;
    PORTFbits.RF12 = 1;
    
//******************************************************    
//          Get Manufacturer ID
//******************************************************    
    //RF12 = /CS2
    PORTFbits.RF12 = 0;
    
    //Enter sequence
    PMADDR = 0x5555;
    PMDIN = 0xaa;
    while(PMMODEbits.BUSY == 1);  
   
    PMADDR = 0x2aaa;
    PMDIN = 0x55;
    while(PMMODEbits.BUSY == 1);  
    
    PMADDR = 0x5555;
    PMDIN = 0x90;
    while(PMMODEbits.BUSY == 1);  
            
    Delay(dly);
    
    //read device ID
    PMADDR = 0x0;
    //dummy read then real data
    Flash_MID = PMDIN;
    while(PMMODEbits.BUSY == 1); 
    
    Flash_MID = PMDIN;
    while(PMMODEbits.BUSY == 1);  
    
    //Exit sequence
    PMDIN = 0xf0;
    while(PMMODEbits.BUSY == 1);  
            
    Delay(dly);
    
    PORTFbits.RF12 = 1;
//*******************************************************    
    
    //Get Device ID
    //upper address lines
    PORTBbits.RB8 = 0;
    
    //RF12 = /CS2
    PORTFbits.RF12 = 0;
    
    //Get Chip ID
    //Enter sequence
    PMADDR = 0x5555;
    PMDIN = 0xaa;
    while(PMMODEbits.BUSY == 1);  
   
    PMADDR = 0x2aaa;
    PMDIN = 0x55;
    while(PMMODEbits.BUSY == 1);  
    
    PMADDR = 0x5555;
    PMDIN = 0x90;
    while(PMMODEbits.BUSY == 1);  
            
    Delay(dly);
    
    //read device ID
    PMADDR = 0x1;
    //dummy read then real data
    Flash_DID = PMDIN;
    while(PMMODEbits.BUSY == 1); 
    
    Flash_DID = PMDIN;
    while(PMMODEbits.BUSY == 1);  
    
    //Exit sequence
    PMDIN = 0xf0;
    while(PMMODEbits.BUSY == 1);  
            
    Delay(dly);
    
    PORTFbits.RF12 = 1;
    
    //Determine the Flash size
    switch(Flash_DID)
    {
      case 0xd5 :
            flash_size = 0x1ffff;
            break;
         
      case 0xd6 :
            flash_size = 0x3ffff;
            break;
         
      case 0xd7 :
            flash_size = 0x7ffff;
            break;
    }

    return;
}

void Flash_GetMID(void)
{  
    int dly = 100;
    //upper address lines
    PORTBbits.RB8 = 0;
    
    //RF12 = /CS2
    PORTFbits.RF12 = 0;
    
    //Get Chip ID
    //Enter sequence
    PMADDR = 0x5555;
    PMDIN = 0xaa;
    while(PMMODEbits.BUSY == 1);  
   
    PMADDR = 0x2aaa;
    PMDIN = 0x55;
    while(PMMODEbits.BUSY == 1);  
    
    PMADDR = 0x5555;
    PMDIN = 0x90;
    while(PMMODEbits.BUSY == 1);  
            
    Delay(dly);
    
    //read device ID
    PMADDR = 0x0;
    //dummy read then real data
    Flash_MID = PMDIN;
    while(PMMODEbits.BUSY == 1); 
    
    Flash_MID = PMDIN;
    while(PMMODEbits.BUSY == 1);  
    
    //Exit sequence
    PMDIN = 0xf0;
    while(PMMODEbits.BUSY == 1);  
            
    Delay(dly);
    
    PORTFbits.RF12 = 1;
    
    return;
}  
    
void Flash_GetDID(void)
{  
    int dly = 100;
    //upper address lines
    PORTBbits.RB8 = 0;
    
    //RF12 = /CS2
    PORTFbits.RF12 = 0;
    
    //Get Chip ID
    //Enter sequence
    PMADDR = 0x5555;
    PMDIN = 0xaa;
    while(PMMODEbits.BUSY == 1);  
   
    PMADDR = 0x2aaa;
    PMDIN = 0x55;
    while(PMMODEbits.BUSY == 1);  
    
    PMADDR = 0x5555;
    PMDIN = 0x90;
    while(PMMODEbits.BUSY == 1);  
            
    Delay(dly);
    
    //read device ID
    PMADDR = 0x1;
    //dummy read then real data
    Flash_DID = PMDIN;
    while(PMMODEbits.BUSY == 1); 
    
    Flash_DID = PMDIN;
    while(PMMODEbits.BUSY == 1);  
    
    //Exit sequence
    PMDIN = 0xf0;
    while(PMMODEbits.BUSY == 1);  
            
    Delay(dly);
    
    PORTFbits.RF12 = 1;
    
    //Determine the Flash size
    switch(Flash_DID)
    {
      case 0xd5 :
            flash_size = 0x1ffff;
            break;
         
      case 0x6d :
            flash_size = 0x3ffff;
            break;
         
      case 0x7d :
            flash_size = 0x7ffff;
            break;
    }
    
    return;
}  
    
void Flash_RD(unsigned address_flash)
{  
    //Save the RD/WR Wait State Value
    int wait_m = PMMODEbits.WAITM;
    //Set new wait state = 0;
    PMMODEbits.WAITM = 2;
    
    //No address can be > 7ffff
    if(address_flash > 0x80000)
    {
        address_flash = 0x7ffff;
    }
    //Handle the upper address pins (A16-18)
    //0000-ffff
    if(address_flash > 0xffff)
    {
        //A16
        PORTBbits.RB8 = 1;
        //A17
        PORTAbits.RA5 = 0;
        //A18
        PORTAbits.RA3 = 0;        
    }
    //10000-1ffff
    if(address_flash > 0x1ffff)
    {
        //A16
        PORTBbits.RB8 = 0;
        //A17
        PORTAbits.RA5 = 1;
        //A18
        PORTAbits.RA3 = 0;        
    }
    //20000-2ffff
    if(address_flash > 0x2ffff)
    {
        //A16
        PORTBbits.RB8 = 1;
        //A17
        PORTAbits.RA5 = 1;
        //A18
        PORTAbits.RA3 = 0;        
    }
    //30000-3ffff
    if(address_flash > 0x3ffff)
    {
        //A16
        PORTBbits.RB8 = 0;
        //A17
        PORTAbits.RA5 = 0;
        //A18
        PORTAbits.RA3 = 1;        
    }
     //40000-4ffff
    if(address_flash > 0x4ffff)
    {
        //A16
        PORTBbits.RB8 = 1;
        //A17
        PORTAbits.RA5 = 0;
        //A18
        PORTAbits.RA3 = 1;        
    }
    //50000-5ffff
    if(address_flash > 0x5ffff)
    {
        //A16
        PORTBbits.RB8 = 0;
        //A17
        PORTAbits.RA5 = 1;
        //A18
        PORTAbits.RA3 = 1;        
    }
    //60000-6ffff
    if(address_flash > 0x6ffff)
    {
        //A16
        PORTBbits.RB8 = 1;
        //A17
        PORTAbits.RA5 = 1;
        //A18
        PORTAbits.RA3 = 1;        
    }    
    
    //Clear off upper bits and load in to PMP
    PMADDR = address_flash & 0x0ffff;
    
    ///CS2
    PORTFbits.RF12 = 0;
        
    //dummy read
    data_flash = PMDIN;
    while(PMMODEbits.BUSY == 1);
   
    data_flash = PMDIN;
    while(PMMODEbits.BUSY == 1);

    ///CS2
    PORTFbits.RF12 = 1;
    
    PORTBbits.RB8 = 0;
    PORTAbits.RA5 = 0;
    PORTAbits.RA3 = 0;        
    
    //Restore the RD/WR Wait State Value
    PMMODEbits.WAITM = wait_m;    
       
    return;
}

void Flash_WR(unsigned address_flash, uint8_t data_flash)
{        
    
    //Save the RD/WR Wait State Value
    int wait_m = PMMODEbits.WAITM;
    //Set new wait state = 0;
    PMMODEbits.WAITM = 2;
    
    //No address can be > 7ffff
    if(address_flash > 0x80000)
    {
        address_flash = 0x7ffff;
    }
    //Handle the upper address pins (A16-18)
    //0000-ffff
    if(address_flash > 0xffff)
    {
        //A16
        PORTBbits.RB8 = 1;
        //A17
        PORTAbits.RA5 = 0;
        //A18
        PORTAbits.RA3 = 0;        
    }
    //10000-1ffff
    if(address_flash > 0x1ffff)
    {
        //A16
        PORTBbits.RB8 = 0;
        //A17
        PORTAbits.RA5 = 1;
        //A18
        PORTAbits.RA3 = 0;        
    }
    //20000-2ffff
    if(address_flash > 0x2ffff)
    {
        //A16
        PORTBbits.RB8 = 1;
        //A17
        PORTAbits.RA5 = 1;
        //A18
        PORTAbits.RA3 = 0;        
    }
    //30000-3ffff
    if(address_flash > 0x3ffff)
    {
        //A16
        PORTBbits.RB8 = 0;
        //A17
        PORTAbits.RA5 = 0;
        //A18
        PORTAbits.RA3 = 1;        
    }
     //40000-4ffff
    if(address_flash > 0x4ffff)
    {
        //A16
        PORTBbits.RB8 = 1;
        //A17
        PORTAbits.RA5 = 0;
        //A18
        PORTAbits.RA3 = 1;        
    }
    //50000-5ffff
    if(address_flash > 0x5ffff)
    {
        //A16
        PORTBbits.RB8 = 0;
        //A17
        PORTAbits.RA5 = 1;
        //A18
        PORTAbits.RA3 = 1;        
    }
    //60000-6ffff
    if(address_flash > 0x6ffff)
    {
        //A16
        PORTBbits.RB8 = 1;
        //A17
        PORTAbits.RA5 = 1;
        //A18
        PORTAbits.RA3 = 1;        
    }    
    
    //Chip Enable - active low
    PORTFbits.RF12 = 0;
    
    //Enter sequence
    PMADDR = 0x5555;
    PMDIN = 0xaa;
    while(PMMODEbits.BUSY == 1);  
   
    PMADDR = 0x2aaa;
    PMDIN = 0x55;
    while(PMMODEbits.BUSY == 1);  
    
    PMADDR = 0x5555;
    PMDIN = 0xa0;
    while(PMMODEbits.BUSY == 1);  
            
    //write data
    //Clear off upper bits and load in to PMP
    PMADDR = address_flash & 0x0ffff;
    
    PMDIN = data_flash;
    while(PMMODEbits.BUSY == 1);  
    
    //Wait for end of program
    //using D7 - Data# polling
    while(data_flash != PMDIN);
    
    //now we must verify at least 2 times
    //to prevent false rejection
    int i = PMDIN;
    while(i != data_flash)
    {
        i = PMDIN;
    }
    
    //Chip Enable - active low
    PORTFbits.RF12 = 1;
    
    PORTBbits.RB8 = 0;
    PORTAbits.RA5 = 0;
    PORTAbits.RA3 = 0;        
    
    //Restore the RD/WR Wait State Value
    PMMODEbits.WAITM = wait_m; 
    
    //Prevents System lock
    Delay(50);

    return;
}

void Flash_Sector_Erase(int erase_sector)
{  
    data_flash = 0;
    
    erase_sector = erase_sector << 12;
    //Chip Enable - active low
    PORTFbits.RF12 = 0;
    
    //Enter sequence
    PMADDR = 0x5555;
    PMDIN = 0xaa;
    while(PMMODEbits.BUSY == 1);  
   
    PMADDR = 0x2aaa;
    PMDIN = 0x55;
    while(PMMODEbits.BUSY == 1);  
    
    PMADDR = 0x5555;
    PMDIN = 0x80;
    while(PMMODEbits.BUSY == 1);  
            
    PMADDR = 0x5555;
    PMDIN = 0xaa;
    while(PMMODEbits.BUSY == 1);  
   
    PMADDR = 0x2aaa;
    PMDIN = 0x55;
    while(PMMODEbits.BUSY == 1);  
    
    //Sector address
    PMADDR = erase_sector;
    PMDIN = 0x30;
    while(PMMODEbits.BUSY == 1);  
    
    while(data_flash  == 0)
    {
        data_flash = PMDIN;
        data_flash = data_flash & 0x80;        
    }
    
    return;
}

void Flash_Chip_Erase(void)
{  
    data_flash = 0;
    
    //Chip Enable - active low
    PORTFbits.RF12 = 0;
    
    //Enter sequence
    PMADDR = 0x5555;
    PMDIN = 0xaa;
    while(PMMODEbits.BUSY == 1);  
   
    PMADDR = 0x2aaa;
    PMDIN = 0x55;
    while(PMMODEbits.BUSY == 1);  
    
    PMADDR = 0x5555;
    PMDIN = 0x80;
    while(PMMODEbits.BUSY == 1);  
            
    PMADDR = 0x5555;
    PMDIN = 0xaa;
    while(PMMODEbits.BUSY == 1);  
   
    PMADDR = 0x2aaa;
    PMDIN = 0x55;
    while(PMMODEbits.BUSY == 1);  
    
    PMADDR = 0x5555;
    PMDIN = 0x10;
    while(PMMODEbits.BUSY == 1);  
    
    while(data_flash  == 0)
    {
        data_flash = PMDIN;
        data_flash = data_flash & 0x80;        
    }
    
    return;
}

void Flash_Get_Bytes_Used(void)
{  
    unsigned long i;
    Bytes_used = 0;
    
    if(Flash_DID == 0xd5)
    {
        for(i=0; i <= 0x1ffff; i++)
        {
            Flash_RD(i);
            if(data_flash != 0xff)
            {
                Bytes_used++;
            }
        }
    }
    if(Flash_DID == 0xd6)
    {
        for(i=0; i <= 0x2ffff; i++)
        {
            Flash_RD(i);
            if(data_flash != 0xff)
            {
                Bytes_used++;
            }
        }
    }
    
    if(Flash_DID == 0xd7)
    {
        for(i=0; i <= 0x7ffff; i++)
        {
            Flash_RD(i);
            if(data_flash != 0xff)
            {
                Bytes_used++;
            }
        }
    }
    
    PORTBbits.RB8 = 0;
    PORTAbits.RA5 = 0;
    PORTAbits.RA3 = 0;        

    return;
}


