Serial RAM (SPI SRAM) library 23K256, 23LC1024 (Microchip) see: http://mbed.org/users/okini3939/notebook/extend-memory/

Dependents:   SPIRAM_23LC1024_FIFO

Revision:
0:69ea2af1d9af
Child:
1:5a261b6a88af
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SerRAM.cpp	Mon Jan 07 14:30:06 2013 +0000
@@ -0,0 +1,431 @@
+/*
+ * Serial RAM (SPI SRAM) library
+ * Copyright (c) 2013 Hiroshi Suga
+ * Released under the MIT License: http://mbed.org/license/mit
+ */
+
+/** @file
+ * @brief Serial RAM (SPI SRAM) library
+ *   23K256, 23LC1024 (Microchip)
+ *   support FIFO
+ *   support DMA http://mbed.org/users/AjK/code/MODDMA/
+ */
+ 
+#include "mbed.h"
+#include "SerRAM.h"
+
+#define DBG(...)
+//#define DBG(...) printf("" __VA_ARGS__) 
+
+#define CMD_READ    0x03
+#define CMD_WRITE   0x02
+#define CMD_RDMR    0x05
+#define CMD_WRMR    0x01
+
+#define MODE_BYTE   0x00
+#define MODE_SEQ    0x40
+
+
+SerRAM::SerRAM (SPI& spi, PinName cs, int size) : _spi(spi), _cs(cs) {
+    _spi.frequency(16000000);
+    _cs = 1;
+    _size = size;
+    _alloc = 0;
+
+#ifdef RAM_USE_DMA
+    dmacfg0 = new MODDMA_Config;
+    dmacfg1 = new MODDMA_Config;
+    ssp_dmacr = &LPC_SSP1->DMACR;
+    dmacon0 = MODDMA::SSP1_Tx;
+    dmacon1 = MODDMA::SSP1_Rx;
+    dmaexit = 1;
+#endif
+
+#ifdef RAM_USE_FIFO
+    fifo_num = 0;
+#endif
+}
+
+SerRAM::SerRAM (PinName mosi, PinName miso, PinName sck, PinName cs, int size) : _spi(mosi, miso, sck), _cs(cs) {
+    _spi.frequency(16000000);
+    _cs = 1;
+    _size = size;
+    _alloc = 0;
+
+#ifdef RAM_USE_DMA
+    dmacfg0 = new MODDMA_Config;
+    dmacfg1 = new MODDMA_Config;
+    if (mosi == p5) {
+        ssp_dmacr = &LPC_SSP1->DMACR;
+        dmacon0 = MODDMA::SSP1_Tx;
+        dmacon1 = MODDMA::SSP1_Rx;
+    } else
+    if (mosi == p11) {
+        ssp_dmacr = &LPC_SSP0->DMACR;
+        dmacon0 = MODDMA::SSP0_Tx;
+        dmacon1 = MODDMA::SSP0_Rx;
+    }
+    dmaexit = 1;
+#endif
+
+#ifdef RAM_USE_FIFO
+    fifo_num = 0;
+#endif
+}
+
+int SerRAM::write (int addr, int dat) {
+
+    _cs = 0;
+    _spi.write(CMD_WRITE);
+    if (_size > 512) {
+        _spi.write((addr >> 16) & 0xff);
+    }
+    _spi.write((addr >> 8) & 0xff);
+    _spi.write(addr & 0xff);
+    _spi.write(dat);
+    _cs = 1;
+    return 0;
+}
+
+int SerRAM::write (int addr, char *buf, int len, int async) {
+    int i;
+#ifdef RAM_USE_DMA
+    static char dummy[RAM_DMA_SIZE];
+#endif
+
+    DBG("DMA write %04x %d\r\n", addr, len);
+#ifdef RAM_USE_DMA
+    while (! dmaexit);
+#endif
+    _cs = 0;
+    _spi.write(CMD_WRITE);
+    if (_size > 512) {
+        _spi.write((addr >> 16) & 0xff);
+    }
+    _spi.write((addr >> 8) & 0xff);
+    _spi.write(addr & 0xff);
+    
+#ifdef RAM_USE_DMA
+    if (len > RAM_DMA_SIZE) len = RAM_DMA_SIZE;
+
+    dmacfg0
+     ->channelNum    ( MODDMA::Channel_0 )
+     ->srcMemAddr    ( (uint32_t)buf )
+     ->dstMemAddr    ( dmacon0 )
+     ->transferSize  ( len )
+     ->transferType  ( MODDMA::m2p )
+     ->dstConn       ( dmacon0 )
+     ->attach_tc     ( this, &SerRAM::tc0_callback )
+     ->attach_err    ( this, &SerRAM::err_callback )
+    ; // config end
+ 
+    dmacfg1
+     ->channelNum    ( MODDMA::Channel_1 )
+     ->srcMemAddr    ( dmacon1 )
+     ->dstMemAddr    ( (uint32_t)dummy )
+     ->transferSize  ( len )
+     ->transferType  ( MODDMA::p2m )
+     ->srcConn       ( dmacon1 )
+     ->attach_tc     ( this, &SerRAM::tc1_callback )
+     ->attach_err    ( this, &SerRAM::err_callback )
+    ; // config end
+    
+    dmaexit = 0;
+    if (dma.Setup( dmacfg0 ) && dma.Setup( dmacfg1 )) {
+        DBG("DMA setup\r\n");
+        *ssp_dmacr = (1<<1)|(1<<0); // TX,RXDMAE
+        dma.Enable( dmacfg0 );
+        dma.Enable( dmacfg1 );
+        DBG("DMA enable\r\n");
+        i = 0;
+    } else {
+        DBG("DMA error\r\n");
+        i = -1;
+    }
+
+    if (async == 0) {
+        while (! dmaexit);
+        DBG("DMA done\r\n");
+    }
+#else
+    for (i = 0; i < len; i ++) {
+        _spi.write(buf[i]);
+    }
+    _cs = 1;
+#endif
+    return i;
+}
+
+int SerRAM::read (int addr) {
+    int dat;
+
+    _cs = 0;
+    _spi.write(CMD_READ);
+    if (_size > 512) {
+        _spi.write((addr >> 16) & 0xff);
+    }
+    _spi.write((addr >> 8) & 0xff);
+    _spi.write(addr & 0xff);
+    dat = _spi.write(0);
+    _cs = 1;
+    return dat;
+}
+
+int SerRAM::read (int addr, char *buf, int len, int async) {
+    int i;
+
+    DBG("DMA read %04x %d\r\n", addr, len);
+#ifdef RAM_USE_DMA
+    while (! dmaexit);
+#endif
+    _cs = 0;
+    _spi.write(CMD_READ);
+    if (_size > 512) {
+        _spi.write((addr >> 16) & 0xff);
+    }
+    _spi.write((addr >> 8) & 0xff);
+    _spi.write(addr & 0xff);
+
+#ifdef RAM_USE_DMA
+    dmacfg0
+     ->channelNum    ( MODDMA::Channel_0 )
+     ->srcMemAddr    ( (uint32_t)buf )
+     ->dstMemAddr    ( dmacon0 )
+     ->transferSize  ( len )
+     ->transferType  ( MODDMA::m2p )
+     ->dstConn       ( dmacon0 )
+     ->attach_tc     ( this, &SerRAM::tc0_callback )
+     ->attach_err    ( this, &SerRAM::err_callback )
+    ; // config end
+
+    dmacfg1
+     ->channelNum    ( MODDMA::Channel_1 )
+     ->srcMemAddr    ( dmacon1 )
+     ->dstMemAddr    ( (uint32_t)buf )
+     ->transferSize  ( len )
+     ->transferType  ( MODDMA::p2m )
+     ->srcConn       ( dmacon1 )
+     ->attach_tc     ( this, &SerRAM::tc1_callback )
+     ->attach_err    ( this, &SerRAM::err_callback )
+    ; // config end
+    
+    dmaexit = 0;
+    if (dma.Setup( dmacfg0 ) && dma.Setup( dmacfg1 )) {
+        *ssp_dmacr = (1<<1)|(1<<0); // TX,RXDMAE
+        dma.Enable( dmacfg0 );
+        dma.Enable( dmacfg1 );
+        i = 0;
+    } else {
+        DBG("DMA error\r\n");
+        i = -1;
+    }
+
+    if (async == 0) {
+        while (! dmaexit);
+    }
+#else
+    for (i = 0; i < len; i ++) {
+        buf[i] = _spi.write(0);
+    }
+    _cs = 1;
+#endif
+    return i;
+}
+
+int SerRAM::setStatus (int status) {
+
+    _cs = 0;
+    _spi.write(CMD_WRMR);
+    _spi.write(status);
+    _cs = 1;
+    return 0;
+}
+
+int SerRAM::getStatus () {
+    int r;
+
+    _cs = 0;
+    _spi.write(CMD_RDMR);
+    r = _spi.write(0);
+    _cs = 1;
+    return r;
+}
+
+#ifdef RAM_USE_DMA
+void SerRAM::tc0_callback () {
+
+    MODDMA_Config *config = dma.getConfig();
+    dma.Disable( (MODDMA::CHANNELS)config->channelNum() );
+
+    // Clear DMA IRQ flags.
+    if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq();    
+    if (dma.irqType() == MODDMA::ErrIrq) dma.clearErrIrq();    
+    DBG("tc0_callback\r\n");
+}
+
+void SerRAM::tc1_callback () {
+
+    dmaexit = 1;
+    _cs = 1;
+    *ssp_dmacr = 0;
+    
+    MODDMA_Config *config = dma.getConfig();
+    dma.Disable( (MODDMA::CHANNELS)config->channelNum() );
+
+    // Clear DMA IRQ flags.
+    if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq();    
+    if (dma.irqType() == MODDMA::ErrIrq) dma.clearErrIrq();    
+    DBG("tc1_callback\r\n");
+}
+
+void SerRAM::err_callback () {
+    dmaexit = -1;
+    _cs = 1;
+    DBG("err_callback\r\n");
+}
+#endif
+
+#ifdef RAM_USE_FIFO
+int SerRAM::fifoAlloc (int size) {
+    int n, s;
+
+    s = ((size + 1) / RAM_DMA_SIZE + 1) * RAM_DMA_SIZE;
+    if (fifo_num >= RAM_FIFO_NUM || (_size * 1024 - _alloc) < s) return -1;
+
+    n = fifo_num;
+    fifo[n].size = size + 1;
+    fifo[n].buf_w = new char[RAM_DMA_SIZE];
+    fifo[n].buf_r = new char[RAM_DMA_SIZE];
+    fifo[n].ram = _alloc;
+    fifoClear(n);
+
+    fifo_num ++;
+    _alloc += s;
+    DBG("alloc %d + %d (%d)\r\n", fifo[n].ram, fifo[n].size, _alloc);
+    return n;
+}
+
+int SerRAM::fifoPut (int n, char dat) {
+    int next, next2;
+
+    next = fifo[n].addr_w + 1;
+    next2 = fifo[n].addr2_w + 1;
+    if (next >= fifo[n].size) {
+        // last of fifo
+        next = 0;
+        next2 = 0;
+    }
+    if (next2 >= RAM_DMA_SIZE) {
+        // last of buffer
+        next2 = 0;
+    }
+
+    if (next == fifo[n].addr_r) {
+        // no data
+        return -1;
+    }
+#ifdef RAM_USE_DMA
+    while (! dmaexit); // busy DMA
+#endif
+    fifo[n].buf_w[fifo[n].addr2_w] = dat;
+
+    if (next2 == 0) {
+        // ring
+        write(fifo[n].ram + fifo[n].ram_w, fifo[n].buf_w, RAM_DMA_SIZE, 1);
+        if (fifo[n].ram_w == fifo[n].ram_r) {
+            // w = r
+            memcpy(fifo[n].buf_r, fifo[n].buf_w, RAM_DMA_SIZE);
+        }
+
+        fifo[n].ram_w += RAM_DMA_SIZE;
+        if (next == 0) {
+            fifo[n].ram_w = 0;
+        }
+    }
+    fifo[n].addr_w = next;
+    fifo[n].addr2_w = next2;
+    return 0;
+}
+
+int SerRAM::fifoPut (int n, char *buf, int len) {
+    int i;
+    
+    for (i = 0; i < len; i ++) {
+        if (fifoPut(n, buf[i])) return -1;
+    }
+    return 0;
+}
+
+int SerRAM::fifoGet (int n, char *dat) {
+
+    if (fifo[n].addr_r == fifo[n].addr_w) {
+        // no data
+        return -1;
+    }
+#ifdef RAM_USE_DMA
+    while (! dmaexit); // busy DMA
+#endif
+    if (fifo[n].ram_r != fifo[n].ram_w) {
+        *dat = fifo[n].buf_r[fifo[n].addr2_r];
+    } else
+    if (fifo[n].addr_w < fifo[n].addr_r) {
+        *dat = fifo[n].buf_r[fifo[n].addr2_r];
+    } else {
+        // w = r and w > r
+        *dat = fifo[n].buf_w[fifo[n].addr2_r];
+    }
+
+    fifo[n].addr_r += 1;
+    fifo[n].addr2_r += 1;
+    if (fifo[n].addr_r >= fifo[n].size) {
+        // last of fifo
+        fifo[n].addr_r = 0;
+        fifo[n].addr2_r = 0;
+    }
+    if (fifo[n].addr2_r >= RAM_DMA_SIZE) {
+        // last of buffer
+        fifo[n].addr2_r = 0;
+    }
+    if (fifo[n].addr2_r == 0) {
+        // ring
+        fifo[n].ram_r += RAM_DMA_SIZE;
+        if (fifo[n].addr_r == 0) {
+            fifo[n].ram_r = 0;
+        }
+        read(fifo[n].ram + fifo[n].ram_r, fifo[n].buf_r, RAM_DMA_SIZE);
+    }
+    return 0;
+}
+
+int SerRAM::fifoGet (int n, char *buf, int len) {
+    int i;
+    
+    for (i = 0; i < len; i ++) {
+        if (fifoGet(n, &buf[i])) return -1;
+    }
+    return 0;
+}
+
+int SerRAM::fifoAvailable (int n) {
+    if (fifo[n].addr_w < fifo[n].addr_r) {
+        return fifo[n].addr_r - fifo[n].addr_w - 1;
+    } else {
+        return (fifo[n].size - fifo[n].addr_w) + fifo[n].addr_r - 1;
+    }
+}
+
+int SerRAM::fifoUse (int n) {
+    return fifo[n].size - fifoAvailable(n) - 1;
+}
+
+void SerRAM::fifoClear (int n) {
+    fifo[n].addr_w = 0;
+    fifo[n].addr_r = 0;
+    fifo[n].addr2_w = 0;
+    fifo[n].addr2_r = 0;
+    fifo[n].ram_w = 0;
+    fifo[n].ram_r = 0;
+
+    read(fifo[n].ram + fifo[n].ram_r, fifo[n].buf_r, RAM_DMA_SIZE);
+}
+#endif