Enhanced EEPROM 25LCxxx driver

Dependents:   eeprom_test

Fork of 25LCxxx_SPI by Hendrik Lipka

Files at this revision

API Documentation at this revision

Comitter:
mariob
Date:
Fri Jul 17 09:47:32 2015 +0000
Parent:
2:3a3404dbd3eb
Commit message:
added fix in writing function; fixed write check; added features (protection bits); changed interfaces

Changed in this revision

Ser25lcxxx.cpp Show annotated file Show diff for this revision Revisions of this file
Ser25lcxxx.h Show annotated file Show diff for this revision Revisions of this file
diff -r 3a3404dbd3eb -r be69a9f6f738 Ser25lcxxx.cpp
--- a/Ser25lcxxx.cpp	Sat Feb 19 18:29:20 2011 +0000
+++ b/Ser25lcxxx.cpp	Fri Jul 17 09:47:32 2015 +0000
@@ -24,177 +24,204 @@
 #include "Ser25lcxxx.h"
 #include "wait_api.h"
 
-#define HIGH(x) ((x&0xff00)>>8)
-#define LOW(x) (x&0xff)
+#define HIGH(x)     ((x&0xff00)>>8)
+#define LOW(x)      (x&0xff)
 
-Ser25LCxxx::Ser25LCxxx(SPI *spi, PinName enable, int bytes, int pagesize) {
-    _spi=spi;
-    _enable=new DigitalOut(enable);
-    _size=bytes;
-    _pageSize=pagesize;
-    _enable->write(1);
+Ser25LCxxx::Ser25LCxxx (PinName sck, PinName si, PinName so, PinName enable,
+                                                int pagenumber, int pagesize):
+                                                spi(si, so, sck), cs(enable) {
+    spi.format(8,3);
+    spi.frequency(EEPROM_SPI_SPEED);
+    size = pagesize*pagesize;
+    pageSize = pagesize;
+    pageNumber = pagenumber;
+    cs.write(1);
 }
 
-Ser25LCxxx::~Ser25LCxxx() {
-    delete _enable;
+unsigned int Ser25LCxxx::read (unsigned int startAdr, unsigned int len,
+                                                        unsigned char* buf) {
+    // assertion
+    if ((startAdr+len) > size)
+        return 0;
+
+    //active the device
+    cs = 0;
+    wait_us(1);
+
+    //send command and address
+    if (size<512) { // 256 and 128 bytes
+        spi.write(EEPROM_CMD_READ);
+        spi.write(LOW(startAdr));
+    } else if (512==size) { // 4k variant adds 9th address bit to command
+        spi.write(startAdr>255?0xb:EEPROM_CMD_READ);
+        spi.write(LOW(startAdr));
+    } else if (size<131072) { // everything up to 512k
+        spi.write(EEPROM_CMD_READ);
+        spi.write(HIGH(startAdr));
+        spi.write(LOW(startAdr));
+    } else { // 25xx1024, needs 3 byte address
+        spi.write(EEPROM_CMD_READ);
+        spi.write(startAdr>>16);
+        spi.write(HIGH(startAdr));
+        spi.write(LOW(startAdr));
+    }
+
+    //read data into buffer
+    for (int i=0; i<len; i++)
+        buf[i] = spi.write(0);
+    wait_us(1);
+
+    cs = 1;
+
+    return len;
+}
+
+unsigned int Ser25LCxxx::write (unsigned int startAdr, unsigned int len,
+                                                   const unsigned char* data) {
+    if ((startAdr+len) > size)
+        return 0;
+
+    unsigned int remaining = len;
+    unsigned int count = 0;
+
+    while (remaining > 0) {
+        // calculate amount of data to write into current page
+        int page_left = pageSize - (startAdr % pageSize);
+        int to_be_written = (remaining > page_left)? page_left : remaining;
+        if (writePage(startAdr, to_be_written, data+count) != to_be_written)
+            return count;
+        // and switch to next page
+        count += to_be_written;
+        remaining -= to_be_written;
+        startAdr += to_be_written;
+    }
+    return count;
 }
 
-char* Ser25LCxxx::read(unsigned int startAdr, unsigned int len) {
-    // assertion
-    if (startAdr+len>_size)
-        return NULL;
-    char* ret=(char*)malloc(len);
-    _enable->write(0);
+unsigned int Ser25LCxxx::writePage (unsigned int startAdr, unsigned int len,
+                                                   const unsigned char* data) {
+    //deactivate write-protection latch
+    enableWrite();
+
+    //activate eeprom
+    cs = 0;
     wait_us(1);
-    // send address
-    if (_size<512) { // 256 and 128 bytes
-        _spi->write(0x03);
-        _spi->write(LOW(startAdr));
-    } else if (512==_size) { // 4k variant adds 9th address bit to command
-        _spi->write(startAdr>255?0xb:0x3);
-        _spi->write(LOW(startAdr));
-    } else if (_size<131072) { // everything up to 512k
-        _spi->write(0x03);
-        _spi->write(HIGH(startAdr));
-        _spi->write(LOW(startAdr));
+
+    //send command and address
+    if (size<512) { // 256 and 128 bytes
+        spi.write(EEPROM_CMD_WRITE);
+        spi.write(LOW(startAdr));
+    } else if (512==size) { // 4k variant adds 9th address bit to command
+        spi.write(startAdr>255?0xa:EEPROM_CMD_WRITE);
+        spi.write(LOW(startAdr));
+    } else if (size<131072) { // everything up to 512k
+        spi.write(EEPROM_CMD_WRITE);
+        spi.write(HIGH(startAdr));
+        spi.write(LOW(startAdr));
     } else { // 25xx1024, needs 3 byte address
-        _spi->write(0x03);
-        _spi->write(startAdr>>16);
-        _spi->write(HIGH(startAdr));
-        _spi->write(LOW(startAdr));
+        spi.write(EEPROM_CMD_WRITE);
+        spi.write(startAdr>>16);
+        spi.write(HIGH(startAdr));
+        spi.write(LOW(startAdr));
     }
-    // read data into buffer
-    for (int i=0;i<len;i++) {
-        ret[i]=_spi->write(0);
-    }
+
+    //write data
+    for (int i=0; i<len; i++)
+        spi.write(data[i]);
     wait_us(1);
-    _enable->write(1);
-    return ret;
+
+    //disable eeprom
+    cs = 1;
+
+    //wait until write operation ends
+    if (!waitForWrite())
+        return 0;
+
+    return len;
 }
 
-bool Ser25LCxxx::write(unsigned int startAdr, unsigned int len, const char* data) {
-    if (startAdr+len>_size)
-        return -1;
+unsigned int Ser25LCxxx::clearPage (unsigned int pageNum) {
+    unsigned char s[pageSize];
+    memset(s, EEPROM_CLEAN_BYTE, pageSize);
+
+    return writePage(pageSize*pageNum, pageSize, s);
+}
+
+unsigned int Ser25LCxxx::clearMem () {
+    /* the implementation does not exploit clearPage to optimize the use
+       of the buffer s - it is not set for each page but just once for the
+       whole eeprom memory */
+    unsigned char s[pageSize];
+    unsigned int counter = 0;
+
+    memset(s, EEPROM_CLEAN_BYTE, pageSize);
+
+    for (int i=0; i<pageNumber; i++) {
+        if (writePage(pageSize*i, pageSize, s) != pageSize)
+            return counter;
+        counter += pageSize;
+    }
+
+    return counter;
+}
 
-    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)
+int Ser25LCxxx::readStatus() {
+    //activate eeprom
+    cs = 0;
+    wait_us(1);
+    
+    //send command
+    spi.write(EEPROM_CMD_RDSR);
+    
+    //read value
+    int status = spi.write(0x00);
+    wait_us(1);
+    
+    //deactivate eeprom
+    cs = 1;
+
+    return status;
+}
+
+bool Ser25LCxxx::waitForWrite() {
+    unsigned int counter = 0;
+
+    while (isWriteInProgress()) {
+        wait_us(10);
+        if ((++counter) > 500)
             return false;
-        // and switch to next page
-        ofs+=pageLen;
     }
+    
     return true;
 }
 
-bool Ser25LCxxx::writePage(unsigned int startAdr, unsigned int len, const char* data) {
-    enableWrite();
-
-    _enable->write(0);
+void Ser25LCxxx::enableWrite () {
+    //enable eeprom
+    cs = 0;
     wait_us(1);
 
-    if (_size<512) { // 256 and 128 bytes
-        _spi->write(0x02);
-        _spi->write(LOW(startAdr));
-    } else if (512==_size) { // 4k variant adds 9th address bit to command
-        _spi->write(startAdr>255?0xa:0x2);
-        _spi->write(LOW(startAdr));
-    } else if (_size<131072) { // everything up to 512k
-        _spi->write(0x02);
-        _spi->write(HIGH(startAdr));
-        _spi->write(LOW(startAdr));
-    } else { // 25xx1024, needs 3 byte address
-        _spi->write(0x02);
-        _spi->write(startAdr>>16);
-        _spi->write(HIGH(startAdr));
-        _spi->write(LOW(startAdr));
-    }
-
-    // do real write
-    for (int i=0;i<len;i++) {
-        _spi->write(data[i]);
-    }
+    //send command
+    spi.write(EEPROM_CMD_WREN);
     wait_us(1);
-    // disable to start physical write
-    _enable->write(1);
     
-    waitForWrite();
-
-    return true;
+    //disable eeprom
+    cs = 1;
 }
 
-bool Ser25LCxxx::clearPage(unsigned int pageNum) {
-    enableWrite();
-    if (_size<65535) {
-        char* s=(char*)malloc(_pageSize);
-        for (int i=0;i<_pageSize;i++) {
-            s[i]=0xff;
-        }
-        bool b=writePage(_pageSize*pageNum,_pageSize,s);
-        delete s;
-        return b;
-    } else {
-        _enable->write(0);
-        wait_us(1);
-        _spi->write(0x42);
-        _spi->write(HIGH(_pageSize*pageNum));
-        _spi->write(LOW(_pageSize*pageNum));
-        wait_us(1);
-        _enable->write(1);
-
-        waitForWrite();
-    }
-    return true;
-}
-
-void Ser25LCxxx::clearMem() {
+void Ser25LCxxx::writeStatus (unsigned char val) {
     enableWrite();
-    if (_size<65535) {
-        for (int i=0;i<_size/_pageSize;i++) {
-            if (!clearPage(i))
-                break;
-        }
-    }
-    else
-    {
-        _enable->write(0);
-        wait_us(1);
-        _spi->write(0xc7);
-        wait_us(1);
-        _enable->write(1);
 
-        waitForWrite();
-    }
-}
-
-int Ser25LCxxx::readStatus() {
-    _enable->write(0);
+    //enable eeprom
+    cs = 0;
     wait_us(1);
-    _spi->write(0x5);
-    int status=_spi->write(0x00);
-    wait_us(1);
-    _enable->write(1);
-    return status;
-}
 
-void Ser25LCxxx::waitForWrite() {
-    while (true) {
-        if (0==readStatus()&1)
-            break;
-        wait_us(10);
-    }
-}
+    //send command
+    spi.write(EEPROM_CMD_WRSR);
+    
+    //write stautus
+    spi.write(val);
+    wait_us(1);
 
-void Ser25LCxxx::enableWrite()
-{
-    _enable->write(0);
-    wait_us(1);
-    _spi->write(0x06);
-    wait_us(1);
-    _enable->write(1);
+    //disable eeprom
+    cs = 1;
 }
diff -r 3a3404dbd3eb -r be69a9f6f738 Ser25lcxxx.h
--- a/Ser25lcxxx.h	Sat Feb 19 18:29:20 2011 +0000
+++ b/Ser25lcxxx.h	Fri Jul 17 09:47:32 2015 +0000
@@ -26,70 +26,182 @@
 
 #include "mbed.h"
 
-/**
-A class to read and write all 25* serial SPI eeprom devices from Microchip (from 25xx010 to 25xx1024).
+
+#define EEPROM_CMD_READ     0x03
+#define EEPROM_CMD_WRITE    0x02
+#define EEPROM_CMD_WRDI     0x04
+#define EEPROM_CMD_WREN     0x06
+#define EEPROM_CMD_RDSR     0x05
+#define EEPROM_CMD_WRSR     0x01
+
+
+#define EEPROM_SPI_SPEED    1000000
 
-One needs to provide total size and page size, since this cannot be read from the devices,
-and the page size differs even by constant size (look up the data sheet for your part!)
+#define EEPROM_CLEAN_BYTE   0xff
+
+
+/**
+A class to read and write all 25* serial SPI eeprom devices from Microchip
+(from 25xx010 to 25xx1024).
+
+One needs to provide total size and page size, since this cannot be read from
+the devices, and the page size differs even by constant size (look up the data
+sheet for your part!)
 */
 class Ser25LCxxx
 {
     public:
+    
         /**
-            create the handler class
-            @param spi the SPI port where the eeprom is connected. Must be set to format(8,3), and with a speed matching the one of your device (up to 5MHz should work)
+            Create the handler class
+            
+            @param sck clock pin of the spi where the eeprom is connected
+            @param si input pin of the spi where the eeprom is connected
+            @param so output pin of the spi where the eeprom is connected
             @param enable the pin name for the port where /CS is connected
-            @param bytes the size of you eeprom in bytes (NOT bits, eg. a 25LC010 has 128 bytes)
-            @param pagesize the size of a single page, to provide overruns
+            @param pagenumber number of pages
+            @param pagesize the size of each page
         */
-        Ser25LCxxx(SPI *spi, PinName enable, int bytes, int pagesize);
+        Ser25LCxxx(PinName sck, PinName si, PinName so, PinName enable,
+                                                int pagenumber, int pagesize);
+        
+        /**
+            Destroy the handler
+        */
+        ~Ser25LCxxx() {};
         
         /**
-            destroys the handler, and frees the /CS pin        
+            Read from the eeprom memory
+            
+            @param startAdr the adress where to start reading
+            @param len the number of bytes to read
+            @param buf destination buffer
+            @return number of read bytes
         */
-        ~Ser25LCxxx();
+        unsigned int read(unsigned int startAdr, unsigned int len,
+                                                        unsigned char* buf);
         
         /**
-            read a part of the eeproms memory. The buffer will be allocated here, and must be freed by the user
-            @param startAdr the adress where to start reading. Doesn't need to match a page boundary
-            @param len the number of bytes to read (must not exceed the end of memory)
-            @return NULL if the adresses are out of range, the pointer to the data otherwise
+            Writes the give buffer into the eeprom memory
+
+            @param startAdr the eeprom adress where to start writing (it 
+                                        doesn't have to match a page boundary)
+            @param len number of bytes to write
+            @param data data to write
+            @return the number of written bytes
         */
-        char* read(unsigned int startAdr, unsigned int len);
+        unsigned int write(unsigned int startAdr, unsigned int len,
+                                                    const unsigned char* data);
+        
+        /**
+            Fills the given page with EEPROM_CLEAN_BYTE
+
+            @param pageNum the page to clear
+            @return the number of written bytes (it should be the page size)
+        */
+        unsigned int clearPage(unsigned int pageNum);
         
         /**
-            writes the give buffer into the memory. This function handles dividing the write into 
-            pages, and waites until the phyiscal write has finished
-            @param startAdr the adress where to start writing. Doesn't need to match a page boundary
-            @param len the number of bytes to read (must not exceed the end of memory)
-            @return false if the adresses are out of range
+            Fills the whole eeprom with EEPROM_CLEAN_BYTE
+            
+            @return the number of written bytes (it should be the eeprom size)
+        */
+        unsigned int clearMem();
+
+        /**
+            Return TRUE if a write is performing
+        */
+        bool isWriteInProgress() {
+            return (readStatus()&0x01)!=0x00;
+        }
+
+        /**
+            Return TRUE if the entire eeprom memory is writable
+        */
+        bool isFullyWritable() {
+            return (readStatus()&0x0C)==0x00;
+        }
+
+        /**
+            Return TRUE if only the first half of the eeprom memory is writable
         */
-        bool write(unsigned int startAdr, unsigned int len, const char* data);
-        
+        bool isHalfWritable() {
+            return (readStatus()&0x0C)==0x08;
+        }
+
+        /**
+            Return TRUE if the eeprom memory is not writable
+        */
+        bool isNotWritable() {
+            return (readStatus()&0x0C)==0x0C;
+        }
+
         /**
-            fills the given page with 0xFF
-            @param pageNum the page number to clear
-            @return if the pageNum is out of range        
+            Set the eeprom memory not writable
         */
-        bool clearPage(unsigned int pageNum);
+        void setNotWritable () {
+            writeStatus(0x0C);
+        }
+
+        /**
+            Set the eeprom memory fully writable
+        */
+        void setFullyWritable () {
+            writeStatus(0x00);
+        }
         
         /**
-            fills the while eeprom with 0xFF
+            Set only the first half of the eeprom memory as writable
         */
-        void clearMem();
+        void setHalfWritable() {
+            writeStatus(0x08);
+        }
+
     private:
-        bool writePage(unsigned int startAdr, unsigned int len, const char* data);
+    
+        /**
+            Write data within an eeprom page
+            
+            @param startAdr destination address of the eeprm memory
+            @param len number of bytes to write
+            @param data data to be written in the eeprom memory
+            @return number of written bytes
+        */
+        unsigned int writePage(unsigned int startAdr, unsigned int len,
+                                                    const unsigned char* data);
+                                                    
+        /**
+            Read the status register of the eeprom memory
+            
+            @return status register
+        */
         int readStatus();
-        void waitForWrite();
-        void enableWrite();
         
+        /**
+            Write the status register
+            
+            @param val status register
+        */
+        void writeStatus(unsigned char val);
 
-        SPI* _spi;
-        DigitalOut* _enable;
-        int _size,_pageSize;
+        /**
+            Wait until a write procedure ends or an interval of 5ms is expired
+            
+            @return TRUE if the write procedure has successfully terminated,
+                FALSE if the interval is expired
+        */
+        bool waitForWrite();
         
+        /**
+            Enable the write procedure
+        */
+        void enableWrite();
+
+        SPI spi;
+        DigitalOut cs;
+        int size;
+        int pageSize;
+        int pageNumber;
 };
 
-
-
 #endif