Just made the changes to up the speed to 8MHz at 19200 baud. Still flawless performance and no rewrites. Tried loading my robot code (317 words ) and it flew in about as fast or faster than my programmer. And I didn't have to unplug anything!!! I'll eventually add in support for programming data memory as well. I don't have a need for it as of now, so no hurry.
I noticed that I left a small gaff in the python code. The python code doesn't check the startSignal value to make sure it is what I was waiting for. I was getting some off values once in awhile. Once this happened, it kept happening until I had rebooted my laptop. My guess is that it is either something to do with the startup of the USART on the pic or the buffer on my PC's USART. Or it could be a bug in the Python uspp code... I'm not too stressed about it. The rest of the code is pretty bullet proof for version 0.0.1.
Now that it is finally working, I can't help tweaking it. ;}
EDIT:
Still pushing things. I removed the gaff. I now expect the pic to send the correct start signal. It's working fine. I upped the baud rate to 38.4k baud. At 8MHz internal, the datasheet says the error rate is +0.16%. So far: flawless.
Search This Blog
Sunday, November 21, 2010
BootLoader - Python Code for PC
Still rough looking. Could use a lot of clean up, optimization, etc.
#/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, 9600)
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[len(s)-8:]
return hex( 0x100 - int(s,2) )[2:]
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()
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 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()
#/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, 9600)
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[len(s)-8:]
return hex( 0x100 - int(s,2) )[2:]
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()
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 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()
BootLoader - Pic Code
Here is the Pic code for the bootloader. Hope it copies ok.
;******************************************************************************
;
; Filename: bootloader.asm
; Date: 2010.11.21
; File Version: 1.0.0
;
; Author: Tom Hunt
;
;******************************************************************************
; NOTES: A bootloader for 16f88. Runs at 9600 baud at 4 MHz currently.
; Relies on Python code icsp.py to run. Reserves the last 224 bytes
; For the bootloader.
;
; PC_flash: 0xC1 nr AddrH AddrL ...(DataLo DataHi)... crc
; PIC: 0xC1 k k
;******************************************************************************
; CHANGE LOG:
;
;
;******************************************************************************
list R=DEC, p=16f88 ; list directive to define processor
#include "p16f88.inc"
__CONFIG _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_ON & _PWRTE_ON & _WDT_OFF & _INTRC_IO
__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF
;******************************************************************************
; CONSTANT DEFINITIONS
;******************************************************************************
#define maxFlash 0x1000
#define bootLoader (maxFlash - 224) ;
#define userFirstFour (maxFlash - 32)
#define oneBefore (userFirstFour - 1 )
#define rewriteAddrH 0x00F
#define rewriteAddrL 0x0E0
#define startSignal 0xc1
;errorlevel 1, -302
;******************************************************************************
; VARIABLE DEFINITIONS
;******************************************************************************
cblock 0x20
buffer:80
endc
cblock 0x70
crc
counter
i
cnt1
cnt2
cnt3
flag
endc
;******************************************************************************
; MACRO DEFINITIONS
;******************************************************************************
bank0_no_status MACRO
clrf STATUS
ENDM
bank0 MACRO
bcf STATUS, RP1
bcf STATUS, RP0
ENDM
bank1 MACRO
bcf STATUS, RP1
bsf STATUS, RP0
ENDM
bank2 MACRO
bsf STATUS, RP1
bcf STATUS, RP0
ENDM
bank3 MACRO
bsf STATUS, RP1
bsf STATUS, RP0
ENDM
;******************************************************************************
; PROGRAM RESET
;******************************************************************************
ORG 0x0000
PAGESEL TomBootloader
GOTO TomBootloader
;******************************************************************************
; BOOTLOADER CODE
;******************************************************************************
ORG bootLoader
Initb
bank2
movwf counter ; # of loops
clrf flag
movlw 0x000
movwf EEADR^0x100 ; prepare flash adress
movwf EEADRH^0x100
movlw buffer ; prepare FSR at index 0
movwf FSR
return
writeloop ; write 2 bytes = 1 instruction
movf INDF,W
movwf EEDATA^0x100
incf FSR,F
movf INDF,W
movwf EEDATH^0x100
incf FSR,F
bank3
bsf EECON1^0x180,EEPGD ; FOR NOW, NO DATA. ALWAYS PROGRAM SPACE
bsf EECON1^0x180,WREN
clrf INTCON ; shouldn't need to, but disable all interrupts
movlw 0x55
movwf EECON2^0x180
movlw 0xaa
movwf EECON2^0x180
bsf EECON1^0x180,WR
nop
nop
waitwre
btfsc EECON1^0x180,WR ;for eeprom writes (wait to finish write)
goto waitwre
bcf EECON1^0x180,WREN
bank2
incf EEADR^0x100,F ;does not cross zones
btfsc STATUS,Z ; if we overflow, increment addr high
incf EEADRH^0x100,F
decfsz counter,F
goto writeloop
return
;******************************************************************************
; USART ROUTINES
;******************************************************************************
sendByte
movwf TXREG
bank1
btfss TXSTA^0x080,TRMT ; when TRMT is set, transmission is completed
goto $ - 1
bank0_no_status
return
; This is the same as my usual delay code. cnt gets loaded with 3 for 4MHz ~1.5 sec delay
Receive
bank0 ;_no_status ; puts us in bank0
movlw 0x03
movwf cnt1
rpt2
clrf cnt2
rpt3
clrf cnt3
rptc
btfss PIR1,RCIF ;test RX
goto $ + 8 ; endless wait for byte
btfss RCSTA,OERR
goto $ + 2
bcf RCSTA,CREN
bsf RCSTA,CREN
movf RCREG,W ;return in W
addwf crc,f ;compute crc
return
decfsz cnt3,F
goto rptc
decfsz cnt2,F
goto rpt3
decfsz cnt1,F
goto rpt2
;timeout:
way_to_exit ;exit in all other cases; must be BANK0/1
bank1
bcf RCSTA,SPEN ; deactivate USART
bank0_no_status
goto programMain
;******************************************************************************
; MAINLINE CODE
;******************************************************************************
TomBootloader
;init serial port
bank0_no_status
clrf INTCON ; disable all interupts
bank1
movlw 0x00 ^ ( 1 << 2 ) ; Port B all output except RX on B2
movwf TRISB ^ 0x080
movlw b'01100010' ; bits for OSCCON internal clock at 4MHz
movwf OSCCON^0x080
; set up bank 1 part of AUSART
movlw 0x19 ; decimal 25 at 4 MHz for 9600 baud
movwf SPBRG^0x080
; txsta gets b'00100100' enables asynch transmit and fast(brgh)
movlw 0x00 ^ ( ( 1 << TXEN ) + ( 1 << BRGH ) )
movwf TXSTA ^ 0x080
bank0_no_status ; puts us in bank0
; rcsta gets b'10010000' enables serial port and continuous receive
movlw 0x00 ^ ( ( 1 << SPEN ) + ( 1 << CREN ) )
movwf RCSTA
movlw startSignal ; send startSignal to begin process
call sendByte
;wait for computer
call Receive
sublw startSignal ;Expect C1
skpz
goto way_to_exit
;******************************************************************************
; ERASE ROUTINES
;******************************************************************************
; for the 16F87/88, flash memory must be erased before writing
; erasing can only be done by blocks of 32 words, with address = integer x 32
; so : 1. save the 4 first words in buffer
; 2. erase all the user memory (0x000 to 0F40)
; 3. erase the block containing the final jump (block at 0xF80)
; 4. rewrite the 4 first words from buffer
; 5. write the user program & data in flash or eeprom
; first read the 4 first words of program
movlw 4 ; and copy them back after bulkerase
call Initb
loopread ; (or direct fill of 8 bytes in buffer)
bank3
bsf EECON1^0x180,EEPGD ; select flash program memory
bsf EECON1^0x180,RD ; select read
nop
nop
bank2 ; now in bank 2
movf EEDATA^0x100,W ; store bytes in buffer
movwf INDF
incf FSR,F
movf EEDATH^0x100,W
movwf INDF
incf FSR,F
incf EEADR^0x100,F
decfsz counter,F
goto loopread
bulkErase ; erase 120 blocks of 32 words
movlw bootLoader/32
call Initb ;
loopErase
call eraseBlock
decfsz counter,F
goto loopErase
movlw rewriteAddrH
movwf EEADRH^0x100
movlw rewriteAddrL
movwf EEADR^0x100
call eraseBlock ; and erase block where user's first 4 go
goto writeJump
eraseBlock
bank3 ; now in bank 3
bcf EECON1^0x180,RD
bsf EECON1^0x180,EEPGD ; select flash program memory
bsf EECON1^0x180,WREN
bsf EECON1^0x180,FREE
clrf INTCON ; shouldn't need to, but disable all interrupts
movlw 0x55
movwf EECON2^0x180
movlw 0xAA
movwf EECON2^0x180
bsf EECON1^0x180,WR ; erase 1 block
nop
nop
bcf EECON1^0x180,WREN
bcf EECON1^0x180,FREE
bank2 ; now in bank 2
movlw 32 ; add 32 to flash memory pointer
addwf EEADR^0x100,F
btfsc STATUS,C ; if we overflow, increment addr high
incf EEADRH^0x100,F
return
writeJump ; rewrite the 4 first words of bootloader program
movlw 4
call Initb
call writeloop
;******************************************************************************
; RECEIVE PACKETS ROUTINES
;******************************************************************************
MainLoop ; back to original program
bank0_no_status ; puts us in bank0
movlw 'k'
call sendByte
mainl
clrf crc
call Receive ;H
bank2
movwf EEADRH^0x100
;movwf flag ;used to detect if is eeprom NOT USED FOR NOW...
call Receive ;L
bank2 ;bank2
movwf EEADR^0x100
call Receive ;counter = numInstr
movwf counter
movwf i
;incf i,F
movlw buffer ;reset FSR to index 0
movwf FSR
receivePacket ; put the data packet and crc into buffer
call Receive
movwf INDF
incf FSR,F
call Receive
movwf INDF
incf FSR,F
decfsz i,F
goto receivePacket
call Receive ; get the crc
movwf INDF
movf crc,f ;check crc
skpz
goto checksumError ;write
bank2
movlw buffer ; reset FSR to index 0
movwf FSR
call writeloop
goto MainLoop
checksumError
movlw 'n'
call sendByte
goto mainl
;******************************************************************************
; SPACE FOR USER'S FIRST FOUR INSTRUCTIONS CODE
;******************************************************************************
ORG oneBefore
programMain
; This is where the program's first 4 instructions will go.
; The 4 nops will be overwritten with the first 4 instructions of the
; user's program
; The next 28 instructions are wasted space, needed for the 32 byte erase
clrf PCLATH ; reset to first page, so loaded program's
ORG userFirstFour
; first 4 instructions can execute
nop
nop
nop
nop
;*************************************************************
; After reset
; Do not expect the memory to be zero,
; Do not expect registers to be initialised like in catalog.
END
;******************************************************************************
;
; Filename: bootloader.asm
; Date: 2010.11.21
; File Version: 1.0.0
;
; Author: Tom Hunt
;
;******************************************************************************
; NOTES: A bootloader for 16f88. Runs at 9600 baud at 4 MHz currently.
; Relies on Python code icsp.py to run. Reserves the last 224 bytes
; For the bootloader.
;
; PC_flash: 0xC1 nr AddrH AddrL ...(DataLo DataHi)... crc
; PIC: 0xC1 k k
;******************************************************************************
; CHANGE LOG:
;
;
;******************************************************************************
list R=DEC, p=16f88 ; list directive to define processor
#include "p16f88.inc"
__CONFIG _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_ON & _PWRTE_ON & _WDT_OFF & _INTRC_IO
__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF
;******************************************************************************
; CONSTANT DEFINITIONS
;******************************************************************************
#define maxFlash 0x1000
#define bootLoader (maxFlash - 224) ;
#define userFirstFour (maxFlash - 32)
#define oneBefore (userFirstFour - 1 )
#define rewriteAddrH 0x00F
#define rewriteAddrL 0x0E0
#define startSignal 0xc1
;errorlevel 1, -302
;******************************************************************************
; VARIABLE DEFINITIONS
;******************************************************************************
cblock 0x20
buffer:80
endc
cblock 0x70
crc
counter
i
cnt1
cnt2
cnt3
flag
endc
;******************************************************************************
; MACRO DEFINITIONS
;******************************************************************************
bank0_no_status MACRO
clrf STATUS
ENDM
bank0 MACRO
bcf STATUS, RP1
bcf STATUS, RP0
ENDM
bank1 MACRO
bcf STATUS, RP1
bsf STATUS, RP0
ENDM
bank2 MACRO
bsf STATUS, RP1
bcf STATUS, RP0
ENDM
bank3 MACRO
bsf STATUS, RP1
bsf STATUS, RP0
ENDM
;******************************************************************************
; PROGRAM RESET
;******************************************************************************
ORG 0x0000
PAGESEL TomBootloader
GOTO TomBootloader
;******************************************************************************
; BOOTLOADER CODE
;******************************************************************************
ORG bootLoader
Initb
bank2
movwf counter ; # of loops
clrf flag
movlw 0x000
movwf EEADR^0x100 ; prepare flash adress
movwf EEADRH^0x100
movlw buffer ; prepare FSR at index 0
movwf FSR
return
writeloop ; write 2 bytes = 1 instruction
movf INDF,W
movwf EEDATA^0x100
incf FSR,F
movf INDF,W
movwf EEDATH^0x100
incf FSR,F
bank3
bsf EECON1^0x180,EEPGD ; FOR NOW, NO DATA. ALWAYS PROGRAM SPACE
bsf EECON1^0x180,WREN
clrf INTCON ; shouldn't need to, but disable all interrupts
movlw 0x55
movwf EECON2^0x180
movlw 0xaa
movwf EECON2^0x180
bsf EECON1^0x180,WR
nop
nop
waitwre
btfsc EECON1^0x180,WR ;for eeprom writes (wait to finish write)
goto waitwre
bcf EECON1^0x180,WREN
bank2
incf EEADR^0x100,F ;does not cross zones
btfsc STATUS,Z ; if we overflow, increment addr high
incf EEADRH^0x100,F
decfsz counter,F
goto writeloop
return
;******************************************************************************
; USART ROUTINES
;******************************************************************************
sendByte
movwf TXREG
bank1
btfss TXSTA^0x080,TRMT ; when TRMT is set, transmission is completed
goto $ - 1
bank0_no_status
return
; This is the same as my usual delay code. cnt gets loaded with 3 for 4MHz ~1.5 sec delay
Receive
bank0 ;_no_status ; puts us in bank0
movlw 0x03
movwf cnt1
rpt2
clrf cnt2
rpt3
clrf cnt3
rptc
btfss PIR1,RCIF ;test RX
goto $ + 8 ; endless wait for byte
btfss RCSTA,OERR
goto $ + 2
bcf RCSTA,CREN
bsf RCSTA,CREN
movf RCREG,W ;return in W
addwf crc,f ;compute crc
return
decfsz cnt3,F
goto rptc
decfsz cnt2,F
goto rpt3
decfsz cnt1,F
goto rpt2
;timeout:
way_to_exit ;exit in all other cases; must be BANK0/1
bank1
bcf RCSTA,SPEN ; deactivate USART
bank0_no_status
goto programMain
;******************************************************************************
; MAINLINE CODE
;******************************************************************************
TomBootloader
;init serial port
bank0_no_status
clrf INTCON ; disable all interupts
bank1
movlw 0x00 ^ ( 1 << 2 ) ; Port B all output except RX on B2
movwf TRISB ^ 0x080
movlw b'01100010' ; bits for OSCCON internal clock at 4MHz
movwf OSCCON^0x080
; set up bank 1 part of AUSART
movlw 0x19 ; decimal 25 at 4 MHz for 9600 baud
movwf SPBRG^0x080
; txsta gets b'00100100' enables asynch transmit and fast(brgh)
movlw 0x00 ^ ( ( 1 << TXEN ) + ( 1 << BRGH ) )
movwf TXSTA ^ 0x080
bank0_no_status ; puts us in bank0
; rcsta gets b'10010000' enables serial port and continuous receive
movlw 0x00 ^ ( ( 1 << SPEN ) + ( 1 << CREN ) )
movwf RCSTA
movlw startSignal ; send startSignal to begin process
call sendByte
;wait for computer
call Receive
sublw startSignal ;Expect C1
skpz
goto way_to_exit
;******************************************************************************
; ERASE ROUTINES
;******************************************************************************
; for the 16F87/88, flash memory must be erased before writing
; erasing can only be done by blocks of 32 words, with address = integer x 32
; so : 1. save the 4 first words in buffer
; 2. erase all the user memory (0x000 to 0F40)
; 3. erase the block containing the final jump (block at 0xF80)
; 4. rewrite the 4 first words from buffer
; 5. write the user program & data in flash or eeprom
; first read the 4 first words of program
movlw 4 ; and copy them back after bulkerase
call Initb
loopread ; (or direct fill of 8 bytes in buffer)
bank3
bsf EECON1^0x180,EEPGD ; select flash program memory
bsf EECON1^0x180,RD ; select read
nop
nop
bank2 ; now in bank 2
movf EEDATA^0x100,W ; store bytes in buffer
movwf INDF
incf FSR,F
movf EEDATH^0x100,W
movwf INDF
incf FSR,F
incf EEADR^0x100,F
decfsz counter,F
goto loopread
bulkErase ; erase 120 blocks of 32 words
movlw bootLoader/32
call Initb ;
loopErase
call eraseBlock
decfsz counter,F
goto loopErase
movlw rewriteAddrH
movwf EEADRH^0x100
movlw rewriteAddrL
movwf EEADR^0x100
call eraseBlock ; and erase block where user's first 4 go
goto writeJump
eraseBlock
bank3 ; now in bank 3
bcf EECON1^0x180,RD
bsf EECON1^0x180,EEPGD ; select flash program memory
bsf EECON1^0x180,WREN
bsf EECON1^0x180,FREE
clrf INTCON ; shouldn't need to, but disable all interrupts
movlw 0x55
movwf EECON2^0x180
movlw 0xAA
movwf EECON2^0x180
bsf EECON1^0x180,WR ; erase 1 block
nop
nop
bcf EECON1^0x180,WREN
bcf EECON1^0x180,FREE
bank2 ; now in bank 2
movlw 32 ; add 32 to flash memory pointer
addwf EEADR^0x100,F
btfsc STATUS,C ; if we overflow, increment addr high
incf EEADRH^0x100,F
return
writeJump ; rewrite the 4 first words of bootloader program
movlw 4
call Initb
call writeloop
;******************************************************************************
; RECEIVE PACKETS ROUTINES
;******************************************************************************
MainLoop ; back to original program
bank0_no_status ; puts us in bank0
movlw 'k'
call sendByte
mainl
clrf crc
call Receive ;H
bank2
movwf EEADRH^0x100
;movwf flag ;used to detect if is eeprom NOT USED FOR NOW...
call Receive ;L
bank2 ;bank2
movwf EEADR^0x100
call Receive ;counter = numInstr
movwf counter
movwf i
;incf i,F
movlw buffer ;reset FSR to index 0
movwf FSR
receivePacket ; put the data packet and crc into buffer
call Receive
movwf INDF
incf FSR,F
call Receive
movwf INDF
incf FSR,F
decfsz i,F
goto receivePacket
call Receive ; get the crc
movwf INDF
movf crc,f ;check crc
skpz
goto checksumError ;write
bank2
movlw buffer ; reset FSR to index 0
movwf FSR
call writeloop
goto MainLoop
checksumError
movlw 'n'
call sendByte
goto mainl
;******************************************************************************
; SPACE FOR USER'S FIRST FOUR INSTRUCTIONS CODE
;******************************************************************************
ORG oneBefore
programMain
; This is where the program's first 4 instructions will go.
; The 4 nops will be overwritten with the first 4 instructions of the
; user's program
; The next 28 instructions are wasted space, needed for the 32 byte erase
clrf PCLATH ; reset to first page, so loaded program's
ORG userFirstFour
; first 4 instructions can execute
nop
nop
nop
nop
;*************************************************************
; After reset
; Do not expect the memory to be zero,
; Do not expect registers to be initialised like in catalog.
END
Working Bootloader
Finally! I got my bootloader code working. It's pretty quick, too. The basic outline ended up looking like this:
The Pic code is written in Asm. The PC code is written in Python, making use of USPP as ascribed earlier. The tinyBld code I looked at was an ok starting point. I ended up rewriting a lot of it. It ended up using 224 bytes of space for a 198 word program.
The startup sequence goes like this: On power up or reset, the first four words pageselect and go to the bootloader at the bottom of the program space. This space initializes the USART and immediately sends a start signal ( 0xC1 ) at 9600 baud. It waits for a little over a one second. If it does not receive the start signal back, it drops to one byte before the user's first four space. This is in the last 32 bytes of program space and holds the first four words of the last loaded program. The byte before the user's space does a pageselect back to page zero. The loaded program then executes.
Assuming you want to load code, you wire up TX, RX, VCC, and GND from an RS232 shifter hooked to the PC. I use a USB cable hooked to a serial to USB converter to the shifter. Fire up the board. Wait 2 seconds. Launch the PC program, called icsp.py. I found I had to set the userid bit on python for this to work. ( Yes, I know the risk and unset it when done ). The icsp program takes a few arguments. The first is the path to the serial port. For my linux machines with the serial to USB, this is /dev/ttyUSB0 ( or one, or two, etc ). The second argument is the location of the hex file you wish to load. The last argument is the debug. This prints out verbose logging as to what is occurring.
The program will load the file and create packets for transmission. This involves "undoubling" a lot of the values the hex file creates. It computes a new checksum value for each line/packet. It keeps the user's program from overwriting the bootloader. It intercepts the first four instructions if they are in 0x00-0x04. It rewrites them in the user's first four area. If there are additional instructions on this line, it creates a new line with address 0x0004 and a new checksum. When this load is complete, the command line lets you know it is listening for the start signal from the Pic.
Hit the reset button ( or power off and on ). The Pic will jump to the bootloader code and send the start signal. The icsp receives this, echoes it back, and goes into and endless wait for the 'Ok' signal ( 'k' ). The Pic, meanwhile, prepares for a new program. It copies the first four instructions from its program space to a buffer. This is the code that jumps to the bootloader. It then erases the program space up to the bootloader. It also rewrites the 32 bytes from the user's first four space. The 16f88 I'm working with has to erase blocks of 32 and wastes 28 bytes of space because of it. Once the erase is done, it rewrites the bootload first four to 0x00-0x04. It then sends the 'k' to the PC and goes into a one second receive delay, waiting for the first line.
The PC loops through the list of packets and sends each to the Pic. The Pic receives each byte, storing the address in the EEADR and EEADRH registers. It stores the data in a buffer. It knows how many data packets are coming because the number of instructions is sent as part of the packet. When the read is complete, it writes the packet to the program space at the address indicated. Wash, rinse, repeat. The pic calculates the checksum. If it doesn't match the sent checksum, it sends a 'n' to the PC for another attempt. The PC will retry three times per line. When the last line has been sent, the Pic will time out waiting for the next line and go to the pageselect and user's first four.
Now I can reprogram my robot in circuit without a lot of rewiring!!! Huzzah!
I'll post code after I convert it.
The Pic code is written in Asm. The PC code is written in Python, making use of USPP as ascribed earlier. The tinyBld code I looked at was an ok starting point. I ended up rewriting a lot of it. It ended up using 224 bytes of space for a 198 word program.
The startup sequence goes like this: On power up or reset, the first four words pageselect and go to the bootloader at the bottom of the program space. This space initializes the USART and immediately sends a start signal ( 0xC1 ) at 9600 baud. It waits for a little over a one second. If it does not receive the start signal back, it drops to one byte before the user's first four space. This is in the last 32 bytes of program space and holds the first four words of the last loaded program. The byte before the user's space does a pageselect back to page zero. The loaded program then executes.
Assuming you want to load code, you wire up TX, RX, VCC, and GND from an RS232 shifter hooked to the PC. I use a USB cable hooked to a serial to USB converter to the shifter. Fire up the board. Wait 2 seconds. Launch the PC program, called icsp.py. I found I had to set the userid bit on python for this to work. ( Yes, I know the risk and unset it when done ). The icsp program takes a few arguments. The first is the path to the serial port. For my linux machines with the serial to USB, this is /dev/ttyUSB0 ( or one, or two, etc ). The second argument is the location of the hex file you wish to load. The last argument is the debug. This prints out verbose logging as to what is occurring.
The program will load the file and create packets for transmission. This involves "undoubling" a lot of the values the hex file creates. It computes a new checksum value for each line/packet. It keeps the user's program from overwriting the bootloader. It intercepts the first four instructions if they are in 0x00-0x04. It rewrites them in the user's first four area. If there are additional instructions on this line, it creates a new line with address 0x0004 and a new checksum. When this load is complete, the command line lets you know it is listening for the start signal from the Pic.
Hit the reset button ( or power off and on ). The Pic will jump to the bootloader code and send the start signal. The icsp receives this, echoes it back, and goes into and endless wait for the 'Ok' signal ( 'k' ). The Pic, meanwhile, prepares for a new program. It copies the first four instructions from its program space to a buffer. This is the code that jumps to the bootloader. It then erases the program space up to the bootloader. It also rewrites the 32 bytes from the user's first four space. The 16f88 I'm working with has to erase blocks of 32 and wastes 28 bytes of space because of it. Once the erase is done, it rewrites the bootload first four to 0x00-0x04. It then sends the 'k' to the PC and goes into a one second receive delay, waiting for the first line.
The PC loops through the list of packets and sends each to the Pic. The Pic receives each byte, storing the address in the EEADR and EEADRH registers. It stores the data in a buffer. It knows how many data packets are coming because the number of instructions is sent as part of the packet. When the read is complete, it writes the packet to the program space at the address indicated. Wash, rinse, repeat. The pic calculates the checksum. If it doesn't match the sent checksum, it sends a 'n' to the PC for another attempt. The PC will retry three times per line. When the last line has been sent, the Pic will time out waiting for the next line and go to the pageselect and user's first four.
Now I can reprogram my robot in circuit without a lot of rewiring!!! Huzzah!
I'll post code after I convert it.
Thursday, October 28, 2010
Pics of My Bot
Here is my robot with a little makeover. I call him CDR ( Compact Disc Robot ). Got a new smartphone this week, so I can finally post some pics and later vids.
Front View:
Bottom view ( Power supplies and gear motor ):
Circuit Board( reusable as dev board ):
Front View:
Bottom view ( Power supplies and gear motor ):
Circuit Board( reusable as dev board ):
Saturday, October 23, 2010
Random Ramblings
Hmmm... Finally got some parts from Sparkfun. I can now program my chips from my laptop thanks to the usb to serial dongle. Hoorah! I got the Sharp IR receiver working by reading over the ADC channel on AN1.
Worked on the Python code some more. I now have a class to load the hex file and start the serial port. The next step is to write the code to communicate with the pic. from the pic side, I'll write my program and the bootloader_test all at once. The bootloader_test will get called on reset, listen for the start and timeout after 1 sec. If it gets the start signal, it will read in each line and spit it back to the PC.
If that goes well, I'll move on to making it write the new program *shudder*. For some reason, this makes me a bit nervous.
Other parts I got from Sparkfun will allow me to wire up the LCD board. I'll let it be powered by another board. I think I'll use the 16C84 chip I have laying around. I now have a 10MHz ceramic that I can use for it. I just need to convert the 16F84A code at 20MHz to work with it.
I should also wire up a board for USART from the PC. Before I do that, I'll probably get a DB9 part that has right angles to a board. Forgot to get that on my last order...
Worked on the Python code some more. I now have a class to load the hex file and start the serial port. The next step is to write the code to communicate with the pic. from the pic side, I'll write my program and the bootloader_test all at once. The bootloader_test will get called on reset, listen for the start and timeout after 1 sec. If it gets the start signal, it will read in each line and spit it back to the PC.
If that goes well, I'll move on to making it write the new program *shudder*. For some reason, this makes me a bit nervous.
Other parts I got from Sparkfun will allow me to wire up the LCD board. I'll let it be powered by another board. I think I'll use the 16C84 chip I have laying around. I now have a 10MHz ceramic that I can use for it. I just need to convert the 16F84A code at 20MHz to work with it.
I should also wire up a board for USART from the PC. Before I do that, I'll probably get a DB9 part that has right angles to a board. Forgot to get that on my last order...
Sunday, August 1, 2010
Bootloader - Send and Receive Protocol
Worked on the bootloader a bit this weekend. This was my first time using the built in AUSART modules of the 16f88 rather than bit-banging routines. Got the pic to transmit pretty quickly at 8MHz internal clock with data rate of 19,200. Then I wrote a simple python program to simulate the protocol I wanted for the bootloader. The program fires up and starts sending the start bit, 0xC1, over and over. After each send, it listens for 20ms for a response of 'K'. It times out after one minute. On the pic side, the program fires up the USART in ansynch mode and listens for 1 second for the start code. If it hears it, it validates that it is the right code. It sends 'K' back, and it then transmits its message ( 'Hello, world!', of course ).
The programs looked great, but they were not working. My most bone-headed move of late, I forgot to hook up the receiving wire! Incidentally, I use some transistors, resistors, and a diode for my converter. I tried the Max232 chip for this, but I got way too much noise on the circuit. I really need to figure that out sometime....
Anyway, the program works great. Below is the code for posterity. I hope it comes through clean this time. As mentioned in previous posts, the Python code uses the uspp code for serial transmission. Great bit of work. I am indebted to the developer for saving me the trouble of learning raw serial transmission in Linux. And it will work with Windoze.
Edit: Looking over past posts, I realized I changed direction again. I am writing my bootloader from whole cloth, stealing from the tinyBld code. I am not going to bit bang. I'll use the built in modules. Robot code will shift the motors to RB0 and 1 and RB7 and 6, leaving RB5(TX) and 2(RX) free for ICSP bootloading.
Pic Code:
;******************************************************************************
;
; Filename: periph.asm
; Date: 20010.07.31
; File Version: 1.0.0
;
; Author: Tom Hunt
;
;******************************************************************************
; NOTES: Echo some ascii over serial to PC usart.
; Use the built-in usart module.
;
; Pic 16f88 running at 8MHz
; data rate: 19200 with spbrg high
;
;******************************************************************************
; CHANGE LOG:
; Backed by a python program (talk.py), the python program sends the start
; char every 1ms and then listens for ~20ms. The pic fires up and listens
; for the start char. When it hears it and validates that it is the
; start char, it sends back a 'K'. The python prog hears the 'K' and
; goes into eternal listen mode. The pic sends the message, 'Hello, world!'
; The python prog prints this to the screen.
;
;******************************************************************************
list R=DEC, p=16f88 ; list directive to define processor
#include <p16f88.inc> ; processor specific variable definitions
;Program Configuration Register 1
__CONFIG _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_ON & _PWRTE_ON & _WDT_OFF & _INTRC_IO
;Program Configuration Register 2
__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF
errorlevel 1, -302 ; suppress Register in operand not in bank 0 -- Get rid of this during development
;******************************************************************************
; VARIABLE DEFINITIONS
;******************************************************************************
#define STARTSIGNAL 0xC1
CBLOCK 0x20
count
buffer:20
i
tIndex
ENDC
CBLOCK 0x70 ; interupt context-saving vars here see datasheet chp 15
w_temp
status_temp
pclath_temp
ENDC
;******************************************************************************
; MACRO DEFINITIONS *
;******************************************************************************
bank0 MACRO
bcf STATUS, RP1
bcf STATUS, RP0
ENDM
bank1 MACRO
bcf STATUS, RP1
bsf STATUS, RP0
ENDM
;******************************************************************************
; RESET VECTOR *
;******************************************************************************
ORG 0x000 ; processor reset vector
nop
nop
nop
goto main ; go to beginning of program
;******************************************************************************
; INTERRUPT VECTOR
;******************************************************************************
ORG 0x004 ; interrupt vector location
MOVWF w_temp ;Copy W to TEMP register
SWAPF STATUS, W ;Swap status to be saved into W
CLRF STATUS ;bank 0, regardless of current bank, Clears IRP,RP1,RP0
MOVWF status_temp ;Save status to bank zero STATUS_TEMP register
MOVF PCLATH, W ;Only required if using page 1
MOVWF pclath_temp ;Save PCLATH into W
CLRF PCLATH ;Page zero, regardless of current page
;
;(ISR) ;(Insert user code here)
;
MOVF pclath_temp, W ;Restore PCLATH
MOVWF PCLATH ;Move W into PCLATH
SWAPF status_temp, W ;Swap STATUS_TEMP register into W
;(sets bank to original state)
MOVWF STATUS ;Move W into STATUS register
SWAPF w_temp, F ;Swap W_TEMP
SWAPF w_temp, W ;Swap W_TEMP into W
retfie ; return from interrupt
;******************************************************************************
; INITIALIZATION CODE
;******************************************************************************
; no interrupts, all digital i/o, 8MHz internal clock
; usart configured for 19,200 data rate
; brgh set to high speed
; spbrg = clock / ( dataRate * 16 * ( 4^0 ) ) - 1
; spbrg = 8e6 / ( 19200 * 16 * 1 ) - 1 = 25.042
init
bank0
clrf INTCON
clrf ADCON0 ; all digital
bank1
movlw 0x00
movwf ANSEL ; go all digital
movlw b'01110010' ; bits for OSCCON internal clock at 8MHz 0111 0000
movwf OSCCON^0x080
movlw 0x19 ; decimal 25
movwf SPBRG^0x080
; txsta gets b'00100100' enables asynch transmit and fast(brgh)
movlw 0x00 ^ ( ( 1 << TXEN ) + ( 1 << BRGH ) )
movwf TXSTA ^ 0x080
; tmr0 set up
movlw 0x00 ^ ( ( 1 << PS2 ) + ( 1 << PS1 ) + ( 1 << PS0 ) ) ; b'00000111' tmr0 prescaler 1:128 ( and the 2x factor )=256
movwf OPTION_REG ^ 0x080
bank0
; rcsta gets b'10010000' enables serial port and continuous receive
movlw 0x00 ^ ( ( 1 << SPEN ) + ( 1 << CREN ) )
movwf RCSTA
return
;******************************************************************************
; RS232 CODE
;******************************************************************************
; receive tries to receive a byte for one second from RCREG
receive
movlw 0x28 ; count = 40
movwf count
timeIt
; tmr0 will give ~25ms @ 8MHz -> 256*195= 49920 instrs
movlw 0x03D ; 256-195=61
movwf TMR0
testRcReg
btfsc PIR1,RCIF ; if set, new byte in RCREG
goto gotAByte
movf TMR0,W
btfss STATUS,Z ; wait for timeout
goto testRcReg
decrementCount
decfsz count,F
goto timeIt
; if we get here, we've timed out after 1 second
goto errorAndExit
gotAByte
movf RCREG,W
return
sendByte
movwf TXREG
bank1
btfss TXSTA,TRMT ; when TRMT is set, transmission is completed
goto $ - 1
bank0
return
;******************************************************************************
; MAINLINE CODE
;******************************************************************************
main
call init
bank1
movlw 0x00 ^ ( 1 << 2 )
movwf TRISB ^0x080 ; enable all PORTB for output except PORTB,2 ( RX )
movlw b'11111011' ; set PORTA to Input except for A2
movwf TRISA ^ 0x080
bank0
waitForSignal
movlw 0x00 ; init W reg
call receive
sublw STARTSIGNAL
btfss STATUS,Z
goto errorAndExit
movlw 'K'
call sendByte
sendMsg
movlw 0
movwf i
sayHi
call table
iorlw 0
btfsc STATUS, Z ; if end of table, end
goto done
call sendByte
incf i, F
movf i, W ; increment index
goto sayHi
errorAndExit ; placeholder
bank0
bcf RCSTA,SPEN ; desactivate UART
clrf PCLATH
done
goto $
;******************************************************************************
; TABLE DATA
;******************************************************************************
table
movwf tIndex ; save table index
movlw HIGH tableEntries ; get the current high page
movwf PCLATH ; put this in PCLATH to move us to the right page
movf tIndex, W ; move the index asked for into w
addlw LOW tableEntries ; compute the offset for the block
btfsc STATUS, C ;
incf PCLATH, F ; if in next page, increment PCLATH
movwf PCL ; put the correct addr in PCL
tableEntries
dt "Hello, world!\0"
END
Python Code:
#!/usr/bin/python
from uspp import *
from time import sleep as sleep
startByte = 0xC1
ok = 'K'
def init( tty ):
tty = SerialPort("/dev/ttyS0", 0, 19200 )
tty.flush() # discard unread bytes
return tty
def sendStart( tty ):
print 'Sending start byte ', 0xC1
gotStart = False
ch = ''
count=800
while count > 0:
tty.write( chr( startByte ) )
tries = 20
while tries > 0:
ch = tty.read()
if ok == ch:
gotStart = True
return True
sleep(.001)
tries = tries - 1
print count, ' ch=\'', ch, '\''
count = count - 1
print ''
if True == gotStart:
print 'Got ok from pic'
else:
print 'Never got ok back from pic'
return gotStart
def listen( tty ):
# ignore timeouts and read
print 'Endless listen:'
ch = ''
try:
while True:
ch = tty.read()
if ch != '':
print ch
except SerialPortException:
pass
def main():
print 'Starting...'
tty = None
tty = init( tty )
ready = sendStart( tty )
if True == ready:
listen( tty )
else:
print 'Never got ready. Aborting...'
tty.__del__()
main()
The programs looked great, but they were not working. My most bone-headed move of late, I forgot to hook up the receiving wire! Incidentally, I use some transistors, resistors, and a diode for my converter. I tried the Max232 chip for this, but I got way too much noise on the circuit. I really need to figure that out sometime....
Anyway, the program works great. Below is the code for posterity. I hope it comes through clean this time. As mentioned in previous posts, the Python code uses the uspp code for serial transmission. Great bit of work. I am indebted to the developer for saving me the trouble of learning raw serial transmission in Linux. And it will work with Windoze.
Edit: Looking over past posts, I realized I changed direction again. I am writing my bootloader from whole cloth, stealing from the tinyBld code. I am not going to bit bang. I'll use the built in modules. Robot code will shift the motors to RB0 and 1 and RB7 and 6, leaving RB5(TX) and 2(RX) free for ICSP bootloading.
Pic Code:
;******************************************************************************
;
; Filename: periph.asm
; Date: 20010.07.31
; File Version: 1.0.0
;
; Author: Tom Hunt
;
;******************************************************************************
; NOTES: Echo some ascii over serial to PC usart.
; Use the built-in usart module.
;
; Pic 16f88 running at 8MHz
; data rate: 19200 with spbrg high
;
;******************************************************************************
; CHANGE LOG:
; Backed by a python program (talk.py), the python program sends the start
; char every 1ms and then listens for ~20ms. The pic fires up and listens
; for the start char. When it hears it and validates that it is the
; start char, it sends back a 'K'. The python prog hears the 'K' and
; goes into eternal listen mode. The pic sends the message, 'Hello, world!'
; The python prog prints this to the screen.
;
;******************************************************************************
list R=DEC, p=16f88 ; list directive to define processor
#include <p16f88.inc> ; processor specific variable definitions
;Program Configuration Register 1
__CONFIG _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_ON & _PWRTE_ON & _WDT_OFF & _INTRC_IO
;Program Configuration Register 2
__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF
errorlevel 1, -302 ; suppress Register in operand not in bank 0 -- Get rid of this during development
;******************************************************************************
; VARIABLE DEFINITIONS
;******************************************************************************
#define STARTSIGNAL 0xC1
CBLOCK 0x20
count
buffer:20
i
tIndex
ENDC
CBLOCK 0x70 ; interupt context-saving vars here see datasheet chp 15
w_temp
status_temp
pclath_temp
ENDC
;******************************************************************************
; MACRO DEFINITIONS *
;******************************************************************************
bank0 MACRO
bcf STATUS, RP1
bcf STATUS, RP0
ENDM
bank1 MACRO
bcf STATUS, RP1
bsf STATUS, RP0
ENDM
;******************************************************************************
; RESET VECTOR *
;******************************************************************************
ORG 0x000 ; processor reset vector
nop
nop
nop
goto main ; go to beginning of program
;******************************************************************************
; INTERRUPT VECTOR
;******************************************************************************
ORG 0x004 ; interrupt vector location
MOVWF w_temp ;Copy W to TEMP register
SWAPF STATUS, W ;Swap status to be saved into W
CLRF STATUS ;bank 0, regardless of current bank, Clears IRP,RP1,RP0
MOVWF status_temp ;Save status to bank zero STATUS_TEMP register
MOVF PCLATH, W ;Only required if using page 1
MOVWF pclath_temp ;Save PCLATH into W
CLRF PCLATH ;Page zero, regardless of current page
;
;(ISR) ;(Insert user code here)
;
MOVF pclath_temp, W ;Restore PCLATH
MOVWF PCLATH ;Move W into PCLATH
SWAPF status_temp, W ;Swap STATUS_TEMP register into W
;(sets bank to original state)
MOVWF STATUS ;Move W into STATUS register
SWAPF w_temp, F ;Swap W_TEMP
SWAPF w_temp, W ;Swap W_TEMP into W
retfie ; return from interrupt
;******************************************************************************
; INITIALIZATION CODE
;******************************************************************************
; no interrupts, all digital i/o, 8MHz internal clock
; usart configured for 19,200 data rate
; brgh set to high speed
; spbrg = clock / ( dataRate * 16 * ( 4^0 ) ) - 1
; spbrg = 8e6 / ( 19200 * 16 * 1 ) - 1 = 25.042
init
bank0
clrf INTCON
clrf ADCON0 ; all digital
bank1
movlw 0x00
movwf ANSEL ; go all digital
movlw b'01110010' ; bits for OSCCON internal clock at 8MHz 0111 0000
movwf OSCCON^0x080
movlw 0x19 ; decimal 25
movwf SPBRG^0x080
; txsta gets b'00100100' enables asynch transmit and fast(brgh)
movlw 0x00 ^ ( ( 1 << TXEN ) + ( 1 << BRGH ) )
movwf TXSTA ^ 0x080
; tmr0 set up
movlw 0x00 ^ ( ( 1 << PS2 ) + ( 1 << PS1 ) + ( 1 << PS0 ) ) ; b'00000111' tmr0 prescaler 1:128 ( and the 2x factor )=256
movwf OPTION_REG ^ 0x080
bank0
; rcsta gets b'10010000' enables serial port and continuous receive
movlw 0x00 ^ ( ( 1 << SPEN ) + ( 1 << CREN ) )
movwf RCSTA
return
;******************************************************************************
; RS232 CODE
;******************************************************************************
; receive tries to receive a byte for one second from RCREG
receive
movlw 0x28 ; count = 40
movwf count
timeIt
; tmr0 will give ~25ms @ 8MHz -> 256*195= 49920 instrs
movlw 0x03D ; 256-195=61
movwf TMR0
testRcReg
btfsc PIR1,RCIF ; if set, new byte in RCREG
goto gotAByte
movf TMR0,W
btfss STATUS,Z ; wait for timeout
goto testRcReg
decrementCount
decfsz count,F
goto timeIt
; if we get here, we've timed out after 1 second
goto errorAndExit
gotAByte
movf RCREG,W
return
sendByte
movwf TXREG
bank1
btfss TXSTA,TRMT ; when TRMT is set, transmission is completed
goto $ - 1
bank0
return
;******************************************************************************
; MAINLINE CODE
;******************************************************************************
main
call init
bank1
movlw 0x00 ^ ( 1 << 2 )
movwf TRISB ^0x080 ; enable all PORTB for output except PORTB,2 ( RX )
movlw b'11111011' ; set PORTA to Input except for A2
movwf TRISA ^ 0x080
bank0
waitForSignal
movlw 0x00 ; init W reg
call receive
sublw STARTSIGNAL
btfss STATUS,Z
goto errorAndExit
movlw 'K'
call sendByte
sendMsg
movlw 0
movwf i
sayHi
call table
iorlw 0
btfsc STATUS, Z ; if end of table, end
goto done
call sendByte
incf i, F
movf i, W ; increment index
goto sayHi
errorAndExit ; placeholder
bank0
bcf RCSTA,SPEN ; desactivate UART
clrf PCLATH
done
goto $
;******************************************************************************
; TABLE DATA
;******************************************************************************
table
movwf tIndex ; save table index
movlw HIGH tableEntries ; get the current high page
movwf PCLATH ; put this in PCLATH to move us to the right page
movf tIndex, W ; move the index asked for into w
addlw LOW tableEntries ; compute the offset for the block
btfsc STATUS, C ;
incf PCLATH, F ; if in next page, increment PCLATH
movwf PCL ; put the correct addr in PCL
tableEntries
dt "Hello, world!\0"
END
Python Code:
#!/usr/bin/python
from uspp import *
from time import sleep as sleep
startByte = 0xC1
ok = 'K'
def init( tty ):
tty = SerialPort("/dev/ttyS0", 0, 19200 )
tty.flush() # discard unread bytes
return tty
def sendStart( tty ):
print 'Sending start byte ', 0xC1
gotStart = False
ch = ''
count=800
while count > 0:
tty.write( chr( startByte ) )
tries = 20
while tries > 0:
ch = tty.read()
if ok == ch:
gotStart = True
return True
sleep(.001)
tries = tries - 1
print count, ' ch=\'', ch, '\''
count = count - 1
print ''
if True == gotStart:
print 'Got ok from pic'
else:
print 'Never got ok back from pic'
return gotStart
def listen( tty ):
# ignore timeouts and read
print 'Endless listen:'
ch = ''
try:
while True:
ch = tty.read()
if ch != '':
print ch
except SerialPortException:
pass
def main():
print 'Starting...'
tty = None
tty = init( tty )
ready = sendStart( tty )
if True == ready:
listen( tty )
else:
print 'Never got ready. Aborting...'
tty.__del__()
main()
Subscribe to:
Posts (Atom)


