A library to read and write all 25* serial SPI flash devices from various manufacturers.

flash25spi.cpp

Committer:
stonie
Date:
2011-02-20
Revision:
2:14c5db5e54df
Parent:
1:aa6409c599cb

File content as of revision 2:14c5db5e54df:

/* This library is based on the Ser25lcxxx library by Hendrik Lipka
* It was adapted to flash memory chips on 19.2.2011 by Klaus Steinhammer
* the BSD license also applies to this code - have fun. 
*/

/*
* Ser25lcxxx library
* Copyright (c) 2010 Hendrik Lipka
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/


#include "flash25spi.h"
#include "wait_api.h"

//#define DEBUG

struct dataBase {
unsigned char vendor;
unsigned char device;
unsigned char capacity;
unsigned int memsize;
unsigned int blocksize;
unsigned int sectorsize;
unsigned int pagesize;
};

const struct dataBase devices[] = {
// vendor, device, capacity,   memsize, blocksize, sectorsize, pagesize
{    0x1c,   0x31,     0x10,   0x10000,    0x8000,     0x1000,    0x100}, // Manufacturer: EON, Device: EN25F05 (untested)
{    0x1c,   0x31,     0x11,   0x20000,    0x8000,     0x1000,    0x100}, // Manufacturer: EON, Device: EN25F10 (untested)
{    0x1c,   0x31,     0x12,   0x40000,   0x10000,     0x1000,    0x100}, // Manufacturer: EON, Device: EN25F20 (untested)
{    0x1c,   0x31,     0x13,   0x80000,   0x10000,     0x1000,    0x100}, // Manufacturer: EON, Device: EN25F40 (untested)
{    0x1c,   0x31,     0x14,  0x100000,   0x10000,     0x1000,    0x100}, // Manufacturer: EON, Device: EN25F80 (untested)
{    0x1c,   0x31,     0x15,  0x200000,   0x10000,     0x1000,    0x100}, // Manufacturer: EON, Device: EN25F16 (untested)
{    0x1c,   0x30,     0x15,  0x200000,   0x10000,     0x1000,    0x100}, // Manufacturer: EON, Device: EN25Q16 (untested)
{    0x1c,   0x31,     0x16,  0x400000,   0x10000,     0x1000,    0x100}, // Manufacturer: EON, Device: EN25F32 (untested)
{    0x1c,   0x30,     0x16,  0x400000,   0x10000,     0x1000,    0x100}, // Manufacturer: EON, Device: EN25Q32A
{    0x1c,   0x30,     0x17,  0x800000,   0x10000,     0x1000,    0x100}, // Manufacturer: EON, Device: EN25Q64 (untested)
{    0x1c,   0x30,     0x18, 0x1000000,   0x10000,     0x1000,    0x100}, // Manufacturer: EON, Device: EN25Q128 (untested)

{    0x20,   0x20,     0x10,  0x10000,    0x8000,          0,    0x100}, // Manufacturer: Numonyx, Device: M25P05-A (untested)
{    0x20,   0x20,     0x16, 0x400000,   0x10000,          0,    0x100}, // Manufacturer: Numonyx, Device: M25P32 (untested)
{    0x00,   0x00,     0x00,     0x00,      0x00,       0x00,     0x00}, // end of table
};


#define HIGH(x) ((x&0xff0000)>>16)
#define MID(x) ((x&0xff00)>>8)
#define LOW(x) (x&0xff)

flash25spi::flash25spi(SPI *spi, PinName enable) {
    unsigned char chipid[3];
    unsigned int i = 0;
    _spi=spi;
    _enable=new DigitalOut(enable);
    _enable->write(1);
    
    wait_us(1000);
     
    _enable->write(0);
    wait_us(1);
    // send address
    _spi->write(0x9f);
    chipid[0] = _spi->write(0); // get vendor ID
    chipid[1] = _spi->write(0); // get device ID
    chipid[2] = _spi->write(0); // get capacity
    wait_us(1);
    _enable->write(1);
    
    _size = 0;

#ifdef DEBUG
    printf ("got flash ids: %x, %x, %x\n", chipid[0], chipid[1], chipid[2]);
#endif

    while (_size == 0) {
#ifdef DEBUG
        printf ("checking: %x, %x, %x\n", devices[i].vendor, devices[i].device, devices[i].capacity);
#endif
        if (devices[i].vendor == 0) {
            printf("flash device not found\n");
            return;
        }
        if ((chipid[0] == devices[i].vendor) &&
            (chipid[1] == devices[i].device) &&
            (chipid[2] == devices[i].capacity)) {
                _size=devices[i].memsize;
                _blockSize=devices[i].blocksize;
                _sectorSize=devices[i].sectorsize;
                _pageSize=devices[i].pagesize;
#ifdef DEBUG
                printf("device found: %x - %x, %x, %x, %x\n",i, _size, _blockSize, _sectorSize, _pageSize);
#endif
        }
        else
            i++;
    }
    return;
}

flash25spi::~flash25spi() {
    delete _enable;
}

char* flash25spi::read(unsigned int startAdr, unsigned int len) {
    // assertion
    if (startAdr+len>_size)
        return NULL;
    char* ret=(char*)malloc(len);
    if (!len) return NULL;
    
    _enable->write(0);
    wait_us(1);
    // send address
    _spi->write(0x03);
    _spi->write(HIGH(startAdr));
    _spi->write(MID(startAdr));
    _spi->write(LOW(startAdr));

    // read data into buffer
    for (unsigned int i=0;i<len;i++) {
        ret[i]=_spi->write(0);
    }
    wait_us(1);
    _enable->write(1);
    return ret;
}

bool flash25spi::write(unsigned int startAdr, unsigned int len, const char* data) {
    if (startAdr+len>_size)
        return false;

    unsigned int ofs=0;
    while (ofs<len) {
        // calculate amount of data to write into current page
        int pageLen=_pageSize-((startAdr+ofs)%_pageSize);
        if (ofs+pageLen>len)
            pageLen=len-ofs;
        // write single page
        bool b=writePage(startAdr+ofs,pageLen,data+ofs);
        if (!b)
            return false;
        // and switch to next page
        ofs+=pageLen;
    }
    return true;
}

bool flash25spi::writePage(unsigned int startAdr, unsigned int len, const char* data) {
    enableWrite();

    _enable->write(0);
    wait_us(1);

    _spi->write(0x02);
    _spi->write(HIGH(startAdr));
    _spi->write(MID(startAdr));
    _spi->write(LOW(startAdr));

    // do real write
    for (unsigned int i=0;i<len;i++) {
        _spi->write(data[i]);
    }
    wait_us(1);
    // disable to start physical write
    _enable->write(1);
    
    waitForWrite();

    return true;
}

void flash25spi::clearSector(unsigned int addr) {
    if (_sectorSize == 0) {
        clearBlock(addr);
        return;
    }
        
    addr &= ~(_sectorSize-1);

    enableWrite();
    _enable->write(0);
    wait_us(1);
    _spi->write(0x20);
    _spi->write(HIGH(addr));
    _spi->write(MID(addr));
    _spi->write(LOW(addr));
    wait_us(1);
    _enable->write(1);
    waitForWrite();
}

void flash25spi::clearBlock(unsigned int addr) {
    addr &= ~(_blockSize-1);

    enableWrite();
    _enable->write(0);
    wait_us(1);
    _spi->write(0xd8);
    _spi->write(HIGH(addr));
    _spi->write(MID(addr));
    _spi->write(LOW(addr));
    wait_us(1);
    _enable->write(1);
    waitForWrite();
}

void flash25spi::clearMem() {
    enableWrite();
    _enable->write(0);
    wait_us(1);
    _spi->write(0xc7);
    wait_us(1);
    _enable->write(1);
    waitForWrite();
}

int flash25spi::readStatus() {
    _enable->write(0);
    wait_us(1);
    _spi->write(0x5);
    int status=_spi->write(0x00);
    wait_us(1);
    _enable->write(1);
    return status;
}

void flash25spi::waitForWrite() {
    while (true) {
        if (0==readStatus()&1)
            break;
        wait_us(10);
    }
}

void flash25spi::enableWrite()
{
    _enable->write(0);
    wait_us(1);
    _spi->write(0x06);
    wait_us(1);
    _enable->write(1);
}