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

Files at this revision

API Documentation at this revision

Comitter:
davervw
Date:
Tue Apr 10 07:17:51 2012 +0000
Child:
1:bf7d00887342
Commit message:
initial revision, tested

Changed in this revision

SPIDebug.lib Show annotated file Show diff for this revision Revisions of this file
SST25VF064C.cpp Show annotated file Show diff for this revision Revisions of this file
SST25VF064C.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SPIDebug.lib	Tue Apr 10 07:17:51 2012 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/davervw/code/SPIDebug/#0f32ac84dca5
--- /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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SST25VF064C.h	Tue Apr 10 07:17:51 2012 +0000
@@ -0,0 +1,102 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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 <mbed.h>
+#ifdef SPIDEBUG
+#include "SPIDebug.h"
+extern bool debug;
+#endif
+
+class SST25VF064C
+{
+public:
+    typedef char int8;
+    typedef short int16;
+    typedef long int32;
+    typedef long long int64;
+    
+    typedef unsigned char uint8;
+    typedef unsigned short uint16;
+    typedef unsigned long uint32;
+    typedef unsigned long long uint64;
+
+    static const int SID_LEN = 32;
+    static const int SECTOR_LEN = 4096;
+    static const int PAGE_LEN = 256;
+    static const int MAX_ADDR = 0x7FFFFF;
+
+    static uint8 sector_buffer[SECTOR_LEN]; // TODO: private
+
+private:
+#ifdef SPIDEBUG
+    SPIDebug* spi;
+    CSDebug* cs;
+#else
+    SPI* spi;
+    DigitalOut* cs;
+#endif    
+    int frequency;
+    
+    static uint8 SID_buffer[SID_LEN];
+    static int32 buffered_addr;
+    static bool buffered_erased;
+
+public:
+    SST25VF064C(PinName mosi, PinName miso, PinName sclk, PinName cs, int frequency=10000000);
+    ~SST25VF064C();
+    
+    uint16 Id();
+    uint32 JEDECId();
+    uint8* SID(); // Security ID (128 bits = 16 bytes)
+
+    // 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
+    uint8 RDSR();
+    void EWSR(); // Enable Write Status Register
+    void WRSR(uint8 status); // Write Status Register
+    
+    void WREN(); // Write Enable
+    void WRDI(); // Write Disable
+    bool BUSY(); // Write in Progress
+    bool WEL(); // Write Enabled
+    uint8 BP(); // Block Protection
+    bool SEC(); // Security ID Locked
+    bool BPL(); // Block Protection Locked
+    void wait_while_busy();
+
+    uint8 read(int32 addr);
+    bool read(int32 addr, uint8* dst, int32 len);
+    void hsread(int32 addr, uint8* dst, int32 len, int frequency);
+
+    void sector_erase_4k(int32 addr);
+    void block_erase_32k(int32 addr);
+    void block_erase_64k(int32 addr);
+    void chip_erase();
+
+    bool page_program(int32 addr, uint8* write_buffer, short len);
+    bool write(int32 addr, uint8* write_buffer, int32 len);
+    bool rewrite(int32 addr, uint8* write_buffer, int32 len);
+
+    bool buffered_write(uint8 data);
+    bool buffered_write(uint8* src, int len);
+    uint8 buffered_read();
+    bool buffered_read(uint8* dest, int len);
+    void buffered_seek(int32 addr);
+    void buffered_sync();
+};