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.
SST25VF064C.cpp
- Committer:
- davervw
- Date:
- 2012-04-10
- Revision:
- 0:332d4b991d16
- Child:
- 2:33d8a5ea4a80
File content as of revision 0:332d4b991d16:
/////////////////////////////////////////////////////////////////////////////// // 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; } }