Last active
April 27, 2022 13:14
-
-
Save cutaway/7b661686be95117767a9 to your computer and use it in GitHub Desktop.
Script to interact with SPI EEPROM memory components using the BusPirate via pyBusPirateLite
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
#!/usr/bin/env python | |
# encoding: utf-8 | |
""" | |
Adapted from spi_test.py by Sean Nelson on 2009-10-14. | |
Modified by Don C. Weber (cutaway) and InGuardians, Inc. 20141015 | |
This file is part of pyBusPirate. | |
pyBusPirate is free software: you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation, either version 3 of the License, or | |
(at your option) any later version. | |
pyBusPirate is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with pyBusPirate. If not, see <http://www.gnu.org/licenses/>. | |
""" | |
from serial.serialutil import SerialException | |
import sys, optparse, struct, time | |
from pyBusPirateLite.SPI import * | |
speed = [\ | |
SPISpeed._30KHZ,\ | |
SPISpeed._125KHZ,\ | |
SPISpeed._250KHZ,\ | |
SPISpeed._1MHZ,\ | |
SPISpeed._2MHZ,\ | |
SPISpeed._2_6MHZ,\ | |
SPISpeed._4MHZ,\ | |
SPISpeed._8MHZ\ | |
] | |
speed_names = [\ | |
"30 KHZ",\ | |
"125 KHZ",\ | |
"250 KHZ",\ | |
"1 MHZ",\ | |
"2 MHZ",\ | |
"26 MHZ",\ | |
"4 MHZ",\ | |
"8 MHZ"\ | |
] | |
def read_list_data(size): | |
data = [] | |
for i in range(size+1): | |
data.append(0) | |
return data | |
# NOTE: bulk_trans is suppose to allow the writing of 16 bytes | |
# NOTE: but it only takes 4 bytes before it errors out and locks up the BusPirate. | |
#def blocks(data,size=16): | |
def blocks(data,size=4): | |
tmp = [] | |
for e in range(0,len(data),size): | |
tmp.append([ord(d) for d in data[e:e+size]]) | |
return tmp | |
def parse_prog_args(): | |
parser = optparse.OptionParser(usage="%prog [options]", | |
version="%prog 1.1") | |
# Setup Parameters | |
parser.add_option("-s", "--size", | |
action="store", type=int, dest="flash_size", default=8192, | |
help="Size of Flashchip in bytes") | |
parser.add_option("-d", "--dev", | |
action="store", dest="dev_name", default="/dev/ttyUSB0", | |
help="The comm device to connect to. Example: -d /dev/ttyUSB0", type="string") | |
parser.add_option("-b", "--bulk_size", | |
action="store", dest="bsize", type=int, default=4, | |
help="Number of bytes to write during one bulk_trans. Default: 4. NOTE: Not EEPROM page or block size.") | |
parser.add_option("-S", "--spi_speed", | |
action="store", type=int, dest="spi_speed", default=5, | |
help="Set speed of SPI communications.\n0=30KHZ, 1=125KHZ, 2=250KHZ, 3=1MHZ, 4=2MHZ, 5=2_6MHZ, 6=4MHZ, 7=8MHZ") | |
# Actions to perform | |
parser.set_defaults(command="test") | |
parser.add_option("-t", "--test", | |
action="store_const", dest="command", const="test", | |
help="Test reads data from memory address 0.") | |
parser.add_option("-r", "--read", | |
action="store_const", dest="command", const="read", | |
help="read from SPI to standard out. Reads one word at a time.") | |
parser.add_option("-R", "--fread", | |
action="store_const", dest="command", const="fread", | |
help="read from SPI to the file destination provided.") | |
parser.add_option("-w", "--write", | |
action="store_const", dest="command", const="write", | |
help="write data to SPI. Example: -w \"Python and BusPirate\"") | |
parser.add_option("-W", "--fwrite", | |
action="store_const", dest="command", const="fwrite", | |
help="write from file to SPI. Not implemented, yet.") | |
parser.add_option("-e", "--erase", | |
action="store_const", dest="command", const="erase", | |
help="erase SPI. Not implemented, yet.") | |
parser.add_option("-i", "--id", | |
action="store_const", dest="command", const="id", | |
help="print Chip ID. Not implemented, yet.") | |
# Data for actions | |
parser.add_option("-a", "--address", | |
action="store", dest="address", type=int, default=0, | |
help="address from which to read or write.") | |
parser.add_option("-n", "--nbytes", | |
action="store", dest="nbytes", type=int, default=2, | |
help="number of bytes to read.") | |
parser.add_option("-f", "--file", | |
dest="dfile", action="store_true", default="/tmp/bp_file.dat", | |
help="File to process Default is /tmp/bp_file.dat. Example: -f /tmp/bp_file.dat") | |
parser.add_option("-X", "--string", | |
dest="dstring", action="store_true", default=False, | |
help="user input for writing are string values. Default is string. Example: -w \"Bus Pirate\" -H") | |
parser.add_option("-D", "--debug", | |
dest="DEBUG", action="store_true", default=False, | |
help="Debug mode to print debug information about SPI transations.") | |
(options, args) = parser.parse_args() | |
if options.DEBUG: print "Options:",options | |
if options.DEBUG: print "Args:",args | |
return (options, args) | |
''' | |
if options.command == "id" or options.command == "test" or options.command == "status": | |
return (options, args) | |
elif len(args) != 1: | |
parser.print_help() | |
print options | |
sys.exit(1) | |
else: | |
return (options, args) | |
''' | |
""" enter binary mode """ | |
if __name__ == '__main__': | |
data = "" | |
(opt, args) = parse_prog_args() | |
if opt.command == "fread": | |
f=open(opt.dfile, 'wb') | |
elif opt.command == "fwrite": | |
f=open(opt.dfile, 'rb') | |
try: | |
#NOTE: Leave USB speed at max because it never really changes when using the BusPirate. | |
spi = SPI(opt.dev_name, 115200) | |
except SerialException as ex: | |
print ex | |
sys.exit(); | |
print "Entering binmode: ", | |
if spi.BBmode(): | |
print "OK." | |
else: | |
print "failed." | |
sys.exit() | |
print "Entering raw SPI mode: ", | |
if spi.enter_SPI(): | |
print "OK." | |
else: | |
print "failed." | |
sys.exit() | |
print "Configuring SPI peripherals: ", | |
if spi.cfg_pins(PinCfg.POWER | PinCfg.CS | PinCfg.AUX): | |
print "OK." | |
else: | |
print "failed." | |
sys.exit() | |
print "Configuring SPI speed: ", | |
if spi.set_speed(speed[opt.spi_speed]): | |
print "OK." | |
if opt.DEBUG: print "SPI Speed set to:",speed_names[opt.spi_speed] | |
else: | |
print "failed." | |
sys.exit() | |
print "Configuring SPI mode configuration: ", | |
if spi.cfg_spi(SPICfg.CLK_EDGE | SPICfg.OUT_TYPE): | |
print "OK." | |
else: | |
print "failed." | |
sys.exit() | |
spi.timeout(0.2) | |
if opt.command == "test": | |
print "Testing EEPROM." | |
# Read Status Register | |
spi.CS_Low() | |
spi.bulk_trans(1, [0x5]) | |
data = spi.bulk_trans(2, read_list_data(16)) | |
print "Status Register:",data.encode('hex') | |
spi.CS_High() | |
# Read first eight bytes | |
print "Reading First 8 Bytes." | |
spi.CS_Low() | |
spi.bulk_trans(3, [0x3, 0, 0]) | |
for e in range(8): | |
data = spi.bulk_trans(1, read_list_data(8)) | |
print "Byte Data", str(e)+":",data.encode('hex') | |
spi.CS_High() | |
# Read first eight words | |
print "Reading First 8 Words." | |
spi.CS_Low() | |
spi.bulk_trans(3, [0x3, 0, 0]) | |
for e in range(8): | |
data = spi.bulk_trans(2, read_list_data(16)) | |
print "Word Data", str(e)+":",data.encode('hex') | |
spi.CS_High() | |
elif opt.command == "read": | |
print "Reading EEPROM." | |
spi.CS_Low() | |
# NOTE: The address is sent in two parts. Some chips have four (4) address bytes. So this may require adjustments. | |
high_addr = opt.address >> 8 | |
low_addr = opt.address & 0xff | |
spi.bulk_trans(3, [0x3, high_addr, low_addr]) | |
for i in range(opt.address,opt.nbytes + opt.address,2): | |
data = spi.bulk_trans(2, read_list_data(2)) | |
print "0x{0:0>5}:".format(i),data.encode('hex') | |
spi.CS_High() | |
elif opt.command == "fread": | |
print "Reading EEPROM." | |
# Using opt.bsize because transfers can be unstable at larger sizes | |
spi.CS_Low() | |
spi.bulk_trans(3, [0x3, 0, 0]) | |
for i in range(0,opt.flash_size,opt.bsize): | |
#spi.CS_Low() | |
data = spi.bulk_trans(opt.bsize, read_list_data(opt.bsize)) | |
if opt.DEBUG: print "0x{0:0>5}:".format(opt.address+i),data.encode('hex') | |
f.write(data) | |
# Leave CS HIGH while reading | |
spi.CS_High() | |
elif opt.command == "write": | |
# NOTES: AUX pin needs to be toggled to disable/enable Write Protect but this SPI module does not control AUX. | |
# NOTES: Therefore AUX needs to be toggled using spi.cfg_pins which requires we manage all pins set HIGH. | |
# NOTES: The write commands need to be managed closely. Therefore cycling CS high/low after every command is necessary. | |
print "Writing user data to EEPROM at:",hex(opt.address) | |
#spi.AUX_Low() # This function should be defined but we need to handle it ourselves | |
# We have to unset the AUX to bring it low. Remember, CS has already been brought low so leave it unset. | |
if spi.cfg_pins(PinCfg.POWER): | |
if opt.DEBUG: print "AUX Low OK." | |
else: | |
if opt.DEBUG: print "AUX Low failed." | |
sys.exit() | |
# Write incoming data | |
addr = opt.address | |
if opt.dstring: | |
indata = blocks(args[0],opt.bsize) | |
else: | |
indata = blocks(args[0].decode('hex'),opt.bsize) | |
for e in indata: | |
high_addr = addr >> 8 | |
low_addr = addr & 0xff | |
whead = [0x2, high_addr, low_addr] | |
dlen = len(e) | |
# WREN must be sent before every write. CS must be cycled to get it set. | |
spi.CS_Low() | |
spi.bulk_trans(1, [0x6]) | |
spi.CS_High() | |
spi.CS_Low() | |
if opt.DEBUG: print "Sending:",(3 + len(e), whead + e) | |
spi.bulk_trans(3 + len(e), whead + e) | |
spi.CS_High() | |
addr += opt.bsize | |
# Disable writing | |
spi.CS_Low() | |
spi.bulk_trans(1, [0x4]) | |
spi.CS_High() | |
#spi.AUX_High() # This function should be defined but we need to handle it ourselves | |
# We have to set the AUX to bring it high. Remember, CS has already been brought HIGH so we have to preserve it. | |
if spi.cfg_pins(PinCfg.POWER | PinCfg.CS | PinCfg.AUX): | |
if opt.DEBUG: print "AUX High OK." | |
else: | |
if opt.DEBUG: print "AUX High failed." | |
elif opt.command == "fwrite": | |
print "File writing is not implemented, yet." | |
pass | |
''' | |
print "Writing EEPROM." | |
spi.CS_Low() | |
# Enable writing | |
spi.bulk_trans(2, [0x1, 2]) | |
spi.bulk_trans(1, [0x6]) | |
spi.bulk_trans(1, [0x5]) | |
data = spi.bulk_trans(2, read_list_data(16)) | |
print "Status Register:",data.encode('hex') | |
spi.CS_High() | |
spi.CS_Low() | |
spi.bulk_trans(4, [0xA, 0, 0, 0]) | |
for i in range((int(opt.flash_size)/16)): | |
spi.bulk_trans(16, None) | |
spi.CS_High() | |
''' | |
elif opt.command == "id": | |
# NOTE: This will be chip dependent and not implemented on all EEPROM | |
print "Chip ID is not implemented, yet." | |
pass | |
''' | |
print "Reading Chip ID: ", | |
spi.CS_Low() | |
d = spi.bulk_trans(4, [0x9F, 0, 0, 0]) | |
spi.CS_High() | |
print "d:",d | |
for each in d[1:]: | |
print "%02X " % ord(each), | |
''' | |
elif opt.command == "erase": | |
print "Erase is not implemented, yet." | |
pass | |
print "Reset Bus Pirate to user terminal: ", | |
if opt.command == "fread" or opt.command == "fwrite": | |
f.close() | |
if spi.resetBP(): | |
print "OK." | |
else: | |
print "failed." | |
sys.exit() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment