TRC 630401

at45db161e.cpp

Committer:
supawat
Date:
2020-04-02
Revision:
0:0b6c94a21953

File content as of revision 0:0b6c94a21953:

#include "at45db161e.h"

//extern Serial debug_pc;

ATD45DB161E::ATD45DB161E(PinName mosi, PinName miso, PinName clk, PinName cs)  
           : _spi(mosi, miso, clk), _cs(cs) {}

ATD45DB161E::ATD45DB161E(SPI &spi, PinName cs)
            : _spi(spi), _cs(cs) {}
/** Setup SPI **/
void ATD45DB161E::initial_chip(void)
{
    _spi.format(8,0); 
    _spi.frequency(16000000);
    _set_pageszie_to_binary();
    _wake_flag = true;
    _deepsleep_flag = false;
    _udeepsleep_flag = false;
}

void ATD45DB161E::_select() { 
    // ensure that the SPI port is set up correctly still 
    // This allows SPI channel sharing 
    _spi.format(8,0); 
    _spi.frequency(16000000); 
    _cs = 0;     
} 

/*  
 * Deselect simply returns nCS to high 
 */ 
void ATD45DB161E::_deselect() { 
    _cs = 1; 
}

/* 
 * Sends the three lest significant bytes of the supplied address 
 */ 
 
void ATD45DB161E::_sendaddr (unsigned int address) { 
     _spi.write(address >> 16); 
     _spi.write(address >> 8); 
     _spi.write(address);       
}

/**
 * Set page size of the flash chip into deep power down mode.
 **/
void ATD45DB161E::_set_pageszie_to_binary(void)
{
    _pollbusy();    // make sure flash isn't already in busy.  
    _select(); 
    _spi.write(0x3d);
    _spi.write(0x2a);
    _spi.write(0x80);
    _spi.write(0xa6); 
    _deselect();
    _pollbusy();    // waitting for the operation complete.
}


/* 
 * return the Status 
 */ 
 
int ATD45DB161E::ReadStatusRegister(void) {  
    int status = 0; 
    _select(); 
    _spi.write(0xd7); 
    status = _spi.write(0x00);  
    _deselect();             
    return status;  
}

/* 
 * Make sure the Flash isnt already doing something 
 */ 
void ATD45DB161E::_pollbusy() { 
    volatile int busy = 1;   
    while (busy) {
        if(!_wake_flag)
        {
            if(_deepsleep_flag) DeepPowerDown(false);
            else if(_udeepsleep_flag) UltraDeepPowerDown(false);
        }
        // if bit 7 is set, we can proceed 
        if ( ReadStatusRegister() & 0x80 ) { 
            busy = 0;} 
    } 
}

void ATD45DB161E::pollbusy() { 
    _pollbusy(); 
}

/** 
 * Read Manufacturer and Device ID 
 * @note if id.extendedInfoLength is not equal to zero,
 *       successive calls to spi_transfer(0xff) will return
 *       the extended device information string bytes.
 * @param id Pointer to the ID structure to initialize
 **/
void ATD45DB161E::ReadManufacturerAndDeviceID(struct flash_id *id)
{
    _pollbusy();    // make sure flash isn't already in busy.
    _select(); 
    /* Send status read command */
    _spi.write(0x9f);
    /* Manufacturer ID */
    id->manufacturer = _spi.write(0x00);
    /* Device ID (part 1) */
    id->device[0] = _spi.write(0x00);
    /* Device ID (part 2) */
    id->device[1] = _spi.write(0x00);
    /* Extended Device Information String Length */
    id->extendedInfoLength = _spi.write(0x00);
    _deselect();
}

/** 
 * Main Memory Page Read. 
 * A main memory page read allows the user to read data directly from
 * any one of the 4096 pages in the main memory, bypassing both of the
 * data buffers and leaving the contents of the buffers unchanged.
 *
 * @param page: Page of the main memory to read
 * @param offset: Starting byte address within the page
 * @param *data: Data buffer to be return the value in flash memory
 * @param len: Lenght of data that you want to read
 * Note: When the end of a page in main memory is reached,
         the device will continue reading back at the beginning 
         of the same page.
 **/
void ATD45DB161E::PageRead(unsigned int page,unsigned int offset,unsigned char *data,unsigned int len)
{   
    unsigned int address,i;
    
    address = (page<<9)|offset;
    
    _pollbusy();    // make sure flash isn't already in busy.
    _select();

    /* Send opcode */
    _spi.write(0xd2);
        
    _sendaddr(address);  // send three address byte
    
    /* 4 "don't care" bytes */
    _spi.write(0x00);
    _spi.write(0x00);
    _spi.write(0x00);
    _spi.write(0x00);
    
    for(i=0;i<len;i++)
    {
        *data++ = _spi.write(0x00);
    }
    _deselect();
}

/** 
 * Continuous Array Read in low power mode. 
 * Continuous Array Read in low power mode allows the user to read data 
 * directly from any address in the main memory, bypassing both of the
 * data buffers and leaving the contents of the buffers unchanged.
 *
 * @param page: Page of the main memory to read
 * @param addr_st: Starting byte address within the main memory
 * @param *data: Data buffer to be return the value in flash memory
 * @param len: Lenght of data that you want to read
 * Note: When the end of a page in main memory is reached, the device will 
         continue reading at the beginning of the next page with no delays 
         incurred during the page boundary crossover.
 **/
void ATD45DB161E::ContinuousArrayRead(unsigned int addr_st,unsigned char *data,unsigned int len)
{   
    unsigned int i;
     
    _pollbusy();    // make sure flash isn't already in busy.
    _select();

    /* Send opcode */
    _spi.write(0x01);
        
    _sendaddr(addr_st);  // send three address byte
    
    for(i=0;i<len;i++)
    {
        *data++ = _spi.write(0x00);
    }
    _deselect();
}

/** 
 * Erase the entire chip memory. Sectors proteced or locked down will
 * not be erased.
 **/
void ATD45DB161E::ChipErase(void)
{
    _pollbusy(); // make sure flash isn't already in busy.

    // There are errata on this. For now, do itthe long way :-( 
    _select(); 
    // 4 byte command sequence 
    _spi.write(0xc7); 
    _spi.write(0x94); 
    _spi.write(0x80); 
    _spi.write(0x9a); 
    _deselect();   
 
    _pollbusy(); // waiting for chip erase 
}

/** 
 * Erase a page in the main memory array.
 * @param page: Number of page that you want to erase
 **/
void ATD45DB161E::PageErase(unsigned int page)
{   
    unsigned int address;
    
    address = page<<9;

    _pollbusy(); // make sure flash isn't already in busy.

    // There are errata on this. For now, do itthe long way :-( 
    _select();
    _spi.write(0x81);   //  Command for page erase
    _sendaddr(address);    //  Send 3 address bytes
    _deselect();
    
    _pollbusy(); // waiting for page erase
}

/**
 * This is a combination of Buffer Write and Buffer to Page with
 * Built-in Erase.
 * @param page Page where the content of the buffer will transfered
 * @param offset Starting byte address within the buffer
 * @param bufferNum Buffer to use (1 or 2)
 * @param *data: Data that you want to write into flash memory
 * @param len: Lenght of data that you want to write
 * Note: If the end of the buffer is reached, the device will
         wrap around back to the beginning of the buffer.
 **/
void ATD45DB161E::PageWriteThroughBuffer(unsigned int page, unsigned int offset, unsigned char bufferNum,unsigned char *data,unsigned int len)
{
    unsigned int address,i;
    unsigned char opcode;
    
    address = (page<<9)|offset;
    // select opcode which buffer have been used
    if (bufferNum == 1) 
        opcode = 0x82;
    else
        opcode = 0x85; 
    
    _pollbusy(); // make sure flash isn't already in busy. 
    _select();
    
    /* Send opcode */
    _spi.write(opcode);
    
    /* Address */
    _sendaddr(address);  // send three address byte
    for(i=0;i<len;i++)
    {
        _spi.write(*data++);
    }
    _deselect();
    _pollbusy(); // waiting for programming from buffer into main memory
}

/**
 * This is a combination of the Main Memory Page to Buffer Transfer, Buffer Write, 
 * and Buffer to Main Memory Page Program with Built-in Erase commands.
 * @param addr_st Starting byte address of the flash chip
 * @param bufferNum Buffer to use (1 or 2)
 * @param *data: Data that you want to write into flash memory
 * @param len: Lenght of data that you want to write
 * Note: If the end of the buffer is reached, the device will
         wrap around back to the beginning of the buffer.
 **/
void ATD45DB161E::ContinuousWriteThroughBuffer(unsigned int addr_st, unsigned char bufferNum,unsigned char *data,unsigned int len)
{
    unsigned int i,data_len;
    unsigned int page_start,page_end;
    unsigned int page_count,dif_page;
    unsigned char opcode;
    
    page_start = addr_st>>9;
    page_end = (addr_st+len)>>9;
    dif_page = page_end - page_start;
        
    // select opcode which buffer have been used
    if (bufferNum == 1) 
        opcode = 0x58;
    else
        opcode = 0x59; 
    
    for(page_count=0;page_count<=dif_page;page_count++)
    {
        if(512-(addr_st&0x1ff)<len) data_len = 512-(addr_st&0x1ff);
        else data_len = len;
        _pollbusy(); // make sure flash isn't already in busy. 
        _select();        
        _spi.write(opcode); /* Send opcode */
        _sendaddr(addr_st);  // send three address byte
        for(i=0;i<data_len;i++)
        {
            _spi.write(*data++);
        }
        _deselect();
        _pollbusy(); // waiting for programming from buffer into main memory
        addr_st = addr_st + data_len;
        len = len - data_len;
    }
}

/** 
 * Read the content of one of the SRAM data buffers (in low or high speed mode).
 * @param bufferNum Buffer to read (1 or 2)
 * @param offset Starting byte within the buffer
 * @param low If true the read operation will be performed in low speed mode (and in high speed mode if it's false).
 * @param *data: Data that you want to read from flash memory
 * @param len: Lenght of data that you want to read
 **/
void ATD45DB161E::BufferRead(unsigned char bufferNum, unsigned int offset, unsigned char low,unsigned char *data,unsigned int len)
{
    unsigned int i;
    unsigned char opcode;

    /* Send opcode */
    if(bufferNum == 1)
    {
        if(low)
            opcode = 0xd1;    // opcode for read buffer 1 in low speed
        else
            opcode = 0xd4;    // opcode for read buffer 1
    }
    else
    {
        if(low)
            opcode = 0xd3;    // opcode for read buffer 1 in low speed
        else
            opcode = 0xd6;  // opcode for read buffer 2
    }
    _pollbusy();    // make sure flash isn't already in busy.
    _select();
    /* Send opcode */
    _spi.write(opcode);    
    _sendaddr(offset);  // send three address byte
    if(!low)
        _spi.write(0x00); // send dummy byte
    for(i=0;i<len;i++)
    {
        *data++ = _spi.write(0x00);
    }
    _deselect();
}

/** 
 * Write data to one of the SRAM data buffers. Any further call to
 * spi_tranfer will return bytes contained in the data buffer until
 * a low-to-high transition is detected on the CS pin. If the end of
 * the data buffer is reached, the device will wrap around back to the
 * beginning of the buffer. 
 * @param bufferNum Buffer to read (1 or 2)
 * @param offset Starting byte within the buffer
 * @param *data: Data that you want to write into flash memory
 * @param len: Lenght of data that you want to write
 **/
void ATD45DB161E::BufferWrite(unsigned char bufferNum, unsigned int offset,unsigned char *data,unsigned int len)
{
    unsigned int i;
    unsigned char opcode;
    
    if (bufferNum == 1) 
        opcode = 0x84;
    else
        opcode = 0x87;
    _pollbusy();    // make sure flash isn't already in busy.
    _select();
    
    /* Send opcode */
    _spi.write(opcode);
    _sendaddr(offset);  // send three address byte
    for(i=0;i<len;i++)
    {
        _spi.write(*data++);
    }
    _deselect();
    _pollbusy();    // waiting for operation complete.
}

/**
 * Transfer data from buffer 1 or 2 to main memory page.
 * @param bufferNum Buffer to use (1 or 2)
 * @param page Page where the content of the buffer will transfered
 * @param erase If set the page will be first erased before the buffer transfer.
 * @note If erase is equal to zero, the page must have been previously erased using one of the erase command (Page or Block Erase).
 **/
void ATD45DB161E::BufferToPage(unsigned char bufferNum, unsigned int page, unsigned char erase)
{
    unsigned int address;
    unsigned char opcode;
    
    address = page<<9;
    if(erase)
    {
        if (bufferNum == 1) 
        opcode = 0x83;
        else
        opcode = 0x86;
    }
    else
    {
        if (bufferNum == 1) 
        opcode = 0x88;
        else
        opcode = 0x89;
    }
    
    _pollbusy(); // make sure flash isn't already in busy.
    _select();
    
    /* Send opcode */
    _spi.write(opcode);
    _sendaddr(address);  // send three address byte
    _deselect();
    
    _pollbusy(); // Wait for buffer write to main memory page
}

/**
 * Transfer data from main memory page to buffer 1 or 2.
 * @param page Page where the content of the main memory page will transfered
 * @param bufferNum Buffer to use (1 or 2) 
 **/
void ATD45DB161E::PageToBuffer(unsigned int page, unsigned char bufferNum)
{
    unsigned int address;
    unsigned char opcode;
    
    address = page<<9;
    
    if (bufferNum == 1) opcode = 0x53;
    else opcode = 0x55;
    
    _pollbusy(); // make sure flash isn't already in busy.
    _select();
    
    /* Send opcode */
    _spi.write(opcode);
    _sendaddr(address);  // send three address byte
    _deselect();
    
    _pollbusy(); // Wait for buffer write to main memory page
}

/**
 * Enter the chip into deep power down mode.
 * @param deep_down_onoff: If set the chip will be entered into deep power down mode. Otherwise, wake up command will be sent to the chip.
 **/
void ATD45DB161E::DeepPowerDown(unsigned char deep_down_onoff)
{
    if(deep_down_onoff)
    {
        _pollbusy();    // make sure flash isn't already in busy.
        _select();
        _spi.write(0xb9);
        _deselect();
        _wake_flag = false;
        _deepsleep_flag = true;
        wait_us(2);        
    }
    else
    {
        _select();
        _spi.write(0xab);
        _deselect();
        _wake_flag = true;
        _deepsleep_flag = false;
        wait_us(35);
    }    
}

/**
 * Enter the chip into ultra deep power down mode.
 * @param ultra_deep_down_onoff: If set the chip will be entered into ultra deep power down mode. Otherwise, CS pin will be asseeted for wake up the chip.
 **/
void ATD45DB161E::UltraDeepPowerDown(unsigned char  ultra_deep_down_onoff)
{
    if(ultra_deep_down_onoff)
    {
        _pollbusy();    // make sure flash isn't already in busy.
        _select();
        _spi.write(0x79);
        _deselect();
        _wake_flag = false;
        _udeepsleep_flag = true;
        wait_us(4);        
    }
    else
    {
        _select();
        wait_us(1);
        _deselect();
        wait_us(180);
        _wake_flag = true;
        _udeepsleep_flag = false;  
    }    
}

/**
 * Check the chip is wake up.
 * Return true if the chip is wake up. Otherwise, the chip is entered in deep power down or ultra deep power down mode mode.
 **/
bool ATD45DB161E::is_it_awake(void)
{
    return _wake_flag;
}