For my Apple ][ projects, I needed an EEPROM programmer. After Googling a while, I found one DIY project on 6502.org. It is the MEEPROMMER, by Mario Keller.
A few nights searching for parts and soldering, I had one built and I added my Arduino Pro micro (5V/16MHz). It all seemed working fine until I tried to write my first bytes. The commandline tool I found (by Zack Nelson, with some tweaks by Paul Annesley), written in Python didn’t receive the ‘%’ prompt when writing more than $180 (384) bytes. After searching the internet (and various forums), I got some reply from someone, who said that maybe the Pro micro was/is the culprit. He tested a test sketch, which failed with the Pro micro, and ran it on his Arduino UNO. This worked for him.
I asked my uncle if he could lend me one of his UNO boards. I uploaded an unmodified sketch and ran the python script by Paul. With success I can now write an EEPROM (I use the AT28C64B for my project).
As a reference I will mention the source(s) of the Arduino sketch and python script below, with which I had success:
Arduino Sketch (original here)
/** * ** project Arduino EEPROM programmer ** * * This sketch can be used to read and write data to a * AT28C64 or AT28C256 parallel EEPROM * * $Author: mario, edited by Zack $ * $Date: 2013/05/05 11:01:54 $ * $Revision: 1.3 $ * * This software is freeware and can be modified, reused or thrown away without any restrictions. * * Use this code at your own risk. I'm not responsible for any bad effect or damages caused by this software!!! * **/ #define VERSIONSTRING "MEEPROMMER $Revision: 1.3 $ $Date: 12/18/2013 15:56:00 $, CMD:R,r,w,W,V, u" // shiftOut part #define PORTC_DS 0 #define PORTC_LATCH 1 #define PORTC_CLOCK 2 // eeprom IO lines:D0-D7 = pins2-9 // IO lines for the eeprom control #define PORTC_CE 3 #define PORTC_OE 4 #define PORTC_WE 5 //a buffer for bytes to burn #define BUFFERSIZE 1024 byte buffer[BUFFERSIZE]; //command buffer for parsing commands #define COMMANDSIZE 16 char cmdbuf[COMMANDSIZE]; unsigned int startAddress,endAddress; unsigned int lineLength,dataLength; //define COMMANDS #define NOCOMMAND 0 #define VERSION 1 #define READ_HEX 10 #define READ_BIN 11 #define WRITE_PAGE 20 #define WRITE_BIN 21 #define UNLOCK 30 /***************************************************************** * * CONTROL and DATA functions * ****************************************************************/ void data_bus_input(){ DDRD &= 0b00000011; DDRB &= 0b11111100; } void data_bus_output(){ DDRD |= 0b11111100; DDRB |= 0b00000011; } byte read_data_bus(){ return ((PINB<<6)|(PIND>>2)); } void write_data_bus(byte data){ PORTB &= 0b11111100; PORTB |= (data>>6); PORTD &= 0b00000011; PORTD |= (data<<2); } //shift out the given address to the 74hc595 registers void set_address_bus(unsigned int address){ bitClear(PORTC,PORTC_LATCH); //disable latch line fastShiftOut(highByte(address)); //shift out highbyte fastShiftOut(lowByte(address)); //shift out lowbyte bitSet(PORTC,PORTC_LATCH); //enable latch and set address } void fastShiftOut(byte data){ for(int i=7; i>=0; i--){//shift MSB first //clear data pin after shift to prevent bleed through bitClear(PORTC,PORTC_DS); bitClear(PORTC,PORTC_CLOCK); if(bitRead(data,i))bitSet(PORTC,PORTC_DS); //register shifts bits on rising clock bitSet(PORTC,PORTC_CLOCK); } bitClear(PORTC,PORTC_CLOCK); } byte read_byte(unsigned int address){ data_bus_input(); //set databus for reading bitClear(PORTC,PORTC_CE); //enable chip select bitSet(PORTC,PORTC_WE); //disable write set_address_bus(address); //set address bus bitClear(PORTC,PORTC_OE); //enable output //delay 312.5ns @ 16MHz __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); byte data = read_data_bus(); bitSet(PORTC,PORTC_OE); //disable output bitSet(PORTC,PORTC_CE); //enable chip select return data; } void fast_write(unsigned int address, byte data){ bitSet(PORTC,PORTC_OE); //first disable output bitSet(PORTC,PORTC_WE); //disable write set_address_bus(address); //set address bus data_bus_output(); //set databus to output write_data_bus(data); //set data bus bitClear(PORTC,PORTC_CE); //enable chip select bitClear(PORTC,PORTC_WE); //enable write __asm__("nop\n\t""nop\n\t"); //delay 125ns @ 16MHz bitSet(PORTC,PORTC_WE); //disable write data_bus_input(); bitClear(PORTC,PORTC_OE); //delay 312.5ns @ 16MHz __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); while(data != read_data_bus()); //poll data bitSet(PORTC,PORTC_OE); bitSet(PORTC,PORTC_CE); } void write_block_page(unsigned int address, byte* buffer, int len, int page){ address &= ~(page-1); //address must not break page boundaries bitSet(PORTC,PORTC_WE); //disable write bitClear(PORTC,PORTC_CE); //enable chip for(int n=0; n<len/page; n++){ bitSet(PORTC,PORTC_OE); data_bus_output(); //set databus to output for(unsigned int i=n*page; i<(n+1)*page; i++){ bitSet(PORTC,PORTC_WE); //disable write set_address_bus(address+i); //set address bus write_data_bus(buffer[i]); //set data bus bitClear(PORTC,PORTC_WE); //enable write //100ns+ tWP delay caused by for looping } bitSet(PORTC,PORTC_WE); //write must be high for tBLC(100us) data_bus_input(); bitClear(PORTC,PORTC_OE); //enable output //delay 312.5ns @ 16MHz __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); while(buffer[(n+1)*page-1] != read_data_bus()); //poll data } bitSet(PORTC,PORTC_CE); } /************************************************ * * COMMAND and PARSING functions * *************************************************/ void readCommand(){ int idx = 0; do {//read 'til linebreak or buffer is full if(Serial.available())cmdbuf[idx++] = Serial.read(); } while (cmdbuf[idx-1] != '\n' && idx < COMMANDSIZE); for(;idx<COMMANDSIZE;idx++)cmdbuf[idx] = 0;//clear the rest of command buffer } byte parseCommand(){ //command format:C AAAA DDDD LL startAddress=hexWord(cmdbuf+2); //A dataLength=hexWord(cmdbuf+7); //D lineLength=hexByte(cmdbuf+12); //L switch(cmdbuf[0]){ case 'R': return(READ_HEX); case 'r': return(READ_BIN); case 'W': return(WRITE_PAGE); case 'w': return(WRITE_BIN); case 'V': return(VERSION); case 'u': return(UNLOCK); default: return(NOCOMMAND); } } byte hexDigit(char c){ //ascii char to value if(c >= '0' && c <= '9')return c - '0'; if(c >= 'a' && c <= 'f')return c - 'a' + 10; if(c >= 'A' && c <= 'F')return c - 'A' + 10; return 0;// getting here is bad: it means the character was invalid } byte hexByte(char* a){ //ascii byte to value return ((hexDigit(a[0])<<4) | hexDigit(a[1])); } //ascii word to value unsigned int hexWord(char* data){ return ((hexDigit(data[0])<<12)| (hexDigit(data[1])<<8)| (hexDigit(data[2])<<4)| (hexDigit(data[3]))); } /************************************************ * * INPUT / OUTPUT Functions * *************************************************/ void read_block(unsigned int from, unsigned int to, int linelength){ int outcount = 0; //loop from "from address" to "to address" (included) for(unsigned int 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 printByte(read_byte(address)); Serial.print(" "); outcount = (++outcount % linelength); } Serial.println(); } void read_binblock(unsigned int from, unsigned int to){ for(unsigned int address = from; address <= to; address++){ Serial.write(read_byte(address)); } Serial.print('\0');//success return code } void write_block(unsigned int address, byte* buffer, int len){ for(unsigned int i = 0; i < len; i++){ fast_write(address+i,buffer[i]); } } void printAddress(unsigned int address){ if(!(address & 0xfff0)) Serial.print("0"); if(!(address & 0xff00)) Serial.print("0"); if(!(address & 0xf000)) Serial.print("0"); Serial.print(address, HEX); } void printByte(byte data){ if(!(data & 0xf0)) Serial.print("0"); Serial.print(data, HEX); } /************************************************ * * MAIN * *************************************************/ void setup(){ DDRC = 0b00111111; //set EEPROM & shiftOut Pins as output PORTC = 0b00111000; //set EEPROM Pins high //Serial.begin(57600); //set speed of serial connection Serial.begin(115200); while(!Serial) { // We might need this! } } void loop(){ int bytes = 0; readCommand(); switch(parseCommand()){ case READ_HEX: if(lineLength==0) lineLength=32; //default endAddress = startAddress + dataLength -1; read_block(startAddress,endAddress,lineLength); Serial.println('%');//success return code break; case READ_BIN: endAddress = startAddress + dataLength -1; read_binblock(startAddress,endAddress); break; case WRITE_PAGE: if(dataLength > 1024) dataLength = 1024; while(bytes < dataLength)if(Serial.available())buffer[bytes++] = Serial.read(); if(lineLength==0) lineLength=32; //page size write_block_page(startAddress,buffer,dataLength,lineLength); Serial.println('%'); break; case WRITE_BIN: if(dataLength > 1024) dataLength = 1024; //1024 is max while(bytes < dataLength)if(Serial.available())buffer[bytes++] = Serial.read(); write_block(startAddress,buffer,dataLength); Serial.println('%'); break; case VERSION: Serial.println(VERSIONSTRING); break; case UNLOCK: fast_write(0x5555,0xAA); fast_write(0x2AAA,0x55); fast_write(0x5555,0x80); fast_write(0x5555,0xAA); fast_write(0x2AAA,0x55); fast_write(0x5555,0x20); Serial.print('%');//success return code break; } }
Python script (original here)
#!/usr/bin/env python3 #Meeprommer commandline interface #By Zack Nelson #Project Home: #https://github.com/mkeller0815/MEEPROMMER #http://www.ichbinzustaendig.de/dev/meeprommer-en # Some tweaks by @pda import serial, sys, argparse # Parse command line arguments parser = argparse.ArgumentParser( description='Meepromer Command Line Interface', epilog='Read source for further information') task = parser.add_mutually_exclusive_group() task.add_argument('-V', '--version', dest="cmd", action="store_const", const="version", help='Request and display device version') task.add_argument('-w', '--write', dest="cmd", action="store_const", const="write", help='Write to EEPROM') task.add_argument('-W', '--write_paged', dest="cmd", action="store_const", const="write_paged", help='Fast paged write to EEPROM') task.add_argument('-r', '--read', dest="cmd", action="store_const", const="read", help='Read from EEPROM as ascii') task.add_argument('-d', '--dump', dest="cmd", action="store_const", const="dump", help='Dump EEPROM to binary file') task.add_argument('-v', '--verify', dest="cmd", action="store_const", const="verify", help='Compare EEPROM with file') task.add_argument('-u', '--unlock', dest="cmd", action="store_const", const="unlock", help='Unlock EEPROM') task.add_argument('-l', '--list', dest="cmd", action="store_const", const="list", help='List serial ports') parser.add_argument('-a', '--address', action='store', default='0', help='Starting eeprom address (as hex), default 0') parser.add_argument('-o', '--offset', action='store', default='0', help='Input file offset (as hex), default 0') parser.add_argument('-b', '--bytes', action='store', default='8', type=int, help='Number of kBytes to r/w, default 8') parser.add_argument('-p', '--page_size', action='store', default='32', type=int, help='Number of bytes per EEPROM page e.g.:'+ 'CAT28C*=32, AT28C*=64, X28C*=64, default 32') parser.add_argument('-f', '--file', action='store', help='Name of data file') parser.add_argument('-c', '--com', action='store', default='COM5', help='Com port address') parser.add_argument('-s', '--speed', action='store', type=int, default='115200', help='Com port baud, default 115200') def list_ports(): from serial.tools import list_ports for x in list_ports.comports(): print(x[0], x[1]) def dump_file(): ser.flushInput() ser.write(bytes("r "+format(args.address,'04x')+" "+ format(args.address+args.bytes*1024,'04x')+ " 10\n", 'ascii')) eeprom = ser.read(args.bytes*1024) if(ser.read(1) != b'\0'): print("Error: no Ack") sys.exit(1) try: fo = open(args.file,'wb+') except OSError: print("Error: File cannot be opened, verify it is not in use") sys.exit(1) fo.write(eeprom) fo.close() def verify(): print("Verifying...") ser.flushInput() ser.write(bytes("r "+format(args.address,'04x')+" "+ format(args.bytes*1024,'04x')+" 10\n", 'ascii')) try: fi = open(args.file,'rb') except FileNotFoundError: print("Error: ",args.file," not found, please select a valid file") sys.exit(1) except TypeError: print("Error: No file specified") sys.exit(1) fi.seek(args.offset) file = fi.read(args.bytes*1024) eeprom = ser.read(args.bytes*1024) if ser.read(1) != b'\0': print("Error: no EOF received") if file != eeprom: print("Not equal") n = 0 for i in range(args.bytes*1024): if file[i] != eeprom[i]: n+=1 print(n,"differences found") sys.exit(1) else: print("Ok") def read_eeprom(): ser.flushInput() ser.write(bytes("R "+format(args.address,'04x')+" "+ format(args.address+args.bytes*1024,'04x')+ " 10\n", 'ascii')) ser.readline()#remove blank starting line for i in range(round(args.bytes*1024/16)): print(ser.readline().decode('ascii').rstrip()) def write_eeprom(paged): import time fi = open(args.file,'rb') fi.seek(args.offset) now = time.time() #start our stopwatch for i in range(args.bytes): #write n blocks of 1024 bytes output = fi.read(1024) print("Writing from",format(args.address+i*1024,'04x'), "to",format(args.address+i*1024+1023,'04x')) if paged: ser.write(bytes("W "+format(args.address+i*1024,'04x')+ " 0400 "+format(args.page_size,'02x')+"\n", 'ascii')) else: ser.write(bytes("w "+format(args.address+i*1024,'04x')+ " 0400 00\n", 'ascii')) ser.flushInput() ser.write(output) if(ser.read(1) != b'%'): print("Error: no Ack") sys.exit(1) print("Wrote",args.bytes*1024,"bytes in","%.2f"%(time.time()-now),"seconds") def unlock(): print("Unlocking...") ser.flushInput() ser.write(bytes("u 0000 0000 00\n", 'ascii')) if ser.read(1) != b'%': print("Error: no ack") sys.exit(1) def version(): ser.write(b'V\n') print(ser.readline().decode('ascii').rstrip()) args = parser.parse_args() #convert our hex strings to ints args.address = int(args.address,16) args.offset = int(args.offset,16) class DebugSerial(serial.Serial): def write(self, data): #print("write: %s" % (data,)) super().write(data) SERIAL_TIMEOUT = 20 #seconds try: ser = DebugSerial(args.com, args.speed, timeout=SERIAL_TIMEOUT) except serial.serialutil.SerialException: print("Error: Serial port is not valid, please select a valid port") sys.exit(1) # It seems necessary to wait >= ~1.8 seconds for the device to respond. import time time.sleep(2) if args.cmd == 'version': version() elif args.cmd == 'write': write_eeprom(False) elif args.cmd == 'write_paged': write_eeprom(True) elif args.cmd == 'read': read_eeprom() elif args.cmd == 'dump': dump_file() elif args.cmd == 'verify': verify() elif args.cmd == 'unlock': unlock(); elif args.cmd == 'list': list_ports() ser.close() sys.exit(0)