From 6c666eba59284ce27a03d228756812ce33a5bbbb Mon Sep 17 00:00:00 2001 From: Alexandre Dumont Date: Sun, 28 Mar 2021 16:30:58 +0200 Subject: [PATCH] programmer cli: refactor argparsing --- programmer/README.md | 96 +++++++++++++++++-------- programmer/eeprom.py | 163 ++++++++++++++++++++++--------------------- 2 files changed, 152 insertions(+), 107 deletions(-) diff --git a/programmer/README.md b/programmer/README.md index 183469e..8fadaa2 100644 --- a/programmer/README.md +++ b/programmer/README.md @@ -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.....`......... @@ -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) diff --git a/programmer/eeprom.py b/programmer/eeprom.py index c8cb971..89514ac 100755 --- a/programmer/eeprom.py +++ b/programmer/eeprom.py @@ -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 @@ -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() \ No newline at end of file + args.func(args) + + wait_for_prompt() + ser.close() \ No newline at end of file