Serial RAM (SPI SRAM) library 23K256, 23LC1024 (Microchip) see: http://mbed.org/users/okini3939/notebook/extend-memory/
Dependents: SPIRAM_23LC1024_FIFO
Diff: SerRAM.cpp
- Revision:
- 0:69ea2af1d9af
- Child:
- 1:5a261b6a88af
diff -r 000000000000 -r 69ea2af1d9af SerRAM.cpp --- /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