测试内容
是去年的补测题目。区别在于,去年的上位机软件直接用的是 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()
界面如下:
新盘新盘 这个月刚上新盘 新车第一个吃螃蟹!coinsrore.com
2025年10月新盘 做第一批吃螃蟹的人coinsrore.com
新车新盘 嘎嘎稳 嘎嘎靠谱coinsrore.com
新车首发,新的一年,只带想赚米的人coinsrore.com
新盘 上车集合 留下 我要发发 立马进裙coinsrore.com
做了几十年的项目 我总结了最好的一个盘(纯干货)coinsrore.com
新车上路,只带前10个人coinsrore.com
新盘首开 新盘首开 征召客户!!!coinsrore.com
新项目准备上线,寻找志同道合 的合作伙伴coinsrore.com
新车即将上线 真正的项目,期待你的参与coinsrore.com
新盘新项目,不再等待,现在就是最佳上车机会!coinsrore.com
新盘新盘 这个月刚上新盘 新车第一个吃螃蟹!coinsrore.com