PyQt5串口测试工具

笔者经常会遇到使用上位机进行相关测试的场景,但现成的上位机并不能完全满足自己的需求,或是上位机缺乏使用说明。所以,自己写?

环境说明

pycharm 2023.2.25

python 3.10

anaconda

环境配置

conda create -n envsram     ##新建虚拟环境,不用anaconda也行自己使用python新建都行
conda env list    ##查看虚拟环境及路径,方便修改python解释器路径
conda activate envsram    
conda install pyqt     ##安装pyqt5及依赖

designer    ##键入,以打开pyqt图像设计界面,设计完成后为.ui文件

pyuic5 -o ccm_Test.py .\Uartsendframe.ui    ##转换.ui文件为测试文件

pip install pyserial    ##我conda install失败了,直接用pip安装,为了上位机实现串口相关操作

代码测试

ui界面代码

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file '.\Uartsendframe.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets



class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(432, 476)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
        self.groupBox.setObjectName("groupBox")
        self.gridLayout = QtWidgets.QGridLayout(self.groupBox)
        self.gridLayout.setObjectName("gridLayout")
        self.pushButton = QtWidgets.QPushButton(self.groupBox)
        self.pushButton.setObjectName("pushButton")
        self.gridLayout.addWidget(self.pushButton, 0, 3, 1, 1)
        self.pushButton2 = QtWidgets.QPushButton(self.groupBox)
        self.pushButton2.setObjectName("pushButton2")
        self.gridLayout.addWidget(self.pushButton2, 0, 4, 1, 1)
        self.pushButton3 = QtWidgets.QPushButton(self.groupBox)
        self.pushButton3.setObjectName("pushButton3")
        self.gridLayout.addWidget(self.pushButton3, 0, 5, 1, 1)
        self.pushButton4 = QtWidgets.QPushButton(self.groupBox)
        self.pushButton4.setObjectName("pushButton4")
        self.gridLayout.addWidget(self.pushButton4, 1, 5, 1, 1)
        self.lineEdit = QtWidgets.QLineEdit(self.groupBox)
        self.lineEdit.setObjectName("lineEdit")
        self.gridLayout.addWidget(self.lineEdit, 1, 1, 1, 4)
        self.label = QtWidgets.QLabel(self.groupBox)
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(9)
        self.label.setFont(font)
        self.label.setObjectName("label")
        self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
        self.label_2 = QtWidgets.QLabel(self.groupBox)
        self.label_2.setObjectName("label_2")
        self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
        self.comboBox = QtWidgets.QComboBox(self.groupBox)
        self.comboBox.setObjectName("comboBox")
        self.comboBox.addItem("")
        self.gridLayout.addWidget(self.comboBox, 0, 1, 1, 2)
        self.verticalLayout.addWidget(self.groupBox)
        self.groupBox_2 = QtWidgets.QGroupBox(self.centralwidget)
        self.groupBox_2.setObjectName("groupBox_2")
        self.gridLayout_2 = QtWidgets.QGridLayout(self.groupBox_2)
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.textEdit = QtWidgets.QTextEdit(self.groupBox_2)
        self.textEdit.setObjectName("textEdit")
        self.gridLayout_2.addWidget(self.textEdit, 0, 0, 1, 1)
        self.verticalLayout.addWidget(self.groupBox_2)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 322, 26))
        self.menubar.setObjectName("menubar")
        self.menu = QtWidgets.QMenu(self.menubar)
        self.menu.setObjectName("menu")
        self.menu_2 = QtWidgets.QMenu(self.menubar)
        self.menu_2.setObjectName("menu_2")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.menubar.addAction(self.menu.menuAction())
        self.menubar.addAction(self.menu_2.menuAction())

        self.retranslateUi(MainWindow)
        self.pushButton.clicked.connect(self.comboBox.update) # type: ignore
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "串口sram测试工具v0.1"))
        self.groupBox.setTitle(_translate("MainWindow", "配置选项"))
        self.pushButton.setText(_translate("MainWindow", "刷新串口"))
        self.label.setText(_translate("MainWindow", "串口选择:"))
        self.label_2.setText(_translate("MainWindow", "文件路径:"))
        self.pushButton2.setText(_translate("MainWindow", "串口打开"))
        self.pushButton3.setText(_translate("MainWindow", "串口发送"))
        self.pushButton4.setText(_translate("MainWindow", "选择文件"))
        self.comboBox.setItemText(0, _translate("MainWindow", "com"))
        self.groupBox_2.setTitle(_translate("MainWindow", "数据显示"))
        self.menu.setTitle(_translate("MainWindow", "帮助"))
        self.menu_2.setTitle(_translate("MainWindow", "关于"))

逻辑代码

这边在处理的时候有几个遇到的bug,

1. 上位机需要一直接收,所以需要开一个线程用来持续接收。

2. 界面更新太平凡容易卡死,所以起一个信号量来更新

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog
from PyQt5.QtCore import pyqtSignal
from ccm_Test import Ui_MainWindow  
import time
import serial
import serial.tools.list_ports
import threading


class MyApp(QMainWindow, Ui_MainWindow):
    data_received_signal = pyqtSignal(str)  # 定义信号,传递字符串数据
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.data_received_signal.connect(self.update_text_edit)  # 连接信号到槽
        self.pushButton_onclik()
        self.pushButton2_onclik()
        self.pushButton3_onclik()
        self.pushButton4_onclik()

    def update_text_edit(self, data):
        self.textEdit.append(data)  # 更新文本编辑器

    def pushButton_onclik(self):
        # self.mainop()
        def cao():
            print("已刷新串口。")
            self.textEdit.append('刷新')
            ports = serial.tools.list_ports.comports()
            self.comboBox.clear()  # 清空所有项
            for port, desc, hwid in sorted(ports):
                print(port, type(port),type(ports))
                self.comboBox.addItem(port)  # 添加新的项列表

            # print(ports[0], type(ports[0]))
            # try:
            #     self.comboBox.clear()  # 清空所有项
            #     self.comboBox.addItems()  # 添加新的项列表
            #
            #     print("Items added successfully.")
            # except Exception as e:
            #     print(f"An error occurred: {e}")

        self.pushButton.clicked.connect(cao)

    def pushButton2_onclik(self):
        # self.mainop()
        def cao():
            global port_name
            global serial_port
            port_name = self.comboBox.currentText()
            baud_rate = 115200
            print(port_name)
            serial_port = self.open_serial(port_name, baud_rate)
            self.textEdit.append('打开'+port_name)

        self.pushButton2.clicked.connect(cao)

    def pushButton3_onclik(self):
        # self.mainop()
        ''' bootloader 使用了0x20000400开始的地址,0xC00大小的区间。共开了0x10000的堆栈。 '''
        hex_string = "1234"
        hex_string1 = "1235"
        hex_string2 = "123455" #"52312000" 2000是实际动态代码 map文件中main的起始地址
        hex_string3 = "123456"
        hex_data_to_send = bytes.fromhex(hex_string)
        hex_data_to_send1 = bytes.fromhex(hex_string1)
        hex_data_to_send2 = bytes.fromhex(hex_string2)
        hex_data_to_send3 = bytes.fromhex(hex_string3)

        def cao():
            try:
                thread = threading.Thread(target=self.read_serial, args=(serial_port,))
                thread.start()
            except serial.SerialException as e:
                print(f"open_serial : {e}")
            ## 第一条命令
            self.write_serial(serial_port, hex_data_to_send)
            self.textEdit.append('>> ' + hex_string)

            time.sleep(0.1)
            # rxbuff = self.read_serial(serial_port)
            # print(rxbuff)
            # self.textEdit.append('<< '+ rxbuff)

            ## 第二条命令
            self.write_serial(serial_port, hex_data_to_send1)
            self.textEdit.append('>> ' + hex_string1)

            time.sleep(0.1)
            # rxbuff = self.read_serial(serial_port)
            # print(rxbuff)
            # self.textEdit.append('<< '+ rxbuff)

            ## 第三条命令
            print(fileName)
            binfilecontent = self.read_bin_file(fileName)
            print(type(binfilecontent), binfilecontent)
            binfilecontent_len = len(binfilecontent)
            binfilecontent_len_hex = bytes.fromhex(self.int_to_hex16(binfilecontent_len))
            # print(binfilecontent_len_hex,type(binfilecontent_len_hex))
            # print(self.read_bin_file(fileName))
            self.write_serial(serial_port, hex_data_to_send2 + binfilecontent_len_hex + binfilecontent)
            self.textEdit.append('>> ' + hex_string2)

            time.sleep(0.1)
            # rxbuff = self.read_serial(serial_port)
            # self.textEdit.append('<< '+ rxbuff)

            ## 第四条命令
            self.write_serial(serial_port, hex_data_to_send3)
            self.textEdit.append('>> ' + hex_string3)

            time.sleep(0.1)
            # rxbuff = self.read_serial(serial_port)
            # if(rxbuff == None):
            #     self.textEdit.append('<< ' + 'None.')
            # else:
            #     self.textEdit.append('<< '+ rxbuff)

            # time.sleep(3)
            # rxbuff = 0
            # rxbuff = self.read_serial(serial_port)
            # self.textEdit.append('等待后续sram测试数据返回...')
            # self.textEdit.append('<< '+ rxbuff)
            # self.close_serial(serial_port)
            pass
        self.pushButton3.clicked.connect(cao)

    def pushButton4_onclik(self):
        # global fileName
        def cao():
            # 打开文件选择对话框
            options = QFileDialog.Options()
            options |= QFileDialog.DontUseNativeDialog
            global fileName
            fileName, _ = QFileDialog.getOpenFileName(self, "QFileDialog.getOpenFileName()", "",
                                                      "All Files (*);;Text Files (*.txt)", options=options)
            if fileName:
                self.lineEdit.setText(fileName)  # 将选择的文件路径设置到 QLineEdit
            pass

        self.pushButton4.clicked.connect(cao)

    # 打开串口
    def open_serial(self, port, baudrate):
        try:
            ser = serial.Serial(
                port, baudrate,
                parity=serial.PARITY_EVEN,  # 设置校验
                stopbits=serial.STOPBITS_ONE,  # 设置停止位
                bytesize=8,  # 数据位为 8 位
                timeout=0  # 超时设置 非阻塞
            )  # 打开串口
            print(f"Serial port {port} opened at {baudrate} baud.")
            # try:
            #     thread = threading.Thread(target=self.read_serial(ser), args=(ser,))
            #     thread.start()
            # except serial.SerialException as e:
            #     print(f"open_serial : {e}")

            return ser
        except serial.SerialException as e:
            print(f"Error opening serial port {port}: {e}")
            return None

    # 从串口读取数据
    def read_serial(self, ser):
        while True:
            if ser:
                try:
                    if ser.in_waiting > 0:
                        received_data = ser.read(ser.in_waiting)
                        received_hex = received_data.hex()
                        if (received_hex == None):
                            self.data_received_signal.emit('<< ' + received_hex)  # 发射信号('<< ' + 'None.')
                        else:
                            self.data_received_signal.emit('<< ' + received_hex)  # 发射信号
                        print("Received data (hex):", received_hex)
                        # return received_hex
                    else:
                        # print("No data received.")
                        pass
                except serial.SerialTimeoutException:
                    print("Read timeout occurred")
                except serial.SerialException as e:
                    print(f"Error during read: {e}")
                time.sleep(0.1)  # 短暂休眠以减少CPU负担

    # 向串口写入数据
    def write_serial(self, ser, data):
        if ser:
            try:
                successlen = ser.write(data)  # 发送数据
                print("successlen: ",successlen)
                print(f"Sent data: {data}")
                # self.textEdit.append('>> ' + data.encode('utf-8').hex())
            except serial.SerialException as e:
                print(f"Error sending data: {e}")

    # 关闭串口
    def close_serial(self, ser):
        if ser:
            ser.close()
            print("Serial port closed.")

    # 读取bin文件
    def read_bin_file(self, file_path):
        try:
            with open(file_path, 'rb') as file:
                return file.read()
        except Exception as e:
            print(f"Failed to read file: {str(e)}")

    def int_to_hex16(self, value):
        if not (0 <= value <= 65535):
            raise ValueError("Integer value out of range for 16-bit representation")

        # 将整数转换为16位十六进制字符串
        hex_string = f"{value:04x}"
        return hex_string

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyApp()
    window.show()
    sys.exit(app.exec_())

界面示例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/676530.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Proxmox Backup Server 命名空间使用

作者&#xff1a;田逸&#xff08;formyz&#xff09; Proxmox Backup Server&#xff08;一下统称PBS&#xff09;从2.2版本开始&#xff0c;新增了命名空间这样一个功能。这个功能大大便利了多Proxmox VE集群或者单节点备份&#xff0c;在以前PBS版本中&#xff0c;如果有多个…

Windows系统下DOS命令

Windows系统下DOS命令 1. 与文件操作相关1.1 mkdir&#xff0c;md命令1.2 rmdir、rd命令1.3 dir命令1.4 start命令1.5 echo命令1.6 type命令1.7 copy命令1.8 move命令1.9 copy和move的区别1.10 del命令1.11 rename命令1.12 attrib命令1.13 fsutil命令1.14 assoc命令 2. 与网络相…

DevOps生命周期的8个阶段和DevOps pipeline 详解

您可能也在探索DevOps pipeline或工作流的概念&#xff0c;这些术语可能会根据不同的解释者而有所交替使用。尽管如此&#xff0c;DevOps生命周期和DevOps pipeline这两个术语更常被提及。 本文将首先阐述DevOps生命周期的概念&#xff0c;然后深入介绍DevOps pipeline。 Dev…

基于4G工业路由器的信息发布系统物联网应用方案

随着物联网技术的快速发展&#xff0c;智能信息发布系统已成为城市管理和信息传播的重要工具。而4G工业路由器作为连接信息发布终端与云平台的关键设备&#xff0c;其在提升信息发布效率方面发挥着至关重要的作用。为了提升智能信息发布系统的效率和智能化水平&#xff0c;智联…

怎么在pdf上编辑文字?帮你解决这个头疼的问题!

在信息爆炸的时代&#xff0c;PDF作为一种通用性极强的文件格式&#xff0c;广泛应用于各个领域。然而&#xff0c;对于许多用户来说&#xff0c;如何在PDF上编辑文字却是一个令人头疼的问题。今天&#xff0c;我们就来聊聊这个话题&#xff0c;并为大家推荐几款实用的PDF编辑软…

Python程序设计 身份证号的奥秘

第1关&#xff1a;判断性别 通过身份证的第17位也就是倒数第二位的数字可以辨别该身份证所属人的性别,奇数为男性,偶数为女性。 任务&#xff1a;输入身份证号&#xff0c;第17位若是偶数&#xff0c;输出男性&#xff0c;否则输出女性 如何截取字符串的一个字符 如何判断一个…

【YOLO 系列】基于YOLO V8的河道违规钓鱼检测识别系统【python源码+Pyqt5界面+数据集+训练代码】

前言&#xff1a; 随着社会发展和人口增长&#xff0c;对自然资源的保护和管理变得越来越重要。河流是重要的自然资源之一&#xff0c;对生态系统和人类社会都至关重要。然而&#xff0c;违规钓鱼等活动可能对河流生态环境造成严重破坏&#xff0c;并影响水域资源的可持续利用…

Codeforces Round 920 (Div. 3) E. Eat the Chip 题解 博弈论 贪心

Eat the Chip 题目描述 Alice and Bob are playing a game on a checkered board. The board has h h h rows, numbered from top to bottom, and w w w columns, numbered from left to right. Both players have a chip each. Initially, Alice’s chip is located at th…

回溯--字母迷宫

1.题目描述 字母迷宫游戏初始界面记作 m x n 二维字符串数组 grid&#xff0c;请判断玩家是否能在 grid 中找到目标单词 target。 注意&#xff1a;寻找单词时 必须 按照字母顺序&#xff0c;通过水平或垂直方向相邻的单元格内的字母构成&#xff0c;同时&#xff0c;同一个单…

SSM民宿在线预订平台的设计与实现-计算机毕业设计源码44449

摘 要 信息化社会内需要与之径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对民宿在线预订平台等问题&#xff0c;对民宿信息管理进行研究分…

【Qt知识】Qt窗口坐标系

Qt的窗口坐标体系遵循标准的计算机图形坐标系统规则 Qt窗口坐标体系特点 坐标原点&#xff1a;窗口坐标体系的原点位于窗口的左上角&#xff0c;即坐标(0, 0)位置。 轴方向&#xff1a; X轴&#xff1a;向右为正方向&#xff0c;随着X坐标值的增加&#xff0c;元素在窗口中从…

Honor of Kings 2024.06.03 50star (S35) AFK

Honor of Kings 2024.06.03 50star (S35) AFK 来个赛季S35总结吧&#xff0c;这个赛季结束以后&#xff0c;可能要和【魔兽世界】一样AFK了&#xff0c;手游来说肯定没法子和WOW相比&#xff0c;干啥都是有队友才好玩。 我玩的基本都是肉&#xff0c;爆发强的英雄&#xff0c;最…

重学java 57.哈希表结构存储过程

别焦虑&#xff0c;生活无非见招拆招 —— 24.6.3 哈希表存储数据去重复的过程: a.先比较元素的哈希值(重写hashCode),再比较内容(重写equals) b.如果哈希值不一样,证明内容不一样,存 c.如果哈希值一样,再比较内容 如果哈希值一样,内容不一样(哈希碰撞,哈希冲突),存 如果哈希值…

FASTGPT:可视化开发、运营和使用的AI原生应用

近年来&#xff0c;随着人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;AI的应用逐渐渗透到各行各业。作为一种全新的开发模式&#xff0c;AI原生应用正逐步成为行业的焦点。在这方面&#xff0c;FASTGPT无疑是一款颇具代表性的产品。本文将详细介绍FASTGPT的设…

再说零信任

什么是零信任&#xff1f; 2010年&#xff0c;由著名研究机构Forrester的首席分析师John Kindervag最早提出了零信任(Zero Trust)的概念&#xff0c; 并由Google在BeyondCorp项目中率先得到了应用&#xff0c;很好的解决了边界安全理念难以应对的安全问题。 我们的网络无时无…

C++多线程同步

C使用多线程必须包含头文件 #include <thread> 来实现 当多个线程同事访问一个对象的时候&#xff0c;会产生数据竞争现象。 这个时候&#xff0c;就可以加锁&#xff0c;同步资源&#xff0c;解决数据竞争。 最简单就是互斥锁mutex 上代码&#xff0c;计算一个数自增到1…

3d模型批量渲图总是会跳怎么办?---模大狮模型网

在进行3D模型批量渲染时&#xff0c;有时会遇到一些问题&#xff0c;其中一个常见的问题就是渲染过程中出现跳帧或者跳图的情况。这不仅会影响到效率&#xff0c;还可能导致输出结果不符合预期。本文将介绍几种解决这一问题的方法&#xff0c;帮助读者更好地应对3D模型批量渲图…

union all 以及标量子查询执行计划

SELECT 1, (SELECT ID1 FROM TE WHERE IDA.ID2) FROM .TA A WHERE COLA X UNION ALL SELECT 1, (SELECT ID2 FROM TD WHERE IDA.ID1) FROM .TB A WHERE COLA X UNION ALL SELECT 1,COL2 AS PARENT_UUID FROM .TC a WHERE COLA X 三个union all 看着像是5个table joi…

正则表达式-是什么?规则有哪些?

正则表达式&#xff08;Regular Expression&#xff0c;常简写为regex、regexp或RE&#xff09;是一种文本模式&#xff0c;包括普通字符&#xff08;如a到z之间的字母&#xff09;和特殊字符&#xff08;称为“元字符”&#xff09;&#xff0c;用于描述、匹配一系列符合某个句…

鸿蒙Ability Kit(程序框架服务)【ExtensionAbility组件】

ExtensionAbility组件 ExtensionAbility组件是基于特定场景&#xff08;例如服务卡片、输入法等&#xff09;提供的应用组件&#xff0c;以便满足更多的使用场景。 每一个具体场景对应一个[ExtensionAbilityType]&#xff0c;开发者只能使用&#xff08;包括实现和访问&#…