Rickta59

Interface 23K256 SPI RAM with USI SPI

9 posts in this topic

I came across the Microchip 23K256 Serial RAM device a while back. It seems to fit the bill

when you need more RAM but don't want to spend a lot of money to get it. These chips are

inexpensive, (about $1,50 at time of this post 06/05/2011) and hold 32K worth of data. You access

the memory using SPI. You can even put multiple 23K256 devices on your SPI bus and get

even more buffer space.

 

The arduino world already has a couple of libraries to access these devices. I adapted

one of these to work with both CCS and the uniarch compiler.

 

I used GRACE to help me configure the msp430g2452 USI in SPI Mode 1. This code might be

also be useful as a starting point for any SPI Mode 1 device. This code is written for the USI

device. It would be nice to have a USCI version that would work with the g2553 chip.

 

Typical use for this device might be to buffer UART received data that is larger than the 128 bytes

of SRAM available on the smaller G series chips.

 

You might want to verify the pin setup, I think I have it correct, however you should always verify.

One interesting item of note is my use of one of the GPIO pins to power the device. You may or

may not want to use that in your setup. According to the datasheet the 23K256 uses somewhere

between 2-10mA of power based on how fast you interface with it. I think the G series should

handle that but I'm not 100% sure. Please comment if you know.

 

https://gist.github.com/1009412

 

/**
* testspi23K256.c - Interface Microchip 23K256 Serial RAM chip with MSP430G2452 USI SPI
*
* This code snippet show you how to configure the USI SPI in SPI Mode 1 to communicate with
* the 23K256 chip at 4MHz. It takes about 98uSecs to write a single byte and 32K takes
* about 100ms.
*
* msp430g2452 - http://focus.ti.com/docs/prod/folders/print/msp430g2452.html
* 23K256 - http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en539039
*
* Pin Connections:
*
* MSP430 SCLK P1.5 -> PIN 6(SCK) 23K256
* MSP430 MISO P1.7 -> PIN 2(SO) 23K256
* MSP430 MOSI P1.6 -> PIN 5(SI) 23K256
* MSP430 GPIO P1.1 -> PIN 1(CS) 23K256
* MSP430 GPIO P1.4 -> PIN 8(VCC) 23K256
* 
* 23K256 PIN 7 Pullup resistor
* 23K256 PIN 8 Pulldown resistor
*
* Linux build:
*
* $ msp430-gcc -mmcu=msp430g2452 -O3 -g -Wall -I. -c testspi23K256.c
* $ msp430-gcc -mmcu=msp430g2452 -O3 -g -Wall -I. -o ./testspi23K256.elf ./testspi23K256.o -Wl,-Map,./testspi23K256.map
* $ msp430-objdump -Sww ./testspi23K256.elf >./testspi23K256.lss
* $ msp430-size ./testspi23K256.elf
* $ mspdebug -q --force-reset rf2500 "erase" "prog ./testspi23K256.elf"
*
* This code was inspired from this code:
*
* http://sourceforge.net/projects/mysudoku/files/
* http://hackaday.com/2009/03/02/parts-32kb-spi-sram-memory-23k256/
*
*/

#include 
#include 

enum {
   POWER_PIN = BIT4, // we power the 23K256 chip from one of our GPIO pins
   SS_PIN = BIT1,    // CS , active low
   DEBUG_PIN = BIT0, // toggle on and off marking time to write
   DUMMY_BYTE = 0xFF // byte we send when we just want to read slave data
};

static inline uint8_t RWData(volatile uint8_t value);
void loop();

#define powerOn P1OUT |= POWER_PIN 
#define powerOff P1OUT &= ~POWER_PIN

#define ssSelect  P1OUT &= ~SS_PIN
#define ssDeselect  P1OUT |= SS_PIN

#define delay_1ms __delay_cycles(16000)

#define SR_WRITE_STATUS 0x01
#define SR_WRITE        0x02
#define SR_READ         0x03
#define SR_READ_STATUS  0x05

// 23K256 Serial Ram functions

uint8_t SR_getMode(void) {
   ssSelect;
   RWData(SR_READ_STATUS); // 0x05
   uint8_t mode = RWData(DUMMY_BYTE);
   ssDeselect;

   return mode;
}

void SR_setMode(uint8_t mode) {
   ssSelect;
   RWData(SR_WRITE_STATUS); // 0x01
   RWData(mode);
   ssDeselect;
}

static inline void SR_writestream(uint16_t addr) {
   ssDeselect; // deselect if we are active
   ssSelect;
   RWData(0x02);
   RWData(addr >> 8);
   RWData(addr);
}

static inline void SR_readstream(uint16_t addr) {
   ssDeselect;
   ssSelect;
   RWData(0x03);
   RWData(addr >> 8);
   RWData(addr);
}

static inline uint8_t RWData(volatile uint8_t value) {
   USISRL = value;
   USICNT = 8; // initialize bit count, start transfer/read

   __delay_cycles(0);

   while (!(USIIFG & USICTL1))
       ; // wait for SPI flag to trip

   return USISRL;
}

/**
* main
*/

int main() {
   // kill the watchdog timer
   WDTCTL = WDTPW + WDTHOLD;

   // configure DCO clock to 16MHz
   BCSCTL2 = SELM_0 + DIVM_0 + DIVS_0;
   if (CALBC1_16MHZ != 0xFF) {
       DCOCTL = 0x00;
       BCSCTL1 = CALBC1_16MHZ;
       DCOCTL = CALDCO_16MHZ;
   }
   BCSCTL1 |= XT2OFF + DIVA_0;
   BCSCTL3 = XT2S_0 + LFXT1S_2 + XCAP_1;

   // configure GPIO
   P1OUT = POWER_PIN;
   P1DIR = SS_PIN + POWER_PIN + DEBUG_PIN;

   // configure USI - SPI Mode 1 @ 4MHz, clocked off SMCLK
   USICTL0 |= USISWRST;
   USICTL0 = USIPE7 + USIPE6 + USIPE5 + USIMST + USIOE + USISWRST;
   USICTL1 |= USICKPH;
   USICKCTL = USIDIV_1 + USISSEL_2;

   /* Enable USI */
   USICTL0 &= ~USISWRST;
   __enable_interrupt(); // Set global interrupt enable

   // toggle the power for the 23K256
   powerOff;
   delay_1ms;
   powerOn;
   ssDeselect;
   delay_1ms;

   while (1) {
       uint8_t chipMode;

       // make sure there is a 23K256 chip and that
       // is wired properly and in sequential mode
       chipMode = SR_getMode();
       if (chipMode != 0x41) {
           SR_setMode(0x41);
       } else {
           while (1) {
               loop();
           }
       }
   }
}

#define BYTES_TO_STREAM 32768     // should be less <= 32768
#define PATTERN_BYTE_VALUE 0x55

/**
* loop - write a test pattern and read it back
*/

void loop() {
   volatile uint16_t i;
   volatile uint8_t storedValue = 0;

   P1OUT |= DEBUG_PIN; // mark start of write for measurement with oscope
   SR_writestream(0); // start writing at address 0
   for (i = 0; i < BYTES_TO_STREAM; ++i) {
       RWData(PATTERN_BYTE_VALUE);
   }
   P1OUT &= ~DEBUG_PIN; // mark end of write for measurement with oscope

   // verify the bytes we wrote were stored and retreived properly
   SR_readstream(0); // start reading at address 0
   for (i = 0; i < BYTES_TO_STREAM; ++i) {
       storedValue = RWData(DUMMY_BYTE);

       // verify our test pattern
       if (storedValue != PATTERN_BYTE_VALUE) {
           // if values aren't the same an error occurred,
           // turn off all leds, then sit and spin
           P1OUT &= ~BIT6;
           P1OUT &= ~DEBUG_PIN;
           while (1) {
               ;
           }
       }
   }
}

Share this post


Link to post
Share on other sites

I should mention that the code above should work fine with any value line chip that has a

USI SPI peripheral ( msp430g2231 etc ... ) not just the msp430g2452 mentioned.

 

-rick

Share this post


Link to post
Share on other sites

Have you used this in a project? I'm working on something where I need more memory and I'm wondering if this might fit the bill. I want to be able to draw lines on a Nokia 5110, and apparently you have to buffer the entire screen in memory because there's no way to draw on a single pixel.

Share this post


Link to post
Share on other sites
Have you used this in a project?

 

I haven't yet. I'm planning on using as memory buffer for serial data. However, I mainly wrote it to figure out how

SPI worked on the msp430 chips with USI peripherals.

 

-rick

Share this post


Link to post
Share on other sites
I'm working on something where I need more memory and I'm wondering if this might fit the bill. I want to be able to draw lines on a Nokia 5110, and apparently you have to buffer the entire screen in memory because there's no way to draw on a single pixel.

 

How many bytes do you need to send to the Nokia? How often are you updating that data? Can you feed the Nokia using SPI? The big question is can you update the memory then read it back faster than your screen refresh rate. When I was testing, I set my MCLK to 16MHz and then measured how long it took to read one byte and one 32k block. The one byte read has some minimum SPI protocol overhead but could be done in about 110ish uSecs. The 32k read took about 100mSecs. I think I was using a 4MHz SPI clock, you might be able to do better than that.

 

-rick

Share this post


Link to post
Share on other sites

Are you planning on doing something fancy or simple read/write? I have some ideas on how to speed data transfer between SRAM and 5110.

Share this post


Link to post
Share on other sites

The project I'm working on is a digital "Etch-A-Sketch" for my kids. I've got an LCD coming on Tuesday (RobG - the one you found on eBay) that allows a much higher level of control, but I was trying to prototype with the Nokia I had on hand. Because graphics are written in blocks, it's not possible to paint a single pixel. The only other option would be to buffer the contents of the screen in memory, but there's not enough in the MCU. For this specific implementation - refresh rate wouldn't really be an issue.

 

Honestly - I should probably just wait until the other LCD arrives :) I thought I had some 23K256 on hand, but I accidentally ordered samples of the low-power 23A256, so I'll have to wait for the 3v version to arrive.

 

BUT - if the SRAM worked out - it could allow us to actually do graphics on the Nokia LCD similar to the FRAM project that someone made. You could obviously do a lot more with 32K of SRAM. Could be an interesting LP project.

Share this post


Link to post
Share on other sites

I've just ordered a few of these, and I intend to use one to store pixel data for a display built from addressable RGB LED strips - I'll keep an eye on this thread and report back on progress once I have the chips, but I'll also be posting updates in my project thread.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now