在上一遍的基础上,做了改动,可以同时升级多个
UI.py
# -*- coding: utf-8 -*-
from tkinter import *
from tkinter.ttk import Combobox
from tkinter import filedialog
from tkinter import messagebox
from ctypes import *
import serial.tools.list_ports
import threading
from ymodem import YMODEM
import os
from time import sleep
class Upgrade_UI(object):
def __init__(self,app,x = 0,y = 0):
self.x = x
self.y = y
self.app = app
self.comportbox=Combobox(app,width = 7, height = 10)
self.baudratebox=Combobox(app,width = 8, height = 10)
self.red_canvas = Canvas(app, width=50, height=50,bg="red")
self.green_canvas = Canvas(app, width=50, height=50,bg="green")
self.progress_bar = Canvas(app, width = 350,height = 26,bg = "white")
self.fill_line = self.progress_bar.create_rectangle(2,2,0,27,width = 0,fill = "green")
self.ser = serial.Serial(bytesize=8, parity='N', stopbits=1, timeout=1, write_timeout=3)
self.linsten_lock = threading.Lock()
self.need_listen = 0
self.exit_listen = False
self.jlink_button = Button(app, text="JLink\r升级",width = 7,height = 3,command=self.jlink_upgrade)
self.connect_button = Button(app, text="连接",width = 8,height = 1,command=self.connect)
self.disconnect_button = Button(app, text="断开",width = 8,height = 1,command=self.disconnect)
self.upgrade_button = Button(app, text="串口升级",width = 8,height = 1,command=self.upgrade)
self.cancel_button = Button(app, text="取消升级",width = 8,height = 1,command=self.cancel)
self.listen_connect = threading.Thread(target=self.listen_connect_thread)
self.send_data_mutex = threading.Lock()
self.ymodem_sender = YMODEM(self.sender_getc, self.sender_putc)
Label(app, text="端口:", font=('Arial', 13)).place(x=10+x*400, y= 14+y*200)
self.comportbox.place(x=75+x*400, y=14+y*200)
Label(app, text="波特率:", font=('Arial', 13)).place(x=200+x*400, y=14+y*200)
self.baudratebox["value"] = ("9600","115200","230400","576000")
self.baudratebox.current(1)
self.baudratebox.place(x=285+x*400, y=14+y*200)
self.connect_button.place(x=10+x*400,y=50+y*200)
self.disconnect_button.place(x=10+x*400,y=100+y*200)
self.cancel_button.place(x=285+x*400,y=50+y*200)
self.upgrade_button.place(x=285+x*400,y=100+y*200)
self.jlink_button.place(x=200+self.x*400,y=60+self.y*200)
self.show_progress_bar(False)
self.set_connect_logo(False)
if self.listen_connect.is_alive() == False:
self.listen_connect.start()
def jlink_upgrade(self):
if self.ser.is_open == False:
messagebox.showinfo(title="Error", message="Please connect the serial first!")
self.upgrade_button.configure(state='active')
self.jlink_button.configure(state='active')
return
file_list = filedialog.askopenfilenames(filetypes=[("bin file", "*.bin"),("all","*.*")])
if len(file_list) <= 0:
self.upgrade_button.configure(state='active')
self.jlink_button.configure(state='active')
return
ret_val = self.prepare_jlink_upgrade()
if ret_val < 0:
self.upgrade_button.configure(state='active')
self.jlink_button.configure(state='active')
return
self.upgrade_button.configure(state='disabled')
self.jlink_button.configure(state='disabled')
self.disconnect_button.configure(state='disabled')
self.cancel_button.configure(state='disabled')
upgrade_thread = threading.Thread(target=self.do_jlink_upgrade,args = (file_list,))
upgrade_thread.start()
def prepare_jlink_upgrade(self):
try:
self.serial_reconnect()
except Exception as e:
messagebox.showinfo(title="Error", message=e)
return -1
self.ser.write("\r".encode("utf-8"))
ret_str = self.ser.read(1024).decode("utf-8")
b_reset= False
if ret_str.find("IET") == -1:
try:
self.serial_reconnect(baud_rate = 9600)
b_reset = True
except Exception as e:
messagebox.showinfo(title="Error", message=e)
return -1
self.ser.read_all()
self.ser.write("flash -u\r".encode("utf-8"))
sleep(0.5)
read_byte = self.ser.read_all()
if len(read_byte) <= 3:
b_reset = True
else:
ret_str = read_byte[0:min(20,len(read_byte))].decode("utf-8")
if ret_str.find("IET") != -1:
b_reset = True
else:
b_reset = False
if b_reset:
messagebox.showinfo(title="Tips", message="Please reset/reconnect board first, then press [OK]")
sleep(0.5)
self.serial_reconnect()
return 0
def do_jlink_upgrade(self,file_list):
self.need_listen = 0
sleep(1)
try:
self.serial_reconnect()
except Exception as e:
print(e)
self.ser.write("mp -q\r".encode("utf-8"))
sleep(1)
for file in file_list:
try:
self.serial_reconnect(timeout = 5)
except Exception as e:
print(e)
self.jlink_send(file)
try:
self.serial_reconnect()
except Exception as e:
print(e)
self.upgrade_button.configure(state='active')
self.jlink_button.configure(state='active')
self.disconnect_button.configure(state='active')
self.cancel_button.configure(state='active')
messagebox.showinfo(title="Info", message="Upgrade finished")
self.need_listen = 1
def jlink_send(self,file):
file_name = os.path.basename(file)
file_path= os.path.dirname(os.path.abspath(file))
j_file = os.path.dirname(file)+"/loadbin.j"
if os.path.exists(j_file):
os.remove(j_file)
with open(j_file,'w',encoding='utf-8') as j_f:
bat_cmd = "JLink -device Cortex-M3 -if JTAG -speed 4000 -autoconnect 1 -JTAGConf -1,-1 -CommanderScript " + j_file
if file_name.find("bootloader_fw_ram.bin") != -1:
j_f.write("h\r")
j_f.write("w4 50b0003c,1\r")
j_f.write("w4 50b00040,0\r")
j_f.write("loadfile " + file_path + "\\bootloader_fw_ram.bin 0\r")
j_f.write("w4 0xe000ed0c,05fa0004\r")
j_f.write("q\r")
j_f.close()
os.system(bat_cmd)
self.wait_iet()
else:
j_f.write("w4 50b00040,0\r")
j_f.write("loadfile " + file_path + "\\" + file_name +" 0xA0000000\r")
j_f.write("q\r")
j_f.close()
os.system(bat_cmd)
try:
self.serial_reconnect(timeout = 3)
except Exception as e:
print(e)
self.ser.read_all()
self.ser.write("upgrade -j\r".encode("utf-8"))
self.wait_iet()
if os.path.exists(j_file):
os.remove(j_file)
def upgrade_callback(self,total_packets,file_size,file_name):
if len(self.progress_bar.gettags("text")) == 0:
self.progress_bar.create_text(175, 15, text=file_name, tags="text")
progress = total_packets*350/(file_size/1024)
self.progress_bar.coords(self.fill_line, (0, 0, progress, 30))
def set_connect_logo(self,is_connected = True):
if is_connected:
self.red_canvas.place_forget()
self.green_canvas.place(x=100+self.x*400,y=60+self.y*200)
self.comportbox.configure(state='disabled')
self.baudratebox.configure(state='disabled')
else:
self.green_canvas.place_forget()
self.red_canvas.place(x=100+self.x*400,y=60+self.y*200)
self.comportbox.configure(state='readonly')
self.baudratebox.configure(state='enabled')
def listen_connect_thread(self):
while self.exit_listen == False:
plist = list(serial.tools.list_ports.comports())
self.comportbox["value"] = ()
select_id = 0
if len(plist) > 0:
for index,item in enumerate(plist):
self.comportbox["value"] += (item[0],)
if item[0] == "COM3":
select_id = index
if len(self.comportbox.get()) == 0:
self.comportbox.current(select_id)
self.linsten_lock.acquire()
if self.need_listen == 0:
self.linsten_lock.release()
sleep(1)
continue
else:
com_number = self.comportbox.get()
port_found = 0
if len(plist) <= 0:
if self.ser.is_open == True:
self.ser.close()
self.set_connect_logo(False)
else:
for item in plist:
if com_number == item[0]:
port_found = 1
break
if port_found == 0:
if self.ser.is_open == True:
self.ser.close()
self.set_connect_logo(False)
else:
if self.ser.is_open == False:
try:
self.ser.port = com_number
self.ser.open()
self.set_connect_logo(True)
except Exception as e:
print(e)
pass
self.linsten_lock.release()
sleep(0.3)
continue
def wait_iet(self):
while True:
if self.ser.read(1).decode("utf-8") == 'I':
if self.ser.read(1).decode("utf-8") == 'E':
if self.ser.read(1).decode("utf-8") == 'T':
self.ymodem_sender.log.info("Receive IET")
break
else:
continue
else:
continue
def connect(self):
com_number = self.comportbox.get()
port_found = 0
plist = list(serial.tools.list_ports.comports())
if self.ser.is_open == True:
messagebox.showinfo(title="Error", message="Already connected!")
return
if len(plist) <= 0:
messagebox.showinfo(title="Error", message="No available serial!")
self.need_listen = 0
self.set_connect_logo(False)
return
else:
for item in plist:
if com_number == item[0]:
port_found = 1
break
if port_found == 0:
self.need_listen = 0
self.set_connect_logo(False)
messagebox.showinfo(title="Error", message="Cannot find serial "+com_number)
return
try:
self.ser.port = com_number
self.ser.baudrate = 115200 #int(baud_rate)
if self.ser.is_open == False:
self.ser.open()
except Exception as e:
if self.ser.is_open == False:
self.need_listen = 0
self.set_connect_logo(False)
messagebox.showinfo(title="Error", message=e)
return
self.set_connect_logo(True)
self.need_listen = 1
def disconnect(self):
self.linsten_lock.acquire()
self.need_listen = 0
self.linsten_lock.release()
if self.ser.is_open == False:
messagebox.showinfo(title="Error", message="Serial not connected!")
else:
try:
self.ser.close()
except Exception as e:
messagebox.showinfo(title="Error", message=e)
return
self.need_listen = 0
self.set_connect_logo(False)
def cancel(self):
if self.upgrade_button['state'] == 'disabled':
self.ymodem_sender.abort()
def upgrade(self):
if self.ser.is_open == False:
messagebox.showinfo(title="Error", message="Please connect the serial first!")
self.upgrade_button.configure(state='active')
self.jlink_button.configure(state='active')
return
file_list = filedialog.askopenfilenames(filetypes=[("bin file", "*.bin"),("all","*.*")])
if len(file_list) <= 0:
self.upgrade_button.configure(state='active')
self.jlink_button.configure(state='active')
return
ret_val = self.prepare_upgrade()
if ret_val < 0:
self.upgrade_button.configure(state='active')
self.jlink_button.configure(state='active')
return
self.upgrade_button.configure(state='disabled')
self.jlink_button.configure(state='disabled')
self.disconnect_button.configure(state='disabled')
upgrade_thread = threading.Thread(target=self.do_upgrade,args = (file_list,))
upgrade_thread.start()
def show_progress_bar(self,show=True):
if show:
self.progress_bar.place(x=10+self.x*400,y=150+self.y*200)
else:
self.progress_bar.place_forget()
self.progress_bar.coords(self.fill_line, (0, 0, 0, 30))
self.progress_bar.delete('text')
def serial_reconnect(self,baud_rate = 115200, timeout = 1):
need_sleep = 1
if self.ser.baudrate == baud_rate:
need_sleep = 0
try:
self.ser.timeout = timeout
self.ser.baudrate = baud_rate
self.ser.close()
self.ser.open()
except Exception as e:
raise Exception("Reconnect Fail")
if need_sleep:
sleep(1)
def do_upgrade(self,file_list):
self.need_listen = 0
sleep(1)
baud_rate = self.baudratebox.get()
try:
self.serial_reconnect()
except Exception as e:
print(e)
self.ser.write("\r".encode("utf-8"))
ch_str = "upgrade -t 0 " + str(int(baud_rate)) + "\r"
self.ser.write(ch_str.encode("utf-8"))
sleep(1)
for file in file_list:
file_size = os.path.getsize(file)
if file_size > 2*1024*1024:
continue
try:
self.serial_reconnect(baud_rate = int(baud_rate), timeout = 5)
except Exception as e:
print(e)
self.show_progress_bar(True)
if len(self.progress_bar.gettags("text")) == 0:
self.progress_bar.create_text(175, 15, text=os.path.basename(file), tags="text")
self.ser.read_all()
self.ser.write("\r".encode("utf-8"))
self.ser.write("upgrade -u\r".encode("utf-8"))
while True:
ch_str = self.ser.read(4).decode("utf-8")
if ch_str == "CCCC":
break
self.ymodem_send(file)
self.wait_iet()
self.ymodem_sender.log.info("Receive IET")
self.show_progress_bar(False)
sleep(1)
self.show_progress_bar(False)
try:
self.ser.write("\r".encode("utf-8"))
self.ser.write("upgrade -t 0 115200\r".encode("utf-8"))
sleep(1)
self.serial_reconnect()
except Exception as e:
print(e)
self.upgrade_button.configure(state='active')
self.jlink_button.configure(state='active')
self.disconnect_button.configure(state='active')
self.need_listen = 1
messagebox.showinfo(title="Info", message="Upgrade finished")
def ymodem_send(self,file):
try:
file_stream = open(file, 'rb')
except IOError as e:
raise Exception("Open file fail!")
file_name = os.path.basename(file)
file_size = os.path.getsize(file)
rate = self.baudratebox.get()
try:
self.serial_reconnect(baud_rate = int(rate), timeout=5)
except Exception as e:
messagebox.showinfo(title="Error", message="Connection error!")
return
try:
self.ymodem_sender.send(file_stream, file_name,file_size,callback=self.upgrade_callback)
except Exception as e:
file_stream.close()
raise
file_stream.close()
def prepare_upgrade(self):
self.ser.flushOutput()
self.ser.flushInput()
self.ser.write("\r".encode("utf-8"))
ret_str = self.ser.read(1024).decode("utf-8")
b_reset= False
if ret_str.find("IET") == -1:
try:
self.serial_reconnect(baud_rate = 9600)
b_reset = True
except Exception as e:
messagebox.showinfo(title="Error", message=e)
return -1
self.ser.read_all()
self.ser.write("flash -u\r".encode("utf-8"))
sleep(0.5)
read_byte = self.ser.read_all()
if len(read_byte) <= 3:
b_reset = True
else:
ret_str = read_byte[0:min(20,len(read_byte))].decode("utf-8")
if ret_str.find("IET") != -1:
b_reset = True
else:
b_reset = False
if b_reset:
messagebox.showinfo(title="Tips", message="Please reset/reconnect board first, then press [OK]")
sleep(0.5)
self.serial_reconnect()
return 0
def sender_getc(self,size):
return self.ser.read(size) or None
def sender_putc(self,data):
self.send_data_mutex.acquire()
self.ser.write(data)
self.send_data_mutex.release()
def on_try_close(self):
self.exit_listen = True
def on_closing(self):
try:
self.ser.close()
except Exception as e:
print(e)
pass
app = Tk()
ui1=Upgrade_UI(app,x=0,y=0)
ui2=Upgrade_UI(app,x=0,y=1)
ui3=Upgrade_UI(app,x=1,y=0)
ui4=Upgrade_UI(app,x=1,y=1)
def init_layout():
app.title("xx科技")
app.iconbitmap(".\\xx.ico")
app.geometry('800x400')
app.protocol("WM_DELETE_WINDOW", on_closing)
app.resizable(width=False, height=False)
Canvas(app, width = 2,height = 410,bg = "black").place(x=400,y=-10)
Canvas(app, width = 820,height = 2,bg = "black").place(x=-20,y=200)
def main():
init_layout()
mainloop()
def on_closing():
ui1.on_try_close()
ui2.on_try_close()
ui3.on_try_close()
ui4.on_try_close()
sleep(0.4)
try:
ui1.on_closing()
ui2.on_closing()
ui3.on_closing()
ui4.on_closing()
except Exception as e:
print(e)
pass
app.destroy()
if __name__ == '__main__':
main()