-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Arduino-Based AT28C256 EEPROM Programmer.
Initial commit with working reads and full dumps.
- Loading branch information
0 parents
commit 6158aa1
Showing
6 changed files
with
384 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.idea/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
Copyright 2019, Erik van Zijst <[email protected]> | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
/* | ||
* AT28C256 EEPROM Reader and Programmer | ||
* | ||
* This code implements the serial wire protocol as described in protocol.txt | ||
* | ||
* Pin Layout | ||
* | ||
* Pin | Circuit | ||
* ----+-------------- | ||
* 2 | EEPROM IO0 | ||
* 3 | EEPROM IO1 | ||
* 4 | EEPROM IO2 | ||
* 5 | EEPROM IO3 | ||
* 6 | EEPROM IO4 | ||
* 7 | EEPROM IO5 | ||
* 8 | EEPROM IO6 | ||
* 9 | EEPROM IO7 | ||
* ----+-------------- | ||
* 10 | 74HC595 SER | ||
* 11 | 74HC595 RCLK | ||
* 12 | 74HC595 SCLK | ||
* ----+-------------- | ||
* A0 | EEPROM CE | ||
* A1 | EEPROM OE | ||
* A2 | EEPROM WE | ||
* ----+-------------- | ||
* 13 | Activity LED | ||
* ----+-------------- | ||
* | ||
* Copyright 2019, Erik van Zijst <[email protected]> | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
// AT28C256 contol lines | ||
const unsigned int EEPROM_CE = A0; | ||
const unsigned int EEPROM_OE = A1; | ||
const unsigned int EEPROM_WE = A2; | ||
|
||
// 74HC595 control lines | ||
const unsigned int SHIFT_SER = 10; | ||
const unsigned int SHIFT_RCLK = 11; | ||
const unsigned int SHIFT_SCLK = 12; | ||
|
||
// Activity indicator LED | ||
const unsigned int ACT_LED = 13; | ||
|
||
const unsigned int dataPins[] = {2, 3, 4, 5, 6, 7, 8, 9}; | ||
|
||
unsigned int len; | ||
byte buf[4]; | ||
|
||
void setup() { | ||
Serial.begin(19200); | ||
|
||
pinMode(EEPROM_CE, OUTPUT); | ||
pinMode(EEPROM_OE, OUTPUT); | ||
pinMode(EEPROM_WE, OUTPUT); | ||
|
||
pinMode(SHIFT_SER, OUTPUT); | ||
pinMode(SHIFT_RCLK, OUTPUT); | ||
pinMode(SHIFT_SCLK, OUTPUT); | ||
|
||
pinMode(ACT_LED, OUTPUT); | ||
digitalWrite(ACT_LED, LOW); | ||
|
||
for (unsigned int i = 0; i < 8; i++) { | ||
pinMode(dataPins[i], INPUT); | ||
} | ||
} | ||
|
||
void pulse(int pin) { | ||
digitalWrite(pin, HIGH); | ||
delayMicroseconds(1); | ||
digitalWrite(pin, LOW); | ||
} | ||
|
||
/* | ||
* Loads the specified 16 bit address into the 595 shift register. | ||
*/ | ||
void loadShiftAddr(unsigned int addr) { | ||
for (int i = 15; i >= 0; i--) { | ||
digitalWrite(SHIFT_SER, ((addr >> i) & 1) ? HIGH : LOW); | ||
pulse(SHIFT_SCLK); | ||
} | ||
pulse(SHIFT_RCLK); | ||
} | ||
|
||
void readAddr(unsigned int addr) { | ||
loadShiftAddr(addr); | ||
|
||
byte val = 0; | ||
for (unsigned int i = 0; i < 8; i++) { | ||
val |= (digitalRead(dataPins[i]) << i); | ||
} | ||
|
||
Serial.write(val); | ||
} | ||
|
||
void writeAddr(unsigned int addr, byte val) { | ||
Serial.write(0); | ||
} | ||
|
||
void dump() { | ||
for (unsigned int addr = 0; addr < 32768; addr++) { | ||
byte val = 0; | ||
loadShiftAddr(addr); | ||
delayMicroseconds(1); | ||
for (unsigned int i = 0; i < 8; i++) { | ||
val |= (digitalRead(dataPins[i]) << i); | ||
} | ||
Serial.write(val); | ||
} | ||
} | ||
|
||
void load() { | ||
} | ||
|
||
void loop() { | ||
if (Serial.available() > 0) { | ||
digitalWrite(ACT_LED, HIGH); | ||
|
||
len = Serial.read(); | ||
Serial.readBytes(buf, len); | ||
|
||
if (buf[0] == 0x72 && len == 3) { | ||
readAddr((buf[1] << 8) + buf[2]); | ||
|
||
} else if (buf[0] == 0x77 && len == 4) { | ||
writeAddr((buf[1] << 8) + buf[2], buf[3]); | ||
|
||
} else if (buf[0] == 0x64 && len == 1) { | ||
dump(); | ||
|
||
} else if (buf[0] == 0x6c && len == 3) { | ||
load(); | ||
|
||
} else { | ||
for (int i = 0; i < 5; i++) { | ||
digitalWrite(ACT_LED, LOW); | ||
delay(200); | ||
digitalWrite(ACT_LED, HIGH); | ||
delay(200); | ||
} | ||
} | ||
digitalWrite(ACT_LED, LOW); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
#!/usr/bin/env python3 | ||
# | ||
# Copyright 2019, Erik van Zijst <[email protected]> | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import argparse | ||
import sys | ||
from struct import pack | ||
|
||
from serial import Serial | ||
from serial.tools import list_ports | ||
|
||
|
||
def atoi(val: str) -> int: | ||
"""Parses an address string into an integer. | ||
Supports decimal, hexadecimal and octal notation. | ||
""" | ||
return int(val, | ||
16 if val.startswith('0x') else | ||
8 if val.startswith('0o') else | ||
10) | ||
|
||
|
||
def read(port: Serial, addr: str) -> None: | ||
port.write(pack('>BcH', 3, b'r', atoi(addr))) | ||
val = port.read(1) | ||
print(int.from_bytes(val, 'big'), '/', '0x' + val.hex()) | ||
|
||
|
||
def write(port: Serial, addr: str, val: str) -> None: | ||
port.write(pack('>BcHB', 4, b'w', atoi(addr), atoi(val))) | ||
print(port.read(1)) | ||
print('OK') | ||
|
||
|
||
def dump(port: Serial, filename: str) -> None: | ||
with open(filename, 'wb') as f: | ||
port.write(pack('>Bc', 1, b'd')) | ||
|
||
for i in range(2**15): | ||
f.write(port.read(1)) | ||
f.flush() | ||
if i % 100 == 0: | ||
print('\r%d%%' % ((i / 2**15) * 100), end='') | ||
print('\nComplete.') | ||
|
||
|
||
def quit(*args) -> None: | ||
raise EOFError() | ||
|
||
|
||
if __name__ == '__main__': | ||
usage = """AT28C256 EEPROM Programmer | ||
Read or write individual addresses, dump out the full contents to a file, or\ | ||
load an image file onto the ERPROM. | ||
To read a single byte: | ||
> [r|read] [addr] | ||
To write a byte to a specific address: | ||
> [w|write] [addr] [value] | ||
To dump the entire EEPROM to a file: | ||
> [d|dump] [filename] | ||
To load a local file into the EEPROM: | ||
> [l|load] [filename] | ||
Address supports hex (0xFF) and octal (0o7) notation. | ||
""" | ||
parser = argparse.ArgumentParser(description='AT28C256 EEPROM Programmer') | ||
parser.add_argument('port', nargs='?', | ||
help='the serial port the Arduino is ' | ||
'connected to (on OSX typically ' | ||
'/dev/tty.usbmodemXXXX)') | ||
args = parser.parse_args() | ||
|
||
dev = args.port | ||
if not args.port: | ||
try: | ||
# attempt to autodetect the Arduino | ||
dev = next( | ||
filter(lambda p: p.product and 'arduino' in p.product.lower(), | ||
list_ports.comports())).device | ||
print('Found Arduino at port', dev) | ||
except StopIteration: | ||
print('Cannot find Arduino. If it is connected, specify the port ' | ||
'manually.', file=sys.stderr) | ||
exit(1) | ||
|
||
port = Serial(dev, 19200, timeout=3) | ||
print(usage) | ||
while True: | ||
try: | ||
expr = input('> ').split() | ||
{'r': read, | ||
'read': read, | ||
'w': write, | ||
'write': write, | ||
'd': dump, | ||
'dump': dump, | ||
'quit': quit, | ||
'q': quit}[expr[0]](port, *expr[1:]) | ||
|
||
except EOFError: | ||
break | ||
except (ValueError, KeyError, IndexError, TypeError) as e: | ||
print('Invalid command:', e) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
Protocol | ||
|
||
The protocol between the Python client and Arduino is a synchronous request | ||
response based system. The client initiates all communication, one request | ||
at a time. | ||
|
||
Each request yields a response from the Arduino which must be consumed fully | ||
before the next request can be sent. | ||
|
||
Each request is packed into a structured binary envelope consisting of a | ||
leading octet indicating the length of the message (excluding the leading | ||
octet), followed by a single octet describing the command that is to be | ||
executed by the Arduino, followed by 0 up to and including 253 bytes of | ||
command-specific payload data: | ||
|
||
0 1 2 3 | ||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| Length | Command | Data | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
|
||
The following commands are defined: | ||
|
||
1. Read | ||
|
||
Command byte: 'r' (0x72 / ASCII 114) | ||
Data: 16 bit address in network byte order. Since the AT28C256 has only 15 | ||
address lines, only the 15 lower bits are used. The leading, MSB is unused. | ||
|
||
0 1 2 3 | ||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| 3 | 'r' |0| 15 bit address | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
|
||
The response is a single byte containing the value at the specified address, | ||
without header or delimiter. | ||
|
||
2. Write | ||
|
||
Command byte: 'w' (0x77 / ASCII 119) | ||
Data: 3 bytes, broken down as follows: 2 bytes containing a 15 bit address | ||
in network byte order, followed by one octet of data to be written to the | ||
specified address. | ||
|
||
0 1 2 3 | ||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| 4 | 'w' |0| 15 bit address | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| Data | | ||
+-+-+-+-+-+-+-+-+ | ||
|
||
The response is a single 0 byte to indicate the operation finished and a new | ||
command can be sent. | ||
|
||
3. Dump | ||
|
||
Command byte: 'd' (0x64 / ASCII 100) | ||
Data: No payload following the command byte. | ||
|
||
0 1 | ||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| 0 | 'd' | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
|
||
The response is the raw contents of the entire EEPROM. This is exactly 32KB | ||
of raw data without header or delimiter. The response is done after the | ||
32768th byte. | ||
|
||
4. Load | ||
|
||
Command byte: 'l' (0x6C / ASCII 108) | ||
Data: 2 bytes indicating the size of the upload that follows the request | ||
message. Since the AT28C256 has only 15 address lines, only the lower 15 bits | ||
are used. The leading, MSB is ignored. | ||
|
||
0 1 2 3 | ||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| 3 | 'l' |0| 15 bit length | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
|
||
Directly following the command message is the stream of bytes to be uploaded | ||
to the EEPROM. Writing always begins at address 0. The length of the stream | ||
must match the length advertised in the command message. | ||
|
||
The response is a single 0 byte to indicate the operation finished and a new | ||
command can be sent. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pyserial==3.4 |