首页
关于
标签
友链
更多
统计
图片
Search
1
A测-气压测控仿真系统
47 阅读
2
自建博客的图床问题-PicGo+github+obsidian
16 阅读
3
高数强化梳理
11 阅读
4
拨号上网无法使用clash verge代理的问题
9 阅读
5
CF Round1043 Div.3
6 阅读
算法竞赛
杂项
技术
考研
专业课
登录
Search
标签搜索
专业课
课程实验
Python
算法竞赛
分布式计算
数学
技术
Codeforces
Java
微服务
考研
Blog
代理
Atcoder
网络编程
Jmeter
gRPC
RPC
ActiveMQ
消息队列
HoshiuZ
累计撰写
12
篇文章
累计收到
13
条评论
首页
栏目
算法竞赛
杂项
技术
考研
专业课
页面
关于
标签
友链
统计
图片
搜索到
1
篇与
的结果
2025-09-20
A测-气压测控仿真系统
测试内容是去年的补测题目。区别在于,去年的上位机软件直接用的是 XCOM,而今年的正测需要自行编写上位机软件。关于软件的安装以及初步的使用,参考了 西电A测:串口通信仿真-CSDN博客 与 2024春季 西电A测-气压测控仿真系统-CSDN博客。过程虽然今年让自己写一个上位机软件,但打算还是先用 XCOM 进行测试,一切正常后打算用 Python Tkinter 来写个 GUI。电路图已经提供了,只需分析电路图然后用 arduino IDE 写代码即可。这是 LCD,发现接了引脚 12,11,5,4,3,2,所以初始化 LCD 时对这几个引脚初始化。这是直流电机驱动电路,发现接了引脚 7,所以控制直流电机的操作可以通过将引脚 7 写高/低电平来实现。Arduino IDE 代码通过上述分析写出如下代码。选用的是串口 COM3.#include <Wire.h> #include <Adafruit_BMP085.h> #include <LiquidCrystal.h> Adafruit_BMP085 bmp; LiquidCrystal lcd(12, 11, 5, 4, 3, 2); void setup() { Serial.begin(9600); lcd.begin(16, 2); pinMode(7, OUTPUT); bmp.begin(); } int setATM = 860; void loop() { int realATM = (bmp.readPressure() + 50) / 100; String str = ""; lcd.print("ID:"); if(Serial.available() > 0) { while(Serial.available() > 0) { str += (char)Serial.read(); delay(10); } str.trim(); lcd.println(str); int len = str.length(); int last = str[len - 1] - '0'; setATM = 860 + last; str = ""; } Serial.print("ATM: "); Serial.print(realATM); Serial.println("hPa"); lcd.setCursor(0, 1); lcd.print("ATM: "); lcd.print(realATM); lcd.print("hPa"); lcd.home(); if(realATM >= setATM) { digitalWrite(7, HIGH); } else { digitalWrite(7, LOW); } delay(1000); }将代码用 arduino IDE 编译后,proteus 选中 arduino UNO 元件,并将 program file 选中生成的 .hex 文件。我所使用的串口是 COM3 和 COM4,用 VSPD 配置一下就行。然后打开 XCOM,设为 COM4,接着用 proteus 开启仿真。如图所示,正常运行。接下来就只剩下 GUI 的编写。GUI编写采用的是 Python 语言的 Tkinter 库,并用 pyserial 来实现串口通信。代码如下:import tkinter as tk from tkinter import ttk, scrolledtext import serial import serial.tools.list_ports from threading import Thread, Event import time class MyCOM: def __init__(self, window): # 初始化主窗口 self.window = window self.window.title("114514 田所浩二 - 气压测控仿真系统") self.window.geometry("800x600") # 串口相关变量 self.serial_port = None self.is_serial_open = False self.receive_thread = None self.stop_event = Event() self.create_widgets() # 创建界面控件 self.update_com_list() # 初始化时更新可用串口列表 def create_widgets(self): """创建并布局所有GUI控件""" # 顶部框架 - 串口控制区域 top_frame = ttk.LabelFrame(self.window, text="串口配置", padding="5") top_frame.grid(row=0, column=0, columnspan=2, padx=5, pady=5, sticky="ew") # 串口选择标签和下拉框 ttk.Label(top_frame, text="选择串口:").grid(row=0, column=0, padx=5, pady=5, sticky="w") self.com_combo = ttk.Combobox(top_frame, width=15, state="readonly") self.com_combo.grid(row=0, column=1, padx=5, pady=5) # 波特率选择标签和下拉框 ttk.Label(top_frame, text="波特率:").grid(row=0, column=2, padx=5, pady=5, sticky="w") self.baud_combo = ttk.Combobox(top_frame, width=10, values=['9600', '19200', '38400', '57600', '115200'], state="readonly") self.baud_combo.set('9600') # 默认波特率 self.baud_combo.grid(row=0, column=3, padx=5, pady=5) # 刷新串口按钮 self.refresh_btn = ttk.Button(top_frame, text="刷新串口", command=self.update_com_list) self.refresh_btn.grid(row=0, column=4, padx=5, pady=5) # 打开/关闭串口按钮 self.open_close_btn = ttk.Button(top_frame, text="打开串口", command=self.toggle_serial) self.open_close_btn.grid(row=0, column=5, padx=5, pady=5) # 状态标签 self.status_label = ttk.Label(top_frame, text="当前状态: 串口未打开", foreground="red") self.status_label.grid(row=0, column=6, padx=10, pady=5, sticky="w") # 中部框架 - 数据发送区域 send_frame = ttk.LabelFrame(self.window, text="发送数据", padding="5") send_frame.grid(row=1, column=0, columnspan=2, padx=5, pady=5, sticky="ew") # 发送数据输入框 ttk.Label(send_frame, text="发送指令:").grid(row=0, column=0, padx=5, pady=5, sticky="w") self.send_entry = ttk.Entry(send_frame, width=50) self.send_entry.insert(0, "114514") # 默认填入学号 self.send_entry.grid(row=0, column=1, padx=5, pady=5, sticky="ew") # 发送按钮 self.send_btn = ttk.Button(send_frame, text="发送", command=self.send_data, state="disabled") self.send_btn.grid(row=0, column=2, padx=5, pady=5) # 底部框架 - 数据接收区域 receive_frame = ttk.LabelFrame(self.window, text="接收数据", padding="5") receive_frame.grid(row=2, column=0, columnspan=2, padx=5, pady=5, sticky="nsew") # 接收数据显示区域 self.receive_text = scrolledtext.ScrolledText(receive_frame, width=70, height=20, state="disabled") self.receive_text.grid(row=0, column=0, columnspan=3, padx=5, pady=5, sticky="nsew") self.receive_text.tag_config("send_color", foreground="blue") self.receive_text.tag_config("receive_color", foreground="green") self.receive_text.tag_config("error_color", foreground="red") # 清空接收区按钮 clear_btn = ttk.Button(receive_frame, text="清空接收区", command=self.clear_receive_area) clear_btn.grid(row=1, column=0, padx=5, pady=5, sticky="w") # 配置网格权重,使界面随窗口调整大小 self.window.columnconfigure(0, weight=1) self.window.rowconfigure(2, weight=1) receive_frame.columnconfigure(0, weight=1) receive_frame.rowconfigure(0, weight=1) def update_com_list(self): """获取可用串口列表并更新下拉框""" com_list = [port.device for port in serial.tools.list_ports.comports()] self.com_combo['values'] = com_list if com_list: self.com_combo.set(com_list[0]) def toggle_serial(self): """打开或关闭串口""" if not self.is_serial_open: self.open_serial() else: self.close_serial() def open_serial(self): """打开串口""" try: selected_port = self.com_combo.get() baud_rate = int(self.baud_combo.get()) if not selected_port: self.update_status("请选择有效的串口", "red") return # 创建串口连接 self.serial_port = serial.Serial( port=selected_port, baudrate=baud_rate, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=1 ) self.is_serial_open = True self.update_ui_for_serial_state() self.update_status(f"已打开 {selected_port} ({baud_rate}bps)", "green") # 启动接收线程 self.stop_event.clear() self.receive_thread = Thread(target=self.receive_data, daemon=True) self.receive_thread.start() except Exception as e: self.update_status(f"打开串口失败: {str(e)}", "red") def close_serial(self): """关闭串口""" try: self.is_serial_open = False self.stop_event.set() if self.serial_port and self.serial_port.is_open: self.serial_port.close() self.update_ui_for_serial_state() self.update_status("串口已关闭", "red") except Exception as e: self.update_status(f"关闭串口时出错: {str(e)}", "red") def update_ui_for_serial_state(self): """根据串口状态更新UI控件状态""" if self.is_serial_open: self.open_close_btn.config(text="关闭串口") self.com_combo.config(state="disabled") self.baud_combo.config(state="disabled") self.refresh_btn.config(state="disabled") self.send_btn.config(state="normal") else: self.open_close_btn.config(text="打开串口") self.com_combo.config(state="readonly") self.baud_combo.config(state="readonly") self.refresh_btn.config(state="normal") self.send_btn.config(state="disabled") def update_status(self, message, color="black"): """更新状态标签""" self.status_label.config(text=f"当前状态: {message}", foreground=color) def send_data(self): """发送数据到串口""" try: data = self.send_entry.get() if data and self.serial_port and self.serial_port.is_open: self.serial_port.write(data.encode()) self.display_data(f"发送: {data}", "send_color") except Exception as e: self.update_status(f"发送数据失败: {str(e)}", "red") def receive_data(self): """在后台线程中接收串口数据""" while not self.stop_event.is_set() and self.is_serial_open: try: if self.serial_port and self.serial_port.in_waiting > 0: data = self.serial_port.readline().decode('utf-8', errors='ignore').strip() if data: self.display_data(f"接收: {data}", "receive_color") except Exception as e: if self.is_serial_open: # 只在串口打开时报告错误 self.window.after(0, lambda: self.update_status(f"接收错误: {str(e)}", "error_color")) break time.sleep(0.01) def display_data(self, data, tag_type="receive_color"): """在接收文本框中显示数据""" def update_display(): self.receive_text.config(state="normal") self.receive_text.insert(tk.END, data + "\n", (tag_type,)) self.receive_text.see(tk.END) # 自动滚动到底部 self.receive_text.config(state="disabled") # 确保UI更新在主线程中执行 self.window.after(0, update_display) def clear_receive_area(self): """清空接收数据显示区域""" self.receive_text.config(state="normal") self.receive_text.delete(1.0, tk.END) self.receive_text.config(state="disabled") def on_closing(self): """窗口关闭时的清理工作""" if self.is_serial_open: self.close_serial() self.window.destroy() if __name__ == "__main__": root = tk.Tk() app = MyCOM(root) root.protocol("WM_DELETE_WINDOW", app.on_closing) root.mainloop()界面如下:
2025年09月20日
47 阅读
2 评论
0 点赞