I found an edge case tonight. My checksum value came up as zero. Here is the rough update to the code to deal with it. The whole program needs an overhaul for optimization, but here is the working version:
#!/usr/bin/python2.6
from time import *
from uspp import uspp
import sys
class Programmer:
tty = None
prog = None
file = None
filename = None
portname = None
debug = False
ok = 'k'
failed = 'n'
dataStart = 9
rewriteAddrH = '0F' # each must be 2 characters
rewriteAddrL = 'FC'
startSignal = chr(0xc1)
def __init__(self, args ):
self.prog = args[0]
if self.prog.rfind( '/' ) >= 0:
self.prog = self.prog[self.prog.rfind('/')+1:]
if len(args) < 3:
print self.prog + ': usage:', self.prog, '/path/to/serial/port /path/to/myHex.hex [-d]'
exit(1)
if len( args ) >3 and args[3] == '-d':
self.debug = True
self.portname = args[1]
self.filename = args[2]
#
def writeProgram(self):
if self.debug == True:
print '\nProgrammer.writeProgram: PROGRAM: ' + self.prog + ' PORT: "' + self.portname + '" FILENAME: ' + self.filename + '\n'
self.openFile( self.filename )
if self.debug == True:
print 'Programmer.writeProgram: File open. Reading packets. . .\n'
packets = self.makePackets( self.file )
self.file.close()
if self.debug == True:
print '\nProgrammer.writeProgram: Packet dump:\n'
for packet in packets:
for c in packet:
val = hex(ord(c))[2:]
if len(val) < 2:
val = '0' + val
print val,
print ''
print '\nProgrammer.writeProgram: File read complete. Ready to reset pic . . .\n'
self.openPort(self.portname)
if self.debug == True:
print 'Programmer.writeProgram: port open. Awaiting start sequence. . .\n'
self.startSequence()
for packet in packets:
self.sendPacket(packet)
# how to end? The chip will time out waiting and reset itself.
print 'Programmer.writeProgram: Programming complete.'
# attempt to send a packet to the pic up to three times. Die on no response.
def sendPacket(self, packet ):
retries = 3
while retries >0:
if self.debug == True:
print 'Programmer.sendPacket: Sending packet...',
for c in packet:
print hex(ord(c)),
print ''
self.tty.write( packet )
while True:
status = self.endlessWait()
if 'k' == status:
print 'Programmer.sendPacket: Packet send successful.'
return
elif 'n' == status:
if self.debug == True:
print 'Programmer.sendPacket: Packet send unsuccessful. Retrying...'
retries = retries - 1
break
else:
print 'Programmer.sendPacket: An unexpected response from the microchip. Received: ', ord(status), 'Aborting. . .'
exit(1)
# if we get down here, we've failed
print 'Programmer.sendPacket: Failed to get the packet to the microchip successfully. Aborting. . . '
exit(1)
# opens the serial port. sets in in class variable and returns handle too
def openPort(self, portname ):
try:
self.tty = uspp.SerialPort( portname, 0, 38400)
self.tty.flush() # discard unread bytes
except Exception:
print 'Programmer.openPort: Could not open the serial port: ' + portname
exit(1)
# opens the hex file, stores file handle in class var and returns the file handle
def openFile(self, filename ):
try:
self.file = open( filename, 'r' )
except Exception:
print 'Programmer.openFile: Could not open file: ' + filename
exit(1)
return self.file
def makePackets(self, file ):
lines = file.readlines()
packets = []
for line in lines:
ind = line.find( '\n' )
if ind > 0:
line = line[:ind]
ni = line[1:3]
intNumInstr = int(ni,16)/2
# Need to put high and low together
# as a single hex value, divide by two, then split them.
addrValue = int(line[3:7],16) / 2
recordType = int(line[7:9],16)
packet = ''
# Record Type 0: data
# Record Type 1: end of file
# Record Type 2 & 3: Extended segment addressing. throw warning for us...
# Record Type 4: signals true 32 bit addressing. throw warning for us...
# Record Type 5: another extended address thingy. throw warning for us...
if recordType == 0: # This is a data line
# compute address high and low
addrH = ''
addrL = ''
if addrValue < 256:
addrH = '0'
addrL = hex( int( bin( addrValue ),2 ) )[2:] # take value as binary int & convert to hex string
else:
b = bin(addrValue)[2:] # get as binary string but trim off the '0b'
addrH = hex( int( b[:len(b)-8], 2 ) )[2:] # turn binary into --> int --> hex string and trim off '0x'
addrL = hex( int( b[len(b)-8:], 2 ) )[2:]
intAddrH = int( addrH, 16 )
intAddrL = int( addrL, 16 )
addrH = chr( intAddrH )
addrL = chr( intAddrL )
# Now deal with the address given
if intAddrH > 32:
print 'Programmer.makePackets: Not sure how to deal with data memory writes yet. Skipping. . .'
continue
# config bits will have address high of 32
elif intAddrH == 32:
if self.debug == True:
print 'Programmer.makePackets: Config bit instruction. skipping this line'
print '\t\t\tConfig bits can\'t be programmed by this bootloader.\n'
continue
# the bootloader will have an address space of 0xfff - 192 = 0xf1f
# this translates to an address high of 15 and address low of 31
elif intAddrH >= 15 and intAddrL >= 31:
print 'Programmer.makePackets: Your program is trying to overwrite the bootloader. Aborting...'
print '\tThis program will not allow you to overwrite the bootloader program. You will need to reprogram the chip if you wish to overwrite.'
exit(1)
# need to deal with the first 4 instructions
elif intAddrH == 0 and intAddrL == 0:
self.createFirstInstructionLines( packets, line, intNumInstr )
else:
packet = self.createRegularPacket( line, intNumInstr, addrH, addrL )
packets.append( packet )
elif recordType == 1: # end of file
if self.debug == True:
print '\nProgrammer.makePackets: Hex file read complete.'
elif recordType == 4: # print inhx32 warning
if self.debug == True:
print 'Programmer.makePackets: Found an indicator line to use 32 bit addressing. Compile with inhx8s or inhx8m. Ignoring this line.\n'
else:
if self.debug == True:
print 'Programmer.makePackets: Found lines with extended segment addressing. Skipping this line. . .\n'
return packets
def computeNumberOfInstructions(self, intNumInstr ):
computedNumInstr = 8
if intNumInstr <= 4:
computedNumInstr = 4
elif intNumInstr > 8:
print 'Programmer.makePackets: Number of instructions was greater than 8. Unexpected. Aborting. . .'
exit(1)
return computedNumInstr
def createFirstInstructionLines(self, packets, line, intNumInstr ):
""" This is an abysmal function and set of algorithms. Need to refactor to make use of redundant operations.
Eventually, the redundant operations in this and the createRegularPacket should be combined. For now, I just want it work."""
computedNumInstr = self.computeNumberOfInstructions( intNumInstr )
numInstr = chr( computedNumInstr )
# If less than 4 instructions, create special rewrite packet with what we have then pad instructions to 4.
# The crc will need to be computed to account for the special address.
# else if 4 or more, create rewrite packet for first 4
# calculate crc
if intNumInstr < 4:
#get the data for all the instructions
lineOne = line[self.dataStart:self.dataStart+4*intNumInstr]
numToPad = (4 - intNumInstr) * 2
for i in range(0,numToPad,1):
lineOne = lineOne + '00'
else:
# create the lineOne data for the first 4 instructions
lineOne = line[self.dataStart:self.dataStart+16]
# expect doubleAddress to return 1e82
crcOne = self.calculateChecksum( '04' + self.rewriteAddrH + self.rewriteAddrL + lineOne )
packetOne = self.makeChar( self.rewriteAddrH ) + self.makeChar( self.rewriteAddrL ) + self.makeChar( '04' )
for i in range( 0, len(lineOne), 4 ):
packetOne = packetOne + self.makeChar( lineOne[i:i+2] ) + self.makeChar( lineOne[i+2:i+4] )
packetOne = packetOne + self.makeChar( crcOne )
packets.append( packetOne )
# if more than 4 instructions and less than 9, create second packet with what's left
# calculate new crc. Address will be 0x04 for low
if computedNumInstr == 8:
start = self.dataStart + 16 #start at 5th instruction
end = len(line) - 2 # knock off old crc
lineTwo = line[start:end]
if intNumInstr < 8:
pad = computedNumInstr - intNumInstr
if pad > 0:
for i in range(0,pad,1):
lineTwo = lineTwo + '0000'
crcTwo = self.calculateChecksum( '04' + '0004' + lineTwo )
packetTwo = self.makeChar( '00' ) + self.makeChar( '04' ) + self.makeChar( '04' )
for i in range( 0, len(lineTwo), 4 ):
packetTwo = packetTwo + self.makeChar( lineTwo[i:i+2] ) + self.makeChar( lineTwo[i+2:i+4] )
packetTwo = packetTwo + self.makeChar( crcTwo )
packets.append( packetTwo )
def calculateChecksum(self, data ):
tot=0
for i in range( 2, len(data)+2, 2 ):
tot = tot + int( data[i-2:i], 16 )
s = bin( tot )
if len(s) <10:
s = s[2:]
else:
s = s[-8:]
hx = 0x100 - int(s,2)
crc = ''
if hx < 0x100:
crc = hex( hx )[2:]
else:
crc = hex( hx )[-2:]
return crc
def createRegularPacket(self, line, intNumInstr, addrH, addrL ):
# compute the number of instructions
computedNumInstr = self.computeNumberOfInstructions( intNumInstr )
numInstr = chr( computedNumInstr )
packet = '' + addrH + addrL + numInstr
# now add the data packets
for i in range( self.dataStart, len(line) - 4, 4 ):
packet = packet + self.makeChar( line[i:i+2] ) + self.makeChar( line[i+2:i+4] )
# need to add padding for short lines
pad = computedNumInstr - intNumInstr
if pad > 0:
for i in range(0,pad,1):
packet = packet + self.makeChar( '00' ) + self.makeChar( '00' )
# now add in the checksum
data = hex( computedNumInstr )[2:].zfill(2) + hex( ord(addrH) )[2:].zfill(2) + hex( ord(addrL) )[2:].zfill(2) + line[7:len(line)-2]
checksum = self.calculateChecksum( data )
# checksum = line[len(line)-2:]
packet = packet + self.makeChar( checksum )
return packet
def makeChar( self, str ):
return chr(int( '0x' + str, 16) )
def startSequence(self):
"""Go into endless wait, listening on port for the startSignal. When it receives the start signal, it
echoes it back to the pic. It then waits endlessly for the pic to send ok ('k'). It then returns."""
# self.waitForResponse( self.startSignal )
# self.endlessWait()
self.waitForStartDiscardJunk()
sleep(.002)
self.tty.write( self.startSignal )
print 'Programmer.startSequence: Sent the start signal to the pic.'
# for echo only, write ok back
# self.tty.write( self.ok )
self.waitForResponse( self.ok )
def waitForStartDiscardJunk(self):
c = ''
while True:
c = self.tty.read()
if self.startSignal == c:
return
elif '' != c:
print 'junk char received: ', c, 'ord:', ord(c)
def waitForResponse( self, charWeExpect ):
c = self.endlessWait()
if charWeExpect != c:
print 'Programmer.waitForResponse: We did not receive the character we were expecting. Looking for:', hex(ord(charWeExpect)), '. We received:', hex(ord(c)), '. Aborting.'
exit(0)
else:
ch = ''
try:
ch = c.decode('ascii')
except:
ch = hex( ord(c) )
print 'Programmer.waitForResponse: Got expected response:', ch
def endlessWait( self ):
c = ''
while True:
c = self.tty.read()
if '' != c:
return c
if __name__ == '__main__':
programmer = Programmer( sys.argv )
programmer.writeProgram()
No comments:
Post a Comment