在工业控制、嵌入式开发和物联网应用中,串口通信是设备调试和数据交互的重要方式。本文将详细介绍如何使用Python开发一个功能完善的串口通信工具,支持串口参数配置、数据收发、实时监控等功能,并提供完整的代码示例和使用说明。
1. 环境准备
1.1 安装依赖库
- pySerial:用于串口通信的核心库。
安装命令:
pip install pyserial
- Tkinter:Python内置的图形用户界面(GUI)库,无需额外安装。
1.2 开发环境
- 操作系统:Windows 7/10/11(支持COM端口)。
- Python版本:3.8及以上(推荐使用最新稳定版)。
2. 功能需求分析
目标开发一个具备以下功能的串口通信工具:
- 串口扫描:自动检测当前可用的串口设备。
- 参数配置:设置波特率、数据位、停止位、校验位等参数。
- 数据收发:
- 支持文本和十六进制模式发送数据。
- 实时接收并显示数据(支持文本和十六进制显示)。
- 多线程处理:确保串口接收数据时界面不卡顿。
- 错误处理:捕获串口操作中的异常并提示用户。
3. 代码实现
3.1 导入依赖库
import serial
import serial.tools.list_ports
import threading
import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
3.2 主窗口类定义
class SerialTool:
def __init__(self, root):
self.root = root
self.root.title("Python串口通信工具")
self.serial_port = None
self.receive_thread = None
self.running = False
# 初始化UI
self.create_widgets()
3.3 UI布局设计
def create_widgets(self):
# 串口参数设置区域
param_frame = ttk.LabelFrame(self.root, text="串口参数")
param_frame.grid(row=0, column=0, padx=10, pady=10, sticky="ew")
# 串口选择
ttk.Label(param_frame, text="串口:").grid(row=0, column=0, padx=5, pady=5)
self.port_combobox = ttk.Combobox(param_frame, width=10)
self.port_combobox.grid(row=0, column=1, padx=5, pady=5)
self.update_ports()
# 波特率
ttk.Label(param_frame, text="波特率:").grid(row=0, column=2, padx=5, pady=5)
self.baudrate_combobox = ttk.Combobox(param_frame, values=["9600", "115200", "230400", "921600"], width=10)
self.baudrate_combobox.set("115200")
self.baudrate_combobox.grid(row=0, column=3, padx=5, pady=5)
# 打开/关闭按钮
self.open_button = ttk.Button(param_frame, text="打开串口", command=self.toggle_serial)
self.open_button.grid(row=0, column=4, padx=5, pady=5)
# 数据收发区域
io_frame = ttk.Frame(self.root)
io_frame.grid(row=1, column=0, padx=10, pady=10, sticky="nsew")
# 接收数据框
self.receive_text = scrolledtext.ScrolledText(io_frame, height=15, state='disabled')
self.receive_text.pack(side="top", fill="both", expand=True)
# 发送数据区域
send_frame = ttk.Frame(io_frame)
send_frame.pack(side="bottom", fill="x")
ttk.Label(send_frame, text="发送数据:").pack(side="left")
self.send_entry = ttk.Entry(send_frame, width=50)
self.send_entry.pack(side="left", fill="x", expand=True)
self.send_button = ttk.Button(send_frame, text="发送", command=self.send_data)
self.send_button.pack(side="right", padx=5)
# 显示模式切换
self.display_mode = tk.StringVar(value="text")
ttk.Radiobutton(send_frame, text="文本模式", variable=self.display_mode, value="text").pack(side="left")
ttk.Radiobutton(send_frame, text="十六进制模式", variable=self.display_mode, value="hex").pack(side="left")
3.4 串口操作函数
def update_ports(self):
"""更新可用串口列表"""
ports = [port.device for port in serial.tools.list_ports.comports()]
self.port_combobox['values'] = ports
if ports:
self.port_combobox.current(0)
def toggle_serial(self):
"""打开/关闭串口"""
if not self.serial_port:
try:
port = self.port_combobox.get()
baudrate = int(self.baudrate_combobox.get())
self.serial_port = serial.Serial(port, baudrate, timeout=1)
self.open_button.config(text="关闭串口")
self.running = True
self.receive_thread = threading.Thread(target=self.receive_data)
self.receive_thread.start()
except Exception as e:
messagebox.showerror("错误", f"无法打开串口: {e}")
else:
self.running = False
self.serial_port.close()
self.serial_port = None
self.open_button.config(text="打开串口")
def send_data(self):
"""发送数据(文本或十六进制)"""
if not self.serial_port:
messagebox.showwarning("警告", "请先打开串口!")
return
data = self.send_entry.get()
if not data:
return
if self.display_mode.get() == "text":
self.serial_port.write(data.encode('utf-8'))
else:
try:
hex_data = bytes.fromhex(data.replace(" ", ""))
self.serial_port.write(hex_data)
except ValueError:
messagebox.showerror("错误", "无效的十六进制数据!")
def receive_data(self):
"""接收数据线程"""
while self.running:
if self.serial_port and self.serial_port.in_waiting > 0:
raw_data = self.serial_port.read(self.serial_port.in_waiting)
display_data = raw_data
if self.display_mode.get() == "text":
try:
display_data = raw_data.decode('utf-8', errors='replace')
except:
pass
self.update_receive_text(display_data)
def update_receive_text(self, data):
"""更新接收数据框(线程安全)"""
self.root.after(0, lambda: self.receive_text.configure(state='normal'))
self.root.after(0, lambda: self.receive_text.insert(tk.END, data))
self.root.after(0, lambda: self.receive_text.configure(state='disabled'))
self.root.after(0, lambda: self.receive_text.see(tk.END))
3.5 主程序入口
if __name__ == "__main__":
root = tk.Tk()
app = SerialTool(root)
root.mainloop()
4. 使用说明
- 启动工具
运行脚本后,窗口将显示可用的串口列表和默认参数。 - 配置串口
- 选择串口(COM端口)。
- 设置波特率(如9600、115200等)。
- 点击“打开串口”按钮建立连接。
- 发送数据
- 在输入框中输入文本或十六进制数据(如
48 65 6C 6C 6F
表示Hello
)。 - 选择“文本模式”或“十六进制模式”。
- 点击“发送”按钮。
- 接收数据
- 接收数据会实时显示在上方的滚动文本框中。
- 支持动态切换显示模式(文本/十六进制)。
- 关闭串口
点击“关闭串口”按钮断开连接并释放资源。
5. 扩展功能建议
- 自动刷新串口列表
增加定时刷新按钮,动态更新可用串口。 - 数据保存与日志
添加“保存日志”功能,将接收数据保存到文件。 - 自动发送
支持定时自动发送预设数据。 - 高级参数配置
增加数据位、停止位、校验位等参数设置选项。 - 图形化数据展示
集成Matplotlib库,将接收的数值数据绘制成图表。
6. 总结
通过本文的实现,我们构建了一个基于Python的Windows串口通信工具,支持基本的串口配置、数据收发和实时监控功能。该工具适用于快速调试硬件设备、验证通信协议或教学演示。结合多线程技术和Tkinter的图形化界面,用户能够高效地进行串口数据交互。对于更复杂的需求,可通过扩展功能进一步增强工具的实用性。
© 版权声明
本站资源来自互联网收集,仅供用于学习和交流,请勿用于商业用途。如有侵权、不妥之处,请联系站长并出示版权证明以便删除。敬请谅解!
THE END