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

Dependents:   SPIRAM_23LC1024_FIFO

SerRAM.cpp

Committer:
okini3939
Date:
2013-01-07
Revision:
0:69ea2af1d9af
Child:
1:5a261b6a88af

File content as of revision 0:69ea2af1d9af:

/*
 * 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