A测-气压测控仿真系统

A测-气压测控仿真系统

HoshiuZ
2025-09-20 / 2 评论 / 38 阅读 / 正在检测是否收录...

测试内容


48ab7a792d4543c0329b0170bc781b36.png

是去年的补测题目。区别在于,去年的上位机软件直接用的是 XCOM,而今年的正测需要自行编写上位机软件。
关于软件的安装以及初步的使用,参考了 西电A测:串口通信仿真-CSDN博客2024春季 西电A测-气压测控仿真系统-CSDN博客

过程

虽然今年让自己写一个上位机软件,但打算还是先用 XCOM 进行测试,一切正常后打算用 Python Tkinter 来写个 GUI。

电路图已经提供了,只需分析电路图然后用 arduino IDE 写代码即可。

620e52dcc5a46e649b2a6839707b0afc.png

这是 LCD,发现接了引脚 12,11,5,4,3,2,所以初始化 LCD 时对这几个引脚初始化。

1db5170a1c0ae81cd460402ec1fb0572.png

这是直流电机驱动电路,发现接了引脚 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 开启仿真。

5328f27aece9c58b9d15f96f23e6d00e.png

如图所示,正常运行。

接下来就只剩下 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()

界面如下:

327b847916ff02654b7e77b4cdb89f8a.png

0

评论 (2)

取消
  1. 头像
    jkvaaklras
    Windows 10 · Google Chrome

    新盘新盘 这个月刚上新盘 新车第一个吃螃蟹!coinsrore.com

    回复
  2. 头像
    oiobzzhmma
    Windows 10 · Google Chrome

    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

    回复