Skip to content

Commit

Permalink
programmer cli: refactor argparsing
Browse files Browse the repository at this point in the history
  • Loading branch information
adumont committed Mar 28, 2021
1 parent fc4cc39 commit 6c666eb
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 107 deletions.
96 changes: 68 additions & 28 deletions programmer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,45 @@ This subproject is an EEPROM Programmer on a breadboard using:

## Command line interface

Use `./eeprom.py` as a command line interface.
Use `./eeprom.py` as a command line interface:

### Dump 256 byte pages as text
```
usage: eeprom.py [-h] [-p PORT] {flash,save,dump,erase} ...
EEPROM Programmer CLI
positional arguments:
{flash,save,dump,erase}
flash Flash a binary file to EEPROM
save Save EEPROM to binary file
dump Dump EEPROM as text (hex/ascii)
erase Erase EEPROM
`./eeprom.py -d/--dump [ADDR [n [offset]]]` will dump `n` pages of 256 bytes starting at address `ADDR` (in hex). If `ADDR` isn't a multiple of 256, it will dump around `ADDR`. If `offset` is specified, the address showned will be offset by `offset` (in hex).
optional arguments:
-h, --help show this help message and exit
-p PORT, --port PORT USB port to use
| Command | Meaning |
| ---------------------------- | ------------------------------------------------------------------------- |
| `./eeprom.py -d` | Dump page 0000 |
| `./eeprom.py -d ADDR` | Dump page at address `ADDR` (hex) |
| `./eeprom.py -d ADDR 2` | Dump 2 pages starting at address `ADDR` (hex) |
| `./eeprom.py -d 0000 1 8000` | Dump 1 page starting at address $0000, and show starting address as $8000 |
Written by @adumont
```

### Dump 256 byte pages as text (hexa/ascii)

```
$ ./eeprom.py --dump 0 1 8000
usage: eeprom.py dump [-h] [-a ADDR] [-n PAGES] [-o OFFSET]
optional arguments:
-h, --help show this help message and exit
-a ADDR, --addr ADDR Address (hexadecimal), default: 0000
-n PAGES, --pages PAGES
Number of 256B pages to dump (decimal)
-o OFFSET, --offset OFFSET
Offset to show addresses (hexadecimal), default: 8000
```

`./eeprom.py dump` will dump `PAGES` pages of 256 bytes starting at address `ADDR` (in hex). If `ADDR` isn't a multiple of 256, it will dump the 256 bytes page aligned around `ADDR`. If `offset` is specified, the addresses showned will be offset by `offset` (in hex).

```
$ ./eeprom.py dump
Connected to programmer on port: /dev/ttyACM0
00008000: 58d8 a9ff 8d03 60a9 aa85 00a9 ffa5 008d X.....`.........
Expand All @@ -50,36 +74,52 @@ Connected to programmer on port: /dev/ttyACM0
00008050: e610 d0ef 6000 0000 0000 0000 0000 0000 ....`...........
[...]
```
### Flash a binary file to EEPROM

### Erase EEPROM
```
usage: eeprom.py flash [-h] [-a ADDR] file
`./eeprom.py --erase [HH]` write HH (hex) to all the EEPROM. It takes about 13s to erase 32KB.
positional arguments:
file File to write to EEPROM
| Command | Meaning |
| ------------------------ | ------------------------------- |
| `./eeprom.py --erase` | Fill the EEPROM with $FF |
| `./eeprom.py --erase HH` | Fill the EEPROM with `HH` (hex) |
optional arguments:
-h, --help show this help message and exit
-a ADDR, --addr ADDR Address (hexadecimal), default: 0000
```

`./eeprom.py flash` will flash the file FILE at address ADDR. It takes about 13s to flash 32KB.

### Save EEPROM content to a binary file

```
usage: eeprom.py save [-h] [-a ADDR] [-l LEN] file
### Upload/Flash a binary file to EEPROM
positional arguments:
file File to save as
`./eeprom.py -p/--put ADDR FILE` upload and flash the file FILE at address ADDR. It takes about 7s to flash 16KB.
optional arguments:
-h, --help show this help message and exit
-a ADDR, --addr ADDR Address (hexadecimal), default: 0000
-l LEN, --len LEN Length (bytes, decimal)
```

| Command | Meaning |
| ------------------------------ | --------------------------------------- |
| `./eeprom.py --put 0 file.bin` | Flash `file.bin` to EEPROM at address 0 |
`./eeprom.py save` will save LEN bytes (dec) from EEPROM starting at address ADDR (hex) to file FILE. It takes about 5.5s to download 16KB.

### Download a binary file from EEPROM
### Erase EEPROM

`./eeprom.py -g/--get ADDR LEN FILE` LEN bytes (dec) from EEPROM startig at address ADDR (hex) to file FILE. It takes about 5.5s to download 16KB.
```
usage: eeprom.py erase [-h] [-f FILL]
optional arguments:
-h, --help show this help message and exit
-f FILL, --fill FILL Fill byte (hexadecimal), default: ff
```

| Command | Meaning |
| ------------------------------------- | ------------------------------------------------------------------ |
| `./eeprom.py --get 02F0 256 file.bin` | Get 256 bytes from EEPROM starting at $02F0 and save to `file.bin` |
| `./eeprom.py --get 0 32768 file.bin` | Dump the whole EEPROM (32K) and save it to `file.bin` |
`./eeprom.py erase` will write FF (hex) to all the EEPROM. It takes about 13s to erase 32KB.

# Credits

Here are some awesome projects from which I have taken inspiration and sometimes also some code:
Here are some awesome projects from which I have taken inspiration and sometimes also some code as a starting point:

- [Build an Arduino EEPROM programmer](https://www.youtube.com/watch?v=K88pgWhEb1M) by Ben Eaters
- [Source code](https://github.com/beneater/eeprom-programmer#arduino-eeprom-programmer)
Expand Down
163 changes: 84 additions & 79 deletions programmer/eeprom.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,40 +10,76 @@

from glob import glob

# logging.basicConfig(format='[%(name)s.%(funcName)s:%(lineno)d] %(levelname)s %(message)s', level=level)
def flash(args):
l = os.stat(args.file).st_size

print("put %s %s\r" % ( args.addr, l))
ser.write( str.encode("put %s %s\r" % ( args.addr, l) ) )

with open(args.file, "rb") as f:
count = 0
while True:
c=f.read(64)
if not c:
break
ser.write(c)
count = count + len(c)
print(" %3.2f %%" % (100.0*count/l), end="\r")
print()

def save(args):
print("get %s %d\r" % ( args.addr, args.len))
ser.write( str.encode("get %s %d\r" % ( args.addr, args.len)) )
with open(args.file, "wb") as f:
count = 0
while True:
c=ser.read(1)
if not c:
continue
f.write(c)
count = count+1
if(count%256) or count == args.len:
print(" %3.2f %%" % (100.0*count/args.len), end="\r")
if count == args.len:
break
print()

def dump(args):
print("d %s %s %s\r" % ( args.addr, args.pages, args.offset))
ser.write( str.encode("d %s %s %s\r" % ( args.addr, args.pages, args.offset) ) )

# def log(className):
# return logging.getLogger(className)
def erase(args):
print("erase %s\r" % args.fill)
ser.write( str.encode("erase %s\r" % args.fill ) )

parser = argparse.ArgumentParser(
description='EEPROM Programmer CLI',
epilog='@adumont')
epilog='Written by @adumont')

task = parser.add_mutually_exclusive_group(required=True)
task.add_argument('-p', '--put', dest="cmd", action="store_const", const="put", help='Write a file to EEPROM (binary)')
task.add_argument('-d', '--dump', dest="cmd", action="store_const", const="dump", help='Dump from EEPROM (ascii)')
task.add_argument('-g', '--get', dest="cmd", action="store_const", const="get", help='Read from EEPROM (binary)')
task.add_argument('--erase', dest="cmd", action="store_const", const="erase", help='Erase EEPROM')
# task.add_argument('-e', '--erase', dest="cmd", action="store_const", const="erase", help='Erase EEPROM')
parser.add_argument('-p', '--port', help='USB port to use' )

parser.add_argument("args",nargs="*")
subparsers = parser.add_subparsers()

args = parser.parse_args()
parser_put = subparsers.add_parser('flash', help='Flash a binary file to EEPROM')
parser_put.add_argument('file', help='File to write to EEPROM')
parser_put.add_argument('-a', '--addr', help='Address (hexadecimal), default: 0000', default='0' )
parser_put.set_defaults(func=flash)

port = glob("/dev/ttyACM*")
parser_get = subparsers.add_parser('save', help='Save EEPROM to binary file')
parser_get.add_argument('file', help='File to save as')
parser_get.add_argument('-a', '--addr', help='Address (hexadecimal), default: 0000', default='0' )
parser_get.add_argument('-l', '--len', type=int, help='Length (bytes, decimal)', default=32*1024 )
parser_get.set_defaults(func=save)

assert( len(port) > 0 )
parser_dump = subparsers.add_parser('dump', help='Dump EEPROM as text (hex/ascii)')
parser_dump.add_argument('-a', '--addr', help='Address (hexadecimal), default: 0000', default='0' )
parser_dump.add_argument('-n', '--pages', type=int, help='Number of 256B pages to dump (decimal)', default=1 )
parser_dump.add_argument('-o', '--offset', help='Offset to show addresses (hexadecimal), default: 8000', default='8000' )
parser_dump.set_defaults(func=dump)

ser = serial.Serial(
port=port[0],
baudrate=115200,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=0
)

print("Connected to programmer on port: " + ser.portstr)
parser_erase = subparsers.add_parser('erase', help='Erase EEPROM')
parser_erase.add_argument('-f', '--fill', help='Fill byte (hexadecimal), default: ff', default="ff" )
parser_erase.set_defaults(func=erase)

def wait_for_prompt(show=True, timeout=0):
prompt = False
Expand All @@ -58,62 +94,31 @@ def wait_for_prompt(show=True, timeout=0):
break
if show:
print("%c" % c, end='', flush=True)


def main():
if __name__ == '__main__':
args = parser.parse_args()
print(vars(args))

if args.port == None:
port = glob("/dev/ttyACM*")
assert( len(port) > 0 )
port = port[0]
else:
port = args.port

ser = serial.Serial(
port=port,
baudrate=115200,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=0
)
print("Connected to programmer on port: " + ser.portstr)

wait_for_prompt(show=False, timeout=200)

print(vars(args), len(args.args))

a0 = "" if len(args.args) == 0 else args.args[0]
a1 = "" if len(args.args) <= 1 else args.args[1]
a2 = "" if len(args.args) <= 2 else args.args[2]

if args.cmd == "dump":
print("d %s %s\r" % ( a0, a1))
ser.write( str.encode("d %s %s %s\r" % ( a0, a1, a2) ) )
elif args.cmd == "erase":
print("erase %s\r" % a0)
ser.write( str.encode("erase %s\r" % a0 ) )
elif args.cmd == "put":
l = os.stat(a1).st_size

print("put %s %s\r" % ( a0, l))
ser.write( str.encode("put %s %s\r" % ( a0, l) ) )

with open(a1, "rb") as f:
count = 0
while True:
c=f.read(64)
if not c:
break
ser.write(c)
count = count + len(c)
# print(" %3.00g %%" % (100.0*count/l), end="\r")
print(" %3.2f %%" % (100.0*count/l), end="\r")
print()

elif args.cmd == "get":
print("get %s %s\r" % ( a0, a1))
ser.write( str.encode("get %s %s\r" % ( a0, a1)) )
l=int(a1)
with open(a2, "wb") as f:
count = 0
while True:
c=ser.read(1)
if not c:
continue
f.write(c)
count = count+1
if(count%256) or count == l:
print(" %3.2f %%" % (100.0*count/l), end="\r")
if count == l:
break
print()

wait_for_prompt()
ser.close()

if __name__== "__main__":
main()
args.func(args)

wait_for_prompt()
ser.close()

0 comments on commit 6c666eb

Please sign in to comment.