#!/usr/bin/python # # spreadspace pic utils # # # Copyright (C) 2011 Christian Pointner # # This file is part of spreadspace pic utils. # # spreadspace pic utils 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 # any later version. # # spreadspace pic utils 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 spreadspace pic utils. If not, see . # '''spreadspace simple pic downloader.''' VERSION_MAJ = 0 VERSION_MIN = 1 def open_serial(device, baud): import os import tty import termios print "opening %s (%s Baud)" % (device, baud) baudrates = { 50: termios.B50, 75: termios.B75, 110: termios.B110, 134: termios.B134, 150: termios.B150, 200: termios.B200, 300: termios.B300, 600: termios.B600, 1200: termios.B1200, 1800: termios.B1800, 2400: termios.B2400, 4800: termios.B4800, 9600: termios.B9600, 19200: termios.B19200, 38400: termios.B38400, 57600: termios.B57600, 115200: termios.B115200, 230400: termios.B230400 } baudreate = termios.B19200 try: baudrate = baudrates[int(baud)] except (KeyError, ValueError): print "ERROR: invalid baudrate" sys.exit(3) try: dev = os.open(device, os.O_RDWR | os.O_NOCTTY) tty.setraw(dev, termios.TCSAFLUSH) tio = termios.tcgetattr(dev) tio[4] = tio[5] = baudrate termios.tcsetattr(dev, termios.TCSAFLUSH, tio) termios.tcflush(dev, termios.TCIFLUSH) return dev except OSError, msg: print "ERROR: opening serial device: %s" % msg sys.exit(3) except termios.error, msg: print "ERROR: configuring serial device: %s" % msg sys.exit(3) def load_hex(file): fin = file if fin == '-': fin = sys.stdin elif not os.path.isfile(fin): print "ERROR: File not found: %s" % fin sys.exit(1) codedata = {} ih = IntelHex(fin) for a in ih.addresses(): if a/2 not in codedata.keys(): codedata[a/2] = 0 if a%2 == 0: codedata[a/2] += ih[a] else: codedata[a/2] += (ih[a] << 8) return codedata def get_lowest_flash_addr(codedata, fss): lowest_code_addr = sorted(codedata.keys())[0] return lowest_code_addr - (lowest_code_addr%fss) def get_highest_flash_addr(codedata, fss): highest_code_addr = sorted(codedata.keys())[-1] return highest_code_addr + (fss - highest_code_addr%fss) def create_flash_image(codedata, fss, sa, ea): img = ( sa, [0xFFFF]*(ea-sa) ) for a,d in codedata.items(): img[1][a-sa] = d return img def create_flash_segments(codedata, fss): sa = get_lowest_flash_addr(codedata, fss) ea = get_highest_flash_addr(codedata, fss) img = create_flash_image(codedata, fss, sa, ea) for i in xrange(0, ea, fss): slice = img[1][i:i+fss] if not all( (elem == 0xFFFF) for elem in slice): yield (i, slice) def calc_csum(str): cs = 0 for c in str: cs ^= ord(c) return chr(cs) def exec_command(dev, cmd, answer): import struct return_codes = { 0: "OK", 1: "invalid command", 2: "bad checksum", 3: "not implemented", 4: "flash write error", 5: "address invalid", 6: "address prohibited" } answer = "=cB" + answer + "B" answer_len = struct.calcsize(answer) cstr = cmd + calc_csum(cmd) os.write(dev, cstr) astr = "" while len(astr) < answer_len: astr += os.read(dev, answer_len - len(astr)) data = list(struct.unpack(answer, astr)) code = data.pop(0) ret = data.pop(1) checksum = data.pop() if code != cmd[0]: print "ERROR: bootloader returned wrong command code" sys.exit(4) if ret != 0: rstr = "invalid return code" try: rstr = return_codes[ret] except KeyError: pass print "ERROR: bootloader returned %d: %s" % (ret, rstr) sys.exit(4) if 0 != calc_csum(astr): print "ERROR: checksum error" sys.exit(4) return data ### commands def identify(dev): data = exec_command(dev, "i", "BB10sHBB") id = { 'ver_maj': data[0], 'ver_min': data[1], 'name': data[2], 'devid': data[3], 'fss': data[4], 'supported': data[5] } if id['ver_maj'] != VERSION_MAJ: print "Incomaptible protocol version, expected: %d, got: %d" % (VERSION_MAJ, id['ver_maj']) sys.exit(4) print "connected with Bootloader '%s', (ID=%04X, FSS=%d)" % (id['name'], id['devid'], id['fss']) return id ### main if __name__ == '__main__': import getopt import sys import os from intelhex import IntelHex usage = '''spreadspace simple pic downloader. Usage: python downloader.py [options] INFILE Arguments: INFILE name of hex file for downloading. Use '-' for reading from stdin. Options: -h, --help this help message. -v, --version version info. --device=N the serial port to use (default: /dev/ttyUSB0). --baud=N baudrate to use (default: 19200). ''' device = "/dev/ttyUSB0" baudrate = 19200 try: opts, args = getopt.getopt(sys.argv[1:], "hv", ["help", "version", "device=", "baud="]) for o, a in opts: if o in ("-h", "--help"): print(usage) sys.exit(0) elif o in ("-v", "--version"): print("Version %d.%d" % (VERSION_MAJ, VERSION_MIN)) sys.exit(0) elif o in ("--device"): device = a elif o in ("--baud"): baudrate = a if not args: raise getopt.GetoptError('Input file is not specified') if len(args) > 1: raise getopt.GetoptError('Too many arguments') except getopt.GetoptError, msg: print "ERROR: %s" % msg print usage sys.exit(2) dev = open_serial(device, baudrate) codedata = load_hex(args[0]) id = identify(dev) for segment in create_flash_segments(codedata, id['fss']): print "%05X: %s" % (segment[0], ''.join('%04X'%i for i in segment[1]))