Problems with parallel sram

Hello,

I'm trying to interface an 32k x 8 sram chip with shift registers but somehow it just won't write or read anything. I've tested the shift registers output which seems to be fine.

I've tried the advice given here: http://arduino.cc/forum/index.php/topic,50633.0.html

I've added decoupling capacitors and simplified the reading and writing routine but to no avail.

I've based the shift register setup on this tutorial: http://www.arduino.cc/en/Tutorial/ShiftOut

Here's the code:

	//Pin connected to ST_CP of 74HC595
	int latchPin = 8;
	//Pin connected to SH_CP of 74HC595
	int clockPin = 12;
	////Pin connected to DS of 74HC595
	int dataPin = 11;
	int bytePins[8] = {2,3,4,5,6,7,9,10};
	int CE = A2;
	int OE = A1;
	int WE = A0;
	
	#define CHECK_BIT(var,pos) ((var) & (1<<(pos)))

	/*	mode: INPUT or OUTPUT
		OUTPUT: for writing out to memory
		INPUT: for reading from memory
	*/
	void setBytePins(int mode){
		for(int i=0 ; i < 8 ; i++){
			pinMode(bytePins[i], mode);
		}
	}
	
	void readBytes(unsigned char * buffer, unsigned short addr, int len){
		unsigned char b;
		setBytePins(INPUT);
		digitalWrite(CE, LOW);
		digitalWrite(OE, LOW);
		digitalWrite(WE, HIGH);
		for( int i=0 ; i < len ; i++){
			b = readByte((int)addr+i);
			buffer[i] = b;
		}
	}
	
	unsigned char readByte(unsigned short addr){
		setAddr(addr);
		return getByte();
	}
	
	unsigned char getByte(){
		unsigned char b=0;
		int state;
		for( int i=0 ; i < 8 ; i++ ){
			state = digitalRead(bytePins[i]);
			if( state == HIGH ){
				b |= 1 << i;
			}else{
				b &= ~(1 << i);
			}
		}
		return b;
	}
	
	void writeBytes(unsigned char * b, unsigned short addr, int len){
		setBytePins(OUTPUT);
		digitalWrite(CE, LOW);
		digitalWrite(WE, LOW);
		for( int i=0 ; i < len ; i++){
			writeByte(b[i],(int)addr+i);
		}
	}
	
	void writeByte(unsigned char b, unsigned short addr){
		setAddr(addr);
		setByte(b);
		writePulse();
	}
	
	void setAddr(unsigned short addr){
		doubleShiftOut(addr);
	}
	
	void setByte(unsigned char b){

		for(int i=0 ; i < 8 ; i++){
			if( bytePins[i] & (0x1 << i) ){
				digitalWrite(bytePins[7-i], HIGH);
			}else{
				digitalWrite(bytePins[7-i], LOW);
			}
		}
	}
	
	void writePulse(){
		digitalWrite(WE, LOW);
		delay(10);
		digitalWrite(WE, HIGH);
	}
	
	void setup() {
	  //set pins to output so you can control the shift register
	  Serial.begin(9600);
	  pinMode(latchPin, OUTPUT);
	  pinMode(clockPin, OUTPUT);
	  pinMode(dataPin, OUTPUT);
	  pinMode(CE, OUTPUT);
	  pinMode(OE, OUTPUT);
	  pinMode(WE, OUTPUT);
	  setBytePins(OUTPUT);
	}

	void loop() {
	
		unsigned char in_byte[4] = { 0xf0, 0x42, 0x43, 0xf };
		unsigned char out_byte[4] = { 0,0,0,0 };
		
		writeBytes(in_byte, 0x0, 4);
		readBytes(out_byte, 0x0, 4);

		Serial.print(out_byte[0], BIN);
		Serial.print(" ");
		Serial.print(out_byte[1], BIN);
		Serial.print(" ");
		Serial.print(out_byte[2], BIN);
		Serial.print(" ");
		Serial.println(out_byte[3], BIN);
		
		delay(500);
	}

	void doubleShiftOut(unsigned short b){
		unsigned char b1 = (b & 0xff00) >> 8;
		unsigned char b2 = b & 0xff;
		digitalWrite(latchPin, LOW);
		shiftOut(dataPin, clockPin, MSBFIRST, b1);
		shiftOut(dataPin, clockPin, MSBFIRST, b2);
		digitalWrite(latchPin, HIGH);
	}

This prints the following to the serial line (which should be what I've written to sram):

1100000 1100000 1100000 1100000
1100000 1100000 1100000 1100000
1100000 1100000 1100000 1100000
1100000 1100000 1100000 1100000
1100000 1100000 1100000 1100000
...

The schematic is attached.

Without looking at the code yet, I would remove ALL those caps and put them in the right places. Most of them go from 5v to VCC ! others go from GND to GND, and some are on data and clock lines.

All you need are caps from VCC to GND near each chip.


Rob

I think part of the problem is that writeBytes() is leaving WE set to LOW (WRITING!). Keep WE set to HIGH except in writePulse() or you risk writing to other parts of your memory.

The 10 millisecond delay in writePulse() is probably WAY overkill. That's like 160,000 instruction cycles for a memory write!

I've removed all the caps and added ones between every VCC and GND pin of the IC's.

I've made sure WE is set to HIGH except for during writepulse (HIGH, LOW, HIGH).

I've removed the 10ms delay.

Now it gives:
0 0 0 0
0 0 0 0
0 0 0 0
...

Anything else it could be?

Another mistake:

	void setByte(unsigned char b){

		for(int i=0 ; i < 8 ; i++){
			if( bytePins[i] & (0x1 << i) ) {   /////// That should be 'b' and not 'bytePins[i]'
				digitalWrite(bytePins[7-i], HIGH);
			}else{
				digitalWrite(bytePins[7-i], LOW);
			}
		}
	}

Because digitalRead() returns 0 or 1 and digitalWrite() takes 0 for LOW and anything else for HIGH and 0==false and anything else == true you can simplify the code somewhat:

void setByte(unsigned char b)
        {
	for(int i=0 ; i < 8 ; i++)
		digitalWrite(bytePins[7-i], b && (1<<i) );
	}

unsigned char getByte()
        {
	unsigned char b=0;

	for( int i=0 ; i < 8 ; i++ )
		b |= digitalRead(bytePins[i] << i;

	return b;
	}
[code]

[/code]

if( bytePins[i] & (0x1 << i) ) {   /////// That should be 'b' and not 'bytePins[i]'

Oh wow, totally missed that.

Because digitalRead() returns 0 or 1 and digitalWrite() takes 0 for LOW and anything else for HIGH and 0==false and anything else == true you can simplify the code somewhat

Nice, changed it out.

What I get now is:
1 1 1 1
1 1 1 1
1 1 1 1
...

When I put the voltmeter on the 5v bus (at the top) I'm only reading around 3.73v when it's operating. When the Arduino powers on it's at 4.8v but quickly falls to 3.73v when the code starts to execute.

Isn't this way too little to properly operate the IC's?

Am I drawing too much power from the Arduino? It's powered by usb.

I forgot I had taken out the caps for tests.

With caps it still only shows around 4.36v while operating.

I think it's time to post your code again. I've lost track of what changes you've made.

Also, looks like I missed a ')':

unsigned char getByte()
        {
	unsigned char b=0;

	for( int i=0 ; i < 8 ; i++ )
		b |= digitalRead(bytePins[i]) << i;
//////////////////////////////////   ^--  This one was missing

	return b;
	}
[code]

[/code]

Post the schematic again too.

If you had 4 of these to make a 16 counter, it could be preloaded from your shift register, and then just clocked once for the next address to quickly access sequential addresses with having to shift out the address every time.

http://focus.ti.com/lit/ds/sdls198/sdls198.pdf

Am looking for more modern version, or a comparable 8-bit preloadable counter.
This perhaps, use one shift register to load both, one at a time.

Looks good. I'll see if I can get that for the finished prototype for the thing it's going to be a part of (a gameboy classic rom loader/interface).

Here's the updated code:

	//Pin connected to ST_CP of 74HC595
	int latchPin = 8;
	//Pin connected to SH_CP of 74HC595
	int clockPin = 12;
	////Pin connected to DS of 74HC595
	int dataPin = 11;
	int bytePins[8] = {2,3,4,5,6,7,9,10};
	int CE = A2;
	int OE = A1;
	int WE = A0;
	
	#define CHECK_BIT(var,pos) ((var) & (1<<(pos)))

	/*	mode: INPUT or OUTPUT
		OUTPUT: for writing out to memory
		INPUT: for reading from memory
	*/
	void setBytePins(int mode){
		for(int i=0 ; i < 8 ; i++){
			pinMode(bytePins[i], mode);
		}
	}
	
	void readBytes(unsigned char * buffer, unsigned short addr, int len){
		unsigned char b;
		setBytePins(INPUT);
		digitalWrite(CE, LOW);
		digitalWrite(OE, LOW);
		for( int i=0 ; i < len ; i++){
			b = readByte((int)addr+i);
			buffer[i] = b;
		}
	}
	
	unsigned char readByte(unsigned short addr){
		setAddr(addr);
		return getByte();
	}
	
	unsigned char getByte(){
		unsigned char b=0;

		for( int i=0 ; i < 8 ; i++ ){
			b |= digitalRead(bytePins[i] << i);
		}

		return b;
	}
	
	void writeBytes(unsigned char * b, unsigned short addr, int len){
		setBytePins(OUTPUT);
		digitalWrite(CE, LOW);
		for( int i=0 ; i < len ; i++){
			writeByte(b[i],(int)addr+i);
		}
	}
	
	void writeByte(unsigned char b, unsigned short addr){
		setAddr(addr);
		setByte(b);
		writePulse();
	}
	
	void setAddr(unsigned short addr){
		doubleShiftOut(addr);
	}
	
	void setByte(unsigned char b){
		for(int i=0 ; i < 8 ; i++)
			digitalWrite(bytePins[7-i], b && (1<<i) );
	}
	
	void writePulse(){
		digitalWrite(WE, LOW);
		digitalWrite(WE, HIGH);
	}
	
	void setup() {
	  //set pins to output so you can control the shift register
	  Serial.begin(9600);
	  pinMode(latchPin, OUTPUT);
	  pinMode(clockPin, OUTPUT);
	  pinMode(dataPin, OUTPUT);
	  pinMode(CE, OUTPUT);
	  pinMode(OE, OUTPUT);
	  pinMode(WE, OUTPUT);
	  setBytePins(OUTPUT);
	  
	  digitalWrite(WE, HIGH);
	}

	void loop() {
	
		unsigned char in_byte[4] = { 0xf1, 0x40, 0x41, 0xf0 };
		unsigned char out_byte[4] = { 1,2,3,4 };
		
		writeBytes(in_byte, 0x0, 4);
		readBytes(out_byte, 0x0, 4);

		Serial.print(out_byte[0], BIN);
		Serial.print(" ");
		Serial.print(out_byte[1], BIN);
		Serial.print(" ");
		Serial.print(out_byte[2], BIN);
		Serial.print(" ");
		Serial.println(out_byte[3], BIN);
		
		delay(500);
	}

	void doubleShiftOut(unsigned short b){
		unsigned char b1 = (b & 0xff00) >> 8;
		unsigned char b2 = b & 0xff;
		digitalWrite(latchPin, LOW);
		shiftOut(dataPin, clockPin, MSBFIRST, b1);
		shiftOut(dataPin, clockPin, MSBFIRST, b2);
		digitalWrite(latchPin, HIGH);
	}

Attached is the updated schematic.

If OE was left LOW by something when writeBytes() is running then the memory chip output drivers will be fighting with the Arduino output drivers. This could cause undue power consumption. You should probably minimize the amount of time that CE and/or OE are LOW:

	void setup() {
	  //set pins to output so you can control the shift register
	  Serial.begin(9600);
	  pinMode(latchPin, OUTPUT);
	  pinMode(clockPin, OUTPUT);
	  pinMode(dataPin, OUTPUT);

	  pinMode(CE, OUTPUT);    ///  Might want to put a pull-up resistor on this pin to make sure chip stays disabled during reset,
	  digitalWrite(CE, HIGH);   /// Add this

	  pinMode(OE, OUTPUT);
	  digitalWrite(OE, HIGH);  /// Add this

	  pinMode(WE, OUTPUT);
	  digitalWrite(WE, HIGH);  /// Add this

	  setBytePins(OUTPUT);   // Don't really need this  readBytes() and writeBytes() both set them properly	  
	}


void readBytes(unsigned char * buffer, unsigned short addr, int len){
		unsigned char b;
		setBytePins(INPUT);
		digitalWrite(CE, LOW);
		digitalWrite(OE, LOW);
		for( int i=0 ; i < len ; i++){
			b = readByte((int)addr+i);
			buffer[i] = b;
		}
		digitalWrite(OE, HIGH);    // Add this
		digitalWrite(CE, LOW);     // Add this
	}
	
	
	void writeBytes(unsigned char * b, unsigned short addr, int len){
		setBytePins(OUTPUT);
		digitalWrite(CE, LOW);
		for( int i=0 ; i < len ; i++){
			writeByte(b[i],(int)addr+i);
		}
		digitalWrite(CE, HIGH);    /// Add this
	}
digitalWrite(bytePins[7-i], b && (1<<i) )

I think just one ampersand.

On the top '595, is RCK really connected to Gnd? That's not going to work too well.

Why is writePulse seperated from writeByte? Where else would you use it?

On the top '595, is RCK really connected to Gnd? That's not going to work too well.

I think it was connected through a cap before. I've removed the connection. Updated schematic is attached.

I've removed the extra ampersand.

I've added Johns suggestions to the code:

	//Pin connected to ST_CP of 74HC595
	int latchPin = 8;
	//Pin connected to SH_CP of 74HC595
	int clockPin = 12;
	////Pin connected to DS of 74HC595
	int dataPin = 11;
	int bytePins[8] = {2,3,4,5,6,7,9,10};
	int CE = A2;
	int OE = A1;
	int WE = A0;
	
	#define CHECK_BIT(var,pos) ((var) & (1<<(pos)))

	/*	mode: INPUT or OUTPUT
		OUTPUT: for writing out to memory
		INPUT: for reading from memory
	*/
	void setBytePins(int mode){
		for(int i=0 ; i < 8 ; i++){
			pinMode(bytePins[i], mode);
		}
	}
	
	void readBytes(unsigned char * buffer, unsigned short addr, int len){
		unsigned char b;
		setBytePins(INPUT);
		digitalWrite(CE, LOW);
		digitalWrite(OE, LOW);
		for( int i=0 ; i < len ; i++){
			b = readByte((int)addr+i);
			buffer[i] = b;
		}
		digitalWrite(OE, HIGH);    // Add this
		digitalWrite(CE, LOW);     // Add this
	}
	
	unsigned char readByte(unsigned short addr){
		setAddr(addr);
		return getByte();
	}
	
	unsigned char getByte(){
		unsigned char b=0;

		for( int i=0 ; i < 8 ; i++ ){
			b |= digitalRead(bytePins[i] << i);
		}

		return b;
	}
	
	void writeBytes(unsigned char * b, unsigned short addr, int len){
		setBytePins(OUTPUT);
		digitalWrite(CE, LOW);
		for( int i=0 ; i < len ; i++){
			writeByte(b[i],(int)addr+i);
		}
		digitalWrite(CE, HIGH);    /// Add this
	}
	
	void writeByte(unsigned char b, unsigned short addr){
		setAddr(addr);
		setByte(b);
		digitalWrite(WE, LOW);
		digitalWrite(WE, HIGH);
	}
	
	void setAddr(unsigned short addr){
		doubleShiftOut(addr);
	}
	
	void setByte(unsigned char b){
		for(int i=0 ; i < 8 ; i++)
			digitalWrite(bytePins[7-i], b & (1<<i) );
	}
	
	void setup() {
	  //set pins to output so you can control the shift register
	  Serial.begin(9600);
	  pinMode(latchPin, OUTPUT);
	  pinMode(clockPin, OUTPUT);
	  pinMode(dataPin, OUTPUT);
	  
	  pinMode(CE, OUTPUT);
	  digitalWrite(CE, HIGH);   /// Add this
	  
	  pinMode(OE, OUTPUT);
	  digitalWrite(OE, HIGH);  /// Add this
	  
	  pinMode(WE, OUTPUT);
	  digitalWrite(WE, HIGH);
	  
	  //setBytePins(OUTPUT);
	}

	void loop() {
	
		unsigned char in_byte[4] = { 0xf1, 0x40, 0x41, 0xf0 };
		unsigned char out_byte[4] = { 1,2,3,4 };
		
		writeBytes(in_byte, 0x0, 4);
		readBytes(out_byte, 0x0, 4);

		Serial.print(out_byte[0], BIN);
		Serial.print(" ");
		Serial.print(out_byte[1], BIN);
		Serial.print(" ");
		Serial.print(out_byte[2], BIN);
		Serial.print(" ");
		Serial.println(out_byte[3], BIN);
		
		delay(500);
	}

	void doubleShiftOut(unsigned short b){
		unsigned char b1 = (b & 0xff00) >> 8;
		unsigned char b2 = b & 0xff;
		digitalWrite(latchPin, LOW);
		shiftOut(dataPin, clockPin, MSBFIRST, b1);
		shiftOut(dataPin, clockPin, MSBFIRST, b2);
		digitalWrite(latchPin, HIGH);
	}

Still outputs 1 1 1 1 though :frowning:

I think this looks a bit sus

b |= digitalRead(bytePins[i] << i);

try

b |= digitalRead(bytePins[i]) << i;

Also in principle I don't think it's good to leave CE low while the address lines are changing,

void writeBytes(unsigned char * b, unsigned short addr, int len){
		setBytePins(OUTPUT);
		digitalWrite(CE, LOW);  <<<<<<<<<<<<<<<<<<<<<<<<<<<<
		for( int i=0 ; i < len ; i++){
			writeByte(b[i],(int)addr+i);
		}
		digitalWrite(CE, HIGH);     <<<<<<<<<<<<<<<<<<<<<<<<<<<<
	}
	
	void writeByte(unsigned char b, unsigned short addr){
		setAddr(addr);
		setByte(b);
		digitalWrite(WE, LOW);
		digitalWrite(WE, HIGH);
	}

I'd move the CE stuff to the writeByte() func

	void writeByte(unsigned char b, unsigned short addr){
		setAddr(addr);
		setByte(b);
                digitalWrite(CE, LOW); 
		digitalWrite(WE, LOW);
		digitalWrite(WE, HIGH);
                digitalWrite(CE, LOW); 
	}

Rob

Just for fun I looked at the chip specification:

It looks like you can leave CE set to LOW all the time without ill effects. Use OE LOW for reading and WE LOW for writing.

Yep I just looked at the data sheet, it seems that it's OK to even leave CE low permanently. I still prefer to keep such things together in one function, but that's as much a style issue as anything. Meaning that a function called writeByte() should do everything involved in writing a byte. On a larger program someone may call writeByte() directly one day and wonder why it didn't work.

Admittedly if speed is an issue the current code will be faster and this is a very small program, it's easy to see what's happening as the CE code is just a few lines above.


Rob

I think it's not functioning properly due to there being a drop in voltage on the bus though. Any ideas on what might be causing that?

The datasheet says it can only operate at 4.5v minimum and I'm reading 4.36v on the bus even though it's fed by a 5v source.

Did you look at the code I think is wrong?

You also need to find out where 5v turns into 4.36v. Measure at the source, if it's good there then work down the line until it goes bad.

If it's bad at the source unplug the ram and see if things change. Do you have 5v with no ram? If so then the ram chip may be at fault. If it doesn't change then the PSU is at fault.

Do you only get this voltage when running your sketch? If you run a blank sketch does it still show 4.36v?


Rob