2023-10-26 22:02:19 +02:00
#!/bin/env python3
from sys import exit
2023-10-26 19:00:17 +02:00
from time import sleep
from RPi . GPIO import IN , OUT
import RPi . GPIO as GPIO
from re import fullmatch
2023-10-26 22:02:19 +02:00
import argparse
import atexit
2023-10-26 19:00:17 +02:00
# EEPROM AT28C256 Pin names and RPi GPIO Pin Numbers
# b means bar = inverted
gpio_l = [ 2 , 3 , 4 , 17 , 27 , 22 , 10 , 9 , 11 , 5 , 6 , 13 , 19 , 26 ]
gpio_r = [ 14 , 15 , 18 , 23 , 24 , 25 , 8 , 7 , 12 , 16 , 20 , 21 ]
#
# Defining which 6502 pin goes to which GPIO pin
#
2023-10-26 22:02:19 +02:00
A = [
# 27, 22, 10, 9, 11, 5, 6, 13,
13 , 6 , 5 , 11 , 9 , 10 , 22 , 27 , # A0 - A7
2 , 3 , 4 , 17 , # A8 - A11
23 , 18 , 15 # A12 - A14
]
2023-10-26 19:00:17 +02:00
2023-10-26 22:02:19 +02:00
# IO = gpio_r[4:12] # 8 io pins
# IO.reverse()
IO = [ 24 , 25 , 8 , 7 , 12 , 16 , 20 , 21 ]
2023-10-26 19:00:17 +02:00
OEb = 26 # Output Enable
WEb = 19 # Write Enable
CEb = 14 # Chip Enable is hooked up to A15 on the processor
controls = [ CEb , WEb , OEb ]
# TIMES
# Read:
t_ACC = 150 * 1e-9 # Address to Output Delay
# Write:
t_AS = 0 # Address Setup time
t_AH = 50 * 1e-9 # Address Hold Time
t_CS = 0 # Chip Select Hold Time
t_WP = 100 * 1e-9 # Write Pulse Width
t_DS = 50 * 1e-9 # Data Setup Time_CS = 0
t_DH = 0 # Data Hold Time
t_WPH = 50 * 1e-9 # Write Puls High
# t_WPH = 50 * 1e-4 # Write Pulse High !!!2*e5 longer than in Datasheet, since shorter high caused Problems with my Chip!!!
2023-10-26 22:02:19 +02:00
def setup_address_control_pins ( ) :
# setup the pins
GPIO . setmode ( GPIO . BCM )
for pin in controls :
GPIO . setup ( pin , OUT , initial = 1 ) # inverted, is 1 means disable
for pin in A :
GPIO . setup ( pin , OUT , initial = 0 )
def setup_io_pins ( IOdirection = OUT ) :
2023-10-26 19:00:17 +02:00
# OUT when writing and IN when reading
for pin in IO :
GPIO . setup ( pin , IOdirection )
2023-10-26 22:02:19 +02:00
def cleanup ( ) :
GPIO . cleanup ( )
2023-10-26 19:00:17 +02:00
def print_pins ( ) :
for i in range ( len ( A ) ) :
2023-10-26 22:02:19 +02:00
print ( f " A { i : 02 } - { A [ i ] } " )
2023-10-26 19:00:17 +02:00
for i in range ( len ( IO ) ) :
2023-10-26 22:02:19 +02:00
print ( f " IO { i : 02 } - { IO [ i ] } " )
print ( f " CEb - { CEb } " )
print ( f " WEb - { WEb } " )
print ( f " OEb - { OEb } " )
2023-10-26 19:00:17 +02:00
def set_address ( address : int , bits = 8 ) :
"""
set the address pins to the given value
"""
ad_bin = format ( address , f " 0 { bits } b " ) # get the x-bit verion if the address, eg 12 -> 00001100
for j in range ( bits ) :
# print("Address:", address, ad_bin, j)
if ad_bin [ bits - 1 - j ] == " 0 " :
GPIO . output ( A [ j ] , 0 )
elif ad_bin [ bits - 1 - j ] == " 1 " :
GPIO . output ( A [ j ] , 1 )
return ad_bin
def get_bits ( i : int ) :
"""
return how many bits are needed to express the number in binary
"""
return len ( bin ( i ) ) - 2 # -2 for the "0x"
2023-10-26 22:02:19 +02:00
def convert_to_int ( v ) :
2023-10-26 19:00:17 +02:00
"""
2023-10-26 22:02:19 +02:00
convert 0xABC and 0b0101 to int
2023-10-26 19:00:17 +02:00
"""
2023-10-26 22:02:19 +02:00
if type ( v ) == str and fullmatch ( f " 0x[0-9,a-f,A-F]+ " , v ) :
return int ( v . replace ( " 0x " , " " ) , 16 )
elif type ( v ) == str and fullmatch ( f " 0b[0]+ " , v ) :
return int ( v . replace ( " 0b " , " " ) , 2 )
return int ( v )
2023-10-26 19:00:17 +02:00
def get_8_bit ( l : list ) :
for i in range ( len ( l ) ) :
l [ i ] = format ( l [ i ] , f " 08b " ) # get the 8-bit bin value
return l
2023-10-26 22:02:19 +02:00
2023-10-26 19:00:17 +02:00
def erase ( from_ad = 0 , to_ad = 32767 , * * keys ) :
"""
Write all 1 to the EEPROM
WEb controlled
"""
data = [ 0xff for i in range ( from_ad , to_ad ) ]
write ( data , from_ad = from_ad , * * keys )
print ( " Erased EEPROM - Done! " )
return
def write_byte ( byte , address , verbose = True ) :
GPIO . output ( OEb , 1 )
# setup the address
ad_bin = set_address ( address , bits = 15 )
2023-10-26 22:02:19 +02:00
setup_io_pins ( OUT )
2023-10-26 19:00:17 +02:00
# Setup Data
for j in range ( 8 ) :
if byte [ 7 - j ] == " 1 " :
bit = 1
else :
bit = 0
GPIO . output ( IO [ j ] , bit )
# wait "Address" Setup Time
sleep ( t_AS )
# wait "Data Setup Time"
sleep ( t_DS )
2023-10-26 22:02:19 +02:00
2023-10-26 19:00:17 +02:00
GPIO . output ( CEb , 0 )
# Start the write pulse -> enable WEb
GPIO . output ( WEb , 0 )
2023-10-26 22:02:19 +02:00
2023-10-26 19:00:17 +02:00
# wait until minimum write pulse width is reached. in theory, should be t_WP-t_DS but this caused problems
sleep ( t_WP )
# End Write Pulse -> disable WEb
GPIO . output ( WEb , 1 )
GPIO . output ( CEb , 1 )
2023-10-26 22:02:19 +02:00
2023-10-26 19:00:17 +02:00
# wait "Data Hold"
sleep ( t_DH )
GPIO . output ( CEb , 0 )
GPIO . cleanup ( IO )
2023-10-26 22:02:19 +02:00
setup_io_pins ( IN )
2023-10-26 19:00:17 +02:00
# check the toggle bit IO6, if it stops toggling the write is done
timeout = 0
while timeout < 1e3 :
GPIO . output ( OEb , 0 )
sleep ( 1e-9 )
bit1 = GPIO . input ( IO [ 6 ] )
GPIO . output ( OEb , 1 )
sleep ( 1e-9 )
GPIO . output ( OEb , 0 )
sleep ( 1e-9 )
bit2 = GPIO . input ( IO [ 6 ] )
GPIO . output ( OEb , 1 )
sleep ( 1e-9 )
if bit1 == bit2 :
timeout = 1e3
timeout + = 1
GPIO . output ( CEb , 1 )
if verbose :
print ( f " Writing: \t 0b { format ( address , ' 015b ' ) } - 0b { byte } ||| 0x { format ( address , ' 04x ' ) } - { hex ( int ( byte , 2 ) ) } " )
2023-10-26 22:02:19 +02:00
2023-10-26 19:00:17 +02:00
def read_byte ( address ) :
GPIO . output ( WEb , 1 )
2023-10-26 22:02:19 +02:00
setup_io_pins ( IN )
2023-10-26 19:00:17 +02:00
# set the address valid
ad_bin = set_address ( address , bits = 15 )
2023-10-26 22:02:19 +02:00
2023-10-26 19:00:17 +02:00
# low in chip/output enable -> enable
GPIO . output ( CEb , 0 )
GPIO . output ( OEb , 0 )
# wait the "Address to Output Delay" until the output is valid
sleep ( t_ACC )
byte = " "
for j in range ( 8 ) :
if GPIO . input ( IO [ 7 - j ] ) == 1 :
byte + = " 1 "
else :
byte + = " 0 "
2023-10-26 22:02:19 +02:00
2023-10-26 19:00:17 +02:00
# high in OEb and CEb -> disable
GPIO . output ( OEb , 1 )
GPIO . output ( CEb , 1 )
2023-10-26 22:02:19 +02:00
return int ( byte , 2 )
2023-10-26 19:00:17 +02:00
def read ( from_ad = 0 , to_ad = 255 , delay = 1e-3 , ignore = [ 0xff ] , verbose = True , single_step = False , compare = None ) :
"""
from_ad : start address from where to read
to_ad : end address to read to
delay : delay between readings in s
verbose wether to print the reading
ignore list of values which are not printed
"""
content = [ ]
unequal = [ ]
for i in range ( from_ad , to_ad + 1 ) :
byte = read_byte ( i )
content . append ( byte )
2023-10-26 22:02:19 +02:00
2023-10-26 19:00:17 +02:00
if not compare and verbose and not byte in ignore :
print ( f " Reading: \t 0b { format ( i , ' 015b ' ) } - 0b { format ( byte , ' 08b ' ) } ||| 0x { format ( i , ' 04x ' ) } - 0x { format ( byte , ' 02x ' ) } " )
elif compare :
if not compare [ i ] == byte :
unequal . append ( i )
print ( f " Unequal at Address 0x { format ( i , ' 04x ' ) } ||| File: 0x { format ( compare [ i ] , ' 02x ' ) } vs EEPROM: 0x { format ( byte , ' 02x ' ) } " )
# wait artifical delay
sleep ( delay )
if single_step :
input ( " Press Return to read the next byte " )
if compare :
return unequal
return content
2023-10-26 22:02:19 +02:00
2023-10-26 19:00:17 +02:00
def write ( content : list , from_ad = 0 , delay = 0 , single_step = False , verbose = True , check_written = True ) :
"""
2023-10-26 22:02:19 +02:00
Write a list if bytes to the eeprom .
2023-10-26 19:00:17 +02:00
WEb controlled
"""
or_content = content . copy ( )
content = get_8_bit ( content )
failed = [ ]
print ( f " Writing to EEPROM: { len ( content ) } bytes from address { hex ( from_ad ) } . " )
for i in range ( len ( content ) ) :
write_byte ( content [ i ] , from_ad + i , verbose = verbose )
# wait artifical delay
sleep ( delay )
if single_step :
input ( " Press Return to write the next byte " )
print ( " Write to EEPROM - Done! " )
if check_written :
print ( " Comparing EEPROM to file... " )
failed = read ( from_ad = from_ad , delay = delay , single_step = single_step , verbose = False , compare = or_content )
while len ( failed ) > 0 :
for ad in failed :
write_byte ( content [ ad ] , ad , verbose = verbose )
failed = read ( from_ad = from_ad , delay = delay , single_step = single_step , verbose = False , compare = or_content )
print ( " Comparing complete " )
return
2023-10-26 22:02:19 +02:00
2023-10-26 19:00:17 +02:00
def get_bytes ( filepath , from_ad = 0 ) :
with open ( filepath , " rb " ) as file :
bindata = [ ]
for byte in file . read ( ) :
bindata . append ( byte )
return bindata [ from_ad : ]
2023-10-26 22:02:19 +02:00
def main ( ) :
parser = argparse . ArgumentParser (
prog = " AT28C256-EEPROM Writer " ,
description = " Read and write from/to an AT28C256 EEPROM via the Raspberry Pi 4 ' s GPIO pins \n 2023 Matthias Quintern " ,
epilog = " Numbers can be passed as int, hex prefixed with ' 0x ' and binary prefixed with ' 0b ' " ,
)
parser . add_argument ( " --write " , " -w " , dest = " write " , action = " store_true " , default = False , help = " write binary file onto the EEPROM " )
parser . add_argument ( " --compare " , " -c " , dest = " compare " , action = " store_true " , default = False , help = " compare EEPROM content to binary file " )
parser . add_argument ( " --read " , " -r " , dest = " read " , action = " store_true " , default = False , help = " read the contents of the EEPROM " )
parser . add_argument ( " --erase " , " -e " , dest = " erase " , action = " store_true " , default = False , help = " erase the contents of the EEPROM " )
parser . add_argument ( " --file " , " -f " , metavar = " file " , dest = " file " , action = " store " , default = " " , help = " file for --write and --compare " )
parser . add_argument ( " --from " , metavar = " start-address " , dest = " from_ad " , action = " store " , default = 0 , type = convert_to_int , help = " start at address x " )
parser . add_argument ( " --to " , metavar = " end-address " , dest = " to_ad " , action = " store " , default = 32767 , type = convert_to_int , help = " end at address y (inclusive) " )
parser . add_argument ( " --single-step " , dest = " single_step " , action = " store_true " , default = False , help = " single step the program " )
parser . add_argument ( " --delay " , dest = " delay " , metavar = " delay " , action = " store " , default = 0 , help = " extra delay between cycles " )
parser . add_argument ( " --ignore " , dest = " ignore " , metavar = " numbers " , action = " store " , nargs = " * " , type = convert_to_int , default = [ 0x100 ] , help = " ignore the numbers a b ... Default=[0xff] " )
parser . add_argument ( " --verbose " , dest = " verbose " , action = " store_true " , default = False , help = " increase verbosity " )
parser . add_argument ( " --print-pins " , dest = " print_pins " , action = " store_true " , default = False , help = " print the pin layout and exit " )
args = parser . parse_args ( )
if ( args . write or args . compare ) and not args . file :
parser . error ( ' --file is required with --write and --compare ' )
if args . print_pins :
print_pins ( )
exit ( 0 )
if args . erase :
erase ( from_ad = args . from_ad , to_ad = args . to_ad , delay = args . delay , single_step = args . single_step , verbose = args . verbose )
if args . write :
write ( get_bytes ( args . file , from_ad = args . from_ad ) , from_ad = args . from_ad , delay = args . delay , single_step = args . single_step , verbose = args . verbose )
if args . read :
read ( from_ad = args . from_ad , to_ad = args . to_ad , delay = args . delay , single_step = args . single_step , verbose = args . verbose , ignore = args . ignore )
if args . compare :
compare ( from_ad = args . from_ad , to_ad = args . to_ad , delay = args . delay , single_step = args . single_step , verbose = args . verbose , ignore = args . ignore , compare = get_bytes ( args . file ) )
if __name__ == ' __main__ ' :
atexit . register ( cleanup )
setup_address_control_pins ( )
main ( )