Driver for SST 64Mbit flash (8Mbyte) model 25VF064C including higher level methods for rewrite and buffered read/write to help optimize I/O. Can also work with other 25 series flash and eeprom devices, requiring minor revisions for their capabilities.

Dependencies:   SPIDebug

Revision:
0:332d4b991d16
Child:
2:33d8a5ea4a80
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SST25VF064C.cpp	Tue Apr 10 07:17:51 2012 +0000
@@ -0,0 +1,480 @@
+///////////////////////////////////////////////////////////////////////////////
+// SST25VF064C.cpp - EEPROM driver
+//
+// COPYRIGHT (c) 2012 by David Van Wagner
+//
+// dave@vanwagner.org
+// http://techwithdave.blogspot.com
+//
+// License: Creative Commons Attribution-ShareAlike 3.0 Unported License
+// http://creativecommons.org/licenses/by-sa/3.0/
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SST25VF064C.h"
+//#include <assert.h>
+
+SST25VF064C::uint8 SST25VF064C::SID_buffer[SID_LEN];
+SST25VF064C::uint8 SST25VF064C::sector_buffer[SECTOR_LEN];
+SST25VF064C::int32 SST25VF064C::buffered_addr;
+bool SST25VF064C::buffered_erased;
+
+SST25VF064C::SST25VF064C(PinName mosi, PinName miso, PinName sclk, PinName cs, int frequency)
+{
+#ifdef SPIDEBUG
+    this->cs = new CSDebug(cs);
+#else
+    this->cs = new DigitalOut(cs);    
+#endif    
+    this->cs->write(true);
+    this->frequency = frequency;
+#ifdef SPIDEBUG    
+    spi = new SPIDebug(mosi, miso, sclk);
+#else
+    spi = new SPI(mosi, miso, sclk);    
+#endif    
+    spi->format(8, 0);
+    spi->frequency(frequency);
+    buffered_addr = -1;
+    buffered_erased = false;
+    //assert(Id() == 0x4bbf);
+    //assert(JEDECId() == 0xbf254b);
+}
+
+SST25VF064C::~SST25VF064C()
+{
+    delete spi;
+    delete cs;
+}
+
+SST25VF064C::uint16 SST25VF064C::Id()
+{
+    cs->write(0);
+    spi->write(0xab);
+    spi->write(0);
+    spi->write(0);
+    spi->write(0);
+    unsigned id = (spi->write(0) << 8) | spi->write(0);
+    cs->write(1);
+    return id;
+}
+
+SST25VF064C::uint32 SST25VF064C::JEDECId()
+{
+    cs->write(0);
+    spi->write(0x9f);
+    unsigned id = (spi->write(0) << 16) | (spi->write(0) << 8) | spi->write(0);
+    cs->write(1);
+    return id;
+}
+
+SST25VF064C::uint8* SST25VF064C::SID()
+{
+    cs->write(0);
+    spi->write(0x88);
+    spi->write(0); // address
+    spi->write(0); // dummy
+    for (int i=0; i<SID_LEN; ++i)
+        SID_buffer[i] = spi->write(0);
+    cs->write(1);
+    return SID_buffer;
+}
+
+// Read Status Register    
+// bit 0 BUSY 1=Write in progress
+// bit 1 WEL  1=Write Enabled
+// bit 2 BP0  block write protection
+// bit 3 BP1  block write protection
+// bit 4 BP2  block write protection
+// bit 5 BP3  block write protection
+// bit 6 SEC  1=Security ID space locked
+// bit 7 BPL  1=BP0..BP3 are read-only, 0=r/w
+SST25VF064C::uint8 SST25VF064C::RDSR()
+{
+    cs->write(0);
+    spi->write(0x05);
+    uint8 status = spi->write(0);
+    cs->write(1);
+    return status;
+}
+
+// Enable Write Status Register
+void SST25VF064C::EWSR()
+{
+    cs->write(0);
+    spi->write(0x50);
+    cs->write(1);
+}
+
+// Write Status Register
+void SST25VF064C::WRSR(SST25VF064C::uint8 status)
+{
+    cs->write(0);
+    spi->write(0x01);
+    spi->write(status);
+    cs->write(1);
+}
+
+// Write Enable
+void SST25VF064C::WREN()
+{
+    cs->write(0);
+    spi->write(0x06);
+    cs->write(1);
+}
+
+// Write Disable
+void SST25VF064C::WRDI()
+{
+    cs->write(0);
+    spi->write(0x04);
+    cs->write(1);
+}
+
+bool SST25VF064C::BUSY()
+{
+    return (RDSR() & 0x01) != 0;
+}
+
+bool SST25VF064C::WEL()
+{
+    return (RDSR() & 0x02) != 0;
+}
+
+SST25VF064C::uint8 SST25VF064C::BP()
+{
+    return (RDSR() >> 2) & 0xF;
+}
+
+bool SST25VF064C::SEC()
+{
+    return (RDSR() & 0x40) != 0;
+}
+
+bool SST25VF064C::BPL()
+{
+    return (RDSR() & 0x80) != 0;
+}
+
+void SST25VF064C::wait_while_busy()
+{
+    while (BUSY())
+        ;
+}
+
+SST25VF064C::uint8 SST25VF064C::read(SST25VF064C::int32 addr)
+{
+    cs->write(0);
+    spi->write(0x03);
+    spi->write((addr >> 16) & 0xff);
+    spi->write((addr >> 8) & 0xff);
+    spi->write(addr & 0xff);
+    uint8 data = spi->write(0);
+    cs->write(1);
+    return data;
+}
+
+bool SST25VF064C::read(SST25VF064C::int32 addr, SST25VF064C::uint8* dst, SST25VF064C::int32 len)
+{
+    if (addr < 0 || addr >= MAX_ADDR || dst == 0 || len < 1 || addr+len > MAX_ADDR)
+        return false;
+
+    cs->write(0);
+    spi->write(0x03);
+    spi->write((addr >> 16) & 0xff);
+    spi->write((addr >> 8) & 0xff);
+    spi->write(addr & 0xff);
+    for (int32 i=0; i<len; ++i)
+        dst[i] = spi->write(0);
+    cs->write(1);
+    
+    return true;
+}
+
+void SST25VF064C::hsread(SST25VF064C::int32 addr, SST25VF064C::uint8* dst, SST25VF064C::int32 len, int frequency)
+{
+    int save_frequency = this->frequency;
+    spi->frequency(frequency);
+    cs->write(0);
+    spi->write(0x0B);
+    spi->write((addr >> 16) & 0xff);
+    spi->write((addr >> 8) & 0xff);
+    spi->write(addr & 0xff);
+    spi->write(0); // dummy
+    for (int32 i=0; i<len; ++i)
+        dst[i] = spi->write(0);
+    cs->write(1);
+    spi->frequency(save_frequency);
+}
+
+void SST25VF064C::sector_erase_4k(SST25VF064C::int32 addr)
+{
+    cs->write(0);
+    spi->write(0x20);
+    spi->write((uint8)(addr >> 16));
+    spi->write((uint8)(addr >> 8));
+    spi->write((uint8)addr);
+    cs->write(1);
+    wait_while_busy();
+}
+
+void SST25VF064C::block_erase_32k(SST25VF064C::int32 addr)
+{
+    cs->write(0);
+    spi->write(0x52);
+    spi->write((uint8)(addr >> 16));
+    spi->write((uint8)(addr >> 8));
+    spi->write((uint8)addr);
+    cs->write(1);
+    wait_while_busy();
+}    
+
+void SST25VF064C::block_erase_64k(SST25VF064C::int32 addr)
+{
+    cs->write(0);
+    spi->write(0xd8);
+    spi->write((uint8)(addr >> 16));
+    spi->write((uint8)(addr >> 8));
+    spi->write((uint8)addr);
+    cs->write(1);
+    wait_while_busy();
+}    
+
+void SST25VF064C::chip_erase()
+{
+    cs->write(0);
+    spi->write(0x60);
+    cs->write(1);
+    wait_while_busy();
+}
+
+bool SST25VF064C::page_program(SST25VF064C::int32 addr, SST25VF064C::uint8* write_buffer, short len)
+{
+    // no point in writing FF as an empty sector already has those
+    // (and if not empty, write won't succeed)
+    bool skipped = false;
+    while (len > 0 && *write_buffer == 0xFF)
+    {
+        ++write_buffer;
+        --len;
+        ++addr;
+        skipped = true;
+    }
+    if (len == 0 && skipped)
+        return true; // special case when succeeds when nothing to do
+
+    if (len < 1 || len > 256)
+        return false;
+        
+    // write enable
+    WREN();
+
+    cs->write(0);
+    spi->write(0x02);
+    spi->write((uint8)(addr >> 16));
+    spi->write((uint8)(addr >> 8));
+    spi->write((uint8)addr);
+    for (short i=0; i<len; ++i)
+        spi->write(write_buffer[i]);
+    cs->write(1);
+    wait_while_busy();
+    
+    return true;
+}
+
+bool SST25VF064C::write(SST25VF064C::int32 addr, SST25VF064C::uint8* write_buffer, SST25VF064C::int32 len)
+{
+    if (len < 0 || addr < 0 || addr > MAX_ADDR || (addr+len > MAX_ADDR) || (addr+len < 0))
+        return false;
+
+    if (len == 0)
+        return true; // done!
+
+    // calculate first page size based on address and length
+    int32 page_len = PAGE_LEN-(addr & (PAGE_LEN-1)); // remaining space in page
+
+    while (len > 0)
+    {
+        if (page_len > len)
+            page_len = len;
+
+        page_program(addr, write_buffer, page_len);
+
+        addr += page_len;        
+        write_buffer += page_len;
+        len -= page_len;
+        page_len = PAGE_LEN;
+    }
+    
+    return true;
+}    
+
+bool SST25VF064C::rewrite(SST25VF064C::int32 addr, SST25VF064C::uint8* write_buffer, SST25VF064C::int32 len)
+{
+    // validate parameters
+    if (len < 0 || addr < 0 || addr > MAX_ADDR || (addr+len > MAX_ADDR) || (addr+len < 0))
+        return false;
+
+    // are we done before we've started?
+    if (len == 0)
+        return true; // done!
+
+    // calculate first sector size based on address and length
+    int32 sector_len = SECTOR_LEN-(addr & (SECTOR_LEN-1)); // remaining space in sector
+
+    while (len > 0)
+    {
+        // adjust if buffer than sector size
+        if (sector_len > len)
+            sector_len = len;
+
+        // read existing data into entire sector buffer
+        read(addr & (MAX_ADDR ^ (SECTOR_LEN-1)), sector_buffer, SECTOR_LEN);
+        
+        // overwrite with requested data at proper offset
+        memcpy(sector_buffer + (addr & (SECTOR_LEN-1)), write_buffer, sector_len);
+
+        // reset to beginning of sector
+        addr = addr ^ (addr & (SECTOR_LEN-1));
+        
+        // erase sector
+        WREN();
+        sector_erase_4k(addr);            
+        
+        // rewrite the sector
+        uint8 *p = sector_buffer;
+        int sectors_in_page = SECTOR_LEN/PAGE_LEN;
+        for (int i=0; i<sectors_in_page; ++i)
+        {
+            page_program(addr, p, PAGE_LEN);
+            addr += PAGE_LEN;
+            p += PAGE_LEN;
+        }
+
+        // adjust where we are, what left to do
+        write_buffer += sector_len;
+        len -= sector_len;
+        sector_len = SECTOR_LEN;
+    }
+
+    wait_while_busy();
+    
+    return true;
+}    
+
+bool SST25VF064C::buffered_write(SST25VF064C::uint8 data)
+{
+    int32& addr = buffered_addr;
+
+    if (addr < 0 || addr > MAX_ADDR)
+        return false;
+
+    bool result = true;
+
+    // if at sector boundary
+    if ((addr & (SECTOR_LEN-1)) == 0 || !buffered_erased)
+    {
+        WREN();
+        sector_erase_4k(addr ^ (addr & (SECTOR_LEN-1)));
+        buffered_erased = true;
+    }
+
+    sector_buffer[addr & (SECTOR_LEN-1)] = data;
+    
+    ++addr;
+
+    // if at sector boundary
+    if ((addr & (SECTOR_LEN-1)) == 0)
+    {
+        // write sector
+        result = write(addr-SECTOR_LEN, sector_buffer, SECTOR_LEN);
+        buffered_erased = false;
+
+        // read more
+        if (addr != MAX_ADDR)
+            result = result && read(addr, sector_buffer, SECTOR_LEN);
+    }
+    
+    return result;
+}
+
+bool SST25VF064C::buffered_write(SST25VF064C::uint8* src, int len)
+{
+    bool result = true;
+    
+    while (result && len > 0)
+    {
+        result = buffered_write(*src);
+        --len;
+        ++src;
+    }
+    
+    return result;
+}
+
+SST25VF064C::uint8 SST25VF064C::buffered_read()
+{
+    int32& addr = buffered_addr;
+    if (addr < 0 || addr >= MAX_ADDR)
+    {
+        addr = -1;
+        return 0xff;
+    }
+    uint8 data = sector_buffer[addr & (SECTOR_LEN-1)];
+    ++addr;
+    if ((addr & (SECTOR_LEN-1)) == 0)
+    {
+        if (buffered_erased)
+        {
+            write(addr-SECTOR_LEN, sector_buffer, SECTOR_LEN);
+            buffered_erased = false;
+        }
+        if (addr == MAX_ADDR)
+            addr = -1;
+        else
+            read(addr, sector_buffer, SECTOR_LEN);
+    }
+    return data;
+}
+
+bool SST25VF064C::buffered_read(SST25VF064C::uint8* dest, int len)
+{
+    while (len > 0)
+    {
+        if (buffered_addr < 0 || buffered_addr >= MAX_ADDR)
+            return false;
+    
+        *dest = buffered_read();
+        --len;
+        ++dest;
+    }
+    
+    return true;
+}
+
+void SST25VF064C::buffered_seek(SST25VF064C::int32 addr)
+{
+    if (buffered_erased)
+        write(buffered_addr ^ (buffered_addr & (SECTOR_LEN-1)), sector_buffer, SECTOR_LEN);
+    if (addr < 0 || addr >= MAX_ADDR)
+    {
+        buffered_addr = -1;
+        buffered_erased = false;
+    }
+    else
+    {
+        read(addr ^ (addr & (SECTOR_LEN-1)), sector_buffer, SECTOR_LEN);
+        buffered_addr = addr;
+        buffered_erased = false;
+    }
+}
+
+void SST25VF064C::buffered_sync()
+{
+    int32& addr = buffered_addr;
+
+    if (buffered_erased)
+    {
+        write(addr ^ (addr & (SECTOR_LEN-1)), sector_buffer, SECTOR_LEN);
+        buffered_erased = false;
+    }
+}