従来に公開していたSHマイコンのフラッシュライターでは、Windows7以降の環境やUbuntu18以降の環境では動作しないケースがありました。
このような環境でも動作するように新規にPython3でGUI版フラッシュライターを開発しました。
対象となるマイコンは、SH3/SH4/SH4A、SH7084、SH7216、SH7124となっており、Ubuntu21とWIndows10で動作確認をしました。
シリアルポートは自動検出しますので、複数ある場合はプルダウンリストで選択します。
loadボタンで書き込むファイルを選択し、writeボタンでマイコンボードのフラッシュに書き込みます。
このときにマイコンボードのターゲットは自動検出します。
起動は
$ python3 flush.py -g
で起動します。
また、Pythonのツールにより、このスクリプトは単一の実行ファイルに変換できますので、簡単に再配布可能といなっています。
import serial
from serial.tools import list_ports
import sys
from sys import exit
import os
from os.path import expanduser
import struct
import glob
import tkinter as tk
from tkinter import ttk
import tkinter.filedialog
BIT_RATE_SET_MAX = 256
BIT_SET_RES = 0xaa
BIT_RATE_SET_MAX_INTER = 30
BIT_SET_RES_INTER = 0xe6
BIT_SET_RES_NG = 0xff
BIT_SET_CODE = 0x00
BIT_SET_CHECK = 0x55
BIT_SET_FIN = 0x00
QUERY_DEV_CMD = 0x20
QUERY_DEV_RES = 0x30
QUERY_DEV_SIZE = 4
SELECT_DEV_CMD = 0x10
SELECT_DEV_RES = 0x06
SELECT_DEV_ERROR_RES = 0x90
SELECT_DEV_ERROR_SUM = 0x11
SELECT_DEV_ERROR_CODE = 0x21
QUERY_CLOCK_CMD = 0x21
QUERY_CLOCK_RES = 0x31
SELECT_CLOCK_CMD = 0x11
SELECT_CLOCK_RES = 0x06
SELECT_CLOCK_ERROR_RES = 0x91
SELECT_CLOCK_ERROR_SUM = 0x11
SELECT_CLOCK_ERROR_MODE = 0x22
SELECT_CLOCK_SIZE = 1
SELECT_CLOCK_MODE0 = 0
SELECT_CLOCK_MODE1 = 1
QUERY_SCALE_CMD = 0x22
QUERY_SCALE_RES = 0x32
QUERY_FREQ_CMD = 0x23
QUERY_FREQ_RES = 0x33
QUERY_PAGE_SIZE_CMD = 0x27
QUERY_PAGE_SIZE_RES = 0x37
SET_RATE_CMD = 0x3f
SET_RATE_RES = 0x06
SET_RATE_ERROR_RES = 0xbf
SET_RATE_ERROR_SUM = 0x11
SET_RATE_ERROR_RATE = 0x24
SET_RATE_ERROR_NEW_FREQ = 0x25
SET_RATE_ERROR_PLL = 0x26
SET_RATE_ERROR_FREQ = 0x27
SET_RATE_CHECK_CMD = 0x06
SET_RATE_SIZE = 7
QUERY_AREA_CMD = 0x25
QUERY_AREA_RES = 0x35
SWITCH_TO_ERASE_CMD = 0x40
SELECT_WRITE_FORMAT = 0x43
QUERY_ERROR_RES = 0x80
NON_ERROR_RES = 0x06
WRITE_CMD = 0x50
WRITE_ERROR_ADDRESS = 0x2a
WRITE_ERROR_WRITE = 0x53
def err_message(msg, gui):
if gui:
status.set(msg)
else:
print(msg)
exit()
def err_message_f(msg, f, gui):
f.close()
if gui:
status.set(msg)
else:
print(msg)
exit()
def out_message(msg, gui):
if gui:
status.set(msg)
label2.update()
else:
print(msg, flush=True)
def out_label(msg, cpu, gui):
if gui:
cpuname.set(cpu)
label3.update()
else:
print(msg, cpu, flush=True)
def flush_write(port, filename, baud, osc, gui, pb):
if not os.path.isfile(filename):
err_message('File not found.\n', gui)
return
try:
ser = serial.Serial(port, baud, timeout=0.05)
except:
err_message('Serial port not found\n', gui)
return
for i in range(10):
for j in range(BIT_RATE_SET_MAX_INTER):
ser.write(b'\x00')
c = ser.read()
if c != b'':
break
if c != b'\x00':
ser.close()
ser = serial.Serial(port, 115200, timeout=0.2)
for i in range(8):
ser.write(b'\x5a')
c = ser.read()
if c != b'':
break
if i == 7:
err_message('Serial port not found', gui)
return
for i in range(255):
c1 = struct.pack('B', i)
if c1 != B'E':
ser.write(c1)
c2 = ser.read()
if c1 != c2:
err_message('Serial port not found', gui)
return
cpu = 'SH3/4/4A'
flushsize = 0x80000
pagesize = 256
ser.timeout = 5
ser.write(b'E')
c = ser.read()
out_label('CPU is', cpu, gui)
out_message('Flush erase', gui)
while c != b'S':
c = ser.read()
if c == b'.':
if gui == False:
print('.', end='', flush=True)
# else:
# pb.update()
if c != b'S' and c != b'.':
err_message('Flush earse fail', gui)
return
if gui == False:
print('.', flush=True)
else:
ser.write(struct.pack('B', BIT_SET_CHECK))
while True:
stat = int.from_bytes(ser.read(),'big')
if stat == BIT_SET_RES_NG:
err_message('Bit set check error', gui)
return
if stat == BIT_SET_RES_INTER:
break
ser.write(struct.pack('B', QUERY_DEV_CMD))
stat = int.from_bytes(ser.read(),'big')
sum1 = stat
if stat != QUERY_DEV_RES:
err_message('Query device error', gui)
return
size = int.from_bytes(ser.read(),'big')
sum1 = sum1 + size
devnum = int.from_bytes(ser.read(),'big')
sum1 = sum1 + devnum
status = bytearray(size - 1)
for i in range(size - 1):
status[i] = int.from_bytes(ser.read(),'big')
sum1 = sum1 + status[i]
sum1 = sum1 + int.from_bytes(ser.read(),'big')
sum1 = sum1 % 0x100
if sum1 != 0:
err_message('Query device error', gui)
return
devcode = bytearray(QUERY_DEV_SIZE)
for i in range(QUERY_DEV_SIZE):
devcode[i] = status[i + 1]
length = status[0] - QUERY_DEV_SIZE;
devname = bytearray(length)
for i in range(length):
devname[i] = status[i + QUERY_DEV_SIZE + 1]
cpu = devname.decode()
if cpu == 'R5F7084':
if osc == 0:
osc = 1000
elif cpu == 'R5F7216':
if osc == 0:
osc = 1200
elif cpu == 'R5F7124':
if osc == 0:
osc = 1250
else:
err_message('This is a unknown cpu', gui)
return
out_label('CPU is', cpu, gui)
c = SELECT_DEV_CMD
sum1 = c
ser.write(struct.pack('B', c))
c = QUERY_DEV_SIZE
sum1 = sum1 + c
ser.write(struct.pack('B', c))
for i in range(QUERY_DEV_SIZE):
c = devcode[i]
ser.write(struct.pack('B', c))
sum1 = sum1 + c
sum1 = sum1 % 0x100
sum1 = (0x100 - sum1) % 0x100
ser.write(struct.pack('B', sum1))
c = int.from_bytes(ser.read(),'big')
if c != SELECT_DEV_RES:
err_message('Select device error', gui)
return
ser.write(struct.pack('B', QUERY_CLOCK_CMD))
c = int.from_bytes(ser.read(),'big')
sum1 = c;
if c != QUERY_CLOCK_RES:
err_message('Query clock error', gui)
return
size = int.from_bytes(ser.read(),'big')
sum1 = sum1 + size
status = bytearray(size)
for i in range(size):
status[i] = int.from_bytes(ser.read(),'big')
sum1 = sum1 + status[i];
sum1 = sum1 + int.from_bytes(ser.read(),'big')
sum1 = sum1 % 0x100
if sum1 != 0:
err_message('Query clock error', gui)
return
c = SELECT_CLOCK_CMD
sum1 = c;
ser.write(struct.pack('B', c))
c = SELECT_CLOCK_SIZE
sum1 = sum1 + c
ser.write(struct.pack('B', c))
if cpu == 'R5F7216':
c = SELECT_CLOCK_MODE1
else:
c = SELECT_CLOCK_MODE0
sum1 = sum1 + c;
ser.write(struct.pack('B', c))
sum1 = sum1 % 0x100
sum1 = (0x100 - sum1) % 0x100
ser.write(struct.pack('B', sum1))
c = int.from_bytes(ser.read(),'big')
if c != SELECT_CLOCK_RES:
err_message('Select clock error', gui)
return
gain = 6
speed = baud * gain
bitrate = speed // 100
c = SET_RATE_CMD
sum1 = c
ser.write(struct.pack('B', c))
c = SET_RATE_SIZE
sum1 = sum1 + c
ser.write(struct.pack('B', c))
c = bitrate // 0x100
sum1 = sum1 + c
ser.write(struct.pack('B', c))
c = bitrate % 0x100
sum1 = sum1 + c
ser.write(struct.pack('B', c))
c = osc // 0x100
sum1 = sum1 + c
ser.write(struct.pack('B', c))
c = osc % 0x100
sum1 = sum1 + c;
ser.write(struct.pack('B', c))
pll = bytearray(3)
if cpu == 'R5F7084':
pll[0] = 2
pll[1] = 4
pll[2] = 4
elif cpu == 'R5F7216':
pll[0] = 2
pll[1] = 8
pll[2] = 4
elif cpu == 'R5F7124':
pll[0] = 2
pll[1] = 4
pll[2] = 2
for i in range(3):
c = pll[i]
sum1 = sum1 + c
ser.write(struct.pack('B', c))
sum1 = sum1 % 0x100
sum1 = (0x100 - sum1) % 0x100
ser.write(struct.pack('B', sum1))
c = int.from_bytes(ser.read(),'big')
if c != SET_RATE_RES:
c = int.from_bytes(ser.read(),'big')
if c == SET_RATE_ERROR_SUM:
err_message('Set rate checksum error', gui)
elif c == SET_RATE_ERROR_RATE:
err_message('Set rate speed error', gui)
elif c == SET_RATE_ERROR_NEW_FREQ:
err_message('Set rate new freq error', gui)
elif c == SET_RATE_ERROR_PLL:
err_message('Set rate pll error', gui)
elif c == SET_RATE_ERROR_FREQ:
err_message('Set rate new freq error', gui)
else:
err_message('Set rate unknown error', gui)
return
ser.close()
ser = serial.Serial(port, speed, timeout=0.05)
ser.write(struct.pack('B', SET_RATE_CHECK_CMD))
c = int.from_bytes(ser.read(),'big')
if c != NON_ERROR_RES:
err_message('Set rate clock error', gui)
return
ser.write(struct.pack('B', QUERY_AREA_CMD))
c = int.from_bytes(ser.read(),'big')
sum1 = c;
if c != QUERY_AREA_RES:
err_message('Query area error', gui)
return
size = int.from_bytes(ser.read(),'big')
sum1 = sum1 + size
num = int.from_bytes(ser.read(),'big')
sum1 = sum1 + num
saddr = list(range(num))
taddr = list(range(num))
for i in range(num):
saddr[i] = 0
for j in range(4):
c = int.from_bytes(ser.read(),'big')
sum1 = sum1 + c
saddr[i] = saddr[i] * 0x100
saddr[i] = saddr[i] + c
taddr[i] = 0
for j in range(4):
c = int.from_bytes(ser.read(),'big')
sum1 = sum1 + c;
taddr[i] = taddr[i] * 0x100
taddr[i] = taddr[i] + c
sum1 = sum1 % 0x100
sum1 = (0x100 - sum1) % 0x100
c = int.from_bytes(ser.read(),'big')
if sum1 != c:
err_message('Query area error', gui)
return
flushsize = taddr[0] + 1;
ser.write(struct.pack('B', QUERY_PAGE_SIZE_CMD))
c = int.from_bytes(ser.read(),'big')
sum1 = c
if c != QUERY_PAGE_SIZE_RES:
err_message('Query page size error', gui)
return
size = int.from_bytes(ser.read(),'big')
sum1 = sum1 + size;
pagesize = 0;
for i in range(size):
c = int.from_bytes(ser.read(),'big')
sum1 = sum1 + c;
pagesize = pagesize * 0x100;
pagesize = pagesize + c;
sum1 = sum1 % 0x100
sum1 = (0x100 - sum1) % 0x100
c = int.from_bytes(ser.read(),'big')
if sum1 != c:
err_message('Query page size error', gui)
return
out_message('Flush erase', gui)
ser.timeout = 10
ser.write(struct.pack('B', SWITCH_TO_ERASE_CMD))
c = int.from_bytes(ser.read(),'big')
if c != NON_ERROR_RES:
err_message('Switch to erase error', gui)
return
ser.timeout = 0.05
ser.write(struct.pack('B', SELECT_WRITE_FORMAT))
c = int.from_bytes(ser.read(),'big')
if c != NON_ERROR_RES:
err_message('Select write format error', gui)
return
out_message('Flush write ready', gui)
buff = bytearray(flushsize)
for i in range(flushsize):
buff[i] = 255
verify = bytearray(pagesize)
nopgm = bytearray(pagesize)
for i in range(pagesize):
nopgm[i] = 255
with open(filename) as f:
while True:
s_line = f.readline()
if not s_line:
break
s_line = s_line.replace('\n','')
if s_line[0] != 'S':
err_message_f('File format error', f, gui)
return
linesize = int(s_line[2:4],16)
length2 = linesize * 2 + 4
length1 = len(s_line)
if length1 != length2:
err_message_f('File format error', f, gui)
return
addrsize = (int(s_line[1:2],16) + 1) * 2
if addrsize >= 4 and addrsize <= 8:
address = int(s_line[4:addrsize + 4], 16)
datasize = linesize - (addrsize // 2) - 1
data = s_line[addrsize + 4:addrsize + 4 + datasize * 2]
if (address + datasize) > flushsize:
err_message_f('Flush address is overflow', f, gui)
return
for i in range(datasize):
buff[address + i] = int(data[i * 2:i * 2 + 2], 16)
if gui == True:
pagecount = 0
for address in range(0, flushsize, pagesize):
for i in range(pagesize):
if buff[address + i] != 255:
pagecount = pagecount + 1
break
pagemax = pagecount // 10 + 1
pb.config(maximum = pagemax)
pagecount = 0
pb.config(variable = pagecount)
pb.start()
out_message('Flush writing', gui)
f.close()
count = 0;
for address in range(0, flushsize, pagesize):
for i in range(pagesize):
if buff[address + i] != 255:
break
if i < (pagesize - 1):
if cpu == 'SH3/4/4A':
ser.writetimeout = 0.24
c2 = struct.pack('B',(address // 0x10000) % 0x100)
ser.write(c2)
c1 = struct.pack('B',(address // 0x100) % 0x100)
ser.write(c1)
ser.write(buff[address:address+pagesize])
verify = ser.read(pagesize)
c = ser.read()
if c != b'S':
err_message(c, 'Flush write error', gui)
return
if buff[address:address+pagesize] != verify:
err_message(address, 'Verify error', gui)
return
else:
c = WRITE_CMD;
ser.write(struct.pack('B', c))
sum1 = c
ser.write(b'\x00')
c = (address // 0x10000) % 0x100
ser.write(struct.pack('B', c))
sum1 = sum1 + c;
c = (address // 0x100) % 0x100
ser.write(struct.pack('B', c))
sum1 = sum1 + c;
c = address % 0x100
ser.write(struct.pack('B', c))
sum1 = sum1 + c;
for j in range(pagesize):
c = buff[address + j]
ser.write(struct.pack('B', c))
sum1 = sum1 + c;
sum1 = sum1 % 0x100
sum1 = (0x100 - sum1) % 0x100
ser.write(struct.pack('B', sum1))
c = int.from_bytes(ser.read(),'big')
if c != NON_ERROR_RES:
c = int.from_bytes(ser.read(),'big')
err_message('Flush write error', gui)
return
count = count + 1
if (count % 10) == 0:
if gui == False:
print('.', end='', flush=True)
else:
pb.update()
pagecount = pagecount + 1
if (count % 800) == 0:
if gui == False:
print('\n', end='', flush=True)
ser.close()
out_message('\nFlush write complete', gui)
def select_quit(event):
exit()
def select_file():
fTyp = [("motororaS", "*.mot")]
home = expanduser("~")
file_name = tk.filedialog.askopenfilename(filetypes=fTyp, initialdir=home)
if len(file_name) > 0:
fname.set(file_name)
def select_flush():
flush.config(state = 'disable')
load.config(state = 'disable')
quit.config(state = 'disable')
if fname.get() == '':
select_file()
pb = ttk.Progressbar(root, maximum=10, value=0, length=100, mode='determinate')
pb.pack(in_=canvas2, expand = True, fill = tk.X)
osc = 0
baud = 19200
flush_write(combobox.get(), fname.get(), baud, osc, True, pb)
pb.stop()
flush.config(state = 'normal')
load.config(state = 'normal')
quit.config(state = 'normal')
pb.destroy()
canvas2.config(height = 0)
def click_file(event):
root.after(1, select_file)
def click_flush(event):
root.after(1, select_flush)
argc = len(sys.argv)
if argc == 2 and sys.argv[1] == '-g':
ports = list_ports.comports()
devices = [info.device for info in ports]
if len(devices) == 0:
exit()
devices.reverse()
root = tk.Tk()
root.title("Flush writer")
root.geometry("400x100")
canvas1 = tk.Canvas(root)
canvas1.pack(expand = True, fill = tk.X, side='top')
fname = tk.StringVar()
fname.set('')
label1 = tk.Label(root, textvariable=fname)
label1.pack(fill = tk.X, side='top')
style = ttk.Style()
style.configure("office.TCombobox", padding=2)
v = tk.StringVar()
combobox = ttk.Combobox(root, textvariable=v, values=devices, width=12, style="office.TCombobox")
combobox.pack(in_=canvas1, side='left')
combobox.set(devices[0])
cpuname = tk.StringVar()
cpuname.set('')
label3 = tk.Label(root, textvariable=cpuname)
label3.pack(in_=canvas1, expand = True, fill = tk.X, side='left')
quit = tk.Button(root, text='quit')
quit.bind('<ButtonPress>', select_quit)
quit.pack(in_=canvas1, side='right')
flush = tk.Button(root, text='write')
flush.bind('<ButtonPress>', click_flush)
flush.pack(in_=canvas1, side='right')
load = tk.Button(root, text='load')
load.bind('<ButtonPress>', click_file)
load.pack(in_=canvas1, side='right')
canvas2 = tk.Canvas(root, height = 0)
canvas2.pack(expand = True, fill = tk.X)
status = tk.StringVar()
status.set('')
label2 = tk.Label(root, textvariable=status)
label2.pack(fill = tk.X, side='bottom')
root.mainloop()
else:
if argc >= 3:
if sys.argv[1] != '-p':
argc = 1
if argc < 4:
err_message('Usage: flush -p port file', False)
filename = sys.argv[argc - 1]
port = sys.argv[2]
osc = 0
baud = 19200
flush_write(port, filename, baud, osc, False, 0)