StraySpark

Arduino bit-banged SPI EEPROM

The ST M95M01 is a 5-volt synchronous-serial(SPI) EEPROM that can store 131,072 8-bit bytes, that I connected to an Arduino, to make a simple datalogger.

http://www.st.com/stonline/products/literature/ds/13264.pdf

(new datasheet:)

http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/CD00152481.pdf

 

Sometimes, I like bit-banging rather than using an SPI library because it gives one more control over what bits go where, to save space.  For instance, if I took a reading with a 10-bit Arduino analog pin, the lower 8 bits would be stored in the EEPROM in one address, and the two most-significant bits would go in the next address.  Leaving 6 whole bits unused.  Which really bugged me.

So I wrote a sketch that will take four 10-bit readings from an analog channel, store the lower-8 bits of each reading in 4 addresses, and then the four upper-2 bits in a 5th address, so that all the space is used.

The data could be chopped up(or not) and stored in any number of ways to use all the space, including storing the readings as one long sequence, but I thought using smaller 5-byte “pages” might be more manageable and flexible down the road.  And mostly, I wanted to see if I could get all the parsing and array-juggling to work out right.  Your mileage may vary, as they say.

I ended up with the SO8W(wide) surface mount package instead of the more common SO8N package, so I had to make my own adapter board:

Here it is wired up on a proto-shield from Lady Ada(www.adafruit.com):

And here’s a fritz.  I left off the channel 0 sensor connection for clarity.  I used an LM34 temperature sensor for testing.  Note the 100k pullup on the chip-select pin, and the 100k pull-down on the clock pin, per the datasheet.  If the button is pressed before a reset, the Arduino starts recording.  Otherwise, after a reset (or a serial connection is made) the Arduino will start yelling the data stored in the EEPROM out the serial port, where it can be captured with a terminal program, such as Bray Terminal, or gtkterm.  I used the red and green LED’s to indicate that something was happening while the program was running.  I had to put a ~3ms delay in the chip-select functions to get everything to work right, so I took the opportunity to flash the LED’s at that point.

And the sketch:

/*spi_memory
*/

/*declares*/

int LM34Pin = 0;
int ButtonPin = 5; //10k pullup, button on lowside
int GreenLEDPin = 6;
int RedLEDPin =  7;    //LED’s connected from pin through 1k to ground
int Chip_Select = 11;
int From_Data_Out = 10;
int Sclk = 9;
int To_Data_In = 8;

byte WREN = B00000110;
byte WRDI = B00000100;
byte RDSR = B00000101;
byte WRSR = B00000001;
byte READ = B00000011;
byte WRITE = B00000010;

int i = 0;
unsigned int starting_address;
int h,l;
int fourhigh;
int readings[9];
int retrieve_readings[9];
int button_state;
void setup(){
pinMode(LM34Pin, INPUT);
pinMode(ButtonPin, INPUT);
pinMode(GreenLEDPin, OUTPUT);
pinMode(RedLEDPin, OUTPUT);
pinMode(Chip_Select, OUTPUT);
pinMode(From_Data_Out, INPUT);
pinMode(Sclk, OUTPUT);
pinMode(To_Data_In, OUTPUT);
Serial.begin(9600);
Serial.println(“Done setting up…”);
button_state = digitalRead(ButtonPin);
}

void loop()
{
if(button_state == LOW){ //button pressed, starts recording
for(starting_address = 0;starting_address < 10800; starting_address +=5)
{
take_four_readings(readings, starting_address);
}
}
else{
for(starting_address = 0;starting_address < 10800; starting_address +=5)
{
rebigulate(retrieve_readings, starting_address);
}
}
/*start looping*/
while(1){
digitalWrite(GreenLEDPin, HIGH);
delay(1000);
digitalWrite(GreenLEDPin,LOW);
delay(1000);
}
}
/*****************************************************/
/*functions*/
void chip_select_low(void)
{
digitalWrite(Chip_Select, LOW);
digitalWrite(GreenLEDPin, HIGH);
delay(3);
digitalWrite(GreenLEDPin, LOW);

}
void chip_select_high(void)
{
digitalWrite(Chip_Select, HIGH);
digitalWrite(RedLEDPin, HIGH);
delay(3);
digitalWrite(RedLEDPin, LOW);
}
void sclk()
{
digitalWrite(Sclk, HIGH);
digitalWrite(Sclk, LOW);
}
/************************************************/
int read_8(int address)
{
char bit;
char inbit;
char index;
int value = 0;
chip_select_low();
send_8(READ);
send_address(address);
for(index = 0; index < 8; index++)
{
bit = digitalRead(From_Data_Out);
inbit = bit & 0x01;
if(inbit == 1)
{
value = value + (0x80>>index);
sclk();
}
else
{
sclk();
}
}
chip_select_high();
return value;
}
/***********************************************/
void send_8(char dat){
char bit;
char index;
for(index = 7;index>=0;index–)
{
bit = ((dat>>index) & 0x01);
if(bit==1)
{
digitalWrite(To_Data_In, HIGH);
sclk();
}
else
{
digitalWrite(To_Data_In, LOW);
sclk();
}
}

}
/******************************************************************/
void send_address(int dat){
char bit;
char index;
for(index = 23;index>=0;index–)
{
bit = ((dat>>index) & 0x01);
if(bit==1)
{
digitalWrite(To_Data_In, HIGH);
sclk();
}
else
{
digitalWrite(To_Data_In, LOW);
sclk();
}
}
}
/************************************************************/
void take_four_readings(int *array,unsigned int address){
char index;
for(index = 0;index < 4;index++)
{
array[index] = analogRead(LM34Pin);//fill the array
delay(1000);
}
four_high(array);
low_byte(array);
for(index=0;index<5;index++)
{
chip_select_low();
send_8(WREN);
chip_select_high();
chip_select_low();
send_8(WRITE);
send_address(address + index);  //need to pass a real address here
send_8(array[index+4]); //B10101010
chip_select_high();
}
}

/*********************************************************/
void four_high(int *array){
char shift = 6;
char index;
fourhigh = 0;
for(index = 0; index <4; index++)
{
fourhigh = fourhigh ^ (highByte(array[index])<<shift);
shift -=2;
}
array[4] = fourhigh;
}
/**********************************************************/
void low_byte(int *array){
char index;
for(index = 5; index < 9; index++)
{
array[index] = lowByte(array[index – 5]);
}
}

/*****************************************************/
void rebigulate(int *array, int address){ //pass retrieve_readings, starting address
char shift = 0;
char rshift = 6;
char index;

for(index=0;index<5;index++)
{
array[index] = read_8(address + index);
}

for(index = 0; index < 4; index++)
{
h = (array[0] & (0xC0 >> shift)) >> rshift;
l =  lowByte(array[index+1]);
array[index+5] = word(h,l);
shift += 2;
rshift -= 2;
Serial.println(array[index+5]);
}
}

4 Comments »

  1. Good stuff, thanks for posting. One update though the part number is M95M01 not M98501.
    http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/CD00152481.pdf

    Comment by Brian Page — January 24, 2014 @ 11:30 pm

  2. Thanks for catching that, Brian!

    Comment by strayspark — January 26, 2014 @ 5:33 pm

  3. In case it is of any use to anyone – I wrote a simple hardware SPI library for the M95M0. It is available on Github here https://github.com/stefandz/M95M01

    Comment by stefanbare — September 17, 2016 @ 8:52 am

  4. Sorry, I meant the M95M01 (can’t see anywhere to edit that comment).

    Comment by stefanbare — September 17, 2016 @ 8:53 am


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: