python 支持多个串口同时升级

在上一遍的基础上,做了改动,可以同时升级多个

python 支持多个串口同时升级_第1张图片

 

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()

 

你可能感兴趣的:(python,工具,嵌入式)