Open colbro51 opened 2 years ago
Finally success at last! By writing myself a dummy serial interface and examining the commands being sent to the K150 I was able to convince myself that by only putting the rom code in the hex file and using the command line fuse overrides and forgetting about the ID value it should work when I commented out the erase, eeprom programming and verification code. By reading the device after programming I was able to verify the write. I attach my dummy serial interface code "cbserial.py".
#
# Serial wrapper - locate in site-packages/picpro directory
#
# Modify picpro.py:
# line 40: "import serial" --> "from picpro.cbserial import Serial"
# line 186: "s = serial.Serial" --> "s = Serial"
# Modify ProtocolInterface.py
# line 4: "import serial" --> "from picpro.cbserial import Serial"
#
# Command documentation: http://www.kitsrus.com/zip/softprotocol5.zip
#
import pprint
import sys
import serial
import easygui as eg
class Serial(serial.Serial):
is_open = True
_port_handle = None
timeout = 0
def __init__(self, *args, **kargs):
self.rcvd = '__init__'
self.header = 'Hello programmer!'.encode('utf-8')
self.socket = False
self.progRom = False
self.rom = []
def read(self, number):
# Choose either display for single stepping or log for file output
display = True
log = False
if self.progRom:
if self.progsize:
if len(self.rcvd) != 32:
eg.exceptionbox('Expected 32 bytes in prog mode')
title = '32 bytes prog data of '+str(self.progsize)
self.progsize -= 32
response = b'Y'
self.rom.append(self.rcvd)
else:
title = 'finished prog'
response = b'P'
self.progRom = False
elif self.rcvd == '__init__':
self.rcvd = b''
response = b'B\x03'
title = 'K150 Power-Up'
elif self.rcvd == b'\x01':
title = 'Command end / Escape jump table to command start'
response = b'Q'
elif self.rcvd == b'P':
title = 'Goto jump table from command start'
response = b'P'
elif self.rcvd and self.rcvd in self.header:
title = 'Echo character'
response = self.rcvd
display = False
elif self.rcvd == b'\x12':
title = 'Detect chip in socket - no'
response = b'A'
self.socket = True
elif self.socket:
title = 'Detect chip in socket - yes'
response = b'Y'
self.socket = False
elif self.rcvd == b'\x04':
title = 'Turn on programming voltages'
response = b'V'
elif self.rcvd == b'\x02\x00\x00\x00\x04\x01\x01\x02\x00\x01\x00':
title = 'Initialise programming variables'
response = b'I'
elif self.rcvd == b'\x0b':
title = 'Read ROM'
if not self.rom:
# a real example value for MOVLW instruction as returned
response = (b'\xff\x0f')*(number//4 - 1) + b'\x74' + b'\x0C'
else:
# CORRECT RESPONSE here commented out
# response = b''.join(self.rom)[:-2] + b'\x74' + b'\x0C'
response = b''.join(self.rom)
elif self.rcvd == b'\x05':
title = 'Turn off programming voltages'
response = b'v'
elif self.rcvd == b'\x0e':
title = 'Erase chip'
response = b'Y'
elif self.rcvd == b'\x06':
title = 'Program ROM'
response = b'\xff' # not checked
elif self.rcvd == b'\x02\x00':
title = 'ROM size: word High/Low'
response = b'Y'
self.progRom = True
self.progsize = 512*2 # bytes = 2*words
elif self.rcvd.startswith(b'00') and self.rcvd.endswith(b'\xff\xff\xff\xff'):
response = b'Y'
title = 'Program ID fuses'
else:
title = 'Unknown command?'
response = b'\xff'*number
msg = 'Rcvd: %s; Want %s bytes; Rtn: %s' % \
(repr(self.rcvd), number, repr(response))
if display:
if not eg.ccbox(msg, title):
sys.exit(0)
elif log:
with open('k150.log','a') as fobj:
fobj.write(title+'\n')
pprint.pprint(msg, stream=fobj)
fobj.write('\n')
self.rcvd = b''
return response
def write(self, data):
self.rcvd = data
For anyone interested in programming the 12C508A, here is a GUI frontend:
#
# Simple GUI interface to picpro for PIC12C508A
# Execute from Linux bash shell:> python3 picPgmr.py
#
"""
Location: 0x1FF (PIC12C508A) contains the MOVLW XX
INTERNAL RC oscillator calibration value.
!!! DO NOT ALTER THIS PRESET VALUE !!!
ID Locations @ 0x200: ( 0x400 in Intel hex file )
Four memory locations are designated as ID locations
where the user can store checksum or other code identification numbers.
Use only the lower 4 bits of the ID locations and
always program the upper 8 bits as ’0’s.
Config word @ 0xFFF: ( 0x1FFE in Intel hex file )
bit 4: MCLRE: MCLR enable bit.
1 = MCLR pin enabled
0 = MCLR tied to VDD, (Internally)
bit 3: CP: Code protection bit.
1 = Code protection off
0 = Code protection on
bit 2: WDTE: Watchdog timer enable bit
1 = WDT enabled
0 = WDT disabled
bit 1-0: FOSC1:FOSC0: Oscillator selection bits
11 = EXTRC - external RC oscillator
10 = INTRC - internal RC oscillator
01 = XT oscillator
00 = LP oscillator
"""
import os, subprocess, sys
import easygui as eg
import PySimpleGUI as sg
DUMPF = '._dump_.dat'
HEXF = '._prog_.hex'
SOCKET = '''
1+---\_/---+40
2: :39
3: :38
4: :37
5: :36 Insert PIC into K150
6: :35 programming socket
7: :34 as shown here !!!
8: :33
9: :32 Pin 1 of PIC is in
10: :31 pin 13 of socket.
11: :30
12: :29
13:-:1\_/-:-:28
14:-: PIC :-:27
15:-: 12C :-:26
16:-: 508A:-:25
17: :24
18: :23
19: :22
20:_________:21'''
K150 = '\n'*3 + '\n'.join(' '*15+line for line in SOCKET.split('\n'))+'\n'*4
class gui():
def __init__(self):
self.setup()
self.source = ''
self.code = [int('0xfff',16)]*512
self.flags = []
self.page()
self.loop()
def setup(self):
if not os.path.exists('picPgmr.cfg'):
self.picdir = eg.diropenbox('PIC development root directory')
with open('picPgmr.cfg','w') as fobj:
fobj.write(self.picdir)
else:
self.picdir = open('picPgmr.cfg','r').read()
def fuses(self):
layout = [[sg.Text('Osc:', size=(8,1), tooltip='CPU clock'),
sg.InputCombo(('IntRC','ExtRC','LP','XT'),size=(6,1),default_value='IntRC',key='OSC')],
[sg.Text('WDT:', size=(8,1), tooltip='Watchdog timer'),
sg.InputCombo(('Off','On'),default_value='Off',key='WDT')],
[sg.Text('MCLR:', size=(8,1), tooltip='MCLR pin enabled'),
sg.InputCombo(('Off','On'),default_value='Off',key='MCLR')],
[sg.Text('CP:', size=(8,1), tooltip='Code Protect'),
sg.InputCombo(('Off','On'),default_value='Off',key='CP')],
[sg.Text('ID:', size=(8,1), tooltip='Identification'),
sg.InputText('', size=(5,1), key='ID')],
[sg.Text('CkSum:', size=(8,1), tooltip='Checksum'),
sg.Text(' '*8,key='CS')]]
return sg.Frame('Fuses',layout)
def functions(self):
column = [[sg.Button('LoadHex',key=':OpenProg')],
[sg.Button('Program', key=':Program')],
[sg.Button('Verify', key=':Verify')],
[sg.Button('Read', key=':Read')],
[sg.Button('BlankCheck', key=':IsBlank')],
[sg.Button('SaveHex', key=':SaveHex')],
[sg.Button('Exit', key=':Close')]]
layout = sg.Frame('Functions', layout=column)
return layout
def display(self):
lines = []
for ndx1 in range(64):
line = []
line.append('0'+self.hexdigits(8*ndx1,3)+': ')
for ndx2 in range(8):
ndx = 8*ndx1 + ndx2
line.append(self.hexdigits(self.code[ndx],3))
if ndx2 in [3, 7]:
line.append('')
for ndx2 in range(8):
ndx = 8*ndx1 + ndx2
byte = self.code[ndx] % 256
if chr(byte).isprintable():
line.append(chr(byte))
else:
line.append('.')
lines.append(' '.join(line))
return '\n'.join(lines)
def data(self):
layout = [[sg.Frame(title='Program Code', key='panel1',
layout=[[sg.Multiline(default_text=K150,\
font='Courier', key='ml1', size=(60, 28))]]),
sg.Frame(title='Select Hex Data File', key='panel2', visible=False,
layout=[[sg.Listbox(values=[], key='lb2', size=(60,28),
font='Courier', enable_events=True)]])]]
return sg.Column(layout)
def control(self):
column = [[self.fuses()],
[self.functions()]]
layout = sg.Column(column)
return layout
def page(self):
layout = [[self.data(), self.control()]]
self.window = sg.Window('PIC12C508A Programmer', layout)
def loop(self):
self.exit = False
timeOut = 5000
while True:
event, self.values = self.window.read(timeout=timeOut)
if not event:
self.window.close()
return
if event == '__TIMEOUT__':
self.window.find_element('ml1').Update(self.display())
self.updateCS()
if event and event[0]==':':
eval('self.'+event[1:]+'()')
if event == 'lb2':
self.OpenProg2()
if self.exit:
self.window.close()
return
timeOut = None
def OpenProg(self):
fileList = []
for root, dirs, files in os.walk(self.picdir):
for name in files:
if name.endswith('.hex') and not name.startswith('.'):
fileList.append(os.path.join(root,name).replace('\\','/'))
if fileList:
self.window.find_element('panel1').Update(visible=False)
self.window.find_element('panel2').Update(visible=True)
self.window.find_element('lb2').Update(values=fileList)
else:
sg.popup('No .hex files under PIC root directory')
os.remove('picPgmr.cfg')
self.setup()
def OpenProg2(self):
self.source = self.values['lb2'][0]
with open(self.source, 'r') as fobj:
data = fobj.read()
if not data:
sg.popup('File '+os.path.basename(self.source)+' is empty!')
return
self.window.find_element('panel2').Update(visible=False)
if self.loadCode(data):
self.window.find_element('ml1').Update(self.display())
self.window.find_element('panel1').Update(visible=True)
def loadCode(self, data):
info = self.unpack(data)
if not info:
return
#code
for ndx in range(0, 1024, 2):
pair = 256*info[ndx+1]+info[ndx]
self.code[ndx//2] = pair % 4096
#id
IDloc = int('0x400', 16)
IDval = (hex(info[IDloc])[-1]+hex(info[IDloc+2])[-1]+\
hex(info[IDloc+4])[-1]+hex(info[IDloc+6])[-1]).upper()
self.window.find_element('ID').Update(IDval)
#config
CFGloc = int('0x1FFE', 16)
Config = bin(info[CFGloc])[::-1]
switch = ('Off', 'On')
oscval = {'00': 'LP', '01': 'XT', '10': 'IntRC', '11': 'ExtRC'}
mclr = switch[Config[4] == '1']
cp = switch[Config[3] == '0']
wdt = switch[Config[2] == '1']
osc = oscval[Config[1]+Config[0]]
self.window.find_element('MCLR').Update(mclr)
self.window.find_element('CP').Update(cp)
self.window.find_element('WDT').Update(wdt)
self.window.find_element('OSC').Update(osc)
self.updateCS()
return True
def updateCS(self):
# checksum (https://www.microchip.com/forums/m788738.aspx & just use code checksum)
csum = 0
for ndx in range(len(self.code)):
csum += self.code[ndx]
csum = csum % int('0xFFFF', 16)
self.window.find_element('CS').Update(self.hexdigits(csum,4))
self.window.find_element('ID').Update(self.hexdigits(csum,4))
def unpack(self, data):
lines = data.replace('\r','').split('\n')
info = [255] * 8192
for line in lines:
if line[0] != ':':
sg.popup('Invalid hex file format, missing ":"')
return
try:
byteCount = int('0x'+line[1:3], 16)
address = int('0x'+line[3:7], 16)
tt = int('0x'+line[7:9], 16)
if tt == 1:
break
if tt == 0:
for ndx in range(0, 2*byteCount, 2):
info[address + ndx//2] = \
int('0x'+line[9 + ndx]+line[9 + ndx+1], 16)
cksum = 0
for ndx in range(0, len(line[1:]), 2):
cksum += int('0x'+line[1+ndx:3+ndx], 16)
if cksum % 256 != 0:
sg.popup('Checksum failure')
return
except:
sg.popup('Invalid hex format')
return
return info
def Program(self):
if os.path.exists(HEXF):
os.remove(HEXF)
self.generateHex(HEXF, full=False)
cmd = 'picpro program -p /dev/ttyUSB0 -i '+HEXF+' -t 12C508A '+\
' '.join(self.flags)
self.runCommand(cmd)
def generateHex(self, outfile, full=True):
#code
hexdata = []
osccal, self.code[511] = self.code[511], 4095
# self.code in words (2*bytes), intel hex addressing in bytes
for ndx in range(0, 512, 4):
if self.code[ndx:ndx+4] == [4095]*4:
continue
else:
if ndx < 508:
head = ':08'+self.hexdigits(2*ndx,4)+'00'
data = [self.hexword(n) for n in self.code[ndx:ndx+4]]
else:
# we do not want to overwrite osccal MOVLW XXX instruction
head = ':06'+self.hexdigits(2*ndx,4)+'00'
data = [self.hexword(n) for n in self.code[ndx:ndx+3]]
line = head+''.join(data)
line += self.makeCS(line)
hexdata.append(line.upper())
self.code[511] = osccal
#ID
ID = self.values['ID']
if ID:
line = ':08040000'+self.hexword(int('0x'+ID[0],16))+\
self.hexword(int('0x'+ID[1],16))+\
self.hexword(int('0x'+ID[2],16))+\
self.hexword(int('0x'+ID[3],16))
line += self.makeCS(line)
if full:
hexdata.append(line.upper())
#fuses
config = ['1']*8
if self.values['MCLR'] == 'Off':
config[4] = '0'
self.flags.append('--fuse=MCLRE:Disabled')
else:
self.flags.append('--fuse=MCLRE:Enabled')
if self.values['CP'] == 'On':
config[3] = '0'
self.flags.append('"--fuse=Code Protect:Enabled"')
else:
self.flags.append('"--fuse=Code Protect:Disabled"')
if self.values['WDT'] == 'Off':
config[2] = '0'
self.flags.append('--fuse=WDT:Disabled')
else:
self.flags.append('--fuse=WDT:Enabled')
if self.values['OSC'] == 'IntRC':
config[0] = '0'
self.flags.append('--fuse=Oscillator:IN_RC')
if self.values['OSC'] == 'XT':
config[1] = '0'
self.flags.append('--fuse=Oscillator:XT')
if self.values['OSC'] == 'LP':
config[0] = '0'
config[1] = '0'
self.flags.append('--fuse=Oscillator:LP')
if self.values['OSC'] == 'ExtRC':
self.flags.append('--fuse=Oscillator:EX_RC')
fuses = hex(int('0b'+''.join(config[::-1]),2))[2:]
line = ':021FFE00'+fuses+'0F'
line += self.makeCS(line)
if full:
hexdata.append(line.upper())
hexdata.append(':00000001FF\n')
with open(outfile, 'w') as fobj:
fobj.write('\n'.join(hexdata))
def makeCS(self, line):
cksum = 0
for ndx in range(0, len(line[1:]), 2):
cksum += int('0x'+line[1+ndx:3+ndx], 16)
cksum = 256 - (cksum % 256)
return self.hexdigits(cksum,2)
def hexdigits(self, num, digits):
return ('000'+hex(num)[2:]).upper()[-digits:]
def hexword(self, num):
hi, lo = divmod(num, 256)
return self.hexdigits(lo,2) + self.hexdigits(hi,2)
def Verify(self):
self.codeSave = self.code.copy()
if not self.Read(update=False):
sg.popup('Verification check failed')
return
for ndx in range(len(self.code)-1):
if self.code[ndx] != self.codeSave[ndx]:
sg.popup('First difference at location: 0x'+self.hexdigits(ndx,3))
self.code = self.codeSave.copy()
return
if int('0xC00', 16) <= self.code[-1] < int('0xD00', 16):
sg.popup('Verification check successful')
else:
sg.popup('Bad last MOVLW instruction: 0x'+self.hexdigits(self.code[-1],3))
self.code = self.codeSave.copy()
def Read(self, checkBlank=False, update=True):
if os.path.exists(DUMPF):
os.remove(DUMPF)
cmd = 'picpro dump rom -p /dev/ttyUSB0 -b '+DUMPF+' -t 12C508A'
self.runCommand(cmd)
if not os.path.exists(DUMPF):
sg.popup('No dump file created so read failed!')
return False
data = open(DUMPF,'rb').read()
if checkBlank:
for ndx in range(0, len(data)-2, 2): # skip OSCCAL word
val = 256*(data[ndx])+data[ndx+1]
if val != 4095:
sg.popup('Chip is not blank!')
return False
sg.popup('Chip is blank!')
return 1
for ndx in range(0, len(data), 2):
self.code[ndx//2] = 256*(data[ndx])+data[ndx+1]
if update:
self.window.find_element('ml1').Update(self.display())
self.updateCS()
return True
def IsBlank(self):
self.Read(checkBlank=True)
def SaveHex(self):
outfile = eg.enterbox('Enter output .hex filename')
if not outfile.endswith('.hex'):
outfile = outfile + '.hex'
if self.source:
outfile = os.path.join(os.path.dirname(self.source),outfile)
self.generateHex(outfile)
def Close(self):
self.exit=True
def runCommand(self,cmd):
print('*'*80)
print(cmd)
print('-'*80)
subprocess.call(cmd, shell=True)
print ('='*80)
if __name__ == '__main__':
G = gui()
Hi again
I finally got around trying to actually program a chip with the following hex file data:
:080000002500C70C0200660098 :080008000B0C0600380A690028 :080010006800E802090AE90298 :08001800080A0008050C2A008B :080020000709EA02100A0008BA :08002800190C2A000709EA0285 :08003000100A0008650C2B000A :080038003C0C2A000709EA0252 :080040001E0AEB021C0A000875 :0800480068008605A8026607A6 :08005000260A860400082409B9 :08005800400C2B006A000802B5 :080060002900E902310AEA025D :080068002F0AEB022E0A00082A :080070000E090606460AA6056A :080078002B09A6041409380A43 :080080001A0946050E094604A9 :080088002606400A0A0C2700BD :080090000607480A06064A0AA9 :0800980046050709460426068F :0800A000400AA6052B09A60485 :0800A8001A09E702480A4605A7 :0800B000070946042606400A78 :0800B8001A09570AFF0FFF0FA0 :080400000800080009000100DA :021FFE00EA0FE8 :00000001FF
and the program failed as below:
`picpro program -p /dev/ttyUSB0 -i .prog.hex -t 12C508A
Waiting for user to insert chip into socket with pin 1 at socket pin 13 Chip detected. Traceback (most recent call last): File "/home/colin/.local/bin/picpro", line 8, in
sys.exit(main())
File "/home/colin/.local/lib/python3.8/site-packages/picpro/bin/picpro.py", line 434, in main
getattr(command, 'chosen')() # Execute the function specified by the user.
File "/home/colin/.local/lib/python3.8/site-packages/picpro/bin/picpro.py", line 98, in program
program_pic(
File "/home/colin/.local/lib/python3.8/site-packages/picpro/bin/picpro.py", line 365, in program_pic
rom_data, eeprom_data, id_data, fuse_values = prepare_flash_data_from_hex_file(chip_info, hex_file, pic_id, fuses)
File "/home/colin/.local/lib/python3.8/site-packages/picpro/bin/picpro.py", line 294, in prepare_flash_data_from_hex_file
rom_data = merge_records(rom_records, rom_blank)
File "/home/colin/.local/lib/python3.8/site-packages/picpro/tools.py", line 59, in merge_records
raise IndexError('Record out of range.')
IndexError: Record out of range.`
Working through the code I found that the ID and fuse data is ending up in the rom data, thus the "record out of range". If I remove both of these from my hex file as a test then the error does not occur. The program does not appear to correctly interpret the hex addressing of ID and flags as per the PIC documentation.
However with this test another issue arises as the output then comes back with "Erasing chip". This chip is not erasable! I would suggest the chipinfo flag is not being checked.
Cheers Colin