A测-气压测控仿真系统

A测-气压测控仿真系统

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

测试内容


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

评论 (4)

取消
  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

    回复
  3. 头像

    果博东方客服开户联系方式【182-8836-2750—】?薇- cxs20250806】
    果博东方公司客服电话联系方式【182-8836-2750—】?薇- cxs20250806】
    果博东方开户流程【182-8836-2750—】?薇- cxs20250806】
    果博东方客服怎么联系【182-8836-2750—】?薇- cxs20250806】

    回复
  4. 头像

    华纳圣淘沙公司开户新手教程

    零基础学会(183-8890-9465薇-STS5099)
    华纳圣淘沙公司开户

    华纳圣淘沙公司开户保姆级教程(183-8890-9465薇-STS5099)

    一步步教你开通华纳圣淘沙公司账户(183-8890-9465薇-STS5099)

    华纳圣淘沙公司开户分步图解

    首次开户必看:(183-8890-9465薇-STS5099)
    华纳圣淘沙全攻略

    华纳圣淘沙公司开户实操手册(183-8890-9465薇-STS5099)
    华纳圣淘沙开户流程视频教程

    手把手教学:(183-8890-9465薇-STS5099)
    华纳圣淘沙公司开户

    华纳圣淘沙公司开户完全指南(183-8890-9465薇-STS5099)

    回复