C#winform上位机开发学习笔记11-串口助手接收数据用波形显示功能添加

1.功能描述

接收串口数据,并将收到的十六进制数据用坐标系的方式将数据波形展示出来

2.代码部分

步骤1:定义链表,用于数据保存

        //数据结构-线性链表
        private List<byte> DataList = new List<byte>();

步骤2:定义波形颜色

        //定义波形颜色
        private Pen LinesPen = new Pen(Color.FromArgb(0xFF,0x00,0x00));//FF 00 00 为红色

步骤3:绘制接收数据的波形

            //如果数据量大于可容纳的数据量,则删除最左数据
            if (DataList.Count >= (this.ClientRectangle.Width - StartPrint) / DrawStep)
            {
                DataList.RemoveRange(0, DataList.Count - (this.ClientRectangle.Width - StartPrint) / DrawStep);
            }
            //绘制波形
            for(int i = 0; i < DataList.Count - 1; i++)
            {
                //点与点之间做直线连接
                e.Graphics.DrawLine(LinesPen, StartPrint + i * DrawStep, StartPrint + Unit_length * 16 - DataList[i] * (Unit_length / 16), StartPrint + (i + 1) * DrawStep, StartPrint + Unit_length * 16 - DataList[i + 1] * (Unit_length / 16));
            }

步骤4:定义数据添加函数

//定义链表尾部添加数据
        public void AddDataToWaveList(byte[] Data)
        {
            for (int i = 0; i < Data.Length; i++)
                DataList.Add(Data[i]);
            this.Invalidate();//刷新显示
        }

步骤5:串口接收函数中添加代码

                    //更新波形显示窗体的链表数据
                    if(WaveForm != null)
                    {
                        WaveForm.AddDataToWaveList(data);
                    }

完整接收函数

//串口接收事件
        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {

            //接收格式为ASCII码
            if (!checkBox16.Checked)//复用框没有被选择时
            {
                try
                {
                    textBox1.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "]" + "->");

                    string str = serialPort1.ReadExisting();//将接收到的数据存在自定义的字符串变量中
                    textBox1.AppendText(str + "\r\n");

                    //统计接收字节数
                    UInt32 RBytes = Convert.ToUInt32(textBox7.Text, 10);//定义接收字节数变量,并初始化为已接收字节数
                    RBytes += (UInt32)str.Length;//加ASCII码字节数
                    textBox7.Text = Convert.ToString(RBytes, 10);//显示总接收字节数
                }
                catch
                {
                    textBox1.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "]" + "->");
                    textBox1.AppendText("ASCII格式接收错误!\r\n");
                }
            }
            //接收格式为HEX
            else
            {
                try
                {
                    //断帧功能
                    if (Timer4_Flag == true)
                    {
                        Timer4_Flag = false;

                        textBox1.AppendText("\r\n");
                        textBox1.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "]" + "->");
                    }

                    //textBox1.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "]" + "->"); //此处被断帧功能替代换行

                    byte[] data = new byte[serialPort1.BytesToRead];//定义接收缓冲区,长度为串口接收的数据长度
                    serialPort1.Read(data, 0, data.Length);//形参,起始位置,终止位置,将读取的数据存放在缓冲区

                    //遍历用法
                    foreach (byte Member in data)//循环函数
                    {
                        string str = Convert.ToString(Member, 16).ToUpper();//转化为十六进制大写
                        textBox1.AppendText((str.Length == 1 ? "0" + str : str) + " ");//数据+空格“”
                    }
                    //textBox1.AppendText("\r\n"); //此处被断帧功能替代换行

                    //统计接收字节数
                    UInt32 RBytes = Convert.ToUInt32(textBox7.Text, 10);//定义接收字节数变量,并初始化为已接收字节数
                    RBytes += (UInt32)data.Length;//加HEX字节数
                    textBox7.Text = Convert.ToString(RBytes, 10);//显示总接收字节数

                    //更新波形显示窗体的链表数据
                    if(WaveForm != null)
                    {
                        WaveForm.AddDataToWaveList(data);
                    }

                }
                catch
                {
                    textBox1.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "]" + "->");
                    textBox1.AppendText("HEX格式接收错误!\r\n");
                }
            }
        }

步骤6:接收HEX数据波形显示时,子窗体出现抖动现象原因:缓存不足,启动双缓存显示,缓存1用于窗体显示,缓存2用于波形更新

        public WaveForm()
        {
            //波形稳定刷新
            //开启双缓冲
            this.SetStyle(ControlStyles.DoubleBuffer | 
                          ControlStyles.UserPaint    |
                          ControlStyles.AllPaintingInWmPaint,
                          true);
            this.UpdateStyles();

            InitializeComponent();
        }

#加入此代码后波形显示不再抖动

步骤7:打开波形显示时同步打开串口及HEX勾选

方便再重复两次操作

这里参考了博客CheckBox控件的用法

            //打开波形显示按钮时同步打开串口
            if (!serialPort1.IsOpen)
            {    
                button2.PerformClick();//打开串口按钮单击事件
                this.checkBox16.Checked = true; //打开HEX勾选
            }

3.完整代码

//主窗体程序
//串口接收事件
        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {

            //接收格式为ASCII码
            if (!checkBox16.Checked)//复用框没有被选择时
            {
                try
                {
                    textBox1.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "]" + "->");

                    string str = serialPort1.ReadExisting();//将接收到的数据存在自定义的字符串变量中
                    textBox1.AppendText(str + "\r\n");

                    //统计接收字节数
                    UInt32 RBytes = Convert.ToUInt32(textBox7.Text, 10);//定义接收字节数变量,并初始化为已接收字节数
                    RBytes += (UInt32)str.Length;//加ASCII码字节数
                    textBox7.Text = Convert.ToString(RBytes, 10);//显示总接收字节数
                }
                catch
                {
                    textBox1.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "]" + "->");
                    textBox1.AppendText("ASCII格式接收错误!\r\n");
                }
            }
            //接收格式为HEX
            else
            {
                try
                {
                    //断帧功能
                    if (Timer4_Flag == true)
                    {
                        Timer4_Flag = false;

                        textBox1.AppendText("\r\n");
                        textBox1.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "]" + "->");
                    }

                    //textBox1.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "]" + "->"); //此处被断帧功能替代换行

                    byte[] data = new byte[serialPort1.BytesToRead];//定义接收缓冲区,长度为串口接收的数据长度
                    serialPort1.Read(data, 0, data.Length);//形参,起始位置,终止位置,将读取的数据存放在缓冲区

                    //遍历用法
                    foreach (byte Member in data)//循环函数
                    {
                        string str = Convert.ToString(Member, 16).ToUpper();//转化为十六进制大写
                        textBox1.AppendText((str.Length == 1 ? "0" + str : str) + " ");//数据+空格“”
                    }
                    //textBox1.AppendText("\r\n"); //此处被断帧功能替代换行

                    //统计接收字节数
                    UInt32 RBytes = Convert.ToUInt32(textBox7.Text, 10);//定义接收字节数变量,并初始化为已接收字节数
                    RBytes += (UInt32)data.Length;//加HEX字节数
                    textBox7.Text = Convert.ToString(RBytes, 10);//显示总接收字节数

                    //更新波形显示窗体的链表数据
                    if(WaveForm != null)
                    {
                        WaveForm.AddDataToWaveList(data);
                    }

                }
                catch
                {
                    textBox1.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "]" + "->");
                    textBox1.AppendText("HEX格式接收错误!\r\n");
                }
            }
        }
//波形显示按钮事件
        private void button30_Click(object sender, EventArgs e)
        {
            //第一次创建WaveForm实体
            if (WaveForm == null)
            {
                //创建新窗体
                WaveForm = new WaveForm();
            }
            else
            {
                //多次创建通过判断IsDisposed确定窗口是否已经关闭,避免同窗口多开
                if (WaveForm.IsDisposed == true)//判断该控件有无释放,若释放则重新创建窗体
                {
                    //如果窗体已经关闭,需要重新创新
                    WaveForm = new WaveForm();
                }
            }
            //新建窗体
            //WaveForm = new WaveForm();//发现不用if语句判断直接创建窗体也能实现一样的功能
            //窗体展示
            WaveForm.Show();

            //设置波形窗体紧靠主窗体
            this.Left = 0;//主窗体左边的坐标为0
            WaveForm.Location = this.Location;//主窗体坐标赋给子窗体
            WaveForm.Left = this.Right;//主窗体的右显示坐标赋给子窗体左显示坐标

            //打开波形显示按钮时同步打开串口
            if (!serialPort1.IsOpen)
            {
                button2.PerformClick();//打开串口按钮单击事件
                this.checkBox16.Checked = true; //打开HEX勾选
            }
//子窗体程序
namespace 上位机初始界面
{
    public partial class WaveForm : Form
    {
        //点坐标偏移量
        private const int StartPrint = 40;
        //单位格大小
        private const int Unit_length = 32;

        //默认绘制单位
        //脚距越大容纳数据越小,脚距越小容纳数据越大
        private int DrawStep = 8;//每一单元格的成分格子长度为8,影响横轴数字宽度,此处参数设置是4倍
        //绘制单位最大值
        private const int MaxStep = 32;
        //绘制单位最小值
        private const int MinStep = 1;
        //定义轴线颜色
        private Pen TablePen = new Pen(Color.FromArgb(0x00,0x00,0x00));//00 00 00 为黑色
        //用于波形显示的变量声明
        //数据结构-线性链表
        private List<byte> DataList = new List<byte>();
        //定义波形颜色
        private Pen LinesPen = new Pen(Color.FromArgb(0xFF,0x00,0x00));//FF 00 00 为红色

        public WaveForm()
        {
            //波形稳定刷新
            //开启双缓冲
            this.SetStyle(ControlStyles.DoubleBuffer | 
                          ControlStyles.UserPaint    |
                          ControlStyles.AllPaintingInWmPaint,
                          true);
            this.UpdateStyles();

            InitializeComponent();
        }

//加载子窗体事件
        private void WaveForm_Load(object sender, EventArgs e)
        {
            //重新设定波形显示窗体尺寸
            int Width = Screen.GetWorkingArea(this).Width - Form1.MainForm.Width;//显示宽度为屏幕尺寸-主窗体尺寸
            int Heigth = this.Height - this.ClientRectangle.Height;//显示整个高度 - 工作区矩形 = 只剩下菜单栏
            Heigth += Unit_length * 16;//32*16
            Heigth += StartPrint * 2;

            this.Size = new Size(Width, Heigth);
        }
//定义链表尾部添加数据-数据从串口输入
        public void AddDataToWaveList(byte[] Data)
        {
            for (int i = 0; i < Data.Length; i++)
                DataList.Add(Data[i]);
            this.Invalidate();//刷新窗体显示
        }

//子窗体绘制事件
        private void WaveForm_Paint(object sender, PaintEventArgs e)
        {
            String Str = "";
            System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath();
            //绘制横轴线
            for(int i = 0; i < (this.ClientRectangle.Height - StartPrint) / Unit_length; i++)
            {
                //画横线-----画笔颜色,起始坐标,终点坐标
                e.Graphics.DrawLine(TablePen, StartPrint, StartPrint + i * Unit_length, this.ClientRectangle.Width, StartPrint + i * Unit_length);
                //绘制纵坐标
                Str = ((16 - i) * 16).ToString("X");
                Str = "0x" + (Str.Length == 1 ? Str + "0" : Str);
                if(i == 0)
                    Str = "0xFF";
                //添加文字
                gp.AddString(Str, this.Font.FontFamily, (int)FontStyle.Regular, 13, new RectangleF(0, StartPrint + i * Unit_length - 8, 400, 50), null);                        
            }
            //绘制纵轴线
            for(int i = 0; i <=(this.ClientRectangle.Width - StartPrint) / Unit_length; i++)
            {
                //画纵线
                e.Graphics.DrawLine(TablePen, StartPrint + i * Unit_length, StartPrint, StartPrint + i * Unit_length, StartPrint + Unit_length * 16);
                //绘制横坐标
                gp.AddString((i * (Unit_length / DrawStep)).ToString(), this.Font.FontFamily,(int)FontStyle.Regular,13,new RectangleF(StartPrint + i * Unit_length - 7,this.ClientRectangle.Height - StartPrint + 4, 400, 50),null);
            }
            //绘制文字
            e.Graphics.DrawPath(Pens.Black, gp);

            //如果数据量大于可容纳的数据量,则删除最左数据
            if (DataList.Count >= (this.ClientRectangle.Width - StartPrint) / DrawStep)
            {
                DataList.RemoveRange(0, DataList.Count - (this.ClientRectangle.Width - StartPrint) / DrawStep);
            }
            //绘制波形
            for(int i = 0; i < DataList.Count - 1; i++)
            {
                //点与点之间做直线连接
                e.Graphics.DrawLine(LinesPen, StartPrint + i * DrawStep, StartPrint + Unit_length * 16 - DataList[i] * (Unit_length / 16), StartPrint + (i + 1) * DrawStep, StartPrint + Unit_length * 16 - DataList[i + 1] * (Unit_length / 16));
            }
        }
    }
}

4.测试结果

接收下位机十六进制数据并显示在子窗体的坐标系中,数据波形显示

参考自B站硬件家园

  • 8
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C# Winform上位是一种基于Windows桌面应用程序的上位开发方式,可以通过串口通信等方式与下位进行数据交互。以下是一个简单的C# Winform上位的例子: 1.在Visual Studio中创建一个Windows Forms应用程序项目。 2.在窗体上添加一个ComboBox控件和一个Button控件。 3.在代码中引用System.IO.Ports命名空间,使用SerialPort类实现串口通信。 4.在Button的Click事件中编写代码,获取可用串口列表并在ComboBox中显示。 5.在ComboBox的SelectedIndexChanged事件中编写代码,获取选中的串口名称并打开串口。 6.在串口接收事件中编写代码,处理下位发送的数据。 以下是一个简单的C# Winform上位的代码示例: ```csharp using System; using System.IO.Ports; using System.Windows.Forms; namespace WinformSerialPort { public partial class Form1 : Form { private SerialPort serialPort; public Form1() { InitializeComponent(); serialPort = new SerialPort(); serialPort.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived); } private void Form1_Load(object sender, EventArgs e) { string[] ports = SerialPort.GetPortNames(); if (ports.Length != 0) { comboBox1.Items.AddRange(ports); comboBox1.SelectedIndex = 0; } else { MessageBox.Show("请插入串口设备!!"); } } private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) { if (serialPort.IsOpen) { serialPort.Close(); } serialPort.PortName = comboBox1.SelectedItem.ToString(); serialPort.BaudRate = Convert.ToInt32(comboBox2.SelectedItem.ToString()); serialPort.Open(); } private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { string data = serialPort.ReadExisting(); // 处理接收到的数据 } } } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值