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

news2024/12/27 0:28:45

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站硬件家园

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1406686.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序(十二)在线图标与字体的获取与引入

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.从IconFont获取图标与文字的样式链接 2.将在线图标配置进页面中&#xff08;源码&#xff09; 3.将字体配置进页面文字中&#xff08;源码&#xff09; 4.css样式的多文件导入 获取链接 1.获取图标链接 登入…

精密空调联网网关网关BA108

随着通讯技术和控制技术的发展&#xff0c;为了实现楼宇的高效、智能化管理&#xff0c;集中监控管理已成为楼宇智能管理发展的必然趋势。在此背景下&#xff0c;高性能的楼宇暖通数据传输解决方案——协议转换网关应运而生&#xff0c;广泛应用于楼宇自控和暖通空调系统应用中…

eNSP学习——配置通过STelnet登陆系统

目录 背景 实验内容 实验目的 实验步骤 实验拓扑 详细配置过程 基础配置 配置SSH server 配置SSH client 配置SFTP server 与client 背景 由于Telnet缺少安全的认证方式&#xff0c;而且传输过程采用的是TCP进行明文传输。单纯的提供Telnet服务容易招致主机IP地址欺骗、路…

ctfshow-命令执行(web53-web72)

目录 web53 web54 web55 web56 web57 web58 web59 web60 web61 web62 web63 web64 web65 web66 web67 web68 web69 web70 web71 web72 web53 …

多维时序 | Matlab实现CNN-BiGRU-Mutilhead-Attention卷积双向门控循环单元融合多头注意力机制多变量时间序列预测

多维时序 | Matlab实现CNN-BiGRU-Mutilhead-Attention卷积双向门控循环单元融合多头注意力机制多变量时间序列预测 目录 多维时序 | Matlab实现CNN-BiGRU-Mutilhead-Attention卷积双向门控循环单元融合多头注意力机制多变量时间序列预测效果一览基本介绍程序设计参考资料 效果一…

数据分析的理念、流程、方法、工具(下)

四、用户分群 1、用户分群 用户分群是精细化运营的基础要求&#xff0c;也是数据分析的最基础方式。对用户进行分群&#xff0c;能帮助我们了解每个细分群体用户的变化情况&#xff0c;进而了解用户的整体现状及发展趋势。同时&#xff0c;由于运营资源本身有限&#xff0c;不…

Nacos持久化配置文件到Mysql(全图文).

1. 确保Mysql版本是在8以下&#xff0c;如果是8或者以上请这一步请参考&#xff1a; 最直接有效的解决nacos配置mysql8.0以上版本后无法启动的问题_nacos 8848端口 和mysql冲突-CSDN博客https://blog.csdn.net/qq_42758288/article/details/108967808 2.初始化数据库在Mysql&a…

# 安徽锐锋科技IDMS系统简介

IDMS 由安徽锐锋科技独立开发 该系统负责和海算以及UE\UNITY的无缝衔接并具备远程数据库访问、高速数据库的自动创建及数据存储、支持MQTT等多种物联网接口&#xff0c;支持多种算法。主要完成由于物料、人员、生产、故障、不良异常、订单异常带来的生产损失&#xff0c;通过海…

在PyCharm中安装GitHub Copilot插件,login之后报出如下错误:

Sign in failed. Reason: Request signInInitiate failed with message: connect ECONNABORTED 20.205.243.166:443, request id: 7, error code: -32603 前提&#xff1a; 设置网址&#xff1a;https://github.com/settings/copilot&#xff0c;已设置为允许 或者&#xff1…

flutter底层架构初探

本文出处&#xff1a;​​​​​​​​​​​​​Flutter 中文开发者网站 架构 embedder嵌入层 提供程序入口&#xff08;其他原生应用也采用此方式&#xff09;&#xff0c;程序由此和底层操作系统协调&#xff08;surface渲染、辅助功能和输入服务&#xff0c;管理事件循环…

C#,计算几何,鼠标点击绘制 (二维,三次)B样条曲线的代码

B样条&#xff08;B-Spline&#xff09;是常用的曲线拟合与插值算法之一。 这里给出在 Form 的 图像 Picturebox 组件上&#xff0c;按鼠标点击点绘制 &#xff08;三次&#xff09;B样条曲线的代码。 2022-12-05 修改了代码。 1 文本格式 using System; using System.Data; …

Java Web(三)--CSS

介绍 为什么需要&#xff1a; 在没有 CSS 之前&#xff0c;想要修改 HTML 元素的样式需要为每个 HTML 元素单独定义样式属性&#xff0c;费心费力&#xff1b;CSS 可以让 html 元素(内容) 样式(CSS)分离&#xff0c;提高web 开发的工作效率(针对前端开发)&#xff0c;从而…

【Vue】父组件如何调用子组件的函数

【Vue】父组件如何调用子组件的函数 在Vue中&#xff0c;父组件可以通过多种方式调用子组件的函数。下面是几种常见的方法&#xff1a; 使用 ref 属性 这是调用子组件方法的最直接方式。首先&#xff0c;在父组件的模板中给子组件添加一个 ref 属性。然后&#xff0c;你可以通…

MongoDB之概述、命令

基础知识 是什么 概念 分布式文件存储数据库&#xff0c;提供高可用、可扩展、易部署的数据存储解决方案。 结构 BSON存储类型 类似JSON的一种二进制存储格式。相比于JSON&#xff0c;提供更丰富的类型支持。 优点是灵活&#xff0c;缺点是空间利用率不佳。 类型说明解释…

【极数系列】Flink环境搭建(02)

【极数系列】Flink环境搭建&#xff08;02&#xff09; 引言 1.linux 直接在linux上使用jdk11flink1.18.0版本部署 2.docker 使用容器部署比较方便&#xff0c;一键启动停止&#xff0c;方便参数调整 3.windows 搭建Flink 1.18.0版本需要使用Cygwin或wsl工具模拟unix环境…

数字图像处理(实践篇)二十六 使用cvlib进行人脸检测、性别检测和目标检测

目录 1 安装cvlib 2 涉及的函数 3 实践 4 其他 cvlib一个简单,高级,易于使用的开源Python计算机视觉库。 1 安装cvlib # 安装依赖pip install opencv-python tensorflow# 安装cvlibpip install cvlib</

macOS跨进程通信: TCP Socket 创建实例

macOS跨进程通信: TCP Socket 创建实例 一&#xff1a; 简介 Socket 是 网络传输的抽象概念。 一般我们常用的有Tcp Socket和 UDP Scoket&#xff0c; 和类Unix 系统&#xff08;包括Mac&#xff09;独有的 Unix Domain Socket&#xff08;UDS&#xff09;。 Tcp Socket 能够…

(2)(2.4) CRSF/ELRS Telemetry

文章目录 前言 1 ArduPilot 参数编辑器 前言 &#xff01;Note ELRS&#xff08;ExpressLRS&#xff09;遥控系统使用穿越火线协议&#xff0c;连接方式类似。不过&#xff0c;它不像穿越火线那样提供双向遥测。 TBS CRSF 接收机与 ArduPilot 的接口中包含遥测和遥控信息。…

【K8S 云原生】K8S之HPA自动扩缩容、命名空间资源限制、容器抓包

目录 一、HPA概述 1、概念 2、两个重要的组件&#xff1a; 3、HPA的规则&#xff1a; 4、pod的副本数扩容有两种方式&#xff1a; 4.1、手动扩缩容&#xff0c;修改副本数&#xff1a; 4.2、自动扩缩容HPA 二、实验部署&#xff1a; 1、部署HPA 2、实现自动扩缩容 三…

Studio One 6 mac 6.5.2 激活版 数字音乐编曲创作

PreSonus Studio One是PreSonus出品的一款功能强大的音乐创作软件。主要为用户提供音乐创作、录音、编辑、制作等功能。它可以让你创造音乐&#xff0c;无限的轨道&#xff0c;无限的MIDI和乐器轨道&#xff0c;虚拟乐器和效果通道&#xff0c;这些都是强大和完美的。 软件下载…