C#学习笔记5:Winform简单上位机串口助手的实现

今日尝试使用C#进行 自己编写一款上位机串口助手:

文章提供源码、解释、测试效果、整体工程下载......

以下为串口助手的界面效果:

目录

1、功能设计与摆放控件:

2.遇到的报错解决与基本提示:

C#System.InvalidOperationException:“线程间操作无效: 从不是创建控件“textBox3”的线程访问它。

设置串口端口停止位波特率等时的数据强制类型转换规范:

串口接收触发异常:

 串口对象的一些常用属性赋值实例:

串口发送信息:

串口接收信息:

3、实际功能展示截图:

4、在没有下位机情况下调试上位机串口的方法:

5、整体工程代码:

6、整体测试工程下载:


1、功能设计与摆放控件:

与之前的串口发送不同,我们此次做的是兼顾接收与发送的串口助手,预设功能如下:

1、串口选择

2、波特率

3、接收显示窗口

4、切换十六进制发送接收功能

5、清除接收区

6、必要的交互按钮

最大的接收区我使用的组件是:richTextBox

清除接收区、打开关闭串口、发送等按键使用的组件是:button

16进制显示、发送我使用了可以框选和不选的组件:checkBox

单条发送后的文本输入区我使用的组件是:textBox(相比richTextBox只有一行)

2.遇到的报错解决与基本提示:

C#System.InvalidOperationException:“线程间操作无效: 从不是创建控件“textBox3”的线程访问它。

这项报错是我  试图   将串口接收到的信息打印在richTextBox1组件时遇到的

解决方案 是在创建窗体时增加代码 禁止编译器对跨线程访问做检查:

          Control.CheckForIllegalCrossThreadCalls = false;

查阅资料网址:

C#System.InvalidOperationException:“线程间操作无效: 从不是创建控件“textBox3”的线程访问它。” - 知乎

设置串口端口停止位波特率等时的数据强制类型转换规范:

串口接收触发异常:

 在串口部分,每个属性的数据类型不太相同,所以赋值时需要注意一下

我们一般将combox下拉框中的text属性作为设置的相应值赋值给seriesport的某些属性,但数据类型不一定对应,所以赋值的时候需要作对应的强制类型转换:

 对于停止位设置进入串口服务事件函数的相关问题,在以下代码注释中有所说明:

              serialPort1.PortName = comboBox1.Text;//设置端口号
              serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);//设置端口波特率
              serialPort1.StopBits = (StopBits)Convert.ToInt32(comboBox3.Text);//设置停止位
              serialPort1.DataBits = Convert.ToInt32(comboBox4.Text);//设置数据位
              /*  一定要加上下面这句话。
               *  意思是接收缓冲区其中假设有一个字节的话就出发接收函数。
                  假设不加上这句话,
                  那就有时候触发接收都发了好多次了也设有触发接收,有时候延时现象等等
              */
              serialPort1.ReceivedBytesThreshold = 1;
              serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);

              serialPort1.Open();                   //打开串口

 串口对象的一些常用属性赋值实例:

这也是我查阅网上资料获取的笔记摘录,为的是

SerialPort mySerialPort = new SerialPort("COM2");//端口
mySerialPort.BaudRate = 9600;//波特率
mySerialPort.Parity = Parity.None;//校验位
mySerialPort.StopBits = StopBits.One;//停止位
mySerialPort.DataBits = 8;//数据位
mySerialPort.Handshake = Handshake.Non;
mySerialPort.ReadTimeout = 1500;
mySerialPort.DtrEnable = true;//启用数据终端就绪信息
mySerialPort.Encoding = Encoding.UTF8;
mySerialPort.ReceivedBytesThreshold = 1;//DataReceived触发前内部输入缓冲器的字节数
mySerialPort.DataReceived += new SerialDataReceivedEvenHandler(DataReceive_Method);
 
mySerialPort.Open();//打开串口

串口发送信息:

Write(Byte[], Int32, Int32) :将指定数量的字节写入串行端口
Write(Char[], Int32, Int32) :将指定数量的字符写入串行端口
Write(String) :将指定的字符串写入串行端口
WriteLine(String) :将指定的字符串和NewLine值写入输出缓冲区

// Write a string
port.Write("Hello World");
 
// Write a set of bytes
port.Write(new byte[] { 0x0A, 0xE2, 0xFF }, 0, 3);
 
// Close the port关闭串口
port.Close();

串口接收信息:

Read(Byte[], Int32, Int32):从SerialPort输入缓冲区读取一些字节,并将那些字节写入字节数组中指定的偏移量处
ReadByte():从SerialPort输入缓冲区中同步读取一个字节
ReadChar(): 从SerialPort输入缓冲区中同步读取一个字符
ReadExisting() :在编码的基础上,读取SerialPort对象的流和输入缓冲区中所有立即可用的字节
ReadLine() :一直读取到输入缓冲区中的NewLine值
ReadTo(String) :一直读取到输入缓冲区中的指定value的字符串

查阅资料网址:

C#实现串口通信解析_c#串口通讯-CSDN博客

3、实际功能展示截图:

 主要是逻辑正确的弹窗提示,弹窗是调用函数实现的:

                MessageBox.Show("当前串口有设备连接,串口已成功打开", "串口发送提示");

打开串口检测端口有无设备:

关闭串口:

串口接收测试:

使用单片机发送字符串测试接收功能 :

接收测试成功,效果成功在richTextBox组件打印:

勾选16进制显示设置:

是否被勾选的判断语句如下示例:

                    //如果不是16进制发送,就直接string形式发送
                    if (!checkBox2.Checked)

清除接收区:

清除richTextBox的接收区可以直接调用函数:

            richTextBox1.Clear();

4、在没有下位机情况下调试上位机串口的方法

这里我使用虚拟串口软件,给电脑的COM3与COM4搭建了虚拟的串口连接:

软件下载地址如下:

https://download.csdn.net/download/qq_64257614/89044980

如图使用软件对COM3与COM4进行虚拟连接:

 使用SSCOM串口助手软件成功检测到COM3与COM4的虚拟连接:

 发送测试:

5、整体工程代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Button;


namespace Serial_Assistant_YZH
{

    public partial class Form1 : Form
    {
        int CXk1 = 0;//蔡徐坤图片计数器


        public Form1()
        {
            InitializeComponent();
            serialPort1.Encoding = Encoding.GetEncoding("GB2312");     //串口接收编码
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        private void Form1_Load(object sender, EventArgs e)
        {

            for(int i=1;i<10;i++)//初始化串口号下拉框内容
            {
                comboBox1.Items.Add("COM" + i.ToString()); //添加串口
            }

            for (int H = 0; H < 5; H++)//初始化串口波特率下拉框内容
            {
                switch (H)
                {
                    case 0: comboBox2.Items.Add("2400");   break;
                    case 1: comboBox2.Items.Add("4800");   break;
                    case 2: comboBox2.Items.Add("9600");   break;
                    case 3: comboBox2.Items.Add("115200"); break;
                }
            }

            for(int j=0;j<3;j++)
            {
                switch (j)
                {
                    case 0: comboBox3.Items.Add("1"); break;
                    case 1: comboBox3.Items.Add("1.5"); break;
                    case 2: comboBox3.Items.Add("2"); break;
                }
            }

            comboBox1.Text = "COM1";//端口下拉框初始值
            comboBox2.Text = "9600";//波特率下拉框初始值
            comboBox3.Text = "1";//停止位
            comboBox4.Text = "8";//数据位

            serialPort1.Close();   //关闭串行端口连接
        }

        //数据接收显示
        private void richTextBox1_TextChanged(object sender, EventArgs e){}

        //打开串口
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                serialPort1.PortName = comboBox1.Text;//设置端口号
                serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);//设置端口波特率
                serialPort1.StopBits = (StopBits)Convert.ToInt32(comboBox3.Text);//设置停止位
                serialPort1.DataBits = Convert.ToInt32(comboBox4.Text);//设置数据位

                /*  一定要加上下面这句话。
                 *  意思是接收缓冲区其中假设有一个字节的话就出发接收函数。
                    假设不加上这句话,
                    那就有时候触发接收都发了好多次了也设有触发接收,有时候延时现象等等
                */
                serialPort1.ReceivedBytesThreshold = 1;
                serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);

                serialPort1.Open();                   //打开串口

                MessageBox.Show("当前串口有设备连接,串口已成功打开", "串口发送提示");
                //按键状态置位
                button1.Enabled = false;
                button2.Enabled = true;
            }
            catch
            {
                MessageBox.Show("端口无设备连接", "错误警告");
            }
        }

        //关闭串口
        private void button2_Click(object sender, EventArgs e)
        {
            try
            {
                serialPort1.Close(); //关闭串口        
                //按键状态置位
                button1.Enabled = true;
                button2.Enabled = false;
                MessageBox.Show("已关闭串口", "串口发送提示");
            }
            catch{}
        }

        
        private void label3_Click(object sender, EventArgs e){}

        //单条发送数据
        private void button3_Click(object sender, EventArgs e)
        {
            byte[] Data = new byte[1];                                                         //单字节发数据     
            if (serialPort1.IsOpen)
            {
                if (textBox1.Text != "")
                {
                    //如果不是16进制发送,就直接string形式发送
                    if (!checkBox2.Checked)
                    {
                        try
                        {
                            serialPort1.Write(textBox1.Text);
                            //serialPort1.WriteLine();                             //字符串写入
                        }
                        catch
                        {
                            MessageBox.Show("串口数据写入错误", "错误");
                        }
                    }
                    else                                                                    //数据模式
                    {
                        try                                                                 //如果此时用户输入字符串中含有非法字符(字母,汉字,符号等等,try,catch块可以捕捉并提示)
                        {
                            for (int i = 0; i < (textBox1.Text.Length - textBox1.Text.Length % 2) / 2; i++)//转换偶数个
                            {
                                Data[0] = Convert.ToByte(textBox1.Text.Substring(i * 2, 2), 16);           //转换
                                serialPort1.Write(Data, 0, 1);
                            }
                            if (textBox1.Text.Length % 2 != 0)
                            {
                                //单独处理最后一个字符
                                Data[0] = Convert.ToByte(textBox1.Text.Substring(textBox1.Text.Length - 1, 1), 16);
                                serialPort1.Write(Data, 0, 1);//写入
                            }
                            //Data = Convert.ToByte(textBox2.Text.Substring(textBox2.Text.Length - 1, 1), 16);
                        }
                        catch
                        {
                            MessageBox.Show("数据转换错误,请输入数字。", "错误");
                        }
                    }
                }
            }

        }

        //单条发送输入文本框
        private void textBox1_TextChanged(object sender, EventArgs e)
        {

        }

        //串口接收
        //一个接收数据事件获取串口发送来的数据
        private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            //处理事件这块可以加上延时确保不定数的数据可以全部收到缓冲后,才去读缓冲内容--单位: 毫秒
            Thread.Sleep(50);
            //如果16进制转换没被勾选
            if (!checkBox1.Checked)
            {
                richTextBox1.AppendText(serialPort1.ReadExisting());  //串口类会自动处理汉字,所以不需要特别转换
            }
            //如果16进制转换被勾选了
            else
            {
                try
                {
                    //定义缓冲区数组大小为串口缓冲区数据的字节数
                    //因为串口事件触发时有可能收到不止一个字节
                    byte[] data = new byte[serialPort1.BytesToRead];
                    serialPort1.Read(data, 0, data.Length);

                    foreach (byte Member in data)  //遍历用法
                    {
                        string str = Convert.ToString(Member, 16).ToUpper();
                        richTextBox1.AppendText("0x" + (str.Length == 1 ? "0" + str : str) + " ");
                    }
                }
                catch { }
            }
        }

        //蔡徐坤图片
        private void pictureBox1_Click(object sender, EventArgs e)
        {
            int CXK = 0;
            CXk1++;
            CXK = CXk1 % 5;
            switch(CXK)
            {
                case 0: MessageBox.Show("你干嘛~哎呦!", "蔡徐坤");label6.Text = "哎呦!"; break;
                case 1: MessageBox.Show("唱跳、RAP、篮球、Music!", "蔡徐坤"); label6.Text = "篮球"; break;
                case 2: MessageBox.Show("鸡你太美~Baby", "蔡徐坤"); label6.Text = "鸡你太美!"; break;
                case 3: MessageBox.Show("练习时长两年半", "蔡徐坤"); label6.Text = "两年半!"; break;
                case 4: MessageBox.Show("别点了,认真学习", "蔡徐坤"); label6.Text = "认真学习!"; break;
            }

        }

        //清除接收区
        private void button4_Click(object sender, EventArgs e)
        {
            richTextBox1.Clear();
            MessageBox.Show("已清除接收区", "清除接收区");
        }

        //停止位
        private void comboBox3_SelectedIndexChanged(object sender, EventArgs e){}
        //数据位
        private void comboBox4_SelectedIndexChanged(object sender, EventArgs e){}
        //串口选择
        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e){}
        //波特率选择
        private void comboBox2_SelectedIndexChanged(object sender, EventArgs e){}
        //
        private void checkBox1_CheckedChanged(object sender, EventArgs e){}
        //16进制发送按钮
        private void checkBox2_CheckedChanged(object sender, EventArgs e){}
    }
}

6、整体测试工程下载:

https://download.csdn.net/download/qq_64257614/89044991?spm=1001.2014.3001.5503

  • 30
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NULL指向我

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值