鱼C论坛

 找回密码
 立即注册
12
返回列表 发新帖
楼主: XYZ_XYZ

[学习笔记] 用C语言写一个串口收发的上位机

[复制链接]
 楼主| 发表于 2024-3-4 22:27:07 | 显示全部楼层
FishC 发表于 2024-3-4 22:15
以下是格式规范化后的代码:using System;
using System.Collections.Generic;
using System.Compone ...

在这个基础上,用C#添加串口接收的功能
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-3-4 22:27:40 | 显示全部楼层
XYZ_XYZ 发表于 2024-03-04 22:27
在这个基础上,用C#添加串口接收的功能


下面是在C#代码中添加串口接收功能的修改:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
using System.Windows.Forms;

namespace 串口数据显示
{
    public partial class Form1 : Form
    {
        private SerialPort serialPort = null;
        private bool isSerialPortOpen = false;

        public Form1()
        {
            InitializeComponent();

            this.comboBox1.DropDown += new System.EventHandler(this.comboBox1_DropDown);
            this.button1.Click += new System.EventHandler(this.button1_Click);
            this.button2.Click += new System.EventHandler(this.button2_Click);
            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);

            // 注册串口数据接收事件
            this.serialPort.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);
        }

        private void comboBox1_DropDown(object sender, EventArgs e)
        {
            string[] ports = SerialPort.GetPortNames();
            comboBox1.Items.Clear();
            foreach (string port in ports)
            {
                comboBox1.Items.Add(port);
            }
            if (comboBox1.Items.Count > 0)
            {
                comboBox1.SelectedIndex = 0;
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (isSerialPortOpen)
            {
                // 如果串口已打开,则关闭串口
                if (serialPort != null && serialPort.IsOpen)
                {
                    serialPort.Close();
                    serialPort.Dispose();
                }
                isSerialPortOpen = false;
                button1.Text = "打开";
            }
            else
            {
                // 如果串口未打开,则打开串口
                try
                {
                    if (serialPort == null)
                    {
                        serialPort = new SerialPort();
                    }
                    if (!serialPort.IsOpen && comboBox1.SelectedItem != null)
                    {
                        serialPort.PortName = comboBox1.SelectedItem.ToString();
                        serialPort.BaudRate = 115200;
                        serialPort.Parity = Parity.None;
                        serialPort.StopBits = StopBits.One;
                        serialPort.DataBits = 8;
                        serialPort.Handshake = Handshake.None;
                        serialPort.Open();
                        isSerialPortOpen = true;
                        button1.Text = "关闭";
                    }
                    else
                    {
                        MessageBox.Show("请选择一个串口或串口已经打开");
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("打开串口时出错: " + ex.Message);
                    if (serialPort != null)
                    {
                        serialPort.Close();
                        serialPort.Dispose();
                    }
                }
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (isSerialPortOpen)
            {
                try
                {
                    // 发送0x99给串口
                    byte[] sendData = { 0x99 };
                    serialPort.Write(sendData, 0, sendData.Length);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("发送数据时出错: " + ex.Message);
                }
            }
            else
            {
                MessageBox.Show("请先打开串口");
            }
        }

        private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                // 从串口读取接收到的数据
                string receivedData = serialPort.ReadLine();

                // 在界面上显示接收到的数据
                this.Invoke(new Action(() =>
                {
                    textBox1.AppendText(receivedData + Environment.NewLine);
                }));
            }
            catch (Exception ex)
            {
                MessageBox.Show("接收数据时出错: " + ex.Message);
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (serialPort != null && serialPort.IsOpen)
            {
                serialPort.Close();
                serialPort.Dispose();
            }
        }
    }
}
以上代码在原有的基础上添加了`serialPort_DataReceived`方法作为串口数据接收事件处理函数。在该方法中,我们使用`ReadLine()`方法从串口读取接收到的数据,并把它显示在`textBox1`控件上。

希望这次的修改能够满足你的需求!如果还有其他问题,请随时提问。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-3-4 22:31:29 | 显示全部楼层
FishC 发表于 2024-3-4 22:27
下面是在C#代码中添加串口接收功能的修改:

以上代码在原有的基础上添加了`serialPort_DataReceived ...

System.NullReferenceException:“未将对象引用设置到对象的实例。”
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-3-4 22:31:41 | 显示全部楼层
XYZ_XYZ 发表于 2024-03-04 22:31
System.NullReferenceException:“未将对象引用设置到 ...


System.NullReferenceException是一个常见的异常,它通常出现在访问一个未初始化或为空的对象时。在你的代码中,出现了这个异常的原因可能是以下几种情况:

1. 在使用serialPort对象之前,没有将其实例化。在Form1类的构造函数中,你应该添加以下代码来实例化serialPort对象:
serialPort = new SerialPort();

2. 串口选择的comboBox1的SelectedItem属性为空。在button1_Click事件处理程序中,在打开串口之前,你需要检查comboBox1.SelectedItem是否为空,如果为空,则应该给用户一个提示。

在解决以上问题之后,你的代码应该能够正常运行,避免出现NullReferenceException异常。如果还有其他问题,请提供完整的错误信息和相关代码,以便更好地帮助你。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-3-4 22:35:43 | 显示全部楼层
FishC 发表于 2024-3-4 22:31
System.NullReferenceException是一个常见的异常,它通常出现在访问一个未初始化或为空的对象时。在你 ...

请帮我把修改好的完整代码打印出来
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-3-4 22:36:05 | 显示全部楼层
XYZ_XYZ 发表于 2024-03-04 22:35
请帮我把修改好的完整代码打印出来


当然!下面是你修改过后的完整代码,请注意我添加的注释:
using System;
using System.IO.Ports;
using System.Windows.Forms;

namespace SerialPortApp
{
    public partial class Form1 : Form
    {
        // 创建一个SerialPort对象
        private SerialPort serialPort;

        public Form1()
        {
            InitializeComponent();

            // 实例化SerialPort对象
            serialPort = new SerialPort();

            // 将可用串口添加到comboBox1中
            string[] ports = SerialPort.GetPortNames();
            comboBox1.Items.AddRange(ports);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                // 检查串口选择是否为空
                if (comboBox1.SelectedItem == null)
                {
                    MessageBox.Show("请选择串口!");
                    return;
                }

                // 配置串口参数
                serialPort.PortName = comboBox1.SelectedItem.ToString();
                serialPort.BaudRate = 9600; // 波特率设置为9600bps
                serialPort.DataBits = 8; // 数据位设置为8位
                serialPort.StopBits = StopBits.One; // 停止位设置为1位停止位
                serialPort.Parity = Parity.None; // 校验位设置为无校验

                // 打开串口
                serialPort.Open();
                MessageBox.Show("串口已打开!");

                // 向串口发送数据
                string sendData = "Hello, Serial!";
                serialPort.Write(sendData);

                // 从串口接收数据
                string recvData = serialPort.ReadExisting();
                MessageBox.Show("接收到的数据:" + recvData);
            }
            catch (Exception ex)
            {
                MessageBox.Show("发生错误:" + ex.Message);
            }
            finally
            {
                // 关闭串口
                if (serialPort.IsOpen)
                {
                    serialPort.Close();
                    MessageBox.Show("串口已关闭!");
                }
            }
        }
    }
}

希望这次的代码能够正常运行,并且满足你的需求!如果还有其他问题,请随时提问。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-3-6 23:18:17 | 显示全部楼层
@FishC 我想用C#写一个上位机软件。
功能:打开软件,comBox1能够自动获取可用的串口端口。点击button1,打开串口;再点击button1,关闭串口。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-3-6 23:18:29 | 显示全部楼层
XYZ_XYZ 发表于 2024-03-06 23:18
@FishC 我想用C#写一个上位机软件。
功能:打开软件 ...


当然可以!我们来一步步实现这个功能。

首先,你需要在界面上添加一个ComboBox控件(comBox1)和两个Button控件(button1和button2),并给button1设置初始文本为"打开串口"。然后,在Form的Load事件中,我们需要使comBox1能够自动获取可用的串口端口。
using System.IO.Ports;

// Form的Load事件
private void Form1_Load(object sender, EventArgs e)
{
    string[] ports = SerialPort.GetPortNames(); // 获取所有可用的串口端口
    comBox1.Items.AddRange(ports); // 将可用的端口添加到comBox1中
}

接下来,我们需要在button1的Click事件中实现打开和关闭串口的功能。我们需要创建一个SerialPort对象,并判断当前串口的状态来执行相应的操作。
SerialPort serialPort;

// button1的Click事件
private void button1_Click(object sender, EventArgs e)
{
    if (serialPort == null)
    {
        // 如果serialPort为空,则表示串口未打开,需要打开串口
        string portName = comBox1.SelectedItem.ToString(); // 获取选中的串口端口
        serialPort = new SerialPort(portName, 9600, Parity.None, 8, StopBits.One); // 创建串口对象

        try
        {
            serialPort.Open(); // 打开串口
            button1.Text = "关闭串口"; // 修改button1的文本为"关闭串口"
        }
        catch (Exception ex)
        {
            // 打开串口失败,显示错误信息
            MessageBox.Show("打开串口失败:" + ex.Message);
            serialPort = null; // 将serialPort设置为空
        }
    }
    else
    {
        // 如果serialPort不为空,则表示串口已经打开,需要关闭串口
        serialPort.Close(); // 关闭串口
        serialPort.Dispose(); // 释放串口资源
        serialPort = null; // 将serialPort设置为空
        button1.Text = "打开串口"; // 修改button1的文本为"打开串口"
    }
}

至此,我们完成了上位机软件的基本功能。用户可以选择需要连接的串口端口,并通过点击button1来打开或关闭串口。希望对你有所帮助!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-3-6 23:36:24 | 显示全部楼层
FishC 发表于 2024-3-6 23:18
当然可以!我们来一步步实现这个功能。

首先,你需要在界面上添加一个ComboBox控件(comBox1)和两个B ...

我还要实现,下拉comBox1,能够显示出可用的串口
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-3-6 23:36:37 | 显示全部楼层
XYZ_XYZ 发表于 2024-03-06 23:36
我还要实现,下拉comBox1,能够显示出可用的串口


很好,我们可以在下拉comBox1的时候动态获取可用的串口,并将它们显示在下拉列表中。请按照以下步骤进行修改:1. 在Form的Load事件中,添加一个事件处理程序来处理串口状态更改事件。using System.IO.Ports;// Form的Load事件private void Form1_Load(object sender, EventArgs e){    string[] ports = SerialPort.GetPortNames(); // 获取所有可用的串口端口    comBox1.Items.AddRange(ports); // 将可用的端口添加到comBox1中    // 串口状态更改事件处理程序    SerialPort.SerialPinChangedEventHandler pinChangedHandler = new SerialPort.SerialPinChangedEventHandler(SerialPort_PinChanged);    SerialPort.DataReceivedEventHandler dataReceivedHandler = new SerialPort.DataReceivedEventHandler(SerialPort_DataReceived);    foreach (string portName in ports)    {        SerialPort serialPort = new SerialPort(portName);        serialPort.PinChanged += pinChangedHandler;        serialPort.DataReceived += dataReceivedHandler;        serialPort.Open();        serialPort.Close();    }}2. 添加两个事件处理程序,分别用于处理串口状态更改事件和接收到数据事件。// 处理串口状态更改事件private void SerialPort_PinChanged(object sender, SerialPinChangedEventArgs e){    SerialPort serialPort = (SerialPort)sender;    // 如果串口打开,则重新获取可用的串口端口    if (serialPort.IsOpen)    {        string[] ports = SerialPort.GetPortNames();        comBox1.Items.Clear();        comBox1.Items.AddRange(ports);    }}// 处理接收到数据事件private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e){    // 在这里处理接收到的数据}3. 在Form的Activated事件中,添加一个事件处理程序来处理串口状态更改事件。这是为了在程序已经打开的情况下,当用户切换回上位机软件时能够刷新可用的串口列表。private void Form1_Activated(object sender, EventArgs e){    comBox1.DroppedDown = true;}现在,当程序加载时会获取可用的串口并显示在下拉comBox1中。当串口状态发生更改时,会自动更新可用的串口列表。同时,当用户切换回上位机软件时,也会自动刷新可用的串口列表。希望对你有所帮助!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-3-8 23:47:32 | 显示全部楼层
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace 串口数据显示
{
    public partial class Form1 : Form
    {
        private SerialPort serialPort = null;
        private bool isSerialPortOpen = false;


        public Form1()
        {
            InitializeComponent();

            this.comboBox1.DropDown += new System.EventHandler(this.comboBox1_DropDown);
            this.button1.Click += new System.EventHandler(this.button1_Click);
            this.button2.Click += new System.EventHandler(this.button2_Click);
            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
        }
        private void comboBox1_DropDown(object sender, EventArgs e)
        {
            string[] ports = SerialPort.GetPortNames();
            comboBox1.Items.Clear();
            foreach (string port in ports)
            {
                comboBox1.Items.Add(port);
            }
            if (comboBox1.Items.Count > 0)
            {
                comboBox1.SelectedIndex = 0;
            }
        }
        private void button1_Click(object sender, EventArgs e)
        {
            if (isSerialPortOpen)
            {
                // 如果串口已打开,则关闭串口
                if (serialPort != null && serialPort.IsOpen)
                {
                    serialPort.Close();
                    serialPort.Dispose();
                }

                isSerialPortOpen = false;
                button1.Text = "打开";
            }
            else
            {
                // 如果串口未打开,则打开串口
                try
                {
                    if (serialPort == null)
                    {
                        serialPort = new SerialPort();
                    }

                    if (!serialPort.IsOpen && comboBox1.SelectedItem != null)
                    {
                        serialPort.PortName = comboBox1.SelectedItem.ToString();
                        serialPort.BaudRate = 115200;
                        serialPort.Parity = Parity.None;
                        serialPort.StopBits = StopBits.One;
                        serialPort.DataBits = 8;
                        serialPort.Handshake = Handshake.None;

                        serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
                        
                        serialPort.Open();

                        isSerialPortOpen = true;
                        button1.Text = "关闭";
                    }
                    else
                    {
                        MessageBox.Show("请选择一个串口或串口已经打开");
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("打开串口时出错: " + ex.Message);

                    if (serialPort != null)
                    {
                        serialPort.Close();
                        serialPort.Dispose();
                    }
                }
            }
        }
        


        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (serialPort != null && serialPort.IsOpen)
            {
                serialPort.Close();
                serialPort.Dispose();
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            textBox1.Clear();
            textBox2.Clear();
            textBox3.Clear();
            textBox4.Clear();
            textBox5.Clear();
        }

        private void button4_Click(object sender, EventArgs e)
        {
            if (isSerialPortOpen)
            {
                try
                {
                    // 定义要发送的16进制数据  
                    byte[] sendData = { 0xFF, 0x99, 0x01, 0x00, 0x9A, 0xFE };

                    // 将发送的字节数组转换为字符串,方便显示在textBox1中  
                    string sendStr = BitConverter.ToString(sendData).Replace("-", " ");

                    // 在textBox1中显示发送的数据  
                    textBox1.AppendText("发送数据: " + sendStr + "\r\n");

                    // 通过串口发送数据  
                    serialPort.Write(sendData, 0, sendData.Length);
                }
                catch (Exception ex)
                {
                    // 显示发送数据时发生的错误  
                    MessageBox.Show("发送数据时出错: " + ex.Message);
                }
            }
            else
            {
                // 如果串口未打开,则提示用户先打开串口  
                MessageBox.Show("请先打开串口");
            }
        }

        private void button5_Click(object sender, EventArgs e)
        {
            if (isSerialPortOpen)
            {
                try
                {
                    // 定义要发送的16进制数据  
                    byte[] sendData = { 0xFF, 0x33, 0x01, 0x00, 0x34, 0xFE };

                    // 将发送的字节数组转换为字符串,方便显示在textBox1中  
                    string sendStr = BitConverter.ToString(sendData).Replace("-", " ");

                    // 在textBox1中显示发送的数据  
                    textBox1.AppendText("发送数据: " + sendStr + "\r\n");

                    // 通过串口发送数据  
                    serialPort.Write(sendData, 0, sendData.Length);
                }
                catch (Exception ex)
                {
                    // 显示发送数据时发生的错误  
                    MessageBox.Show("发送数据时出错: " + ex.Message);
                }
            }
            else
            {
                // 如果串口未打开,则提示用户先打开串口  
                MessageBox.Show("请先打开串口");
            }
        }

        private void button6_Click(object sender, EventArgs e)
        {
            if (isSerialPortOpen)
            {
                try
                {
                    // 定义要发送的16进制数据  
                    byte[] sendData = { 0xFF, 0x99, 0x02, 0x00, 0x9B, 0xFE };

                    // 将发送的字节数组转换为字符串,方便显示在textBox1中  
                    string sendStr = BitConverter.ToString(sendData).Replace("-", " ");

                    // 在textBox1中显示发送的数据  
                    textBox1.AppendText("发送数据: " + sendStr + "\r\n");

                    // 通过串口发送数据  
                    serialPort.Write(sendData, 0, sendData.Length);
                }
                catch (Exception ex)
                {
                    // 显示发送数据时发生的错误  
                    MessageBox.Show("发送数据时出错: " + ex.Message);
                }
            }
            else
            {
                // 如果串口未打开,则提示用户先打开串口  
                MessageBox.Show("请先打开串口");
            }
        }

        private void button7_Click(object sender, EventArgs e)
        {
            if (isSerialPortOpen)
            {
                try
                {
                    // 定义要发送的16进制数据  
                    byte[] sendData = { 0xFF, 0x33, 0x02, 0x00, 0x35, 0xFE };

                    // 将发送的字节数组转换为字符串,方便显示在textBox1中  
                    string sendStr = BitConverter.ToString(sendData).Replace("-", " ");

                    // 在textBox1中显示发送的数据  
                    textBox1.AppendText("发送数据: " + sendStr + "\r\n");

                    // 通过串口发送数据  
                    serialPort.Write(sendData, 0, sendData.Length);
                }
                catch (Exception ex)
                {
                    // 显示发送数据时发生的错误  
                    MessageBox.Show("发送数据时出错: " + ex.Message);
                }
            }
            else
            {
                // 如果串口未打开,则提示用户先打开串口  
                MessageBox.Show("请先打开串口");
            }
        }

        // 定义 DataReceivedHandler 方法  
        private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort sp = (SerialPort)sender;
            string indata = sp.ReadExisting();

            if (indata.StartsWith(""))
            {
                string[] dataParts = indata.Split(' ');
                if (dataParts.Length >= 5)
                {
                    UpdateTextBoxes(dataParts);
                }
            }
        }

        // 另一个方法来更新文本框,以避免代码重复  
        private void UpdateTextBoxes(string[] dataParts)
        {
            if (textBox2.InvokeRequired)
            {
                textBox2.Invoke(new MethodInvoker(delegate { textBox2.Text = dataParts[1]; }));
            }
            else
            {
                textBox2.Text = dataParts[1];
            }

            if (textBox3.InvokeRequired)
            {
                textBox3.Invoke(new MethodInvoker(delegate { textBox3.Text = dataParts[2]; }));
            }
            else
            {
                textBox3.Text = dataParts[2];
            }

            if (textBox4.InvokeRequired)
            {
                textBox4.Invoke(new MethodInvoker(delegate { textBox4.Text = dataParts[3]; }));
            }
            else
            {
                textBox4.Text = dataParts[3];
            }

            if (textBox5.InvokeRequired)
            {
                textBox5.Invoke(new MethodInvoker(delegate { textBox5.Text = dataParts[4]; }));
            }
            else
            {
                textBox5.Text = dataParts[4];
            }
        }

        private void button8_Click(object sender, EventArgs e)
        {
            if (isSerialPortOpen)
            {
                try
                {
                    // 定义要发送的字符串  
                    string sendStr = "*1 2050 2121 510 004 ";

                    // 将字符串转换为字节数组  
                    byte[] sendData = Encoding.ASCII.GetBytes(sendStr);

                    // 添加发送的字符串到 textBox1 中  
                    textBox1.AppendText(sendStr + "\r\n");

                    // 发送数据到串口  
                    serialPort.Write(sendData, 0, sendData.Length);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("发送数据时出错: " + ex.Message);
                }
            }
            else
            {
                MessageBox.Show("请先打开串口");
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (isSerialPortOpen)
            {
                try
                {
                    // 定义要发送的字符串  
                    string sendStr = "*1 2048 3000 525 400";

                    // 将字符串转换为字节数组  
                    byte[] sendData = Encoding.ASCII.GetBytes(sendStr);

                    // 添加发送的字符串到 textBox1 中  
                    textBox1.AppendText(sendStr + "\r\n");

                    // 发送数据到串口  
                    serialPort.Write(sendData, 0, sendData.Length);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("发送数据时出错: " + ex.Message);
                }
            }
            else
            {
                MessageBox.Show("请先打开串口");
            }
        }
    }
}
@FishC 我串口发送数据的时候,我的DataReceivedHandler函数,并没有触发到。请帮我检查一下代码不合理的地方。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-3-8 23:48:04 | 显示全部楼层
XYZ_XYZ 发表于 2024-03-08 23:47
using System;
using System.Collections.Generic;
...


在您的代码中,将DataReceivedHandler方法与DataReceived事件绑定的部分有问题。在您的button1_Click方法中,您应该添加以下代码:
serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);

这样,当串口接收到数据时,DataReceived事件将触发DataReceivedHandler方法。

另外,在DataReceivedHandler方法中,您使用ReadExisting方法来获取接收到的数据。但是,ReadExisting方法将返回缓冲区中所有的字节,并且不保证接收到的数据长度为您期望的格式。建议您使用ReadLine或ReadTo方法来读取特定的结束字符或字符串,以确保接收到完整的数据。

下面是您修改后的代码:
using System;
using System.IO.Ports;
using System.Windows.Forms;

namespace 串口数据显示
{
    public partial class Form1 : Form
    {
        private SerialPort serialPort = null;
        private bool isSerialPortOpen = false;

        public Form1()
        {
            InitializeComponent();

            this.comboBox1.DropDown += new EventHandler(this.comboBox1_DropDown);
            this.button1.Click += new EventHandler(this.button1_Click);
            this.FormClosing += new FormClosingEventHandler(this.Form1_FormClosing);
            
            this.serialPort = new SerialPort();
            this.serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
        }

        private void comboBox1_DropDown(object sender, EventArgs e)
        {
            string[] ports = SerialPort.GetPortNames();
            comboBox1.Items.Clear();
            foreach (string port in ports)
            {
                comboBox1.Items.Add(port);
            }
            if (comboBox1.Items.Count > 0)
            {
                comboBox1.SelectedIndex = 0;
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (isSerialPortOpen)
            {
                // 如果串口已打开,则关闭串口
                if (serialPort != null && serialPort.IsOpen)
                {
                    serialPort.Close();
                    serialPort.Dispose();
                }

                isSerialPortOpen = false;
                button1.Text = "打开";
            }
            else
            {
                // 如果串口未打开,则打开串口
                try
                {
                    if (!serialPort.IsOpen && comboBox1.SelectedItem != null)
                    {
                        serialPort.PortName = comboBox1.SelectedItem.ToString();
                        serialPort.BaudRate = 115200;
                        serialPort.Parity = Parity.None;
                        serialPort.StopBits = StopBits.One;
                        serialPort.DataBits = 8;
                        serialPort.Handshake = Handshake.None;

                        serialPort.Open();

                        isSerialPortOpen = true;
                        button1.Text = "关闭";
                    }
                    else
                    {
                        MessageBox.Show("请选择一个串口或串口已经打开");
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("打开串口时出错: " + ex.Message);

                    if (serialPort != null)
                    {
                        serialPort.Close();
                        serialPort.Dispose();
                    }
                }
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (serialPort != null && serialPort.IsOpen)
            {
                serialPort.Close();
                serialPort.Dispose();
            }
        }

        // 定义 DataReceivedHandler 方法  
        private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort sp = (SerialPort)sender;
            string indata = sp.ReadLine();

            // 处理接收到的数据
            if (indata.StartsWith(""))
            {
                string[] dataParts = indata.Split(' ');
                if (dataParts.Length >= 5)
                {
                    UpdateTextBoxes(dataParts);
                }
            }
        }

        // 另一个方法来更新文本框,以避免代码重复  
        private void UpdateTextBoxes(string[] dataParts)
        {
            if (textBox2.InvokeRequired)
            {
                textBox2.Invoke(new MethodInvoker(delegate { textBox2.Text = dataParts[1]; }));
            }
            else
            {
                textBox2.Text = dataParts[1];
            }

            if (textBox3.InvokeRequired)
            {
                textBox3.Invoke(new MethodInvoker(delegate { textBox3.Text = dataParts[2]; }));
            }
            else
            {
                textBox3.Text = dataParts[2];
            }

            if (textBox4.InvokeRequired)
            {
                textBox4.Invoke(new MethodInvoker(delegate { textBox4.Text = dataParts[3]; }));
            }
            else
            {
                textBox4.Text = dataParts[3];
            }

            if (textBox5.InvokeRequired)
            {
                textBox5.Invoke(new MethodInvoker(delegate { textBox5.Text = dataParts[4]; }));
            }
            else
            {
                textBox5.Text = dataParts[4];
            }
        }

        // 其他按钮事件处理方法...
    }
}

希望这可以解决您的问题!如果还有其他问题,请随时提问。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 前天 15:38 | 显示全部楼层
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "lusb0_usb.h"

#define m_dev_VENDOR    0xC251         /* Vendor ID of the m_dev         */
#define m_dev_PRODUCT   0x3505         /* Product ID of the m_dev        */
#define m_dev_DATA_SIZE 10240    /* Number of bytes to get at once */

char RecTraData[m_dev_DATA_SIZE] = {0};
struct usb_bus *bus;
struct usb_device * m_dev = NULL;
struct usb_dev_handle *m_dev_handle;

int OpenUSB(void)
{
    int ret;

    QString str;

    /* 第1步:USB 初始化 */
    usb_init();

    /* 第2步:寻找系统上的usb总线,任何usb设备都通过usb总线和计算机总线通信。进而和其他设备通信。此函数返回总线数 */
    ret = usb_find_busses();
    if(ret < 0)
    {
        qDebug("usb_find_busses失败 \n") ;
    }

    /* 第3步:寻找总线上的usb设备,这个函数必要在调用usb_find_busses()后使用。以上的三个函数都是一开始就要用到的,此函数返回设备数量 */
    ret = usb_find_devices();
    if(ret < 0)
    {
        str = str.asprintf("寻找USB设备失败:%d", ret);
        qDebug() << str;
        return -1;
    }
    else
    {
        /* 成功 */
        str = str.asprintf("寻找USB设备成功:%d", ret);
        qDebug() << str;
    }

    /*  第4步:这个函数返回总线的列表,在高一些的版本中已经用不到了 */
    for(bus = usb_get_busses(); bus; bus = bus->next)
    {
        struct usb_device *dev;
        for(dev = bus->devices; dev; dev = dev->next)
        {
            if(dev->descriptor.idVendor == m_dev_VENDOR && dev->descriptor.idProduct == m_dev_PRODUCT)
                m_dev=dev;
        }
    }
    if(!m_dev)
    {
        str = str.asprintf("设备查找失败:%d", -2);
        qDebug() << str;
        return -2;
    }

    /*  第5步:
        打开要使用的设备,在对硬件进行操作前必须要调用usb_open 来打开设备,这里大家看到有两个结构体
        usb_dev_handle 和 usb_device 是我们在开发中经常碰到的,有必要把它们的结构看一看。在libusb 中
        的usb.h和usbi.h中有定义。

        这里我们不妨理解为返回的 usb_dev_handle 指针是指向设备的句柄,而行参里输入就是需要打开的设备。
    */
    m_dev_handle = usb_open(m_dev);
    if(!m_dev_handle)
    {
        str = str.asprintf("设备打开失败:%d", -3);
        qDebug() << str;
        return -3;
    }

    /*  第6步:
        设置当前设备使用的configuration,参数configuration 是你要使用的configurtation descriptoes中的
        bConfigurationValue, 返回0成功,<0失败( 一个设备可能包含多个configuration,比如同时支持高速和
        低速的设备就有对应的两个configuration,详细可查看usb标准)

    */
    if(usb_set_configuration(m_dev_handle, 1) < 0)
    {
         qDebug() << "config失败" ;
        return -4;
    }

    /*
        第7步:
        注册与操作系统通信的接口,这个函数必须被调用,因为只有注册接口,才能做相应的操作。

        Interface 指 bInterfaceNumber. (usb_release_interface 与之相对应,也是必须调用的函数)
    */
    if(usb_claim_interface(m_dev_handle, 0) < 0) //claim_interface 0指向第一个设备
    {
        qDebug() << "Interface失败" ;
        return -5;
    }

    return 1;
}


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 动态填充 comboBox_DZXG
    for (int i = 0; i <= 255; ++i) {
        ui->comboBox_DZXG->addItem(QString::number(i));
    }

    // 动态填充 comboBox_DZ
    for (int i = 0; i <= 255; ++i) {
        ui->comboBox_DZ->addItem(QString::number(i));
    }

    // 动态填充 comboBox_TD
    for (int i = 1; i <= 12; ++i) {
        ui->comboBox_TD->addItem(QString::number(i));
    }

    // 动态填充 comboBox_SJ
    for (int i = 0; i <= 15; ++i) {
        ui->comboBox_SJ->addItem(QString::number(i));
    }

    // 连接 pushButton_SJ 的 clicked() 信号到槽函数
    connect(ui->pushButton_SJ, &QPushButton::clicked, this, &MainWindow::onPushButtonSJClicked);

    // 动态填充 comboBox_DZ_all
    for (int i = 0; i <= 255; ++i) {
        ui->comboBox_DZ_all->addItem(QString::number(i));
    }

    // 为每个通道的衰减comboBox动态添加值
    for (int channel = 1; channel <= 12; ++channel) {
        QString comboBoxName = QString("comboBox_SJ_TD_%1").arg(channel);
        QComboBox *comboBox = this->findChild<QComboBox *>(comboBoxName);
        if (comboBox) {
            // 清除现有项(如果有的话)
            comboBox->clear();
            // 添加新的十六进制值
            for (int value = 0; value <= 15; ++value) {
                comboBox->addItem(QString::number(value)); // 直接添加十进制数字
            }
        } else {
            // 如果没有找到组合框,可以打印一个错误或警告
            qDebug() << "ComboBox" << comboBoxName << "not found";
        }
    }

}

MainWindow::~MainWindow()
{
    delete ui;
}



void MainWindow::onPushButtonSJClicked()
{
    // 假设你已经有了以下变量,它们代表了组合包所需的各个部分
    QString dzHex = QString::number(ui->comboBox_DZ->currentIndex(), 16).toUpper().rightJustified(2, '0'); // 地址
    QString tdHex = QString::number(ui->comboBox_TD->currentIndex() + 1, 16).toUpper().rightJustified(2, '0'); // 通道
    QString sjHex = QString::number(ui->comboBox_SJ->currentIndex(), 16).toUpper().rightJustified(2, '0'); // 衰减值

    // 构建HEX包(不包括校验和)
    QByteArray hexPacket;
    // 帧头1: 5A
    hexPacket.append(static_cast<char>(0x5A));
    // 帧头2: A5
    hexPacket.append(static_cast<char>(0xA5));
    // 命令分类: FD
    hexPacket.append(static_cast<char>(0xFD));
    // 地址: dzHex
    hexPacket.append(QByteArray::fromHex(dzHex.toLatin1()));
    // 通道: tdHex
    hexPacket.append(QByteArray::fromHex(tdHex.toLatin1()));
    // 衰减值: sjHex
    hexPacket.append(QByteArray::fromHex(sjHex.toLatin1()));

    // 计算校验和: 通常是对除了帧头和校验和之外的所有字节进行求和,然后取低8位
    quint8 checksum = 0;
    for (int i = 2; i < hexPacket.size(); ++i) { // 跳过帧头(索引0和1)
        checksum += static_cast<quint8>(hexPacket.at(i));
    }
    checksum &= 0xFF; // 取低8位

    // 添加校验和到HEX包
    hexPacket.append(static_cast<char>(checksum));

    // // 获取hexPacket的指针和大小
    // const char* dataPtr = hexPacket.constData();
    // int dataSize = hexPacket.size();

    // 打印HEX包(用于调试)
    qDebug() << "HEX Packet:" << hexPacket.toHex().toUpper();
}


void MainWindow::on_pushButton_SJ_all_clicked()
{
    // 获取地址值
    int address = ui->comboBox_DZ_all->currentIndex(); // 假设这里返回的是地址的索引值,你可能需要转换它
    // 转换为十六进制字符串,假设地址是单个字节
    QString dzHex = QString::number(address, 16).toUpper().rightJustified(2, '0');

    // 初始化HEX包
    QByteArray hexPacket;

    // 添加帧头
    hexPacket.append(static_cast<char>(0x5A)); // 帧头1: 5A
    hexPacket.append(static_cast<char>(0xA5)); // 帧头2: A5

    // 添加命令分类
    hexPacket.append(static_cast<char>(0xFC)); // 命令分类: FC

    // 添加地址
    hexPacket.append(QByteArray::fromHex(dzHex.toLatin1()));

    // 添加通道衰减值
    for (int i = 1; i <= 12; ++i) {
        QString comboBoxName = QString("comboBox_SJ_TD_%1").arg(i);
        QComboBox *comboBox = findChild<QComboBox *>(comboBoxName);
        if (comboBox) {
            int attenuation = comboBox->currentIndex(); // 假设这里返回的是衰减值的索引值
            QString attenuationHex = QString::number(attenuation, 16).toUpper().rightJustified(2, '0');
            hexPacket.append(QByteArray::fromHex(attenuationHex.toLatin1()));
        }
    }

    // 计算校验和
    quint8 checksum = 0;
    for (int i = 2; i < hexPacket.size(); ++i) { // 从命令分类开始计算
        checksum += static_cast<quint8>(hexPacket.at(i));
    }
    checksum &= 0xFF; // 取低8位

    // 添加校验和
    hexPacket.append(static_cast<char>(checksum));

    // 打印HEX包(用于调试)
    qDebug() << "HEX Packet:" << hexPacket.toHex().toUpper();
}


void MainWindow::on_pushButton_openUSB_clicked()
{
    // QString debugstr;
    int usbstatus;
    // char str[64];

    if (UsbOpenFlag == 0)//如果USB没打开
    {
        /* 打开USB连接 */
        usbstatus = OpenUSB();
        if (usbstatus == -1)
        {
            ui->label_stateUSB->setText("USB开关状态:寻找USB设备失败");
        }
        else if (usbstatus == -2)
        {
            ui->label_stateUSB->setText("USB开关状态:设备查找失败,VID和PID不匹配");
        }
        else if (usbstatus == -3)
        {
            ui->label_stateUSB->setText("USB开关状态:设备打开失败");
        }
        else if (usbstatus == -4)
        {
            ui->label_stateUSB->setText("USB开关状态:设备打开失败4");
        }
        else if (usbstatus == -5)
        {
            ui->label_stateUSB->setText("USB开关状态:设备打开失败5");
        }
        else
        {
            /* 打开成功 */
            UsbOpenFlag = 1;

            ui->label_stateUSB->setText("USB开关状态:设备打开成功");

            ui->pushButton_openUSB->setText("关闭USB");

        }
    }
    else
    {
        UsbOpenFlag = 0;
        DataSampleStart = 0;
        usb_release_interface(m_dev_handle, 0);
        //usb_reset(m_dev_handle);
        usb_close(m_dev_handle);
        ui->label_stateUSB->setText("USB开关状态:关闭");
        ui->pushButton_openUSB->setText("打开USB");
    }
}

@FishC 以上是我用QT写的上位机,它现在的问题是,当我关闭USB设备后重新打开,ret = usb_find_devices();ret得到的数是0

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 前天 15:38 | 显示全部楼层
XYZ_XYZ 发表于 2024-06-11 15:38
#include "mainwindow.h"
#include "ui_mainwindow.h ...


你好!根据你提供的代码,我发现在重新打开USB设备之前,并没有调用`usb_release_interface`和`usb_close`来释放之前打开的设备资源。这可能导致在重新打开设备时出现问题。

你可以在关闭USB设备之前添加以下代码来释放资源:
if (m_dev_handle) {
    usb_release_interface(m_dev_handle, 0);
    usb_close(m_dev_handle);
    m_dev_handle = NULL;
}

这样,在重新打开设备时就能够正确地找到设备了。

另外,你还可以尝试在重新打开设备之前先调用`usb_reset`函数来复位USB设备:
usb_reset(m_dev_handle);

这样做有助于确保设备处于可用状态,并且可以避免一些潜在的通信问题。

更正以上的问题后,你的代码应该可以正常打开和关闭USB设备了。希望对你有帮助!如果还有其他问题,请随时提问。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 昨天 18:48 | 显示全部楼层
我记得用python写很方便来着,哦说错了,好像是labview,b站有别人写好的,可以直接拿来用
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-6-13 21:56

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表