Interfacing with parallel EEPROM

Status
Not open for further replies.
Hi! This is my first time posting. I have begun to learn C in hopes of using a Teensy 2.0++ to write to an EEPROM that I will use in my car's ECU. I have been trying to find other code that I can adapt to my scenario, and while I seem to be able to get the basics accomplished, I am stuck on how exactly to transmit the data in hex form over the serial port.
The EEPROM I am interfacing with is a 512kbit EEPROM, datasheet here: http://static.moates.net/zips/27SF512.pdf
Here is the code that I have so far.
Code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include "delay_x.h"
#include "usb_serial.h"

#define CPU_PRESCALE(n)	(CLKPR = 0x80, CLKPR = (n))

// Define data ports
#define DATA1_PORT	PORTD
#define DATA1_PIN	PIND
#define DATA1_DDR	DDRD

// Define address line ports
#define ADDR1_PORT	PORTF
#define ADDR1_PIN	PINF
#define ADDR1_DDR	DDRF

#define ADDR2_PORT	PORTA
#define ADDR2_PIN	PINA
#define ADDR2_DDR	DDRA

// Define control port and pins
#define CONT_PORT	PORTE
#define CONT_DDR	DDRE
#define CONT_PIN	PINE

#define CONT_CE		0		// Chip Enable

void put_address(uint8_t address2, uint8_t address1) {
	//CONT_PORT |= (1<<CONT_CE); //HIGH
	ADDR2_PORT = address2;
	ADDR1_PORT = address1;
	//CONT_PORT &= ~(1<<CONT_CE); //LOW
}

void put_data(uint8_t data1) {
	DATA1_PORT = data1;
	_delay_us(20);
	CONT_PORT &= ~(1<<CONT_WE); //LOW
	CONT_PORT |= (1<<CONT_WE); //HIGH
}

uint8_t ADDR1, ADDR2;
void addr_increment(uint8_t lock_address)
{
	if (ADDR1 < 0xFF)
		ADDR1++;
	else {
		ADDR1 = 0;
		if (ADDR2 < 0xFF)
			ADDR2++;
		else {
			ADDR2 = 0;
		}
	}

	if (lock_address == 0)
		return;
		
	//CONT_PORT |= (1<<CONT_CE); //HIGH

	ADDR2_PORT = ADDR2;
	ADDR1_PORT = ADDR1;

	//CONT_PORT &= ~(1<<CONT_CE); //LOW
}
void bootloader()
{
	cli();
	// disable watchdog, if enabled
	// disable all peripherals
	UDCON = 1;
	USBCON = (1<<FRZCLK);  // disable USB
	UCSR1B = 0;
	_delay_ms(50);

	EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; ADCSRA = 0;
	TIMSK0 = 0; TIMSK1 = 0; TIMSK2 = 0; TIMSK3 = 0; UCSR1B = 0; TWCR = 0;
	DDRA = 0; DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0;
	PORTA = 0; PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0;

	__asm volatile("jmp 0x1FC00");
}

void initports()
{
	DATA1_DDR = 0xFF;	// set for output
	ADDR1_DDR = ADDR2_DDR = 0xFF; //address ports are always output
	ADDR1_PORT = ADDR2_PORT = 0;
	
	CONT_DDR = 0xFF; //all control ports are always output

	CONT_DDR &= ~(1<<CONT_RYBY); //except RY/BY# (input)
	CONT_PORT |= (1<<CONT_RYBY); //enable pull up
	
	CONT_PORT &= ~((1<<CONT_TRI) | (1<<CONT_CE)); //LOW
	CONT_PORT |= ((1<<CONT_WE) | (1<<CONT_OE) | (1<<CONT_RESET)); //HIGH

//	ADDR1_DDR = ADDR2_DDR = ADDR3_DDR = DATA1_DDR = DATA2_DDR = CONT_DDR = 0; //all ports are always input
//	ADDR1_PORT = ADDR2_PORT = ADDR3_PORT = DATA1_PORT = DATA2_PORT = CONT_PORT = 0; //disable pull ups for all ports

//	CONT_DDR |= (1<<CONT_TRI);
//	CONT_PORT &= ~(1<<CONT_TRI); //LOW
//	CONT_PORT |= (1<<CONT_TRI); //HIGH
}

int main(void)
{
		int16_t in_data;
	uint16_t addr, i;
	uint32_t bss;
	uint8_t vaddr1, vaddr2;
	uint8_t state, cycle, tx_data, tx_wr, buf_ix, do_increment, do_verify, offset_2nddie;
	uint8_t buf_read[4096];
	
	// set for 8 MHz clock
	CPU_PRESCALE(1);
	
	releaseports();
	
	usb_init();
		while (!usb_configured());

	initports();
		
	_delay_ms(1000);
	
	ADDR1 = ADDR2 = 0
The biggest part is setting up addresses, inputting the data, pulsing the write enable pin, and then getting to the next set in the data, which may be able to be accomplished with a simple ++ that brings a value up until the max value of the array is reached.
Where I am stuck at the main program loop. There may be pieces of that code that can be removed, but, I believe it should work. My main issue is, how do I send the data over the serial port? My initial idea was to have it stored into an array byte by byte, but I'm not sure if the Teensy's ram could hold it all at once. Also, I am not sure how to get a hexadecimal value through with usb_serial_getchar. I hope I have given enough data, and any help would be appreciated. I am new to all of this, and just began learning C about a month ago. Thanks!
 
Normally the simple hammer for something like this is Processing

https://www.processing.org/reference/libraries/serial/

Though as you note you will have to deal with the fact that your hex will have non printing chars in it and you'd need to work out flow control etc. Ugly approch is to just stream characters one at a time with a delay longer than the EEPROM write takes, and just feed them out. Things should be able just pass them as is as long as you avoid them being turned into chars anywhere along the way (write not print).

Possible problem in this is lack of error correction. If you are prepared to just write the EEPROM and give it a go that's fine but the more robust choice would be to also close the loop and write all the bites back to the PC and compare or possible break them into frames of 16 or so wrapped in a packet number and checksum info.

Another option is to put the hex file on and SD card and use and SD card breakout (needs 3.3 to 5V level conversion) to talk to your T2.
 
I found a way to read data off of the EEPROM through serial, which could be used for verification with a program called HxD. What I am unsure of, is if I copy and paste the contents of a hex file into Tera Term's serial terminal, would it be sent through character by character, or would it be one large chunk of data that would overwhelm the teensy? Flow control would be easy if I could copy and paste the file into the terminal, and then have the Teensy move up a byte on the address ports for each space, of course I'd also have to account for the end of line character, but I think that could be done as an exception similar to space. My main question is, how do I get the data that is being inputted through the serial getchar command to be converted over to hex? Is there a simple conversion I am missing? And if copy pasting a file over the serial monitor, will all of that be potentially written to a buffer somewhere? It's a little over my head, being new to this. I was hoping someone maybe give me an outline as to how to solve this problem.
 
Sorry for the double post, but, I have also found an arduino code called MEEPromer, that uses an arduino nano with a shift register to write the address lines. Is there a simplistic way to convert the code for the shift registers into real pins? I am just throwing stuff out there. Maybe I need to learn more about programming and come back to this project later. ROM burners aren't cheap, and I imagine if I was able to accomplish this, it would be a very useful program for anyone who owns a Teensy.
 
Just pasting into a serial terminal will almost certainly not work. Doing a serial transfer of the file may work if you can convince it to not add header information, would suggest making a very small hex file (say 10 bytes) to test with and go from there. Ditto you will need flow control unless you really can fit all of your EEPROM into RAM. Flow control is probably possible on the PC/Arduino USB link but disabled by default. Or if you know how long an EEPROM write takes you could pick a really slow baud rate and hope nothing glitches. Come to that, do you have enough space on the Teensy internal EEPROM to hold the data you need? That would turn this project into two simpler programs, one to stream the data into the internal memory (and most likely read it back to prove it worked) and a second one to push bytes at a time from the Teensy EEPROM to you subject EEPROM.

Lots of ways to attack the problem but none of them will be a plug and go (see note below). Do be wary of grabing random PC side host programs to try since you may find it's got trojans on unless you have a think about how it got to where you find it (someones github with source and examples is probably safe, random hosting site.... less so)

Would suggest you need to draw this out for each of the options for:
PC host software (does it read your file in the first place?)
Interface to Teensy (formating, with/without flow control, any special packaging)
Teensy/Arduino Code (roll your own, modded existing or off the shelf)
Any additional hardware to toggle all the pins

Then work out what you need to do getting from A through to Z and what holes in each case you need to fill. Some of those holes can be fixed by finding online sources that have done this sort of thing already, but as you found with the MEEPromer software, reading further finds complications so you are trading off reading instructions to use existing work with just going off and writing your own. Almost certainly somewhere online is Arduino code and PC side software that does what you want, but even if you find it there is going to be a bunch of work to confirm it's for your flavor of EEPROM etc.

One way or another this project will need a bit of time put into it, hence suggestion to plot out the options.
 
Okay, I managed to modify the MEEPrommer code in order to make it work with the Teensy 2.0++. Here is what I have done so far:
Code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include "delay_x.h"
#include "usb_serial.h"

#define VERSIONSTRING "MEEPROMMER $Revision: 1.2 $ $Date: 2013/05/05 11:01:54 $, CMD:R,r,w,W,V"
#define DATA1_PORT  PORTD
#define DATA1_PIN PIND
#define DATA1_DDR DDRD


#define ADDR1_PORT  PORTF
#define ADDR1_PIN PINF
#define ADDR1_DDR DDRF

#define ADDR2_PORT  PORTC
#define ADDR2_PIN PINC
#define ADDR2_DDR DDRC


#define CONT_PORT PORTE
#define CONT_DDR  DDRE
#define CONT_PIN  PINE

#define CONT_CE 8
#define CONT_OE 9   // Output Enable


//a buffer for bytes to burn
#define BUFFERSIZE 1024 
byte buffer[BUFFERSIZE];
//command buffer for parsing commands
#define COMMANDSIZE 32
char cmdbuf[COMMANDSIZE];

uint32_t startAddress,endAddress;
uint32_t lineLength,dataLength;

//define COMMANDS
#define NOCOMMAND    0
#define VERSION      1
#define SET_ADDRESS  2

#define READ_HEX    10
#define READ_BIN    11
#define READ_ITL    12

#define WRITE_HEX   20
#define WRITE_BIN   21
#define WRITE_ITL   22


/*****************************************************************
 *
 *  CONTROL and DATA functions
 *
 ****************************************************************/

// switch IO lines of databus to INPUT state
void      data_bus_input() {
pinMode(DATA1_DDR, INPUT);
}

//switch IO lines of databus to OUTPUT state
void data_bus_output() {
pinMode(DATA1_DDR, OUTPUT);
}

//set databus to input and read a complete byte from the bus
//be sure to set data_bus to input before
byte read_data_bus()
{
  return(DATA1_PIN);
  
}


//write a byte to the data bus
//be sure to set data_bus to output before
void write_data_bus(byte data)
{

  DATA1_PORT = data;
}

//shift out the given address to the 74hc595 registers
void set_address_bus(uint16_t address)
{

  //get high - byte of 16 bit address
  byte hi = address >> 8;
  //get low - byte of 16 bit address
  byte low = address & 0xff;
  
  ADDR2_PORT = hi;
  //shift out lowbyte
  ADDR1_PORT = low;
}


//short function to set the OE(output enable line of the eeprom)
// attention, this line is LOW - active
void set_oe (byte state)
{
  digitalWrite(CONT_OE, state);
}

//short function to set the CE(chip enable line of the eeprom)
// attention, this line is LOW - active
void set_ce (byte state)
{
  digitalWrite(CONT_CE, state);
}

//short function to set the WE(write enable line of the eeprom)
// attention, this line is LOW - active

//highlevel function to read a byte from a given address
byte read_byte(unsigned int address)
{
  byte      data = 0;
  //set databus for reading
  data_bus_input();
  //first disbale output
  set_oe(HIGH);
  //enable chip select
  set_ce(LOW);
  //disable write
  //set address bus
  set_address_bus(address);
  //enable output
  set_oe(LOW);
  data = read_data_bus();

  //disable output
  set_oe(HIGH);

  return data;

}


//highlevel function to write a byte to a given address
//this function uses /DATA polling to get the end of the
//write cycle. This is much faster then waiting 10ms
void fast_write(uint32_t address, byte data)
{
  byte cyclecount=0;

  //set address bus
  set_address_bus(address);

  //set databus to output
  data_bus_output();

  set_ce(HIGH);

  //set data bus
  write_data_bus(data);

  //enable chip select
  set_ce(LOW);

  _delay_us(20);

  set_ce(HIGH);
 
  data_bus_input();

  
  while(data != read_data_bus()) {
    cyclecount++;
  };



}



/************************************************
 *
 * COMMAND and PARSING functions
 *
 *************************************************/

//waits for a string submitted via serial connection
//returns only if linebreak is sent or the buffer is filled
void readCommand() {
  //first clear command buffer
  for(int i=0; i< COMMANDSIZE;i++) cmdbuf[i] = 0;
  //initialize variables
  char c = ' ';
  int idx = 0;
  //now read serial data until linebreak or buffer is full
  do {
    if(Serial.available()) {
      c = Serial.read();
      cmdbuf[idx++] = c;
    }
  } 
  while (c != '\n' && idx < (COMMANDSIZE)); //save the last '\0' for string end
  //change last newline to '\0' termination
  cmdbuf[idx - 1] = 0;
}

//parse the given command by separating command character and parameters
//at the moment only 5 commands are supported
byte parseCommand() {
  //set ',' to '\0' terminator (command string has a fixed strucure)
  //first string is the command character
  cmdbuf[1]  = 0;
  //second string is startaddress (4 bytes)
  cmdbuf[6]  = 0;
  //third string is endaddress (4 bytes)
  cmdbuf[11] = 0;
  //fourth string is length (2 bytes)
  cmdbuf[14] = 0;
  startAddress=hexWord((cmdbuf+2));
  dataLength=hexWord((cmdbuf+7));
  lineLength=hexByte(cmdbuf+12);
  byte retval = 0;
  switch(cmdbuf[0]) {
  case 'A':
    retval = SET_ADDRESS;
    break;
  case 'R':
    retval = READ_HEX;
    break;
  case 'r':
    retval = READ_BIN;
    break;
  case 'W':
    retval = WRITE_HEX;
    break;
  case 'w': 
    retval = WRITE_BIN;
    break;
  case 'V':
    retval = VERSION; 
    break;
  default:
    retval = NOCOMMAND;
    break;
  }

  return retval;
}

/************************************************************
 * convert a single hex digit (0-9,a-f) to byte
 * @param char c single character (digit)
 * @return byte represented by the digit 
 ************************************************************/
byte hexDigit(char c)
{
  if (c >= '0' && c <= '9') {
    return c - '0';
  } 
  else if (c >= 'a' && c <= 'f') {
    return c - 'a' + 10;
  } 
  else if (c >= 'A' && c <= 'F') {
    return c - 'A' + 10;
  } 
  else {
    return 0;   // getting here is bad: it means the character was invalid
  }
}

/************************************************************
 * convert a hex byte (00 - ff) to byte
 * @param c-string with the hex value of the byte
 * @return byte represented by the digits 
 ************************************************************/
byte hexByte(char* a)
{
  return ((hexDigit(a[0])*16) + hexDigit(a[1]));
}

/************************************************************
 * convert a hex word (0000 - ffff) to unsigned int
 * @param c-string with the hex value of the word
 * @return unsigned int represented by the digits 
 ************************************************************/
unsigned int hexWord(char* data) {
  return ((hexDigit(data[0])*4096)+
    (hexDigit(data[1])*256)+
    (hexDigit(data[2])*16)+
    (hexDigit(data[3]))); 
}


/************************************************
 *
 * INPUT / OUTPUT Functions
 *
 *************************************************/


/**
 * read a data block from eeprom and write out a hex dump 
 * of the data to serial connection
 * @param from       start address to read fromm
 * @param to         last address to read from
 * @param linelength how many hex values are written in one line
 **/
void read_block(uint32_t from, uint32_t to, int linelength)
{
  //count the number fo values that are already printed out on the
  //current line
  uint32_t       outcount = 0;
  //loop from "from address" to "to address" (included)
  for (uint32_t address = from; address <= to; address++) {
    if (outcount == 0) {
      //print out the address at the beginning of the line
      Serial.println();
      Serial.print("0x");
      printAddress(address);
      Serial.print(" : ");
    }
    //print data, separated by a space
    byte data = read_byte(address);
    printByte(data);
    Serial.print(" ");
    outcount = (++outcount % linelength);

  }
  //print a newline after the last data line
  Serial.println();

}

/**
 * read a data block from eeprom and write out the binary data 
 * to the serial connection
 * @param from       start address to read fromm
 * @param to         last address to read from
 **/
void read_binblock(unsigned int from, unsigned int to) {
  for (unsigned int address = from; address <= to; address++) {
    Serial.write(read_byte(address));
  }
  //print out an additional 0-byte as success return code
  Serial.print('\0');
  
}  
  
/**
 * write a data block to the eeprom
 * @param address  startaddress to write on eeprom
 * @param buffer   data buffer to get the data from
 * @param len      number of bytes to be written
 **/
void write_block(unsigned int address, byte* buffer, int len) {
  for (uint8_t i = 0; i < len; i++) {
    fast_write(address+i,buffer[i]);
  }   
}


/**
 * print out a 16 bit word as 4 character hex value
 **/
void printAddress(unsigned int address) {
  if(address < 0x0010) Serial.print("0");
  if(address < 0x0100) Serial.print("0");
  if(address < 0x1000) Serial.print("0");
  Serial.print(address, HEX);

}

/**
 * print out a byte as 2 character hex value
 **/
void printByte(byte data) {
  if(data < 0x10) Serial.print("0");
  Serial.print(data, HEX);  
}





/************************************************
 *
 * MAIN
 *
 *************************************************/
void setup() {

  //define the EEPROM Pins as output
  // take care that they are HIGH
  digitalWrite(CONT_OE, HIGH);
  pinMode(CONT_OE, OUTPUT);
  digitalWrite(CONT_CE, HIGH);
  pinMode(CONT_CE, OUTPUT);
 

  //set speed of serial connection
  //Serial.begin(57600);
  Serial.begin(115200);
}

/**
 * main loop, that runs invinite times, parsing a given command and 
 * executing the given read or write requestes.
 **/
void loop() {
  readCommand();
  byte cmd = parseCommand();
 uint32_t bytes = 0;
  switch(cmd) {
  case SET_ADDRESS:
    // Set the address bus to an arbitrary value.
    // Useful for debugging shift-register wiring, byte-order.
    // e.g. A,00FF
    Serial.print("Setting address bus to 0x");
    Serial.println(cmdbuf + 2);
    set_address_bus(startAddress);
    break;
  case READ_HEX:
    //set a default if needed to prevent infinite loop
    if(lineLength==0) lineLength=16;
    endAddress = startAddress + dataLength -1;
    read_block(startAddress,endAddress,lineLength);
    Serial.println('%');
    break;
  case READ_BIN:
    endAddress = startAddress + dataLength -1;
    read_binblock(startAddress,endAddress);
    break;
  case READ_ITL:
    break;
  case WRITE_BIN:
    //take care for max buffer size
    if(dataLength > 1024) dataLength = 1024;
    endAddress = 32768 + dataLength -1;
    while(bytes < dataLength) {
      if(Serial.available()) buffer[bytes++] = Serial.read();
    }    
    write_block(startAddress,buffer,dataLength);
    Serial.println('%');
    
    break;
  case WRITE_HEX:
    break;
  case WRITE_ITL:
    break;
  case VERSION:
    Serial.println(VERSIONSTRING);
    //set a default if needed to prevent infinite loop
    if(lineLength==0) lineLength=16;
    
    read_block(32768,65535,lineLength);
    Serial.println('%');
    break;
  default:
    break;    
  }


}

Now, I am back at the point of how I will write data. I attempted to edit the python script included in the MEEPrommer-master zip, but it doesn't seem to work, and I can't figure out why. I'm in the process of editing the Java program included with it, and hopefully that will produce results. I'm not sure if it will be capable of proper timing. However, I had an idea. The Teensy's internal EEPROM memory is not enough to store the file I want to write, and neither is it's SRAM. However, the internal flash memory that stores the program is. Could I possibly paste the contents of the file I want to write into the code, and have the Teensy write it out from there? If so, how would I go about doing that? Thanks.
 
The easiest way is to convert the to an array in C and include it in your sketch. There are tools out there.. sorry, have no link at the moment, but Google should find it. Try searching for "bin2h"
 
Okay, I managed to get the hex values into the code, and used the prefix const to keep it from overfilling the 'dynamic memory' which I assume is the onboard ram. However, now, when I attempt to write the values to the pins, it seems to be storing the values into the ram. Here's what I've got:
Code:
while(b != 65537)
  {
    
    fast_write(b, rawData[r]);
    b++;
    r++;
    
  }
the value b is the address, and it starts at 32769 because the offset necessary for my ECU to read EEProm needs it to be started in the second half of the memory. The array rawData has the hex file that I need to write stored in it, and r begins at zero and should finish itself out at 32767 by the time the address, b, is equal to 65537. For some reason, it seems as if the values being read from rawData and printed to fast_write are being stored inside of the ram. Is there a way to clear it everytime the command is called? Thanks for any help.
 
I managed to get it to work by using the PROGMEM library. Here's the code I used:

Code:
case WRITE_HEX;
set_ce(HIGH);
  while(b != 65536)
  {
  byte data;
  data = pgm_read_word_near(rawData + r);
    fast_write(b, data);
    b++;
    r++;
  }
  fast_write(65536, final);
  Serial.println('\n');
  Serial.println("Finished!");
    break;

I'm not sure if there's a way of knowing how long this is going to take, but judging by the flickering of the onboard LED on the teensy, it seems to do about 40 bytes per minute. Is reading from the internal memory that slow, or have I done something within my code that is causing a hang? Either way, I will report back if it works. Sorry if double posts are prohibited, I will go look at the rules and I won't do it again. Hopefully this works.
 
Double posts in your own thread are generally OK. Normal thing is to edit the old one, but you actually have something new there and a table to add, and critically changing from 'it doesn't work' to 'I fixed it'. And yes it should be doing better than 40 bytes per minute! Progmem can certainly be fast, since it's just pulling data from program space flash and is normally used for things like lookup tables, since it's faster than doing actual math. Would suggest checking that you aren't looping in the actual write (doing multiples of the same byte) or having any big delays. EEPROM writes may require a non zero amount of time to achieve but normally in mseconds not more than a second.
 
Something isn't right. It shouldn't be *that* slow!

I'm looking at the datasheet linked from message #1 and the code message #6...

The first issue I see is this:

Code:
// switch IO lines of databus to INPUT state
void      data_bus_input() {
pinMode(DATA1_DDR, INPUT);
}

//switch IO lines of databus to OUTPUT state
void data_bus_output() {
pinMode(DATA1_DDR, OUTPUT);
}

You almost certainly meant to use 8 pinMode calls, one for each pin. The first input to pinMode must be the Arduino pin number, not the AVR register.

Or using the register, you could do this:

Code:
// switch IO lines of databus to INPUT state
void      data_bus_input() {
  DATA1_DDR = 0;   // all 8 pins to input mode
}

//switch IO lines of databus to OUTPUT state
void data_bus_output() {
  DATA1_DDR = 255;   // all 8 pins to output mode
}

Beyond this issue, it seems this code was designed for a EEPROM chip, where individual bytes are reprogrammable without requiring erase.

But the SST27S512 chip is a Flash memory, which requires first erasing the entire chip. It also has some special requirements mentioned in the datasheet, in particular the need to apply 12V to the Vpp pin.
 
I have been letting it write all night, and it continues to flash, so I think it is at least doing something. I am going to let it go for a day more, and if it did not work, I will try some different code. The first post was some code I tried to modify from something else, my current code is this:
Code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include "delay_x.h"
#include "usb_serial.h"

#define VERSIONSTRING "MEEPROMMER $Revision: 1.2 $ $Date: 2013/05/05 11:01:54 $, CMD:R,r,w,W,V"
#define DATA1_PORT  PORTD
#define DATA1_PIN PIND
#define DATA1_DDR DDRD


#define ADDR1_PORT  PORTF
#define ADDR1_PIN PINF
#define ADDR1_DDR DDRF

#define ADDR2_PORT  PORTC
#define ADDR2_PIN PINC
#define ADDR2_DDR DDRC


#define CONT_PORT PORTE
#define CONT_DDR  DDRE
#define CONT_PIN  PINE

#define CONT_CE 8
#define CONT_OE 9   // Output Enable


//a buffer for bytes to burn
#define BUFFERSIZE 1024 
byte buffer[BUFFERSIZE];
//command buffer for parsing commands
#define COMMANDSIZE 32
char cmdbuf[COMMANDSIZE];

uint32_t startAddress,endAddress;
uint32_t lineLength,dataLength;

//define COMMANDS
#define NOCOMMAND    0
#define VERSION      1
#define SET_ADDRESS  2

#define READ_HEX    100
#define READ_BIN    110
#define READ_ITL    120

#define WRITE_HEX   200
#define WRITE_BIN   210
#define WRITE_ITL   220

const PROGMEM unsigned char rawData[32767] = {
*insert large amounts of hex code here*
};

/*****************************************************************
 *
 *  CONTROL and DATA functions
 *
 ****************************************************************/

// switch IO lines of databus to INPUT state
void      data_bus_input() {
pinMode(DATA1_DDR, INPUT);
}

//switch IO lines of databus to OUTPUT state
void data_bus_output() {
pinMode(DATA1_DDR, OUTPUT);
}

//set databus to input and read a complete byte from the bus
//be sure to set data_bus to input before
byte read_data_bus()
{
  return(DATA1_PIN);
  
}


//write a byte to the data bus
//be sure to set data_bus to output before
void write_data_bus(byte data)
{

  DATA1_PORT = data;
}

//shift out the given address to the 74hc595 registers
void set_address_bus(uint16_t address)
{

  //get high - byte of 16 bit address
  byte hi = address >> 8;
  //get low - byte of 16 bit address
  byte low = address & 0xff;
  
  ADDR2_PORT = hi;
  //shift out lowbyte
  ADDR1_PORT = low;
}


//short function to set the OE(output enable line of the eeprom)
// attention, this line is LOW - active
void set_oe (byte state)
{
  digitalWrite(CONT_OE, state);
}

//short function to set the CE(chip enable line of the eeprom)
// attention, this line is LOW - active
void set_ce (byte state)
{
  digitalWrite(CONT_CE, state);
}

//short function to set the WE(write enable line of the eeprom)
// attention, this line is LOW - active

//highlevel function to read a byte from a given address
byte read_byte(unsigned int address)
{
  byte      data = 0;
  //set databus for reading
  data_bus_input();
  //first disbale output
  set_oe(HIGH);
  //enable chip select
  set_ce(LOW);
  //disable write
  //set address bus
  set_address_bus(address);
  //enable output
  set_oe(LOW);
  data = read_data_bus();

  //disable output
  set_oe(HIGH);

  return data;

}


//highlevel function to write a byte to a given address
//this function uses /DATA polling to get the end of the
//write cycle. This is much faster then waiting 10ms
void fast_write(uint32_t address, byte data)
{
  
   //set address bus
  set_address_bus(address);

  //set databus to output
  data_bus_output();

  //set data bus
  write_data_bus(data);

  //enable chip select
  set_ce(LOW);

  _delay_us(20);

  set_ce(HIGH);

}



/************************************************
 *
 * COMMAND and PARSING functions
 *
 *************************************************/

//waits for a string submitted via serial connection
//returns only if linebreak is sent or the buffer is filled
void readCommand() {
  //first clear command buffer
  for(int i=0; i< COMMANDSIZE;i++) cmdbuf[i] = 0;
  //initialize variables
  char c = ' ';
  int idx = 0;
  //now read serial data until linebreak or buffer is full
  do {
    if(Serial.available()) {
      c = Serial.read();
      cmdbuf[idx++] = c;
    }
  } 
  while (c != '\n' && idx < (COMMANDSIZE)); //save the last '\0' for string end
  //change last newline to '\0' termination
  cmdbuf[idx - 1] = 0;
}

//parse the given command by separating command character and parameters
//at the moment only 5 commands are supported
byte parseCommand() {
  //set ',' to '\0' terminator (command string has a fixed strucure)
  //first string is the command character
  cmdbuf[1]  = 0;
  //second string is startaddress (4 bytes)
  cmdbuf[6]  = 0;
  //third string is endaddress (4 bytes)
  cmdbuf[11] = 0;
  //fourth string is length (2 bytes)
  cmdbuf[14] = 0;
  startAddress=hexWord((cmdbuf+2));
  dataLength=hexWord((cmdbuf+7));
  lineLength=hexByte(cmdbuf+12);
  byte retval = 0;
  switch(cmdbuf[0]) {
  case 'A':
    retval = SET_ADDRESS;
    break;
  case 'R':
    retval = READ_HEX;
    break;
  case 'r':
    retval = READ_BIN;
    break;
  case 'W':
    retval = WRITE_HEX;
    break;
  case 'w': 
    retval = WRITE_BIN;
    break;
  case 'V':
    retval = VERSION; 
    break;
  default:
    retval = NOCOMMAND;
    break;
  }

  return retval;
}

/************************************************************
 * convert a single hex digit (0-9,a-f) to byte
 * @param char c single character (digit)
 * @return byte represented by the digit 
 ************************************************************/
byte hexDigit(char c)
{
  if (c >= '0' && c <= '9') {
    return c - '0';
  } 
  else if (c >= 'a' && c <= 'f') {
    return c - 'a' + 10;
  } 
  else if (c >= 'A' && c <= 'F') {
    return c - 'A' + 10;
  } 
  else {
    return 0;   // getting here is bad: it means the character was invalid
  }
}

/************************************************************
 * convert a hex byte (00 - ff) to byte
 * @param c-string with the hex value of the byte
 * @return byte represented by the digits 
 ************************************************************/
byte hexByte(char* a)
{
  return ((hexDigit(a[0])*16) + hexDigit(a[1]));
}

/************************************************************
 * convert a hex word (0000 - ffff) to unsigned int
 * @param c-string with the hex value of the word
 * @return unsigned int represented by the digits 
 ************************************************************/
unsigned int hexWord(char* data) {
  return ((hexDigit(data[0])*4096)+
    (hexDigit(data[1])*256)+
    (hexDigit(data[2])*16)+
    (hexDigit(data[3]))); 
}


/************************************************
 *
 * INPUT / OUTPUT Functions
 *
 *************************************************/


/**
 * read a data block from eeprom and write out a hex dump 
 * of the data to serial connection
 * @param from       start address to read fromm
 * @param to         last address to read from
 * @param linelength how many hex values are written in one line
 **/
void read_block(uint32_t from, uint32_t to, int linelength)
{
  //count the number fo values that are already printed out on the
  //current line
  uint32_t       outcount = 0;
  //loop from "from address" to "to address" (included)
  for (uint32_t address = from; address <= to; address++) {
    if (outcount == 0) {
      //print out the address at the beginning of the line
      Serial.println();
      Serial.print("0x");
      printAddress(address);
      Serial.print(" : ");
    }
    //print data, separated by a space
    byte data = read_byte(address);
    printByte(data);
    Serial.print(" ");
    outcount = (++outcount % linelength);

  }
  //print a newline after the last data line
  Serial.println();

}

/**
 * read a data block from eeprom and write out the binary data 
 * to the serial connection
 * @param from       start address to read fromm
 * @param to         last address to read from
 **/
void read_binblock(unsigned int from, unsigned int to) {
  for (unsigned int address = from; address <= to; address++) {
    Serial.write(read_byte(address));
  }
  //print out an additional 0-byte as success return code
  Serial.print('\0');
  
}  
  
/**
 * write a data block to the eeprom
 * @param address  startaddress to write on eeprom
 * @param buffer   data buffer to get the data from
 * @param len      number of bytes to be written
 **/
void write_block(unsigned int address, byte* buffer, int len) {
  for (uint8_t i = 0; i < len; i++) {
    fast_write(address+i,buffer[i]);
  }   
}


/**
 * print out a 16 bit word as 4 character hex value
 **/
void printAddress(unsigned int address) {
  if(address < 0x0010) Serial.print("0");
  if(address < 0x0100) Serial.print("0");
  if(address < 0x1000) Serial.print("0");
  Serial.print(address, HEX);

}

/**
 * print out a byte as 2 character hex value
 **/
void printByte(byte data) {
  if(data < 0x10) Serial.print("0");
  Serial.print(data, HEX);  
}





/************************************************
 *
 * MAIN
 *
 *************************************************/
void setup() {

  //define the EEPROM Pins as output
  // take care that they are HIGH
  digitalWrite(CONT_OE, HIGH);
  pinMode(CONT_OE, OUTPUT);
  digitalWrite(CONT_CE, HIGH);
  pinMode(CONT_CE, OUTPUT);
 

  //set speed of serial connection
  //Serial.begin(57600);
  Serial.begin(115200);
}

/**
 * main loop, that runs invinite times, parsing a given command and 
 * executing the given read or write requestes.
 **/
void loop() {
  int b = 32769;
  int r = 0;
  byte final =  0xE1;
  readCommand();
  byte cmd = parseCommand();
 uint32_t bytes = 0;
  switch(cmd) {
  case SET_ADDRESS:
    // Set the address bus to an arbitrary value.
    // Useful for debugging shift-register wiring, byte-order.
    // e.g. A,00FF
    Serial.print("Setting address bus to 0x");
    Serial.println(cmdbuf + 2);
    set_address_bus(startAddress);
    break;
  case READ_HEX:
    //set a default if needed to prevent infinite loop
    if(lineLength==0) lineLength=16;
    endAddress = startAddress + dataLength -1;
    read_block(startAddress,endAddress,lineLength);
    Serial.println('%');
    break;
  case READ_BIN:
    endAddress = startAddress + dataLength -1;
    read_binblock(startAddress,endAddress);
    break;
  case READ_ITL:
    break;
  case WRITE_BIN:
    //take care for max buffer size
    if(dataLength > 1024) dataLength = 1024;
    endAddress = 32768 + dataLength -1;
    while(bytes < dataLength) {
      if(Serial.available()) buffer[bytes++] = Serial.read();
    }    
    write_block(startAddress,buffer,dataLength);
    Serial.println('%');
    
    break;
  case WRITE_HEX:
  data_bus_output();
  set_ce(HIGH);
  while(b != 65536)
  {
  byte data;
  data = pgm_read_word_near(rawData + r);
    fast_write(b, data);
    b++;
    r++;
  }
  fast_write(65536, final);
  Serial.println('\n');
  Serial.println("Finished!");
    break;
  case WRITE_ITL:
    break;
  case VERSION:
  
  while(r != 1)
  { 
    byte data1;
    data1 =  0xE1;
    Serial.println(final, HEX);
    ++r;
  }
    break;
  default:
    break;    
  }


}

I did wipe the chip before the write, and I am using a 12v power supply on the Output enable chip, which says that I need to set it to 12v for programming. The code is kind of a mess, as I have been piecing it together from multiple different projects. Perhaps I should try condensing it down to just the write command? Would the placement of the data array make any difference? If my array was one value larger, it would be too big, so I made the final value write separately once the while loop was finished. Another thing I have considered, other examples of code I have looked at do not use the digitalwrite or pinmode commands, instead they use values such as 0xFF. Perhaps they are slowing it down. I am using a delay library from one of the sets I looked at, and it included the 50us delay, which is what the datasheet says is necessary for write. Maybe it is delaying too long? I will do some more tinkering, and try it all out if the current iteration ends up not working. Thank you all for responding. I am new to this, and I appreciate all the help and opinions I can get.

EDIT: Reading over your post some more, I see what you mean on setting the data bus to input or output. The way it was, would it not work? Maybe it isn't working... It did seem to be reading data properly though. That is similar to what I saw in the other code examples, with people setting the busses to hexadecimal values. I will fix those.

EDIT #2: Turns out that because my step up values were not unsigned integers, I was getting negative values for some reason. Made them unsigned, fixed the step up. However, it is still not writing properly. I am going to try setting the delay up a little bit, and setting the data bus to certain values and testing if they're accurate with my multi-meter.
 
Last edited:
I'm not sure if it's appropriate to revive this thread, but, I just finished this project. The code can be found here: https://github.com/Conmanx360/Teeprom . I was in way over my head when I first started this, but after reading a few books it all makes a lot more sense now. Thank you guys for all the help, hopefully someone else can find this project useful.
 
Status
Not open for further replies.
Back
Top