C# Winform串口助手

news2025/1/23 7:14:34

界面设置

在这里插入图片描述

  • 修改控件name属性

  • 了解SerialPort类

  • 实现串口的初始化,开关

创建虚拟串口

在这里插入图片描述

namespace 串口助手
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //在设计页面已经预先添加了COM1 COM2 COM3
            this.port_cbb.Items.Add("COM4");
            this.port_cbb.Items.Add("COM5");

            //给发送框加预设文本
            this.send_rtb.Text = "我是发送框";
            //给接收框加预设文本
            this.recive_rtb.Text = "我是接收框";
        }

        private void open_btn_Click(object sender, EventArgs e)
        {
            try
            {
                if (port_cbb.Text != "")
                {
                    serialPort1.PortName = port_cbb.Text;
                }
                serialPort1.Open();
                if (serialPort1.IsOpen)
                {
                    MessageBox.Show("serialport is open");
                }
            }
            catch (Exception ex)
            {

                MessageBox.Show(ex.ToString() + serialPort1.PortName.ToString());
            }

        }

        private void send_btn_Click(object sender, EventArgs e)
        {
            //如果发送的数据不为空,则接收
            if (this.send_rtb.Text !="")
            {
                this.recive_rtb.AppendText(this.send_rtb.Text);
            }
            else
            {
                MessageBox.Show("请先输入发送数据");
            }
        }
    }
}

详细的Serial参数

  • string PortName 端口号
  • int DataBits 数据位
  • StopBits StopBits 停止位
  • Parity Parity 校验位
  • int BaudRate 波特率
  • bool IsOpen 状态
  • DtrEnable/RtsEnable

波特率

bit 与byte

  • bit就是位,也叫比特位,是计算机中最小的单位
  • byte是字节,也是B
  • 1字节(byte) = 8位(bit)
  • 位只有两种形式0和1,只能表示2种状态,而字节是有8个位组成的。可以表示256个状态。
  • 1byte = 8 bit,1KB = 1024 byte, 1MB = 1024KB,1G = 1024MB,1T = 1024G

波特率:表示每秒传输Bit的位数

校验位

  • 无校验(no parity)
  • 奇校验(odd parity):如果字符数据位中"1"的数目是偶数,校验位为"1",如果"1"的数目是奇数,校验位应为"0"。(校验位调整个数)
  • 偶校验(even parity):如果字符数据位中"1"的数目是偶数,则校验位应为"0",如果是奇数则为"1"。(校验位调整个数)
  • mark parity:校验位始终为1
  • space parity:校验位始终为0

数据位

紧跟在起始位之后,是通信中的真正有效信息。

数据位的位数可以由通信双方共同约定,一般可以是5位、7位或8位,标准ASCII码是0127(7位),扩展的ASCII码是0256(8位)。

传输数据时先传送字符的低位,后传送字符的高位。

停止位

表示单个包的最后一位。典型的值为1,1.5和2位。

由于数据时在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。

适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。

串口操作

开关串口

  • 获取端口号
RegistryKEy keyCom = Registry.LocalMachine.OpenSubKey(@"Harsware\DeviceMap\SerialComm");
  • 初始化配置参数

主要包含:端口号,波特率,数据位,停止位,校验位,编码格式

serialPort.Encoding = Encoding.GetEncoding("Gb2312");
  • 开启关闭操作

通过按钮事件来控制开关

配置参数

namespace 串口助手
{
    public partial class Form1 : Form
    {
        private bool isOpen = false;
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //初始化串口参数配置

            //在设计页面已经预先添加了COM1 COM2 COM3
            this.port_cbb.Items.Add("COM4");
            this.port_cbb.Items.Add("COM5");

            this.port_cbb.SelectedIndex = 2;
            this.baud_cbb.SelectedIndex = 1;
            this.check_cbb.SelectedIndex = 0;
            this.databit_cbb.SelectedIndex = 3;
            this.stopbit_cbb.SelectedIndex = 0;


            /*//给发送框加预设文本
            this.send_rtb.Text = "我是发送框";
            //给接收框加预设文本
            this.recive_rtb.Text = "我是接收框";*/
        }

        private void open_btn_Click(object sender, EventArgs e)
        {
            try
            {
                if (serialPort1.IsOpen == false)
                {
                    serialPort1.PortName = port_cbb.Text;
                    serialPort1.BaudRate = Convert.ToInt32(baud_cbb.Text);
                    serialPort1.DataBits = Convert.ToInt32(databit_cbb.Text);
                    switch(check_cbb.SelectedIndex)
                    {
                        case 0:
                            serialPort1.Parity = System.IO.Ports.Parity.None;
                            break;
                        case 1:
                            serialPort1.Parity = System.IO.Ports.Parity.Odd;
                            break;
                        case 2:
                            serialPort1.Parity = System.IO.Ports.Parity.Even;
                            break;
                        case 3:
                            serialPort1.Parity = System.IO.Ports.Parity.None;
                            break;
                    }
                    switch (stopbit_cbb.SelectedIndex)
                    {
                        case 0:
                            serialPort1.StopBits = System.IO.Ports.StopBits.One;
                            break;
                        case 1:
                            serialPort1.StopBits = System.IO.Ports.StopBits.OnePointFive;
                            break;
                        case 2:
                            serialPort1.StopBits = System.IO.Ports.StopBits.Two;
                            break;
                        case 3:
                            serialPort1.StopBits = System.IO.Ports.StopBits.One;
                            break;
                        default:
                            serialPort1.StopBits = System.IO.Ports.StopBits.One;
                            break;
                    }
                    serialPort1.Open();
                    isOpen = true;
                    open_btn.Text = "关闭串口";
                }
                else
                {
                    serialPort1.Close();
                    isOpen = false;
                    open_btn.Text = "打开串口";
                }

                /*if (port_cbb.Text != "")
                {
                    serialPort1.PortName = port_cbb.Text;
                }

                serialPort1.Open();
                if (serialPort1.IsOpen)
                {
                    MessageBox.Show("serialport is open");
                }*/
            }
            catch (Exception ex)
            {

                MessageBox.Show(ex.ToString() + serialPort1.PortName.ToString());
            }

        }

        private void send_btn_Click(object sender, EventArgs e)
        {
            //如果发送的数据不为空,则接收
            if (this.send_rtb.Text !="")
            {
                this.recive_rtb.AppendText(this.send_rtb.Text);
            }
            else
            {
                MessageBox.Show("请先输入发送数据");
            }
        }
    }
}

在这里插入图片描述

串口收发

从本地设备列表获取串口

获取端口号:注册列表中获取信息

RegistryKey keyCom = Registry.LocalMachine.OpenSubKey(@"Hardware\DeviceMap\SerialComm");
keyCom.GetValueNames();
keyCom.GetValue(xxx);

或使用**SerialPort.GetPortNames()**方法获取当前计算机的串行端口名的数组。

namespace 串口助手
{
    public partial class Form1 : Form
    {
        private bool isOpen = false;
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //初始化串口参数配置

            //在设计页面已经预先添加了COM1 COM2 COM3
            //this.port_cbb.Items.Add("COM4");
            //this.port_cbb.Items.Add("COM5");

            //this.port_cbb.SelectedIndex = 2;
            //this.baud_cbb.SelectedIndex = 1;
            //this.check_cbb.SelectedIndex = 0;
            //this.databit_cbb.SelectedIndex = 3;
            //this.stopbit_cbb.SelectedIndex = 0;

            serialLoad();

            /*//给发送框加预设文本
            this.send_rtb.Text = "我是发送框";
            //给接收框加预设文本
            this.recive_rtb.Text = "我是接收框";*/
        }
        private void serialLoad()
        {
            //string[] cur_port = SerialPort.GetPortNames();
            RegistryKey keyCom = Registry.LocalMachine.OpenSubKey(@"Hardware\DeviceMap\SerialComm");
            string[] sSubKeys = keyCom.GetValueNames();
             
            port_cbb.Items.Clear();

            foreach (var sValue in sSubKeys)
            {
                string portName =(string) keyCom.GetValue(sValue);
                port_cbb.Items.Add(portName);
            }
            

            this.port_cbb.SelectedIndex = 0;
            this.baud_cbb.SelectedIndex = 1;
            this.check_cbb.SelectedIndex = 0;
            this.databit_cbb.SelectedIndex = 3;
            this.stopbit_cbb.SelectedIndex = 0;
        }

        private void open_btn_Click(object sender, EventArgs e)
        {
            try
            {
                if (serialPort1.IsOpen == false)
                {
                    serialPort1.PortName = port_cbb.Text;
                    serialPort1.BaudRate = Convert.ToInt32(baud_cbb.Text);
                    serialPort1.DataBits = Convert.ToInt32(databit_cbb.Text);
                    switch(check_cbb.SelectedIndex)
                    {
                        case 0:
                            serialPort1.Parity = System.IO.Ports.Parity.None;
                            break;
                        case 1:
                            serialPort1.Parity = System.IO.Ports.Parity.Odd;
                            break;
                        case 2:
                            serialPort1.Parity = System.IO.Ports.Parity.Even;
                            break;
                        case 3:
                            serialPort1.Parity = System.IO.Ports.Parity.None;
                            break;
                    }
                    switch (stopbit_cbb.SelectedIndex)
                    {
                        case 0:
                            serialPort1.StopBits = System.IO.Ports.StopBits.One;
                            break;
                        case 1:
                            serialPort1.StopBits = System.IO.Ports.StopBits.OnePointFive;
                            break;
                        case 2:
                            serialPort1.StopBits = System.IO.Ports.StopBits.Two;
                            break;
                        case 3:
                            serialPort1.StopBits = System.IO.Ports.StopBits.One;
                            break;
                        default:
                            serialPort1.StopBits = System.IO.Ports.StopBits.One;
                            break;
                    }
                    serialPort1.Open();
                    isOpen = true;
                    open_btn.Text = "关闭串口";
                }
                else
                {
                    serialPort1.Close();
                    isOpen = false;
                    open_btn.Text = "打开串口";
                }

                /*if (port_cbb.Text != "")
                {
                    serialPort1.PortName = port_cbb.Text;
                }

                serialPort1.Open();
                if (serialPort1.IsOpen)
                {
                    MessageBox.Show("serialport is open");
                }*/
            }
            catch (Exception ex)
            {

                MessageBox.Show(ex.ToString() + serialPort1.PortName.ToString());
            }

        }

        private void send_btn_Click(object sender, EventArgs e)
        {
            //如果发送的数据不为空,则接收
            if (this.send_rtb.Text !="")
            {
                this.recive_rtb.AppendText(this.send_rtb.Text);
            }
            else
            {
                MessageBox.Show("请先输入发送数据");
            }
        }
    }
}

在这里插入图片描述

串口数据的发送

public void Write(byte[] buffer, int offset, int count);
public void Write(strng text);
public void Write(char[] buffer, int offest, int count);
public void WriteLine(string text);

可以在串口控件类中查看到这些方法,并可以尝试使用。

 private void send_btn_Click(object sender, EventArgs e)
        {
            //判断发送数据是否为空且串口是否打开
            if (this.send_rtb.Text !="" & serialPort1.IsOpen)
            {
                //this.recive_rtb.AppendText(this.send_rtb.Text);
                serialPort1.Write(send_rtb.Text);
            }
            else
            {
                MessageBox.Show("请先输入发送数据");
            }
        }

串口数据的接收

public void Read(byte[] buffer, int offset, int count);
public void Read(char[] buffer, int offset, int count);
public int ReadByte();
public int ReadChar();
public string ReadExisting();
public string ReadLine();
public string ReadTo(string value);
public event SerialDataReceivedEventHandler DataReceived;
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            string dataReceive = serialPort1.ReadExisting();
            receive_rtb.AppendText(dataReceive);
            
        }

收发数据

异步线程:更新UI

this.Invoke((EventHandler)delegate{});

执行一个异步线程来处理跨线程的数据。

DataReceived是在辅助线程执行,数据要更新到UI的主线程时,这个操作就跨线程了,可以通过异步线程来执行更新。

串口数据:接受字符处理

  • 将数据接受并缓存到数据缓存区(List 优于byte[])
  • 字符的编码格式:“GB2312”,“UTF8” 等等…
EncodingInfo[] encodingInfos = Encoding.GetEncodings();
  • 使用GB2312处理接收的数据
ribreceive.AppendText(Encoding.GetEncoding("gb2312").GetString(data).Replace("\0","\\0"));

在这里插入图片描述

代码:

//接收数据
 private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
 {
     //显示暂停则不接受数据
     if (isRxShow == false) return;
     //1、需要读取有效的数据BytesToRead
     byte[] dataTemp = new byte[serialPort1.BytesToRead];

     serialPort1.Read(dataTemp, 0, dataTemp.Length);

     receiveBuffer.AddRange(dataTemp);
     //计数器
     receiveCount += dataTemp.Length;

     this.Invoke(new EventHandler(delegate
     {
         //显示接收到的数据长度
         receivecount_tssl.Text = receiveCount.ToString();

         //判断是否选中十六进制
         if (!receivehex_chb.Checked)
         {
             //编码格式选择 国标2312
             string str = Encoding.GetEncoding("gb2312").GetString(dataTemp);
             //0x00 -> \0结束,不会显示
             str = str.Replace("\0", "\\0");

             receive_rtb.AppendText(str);
         }
         else
         {
             //十六进制是选中的状态
             receive_rtb.AppendText(Transform.ToHexString(dataTemp, " "));

         }
     }));


     //string dataReceive = serialPort1.ReadExisting();
     //receive_rtb.AppendText(dataReceive);

 }

 private void stop_btn_Click(object sender, EventArgs e)
 {
     if (isRxShow == true)
     {
         isRxShow = false;
         stop_btn.Text = "取消暂停";
     }
     else
     {
         isRxShow = true;
         stop_btn.Text = "暂停";
     }
 }

 private void receivehex_chb_CheckedChanged(object sender, EventArgs e)
 {
     if (receive_rtb.Text == "") return;
     if (receivehex_chb.Checked)
     {
         receive_rtb.Text = Transform.ToHexString(receiveBuffer.ToArray(), " ");

     }
     else
     {
         receive_rtb.Text = Encoding.GetEncoding("gb2312").GetString(receiveBuffer.ToArray()).Replace("\0", "\\0");
     }
     
 }

 private void clear_btn_Click(object sender, EventArgs e)
 {
     receiveBuffer.Clear();
     receivecount_tssl.Text = "";
     receive_rtb.Text = "";
 }

 private void autoclear_chb_CheckedChanged(object sender, EventArgs e)
 {
     if (autoclear_chb.Checked)
     {
         timer1.Start();
     }
     else
     {
         timer1.Stop();
     }
 }

 private void timer1_Tick(object sender, EventArgs e)
 {
     if (receive_rtb.Text.Length > 4096)
     {
         receiveBuffer.Clear();
         receive_rtb.Text = "";
         receivecount_tssl.Text = "";
     }
 }

串口数据:发送字符处理

  • 发送普通字符串
  • 发送十六进制字符串

在这里插入图片描述


namespace 串口助手
{
    public partial class Form1 : Form
    {
        private bool isOpen = false;
        private bool isRxShow = true;

        //接收缓存区
        private List<byte> receiveBuffer = new List<byte>();
        //发送缓存区
        private List<byte> sendBuffer = new List<byte>();
        //接收计数
        private int receiveCount = 0;
        //发送计数
        private int sendCount = 0;

        public Form1()
        {
            InitializeComponent();
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //初始化串口参数配置

            //在设计页面已经预先添加了COM1 COM2 COM3
            //this.port_cbb.Items.Add("COM4");
            //this.port_cbb.Items.Add("COM5");

            //this.port_cbb.SelectedIndex = 2;
            //this.baud_cbb.SelectedIndex = 1;
            //this.check_cbb.SelectedIndex = 0;
            //this.databit_cbb.SelectedIndex = 3;
            //this.stopbit_cbb.SelectedIndex = 0;

            serialLoad();

            /*//给发送框加预设文本
            this.send_rtb.Text = "我是发送框";
            //给接收框加预设文本
            this.recive_rtb.Text = "我是接收框";*/
        }
        private void serialLoad()
        {
            //string[] cur_port = SerialPort.GetPortNames();

            RegistryKey keyCom = Registry.LocalMachine.OpenSubKey(@"Hardware\DeviceMap\SerialComm");
            string[] sSubKeys = keyCom.GetValueNames();
             
            port_cbb.Items.Clear();

            foreach (var sValue in sSubKeys)
            {
                string portName =(string) keyCom.GetValue(sValue);
                port_cbb.Items.Add(portName);
            }
            

            this.port_cbb.SelectedIndex = 0;
            this.baud_cbb.SelectedIndex = 1;
            this.check_cbb.SelectedIndex = 0;
            this.databit_cbb.SelectedIndex = 3;
            this.stopbit_cbb.SelectedIndex = 0;
        }

        private void open_btn_Click(object sender, EventArgs e)
        {
            try
            {
                if (serialPort1.IsOpen == false)
                {
                    serialPort1.PortName = port_cbb.Text;
                    serialPort1.BaudRate = Convert.ToInt32(baud_cbb.Text);
                    serialPort1.DataBits = Convert.ToInt32(databit_cbb.Text);
                    switch(check_cbb.SelectedIndex)
                    {
                        case 0:
                            serialPort1.Parity = System.IO.Ports.Parity.None;
                            break;
                        case 1:
                            serialPort1.Parity = System.IO.Ports.Parity.Odd;
                            break;
                        case 2:
                            serialPort1.Parity = System.IO.Ports.Parity.Even;
                            break;
                        case 3:
                            serialPort1.Parity = System.IO.Ports.Parity.None;
                            break;
                    }
                    switch (stopbit_cbb.SelectedIndex)
                    {
                        case 0:
                            serialPort1.StopBits = System.IO.Ports.StopBits.One;
                            break;
                        case 1:
                            serialPort1.StopBits = System.IO.Ports.StopBits.OnePointFive;
                            break;
                        case 2:
                            serialPort1.StopBits = System.IO.Ports.StopBits.Two;
                            break;
                        case 3:
                            serialPort1.StopBits = System.IO.Ports.StopBits.One;
                            break;
                        default:
                            serialPort1.StopBits = System.IO.Ports.StopBits.One;
                            break;
                    }
                    serialPort1.Open();
                    isOpen = true;
                    open_btn.Text = "关闭串口";
                }
                else
                {
                    serialPort1.Close();
                    isOpen = false;
                    open_btn.Text = "打开串口";
                }

                /*if (port_cbb.Text != "")
                {
                    serialPort1.PortName = port_cbb.Text;
                }

                serialPort1.Open();
                if (serialPort1.IsOpen)
                {
                    MessageBox.Show("serialport is open");
                }*/
            }
            catch (Exception ex)
            {

                MessageBox.Show(ex.ToString() + serialPort1.PortName.ToString());
            }

        }

        private void sendData()
        {
            serialPort1.Write(sendBuffer.ToArray(), 0, sendBuffer.Count);
            sendCount += sendBuffer.Count();
            sendcount_tssl.Text = sendCount.ToString();
            
        }

        private void send_btn_Click(object sender, EventArgs e)
        {
            //如果发送的数据不为空,则接收
            if (this.send_rtb.Text !="" && serialPort1.IsOpen)
            {
                Console.WriteLine(Transform.ToHexString(sendBuffer.ToArray()));
                sendData();
            }
            else
            {
                MessageBox.Show("请先输入发送数据");
            }
        }

        //接收数据
        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            //显示暂停则不接受数据
            if (isRxShow == false) return;
            //1、需要读取有效的数据BytesToRead
            byte[] dataTemp = new byte[serialPort1.BytesToRead];

            serialPort1.Read(dataTemp, 0, dataTemp.Length);

            receiveBuffer.AddRange(dataTemp);
            //计数器
            receiveCount += dataTemp.Length;

            this.Invoke(new EventHandler(delegate
            {
                //显示接收到的数据长度
                receivecount_tssl.Text = receiveCount.ToString();

                //判断是否选中十六进制
                if (!receivehex_chb.Checked)
                {
                    //编码格式选择 国标2312
                    string str = Encoding.GetEncoding("gb2312").GetString(dataTemp);
                    //0x00 -> \0结束,不会显示
                    str = str.Replace("\0", "\\0");

                    receive_rtb.AppendText(str);
                }
                else
                {
                    //十六进制是选中的状态
                    receive_rtb.AppendText(Transform.ToHexString(dataTemp, " "));

                }
            }));


            //string dataReceive = serialPort1.ReadExisting();
            //receive_rtb.AppendText(dataReceive);

        }

        private void stop_btn_Click(object sender, EventArgs e)
        {
            if (isRxShow == true)
            {
                isRxShow = false;
                stop_btn.Text = "取消暂停";
            }
            else
            {
                isRxShow = true;
                stop_btn.Text = "暂停";
            }
        }

        private void receivehex_chb_CheckedChanged(object sender, EventArgs e)
        {
            if (receive_rtb.Text == "") return;
            if (receivehex_chb.Checked)
            {
                receive_rtb.Text = Transform.ToHexString(receiveBuffer.ToArray(), " ");

            }
            else
            {
                receive_rtb.Text = Encoding.GetEncoding("gb2312").GetString(receiveBuffer.ToArray()).Replace("\0", "\\0");
            }
            
        }

        private void clear_btn_Click(object sender, EventArgs e)
        {
            receiveBuffer.Clear();
            receivecount_tssl.Text = "";
            receive_rtb.Text = "";
        }

        private void autoclear_chb_CheckedChanged(object sender, EventArgs e)
        {
            if (autoclear_chb.Checked)
            {
                timer1.Start();
            }
            else
            {
                timer1.Stop();
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (receive_rtb.Text.Length > 4096)
            {
                receiveBuffer.Clear();
                receive_rtb.Text = "";
                receivecount_tssl.Text = "";
            }
        }

        private void send_rtb_Leave(object sender, EventArgs e)
        {
            if (sendhex_chb.Checked)
            {
                //判断是否为十六进制字符
                if (DataEncoding.IsHexString(send_rtb.Text.Replace(" ","")))
                {
                    sendBuffer.Clear();
                    sendBuffer.AddRange(Transform.ToBytes(send_rtb.Text.Replace(" ", "")));
                }
                else
                {
                    MessageBox.Show("请输入正确的十六进制数据!");
                    send_rtb.Select();
                }
            }
            else
            {
                sendBuffer.Clear();
                sendBuffer.AddRange(Encoding.GetEncoding("gb2312").GetBytes(send_rtb.Text));
            }

        }

        private void send_rtb_TextChanged(object sender, EventArgs e)
        {
            // 十六进制切换 会出现问题  这问题就是0x00 转换
        }

        private void sendhex_chb_CheckedChanged(object sender, EventArgs e)
        {
            if (send_rtb.Text == "") return;
            if (sendhex_chb.Checked)
            {
                send_rtb.Text = Transform.ToHexString(sendBuffer.ToArray(), " ");
            }
            else
            {
                send_rtb.Text = Encoding.GetEncoding("gb2312").GetString(sendBuffer.ToArray()).Replace("\0", "\\0");
            }
        }

        private void sendclear_btn_Click(object sender, EventArgs e)
        {
            sendBuffer.Clear();
            sendcount_tssl.Text = "0";
            send_rtb.Text = "";
            sendCount = 0;
        }

    }
}

协议解析

数据大小算端

大端模式

指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址
由小向大增加,而数据从高位往低位放;

小端模式

指数据的高字节保存在内存的高地址中, 而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。

数据校验方式:和校验,CRC,LRC等等

  • CRC 有多版本:今天使用的是CRC16_ccitt_xmode
  • LRC
  • 和校验
  • 奇偶校验
  • 异或校验

解析数据处理

  • 关键 :queue 队列的先进先出逻辑
  • 关键 :控制协议,决定了数据解析逻辑,不同数据解析方式不同
  • 案例:帧头(0x7F)+数据长度+数据+CRC
  • 数据样本:7f+04+31323334+DE10

在这里插入图片描述

声明

在这里插入图片描述

实例化队列Queue

在这里插入图片描述

//接收数据
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    //显示暂停则不接受数据
    if (isRxShow == false) return;
    //1、需要读取有效的数据BytesToRead
    byte[] dataTemp = new byte[serialPort1.BytesToRead];

    serialPort1.Read(dataTemp, 0, dataTemp.Length);

    receiveBuffer.AddRange(dataTemp);
    //计数器
    receiveCount += dataTemp.Length;

    this.Invoke(new EventHandler(delegate
    {
        //显示接收到的数据长度
        receivecount_tssl.Text = receiveCount.ToString();
        
        //判断是否选中"启动数据帧接收"
        if (!startData_chb.Checked)
        {
           //判断是否选中十六进制
           if (!receivehex_chb.Checked)
           {
               //编码格式选择 国标2312
               string str = Encoding.GetEncoding("gb2312").GetString(dataTemp);
               //0x00 -> \0结束,不会显示
               str = str.Replace("\0", "\\0");

               receive_rtb.AppendText(str);
           }
           else
           {
               //十六进制是选中的状态
               receive_rtb.AppendText(Transform.ToHexString(dataTemp, " "));
           }
        }
        else
        {
            //解析数据 queue
            foreach (byte item in dataTemp)
            {
                //入列
                bufferQueue.Enqueue(item);
            }

            //解析获取帧头
            if (!isHeadReceive)
            {
                //遍历队列获取0x7f位置
                foreach (byte item in bufferQueue.ToArray())
                {
                    if (item != 0x7f)
                    {
                        //移除数据,保证0x7f在头位置
                        //出列
                        bufferQueue.Dequeue();
                        Console.WriteLine("not 0x7f, Dequeue !");
                    }
                    else
                    {
                        //获取到0x7f,停止遍历
                        //get 0x7f from bufferQueue
                        isHeadReceive = true;
                        Console.WriteLine("0x7f is receive !");
                        break;
                    }
                }
            }
            if (isHeadReceive)
            {
                //判断数据帧长度
                if (bufferQueue.Count >= 2)
                {
                    Console.WriteLine(DateTime.Now.ToLongTimeString());
                    //输出缓冲区中的数据,数据以十六进制字符串的形式显示。
                    Console.WriteLine($"show the data in bufferQueue{Transform.ToHexString(bufferQueue.ToArray())}");

                    //使用 X2 格式说明符,表示将值转换为至少两位的大写十六进制数。
                    //如果值的位数少于两位,则在前面补零
                    Console.WriteLine($"frame lenth ={String.Format("{0:X2}", bufferQueue.ToArray()[1])}");

                    frameLenth = bufferQueue.ToArray()[1];
                    // 一帧完整的数据长度判断,不代表数据是正确的
                    if (bufferQueue.Count>=1+1+frameLenth+2)
                    {
                        byte[] frameBuffer = new byte[1+1+frameLenth+2];
                        //源数组、源数组的起始索引、目标数组、目标数组的起始索引和要复制的元素数量
                        Array.Copy(bufferQueue.ToArray(), 0, frameBuffer, 0, frameBuffer.Length);
                        if (crc_check(frameBuffer))
                        {
                            Console.WriteLine("frame is check ok,pick it");
                            data_txb.Text = Transform.ToHexString(frameBuffer);
                            data1_txb.Text = String.Format("{0:X2}", frameBuffer[2]);
                            data2_txb.Text = String.Format("{0:X2}", frameBuffer[3]);
                            data3_txb.Text = String.Format("{0:X2}", frameBuffer[4]);
                            data4_txb.Text = String.Format("{0:X2}", frameBuffer[5]);
                        }
                        else
                        {
                            // 无效数据
                            Console.WriteLine("bad frame, drop it");
                        }

                        for (int i = 0; i < 1 + 1 + frameLenth + 2; i++)
                        {
                            bufferQueue.Dequeue();
                        }
                        isHeadReceive = false;
                    }
                }
            }

        }
    }));

    //string dataReceive = serialPort1.ReadExisting();
    //receive_rtb.AppendText(dataReceive);

}

private bool crc_check(byte[] frameBuffer)
{
    //大小端数据
    bool ret = false;
    byte[] temp = new byte[frameBuffer.Length-2];
    Array.Copy(frameBuffer, 0, temp,0, temp.Length);
    byte[] crcdata = DataCheck.DataCrc16_Ccitt(temp, DataCheck.BigOrLittle.BigEndian);
    if (crcdata[0] == frameBuffer[frameBuffer.Length -2] &&
        crcdata[1] == frameBuffer[frameBuffer.Length-1])
    {
        ret = true;
    }
    return ret;
}

在这里插入图片描述

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

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

相关文章

手写一个uniapp的步骤条组件

在template实现 <template><view class"process_more"><!-- 步骤条 --><view class"set-2" :key"index" v-for"(item,index) in options"><!-- 图片 --><view class"img-border"><…

造物者:专注游戏音乐创造——奏响游戏世界乐章

游戏的世界宛如一幅壮丽的画卷&#xff0c;由华丽的图像和引人入胜的故事构成&#xff0c;然而&#xff0c;其完美之作还有一部分不可或缺的元素&#xff0c;那就是音乐。在这个数字时代&#xff0c;北京造物者科技有限公司&#xff08;以下简称造物者&#xff09;正崭露头角&a…

【RP-RV1126】配置一套简单的板级配置

文章目录 官方配置新建一套新配置新建板级pro-liefyuan-rv1126.mk配置文件新建一个Buildroot的defconfigs文件 吐槽&#xff1a;RP-RV1126 的SDK奇怪的地方make ARCHarm xxx_defconfig 生成的.config文件位置不一样savedefconfig命令直接替换原配置文件坑爹的地方 Buildroot上增…

【本周骑行香杆箐活动简介】- 探索秋天的美景与健康同行

校长骑行的骑友们&#xff0c;大家好&#xff01;在这个秋高气爽的季节里&#xff0c;是不是已经跃跃欲试&#xff0c;想要投入大自然的怀抱&#xff0c;感受那无比清新的空气和金黄色的落叶呢&#xff1f;别再犹豫了&#xff0c;让我们一起骑行在香杆箐&#xff0c;体验一次不…

91 前K个高频元素

前K个高频元素 题解1 大根堆(STL) 给你一个整数数组 nums 和一个整数 k &#xff0c;请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 示例 1: 输入: nums [1,1,1,2,2,3], k 2 输出: [1,2] 示例 2: 输入: nums [1], k 1 输出: [1] 提示&#xff1a;…

KADP应用加密组件实现数据动态脱敏 安当加密

动态脱敏是一种针对敏感数据进行数据抽取、数据漂白和动态掩码的专业数据脱敏技术。它通过在不动数据库中原始数据的前提下&#xff0c;依据用户的角色、职责和其他IT定义身份特征&#xff0c;动态的对生产数据库返回的数据进行专门的屏蔽、加密、隐藏和审计。可确保不同级别的…

双十一数码推荐什么?双十一选购攻略大全!实用数码产品推荐!

​在双十一这个购物狂欢节里&#xff0c;各大品牌和商家都会推出各种优惠活动&#xff0c;为消费者提供丰富的购物选择。在这个特殊的日子里&#xff0c;你是否也准备为自己或亲朋好友选购一些数码好物呢?本次推荐将为你精选一些值得购买的数码产品&#xff0c;让你在双十一这…

MATLAB和西门子SMART PLC OPC通信

西门子S7-200SMART PLC OPC软件的下载和使用,请查看下面文章 Smart 200PLC PC Access SMART OPC通信_基于pc access smart的opc通信_RXXW_Dor的博客-CSDN博客文章浏览阅读2.7k次,点赞2次,收藏5次。OPC是一种利用微软COM/DCOM技术达成自动控制的协议,采用典型的C/S模式,针…

(01)Mycat说明与介绍

1、Mycat是什么 Mycat是一个数据库中间件&#xff0c;前身是阿里的cobar。 2、Mycat可以用来做什么 1.读写分离 2.数据分片 &#xff08;1&#xff09;垂直拆分 &#xff08;2&#xff09;水平拆分 &#xff08;3&#xff09;垂直水平拆分 3.多数据源整合 3、Mycat实现的…

前端出大事儿了

大家好&#xff0c;我是风筝 文章首发于 前端出大事儿了 最近这两天&#xff0c;在前端圈最火的图片莫过于下面这张了。 这是一段 React 代码&#xff0c;就算你完全没用过 React 也没关系&#xff0c;一眼看过去就能看到其中最敏感的一句代码&#xff0c;就是那句 SQL 。 咱…

Linux安装sysv-rc-conf报错:出现NO_PUBKEY...问题,急需安装证书的情况

Linux下安装MySQL时&#xff0c;出现一个使用chkconfig命令&#xff0c;但无该命令的情况&#xff01; chkconfig --add mysql # 出现chkconfig command not found于是就展开了一次替换的行动&#xff0c;将chkconfig替换为sysv-rc-conf 第一步&#xff1a; 尝试直接安装&am…

最新阿里云服务器优惠价格表,企鹅看了瑟瑟发抖!

今年2023年阿里云双十一优惠活动云服务器价格太低了&#xff0c;比腾讯云都便宜&#xff0c;轻量2核2G服务器3M带宽优惠价87元一年、2核4G4M带宽优惠价165元一年&#xff0c;云服务器ECS经济型e实例2核2G3M固定带宽优惠价格99元一年&#xff0c;还有2核4G、2核8G、4核8G、4核16…

Leetcode刷题---轮转数组

轮转数组 题目描述&#xff1a; Java中List是有序、可重复的单列集合&#xff0c;集合中的每个元素都有对应的顺序索引&#xff0c;我们可以通过该索引来访问指定位置上的集合元素。 思路&#xff1a; 首先选用list来存储中间结果。首先用k对n(数组长度)求余获取要移动的位数…

2023年【P气瓶充装】最新解析及P气瓶充装考试技巧

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 P气瓶充装最新解析参考答案及P气瓶充装考试试题解析是安全生产模拟考试一点通题库老师及P气瓶充装操作证已考过的学员汇总&#xff0c;相对有效帮助P气瓶充装考试技巧学员顺利通过考试。 1、【多选题】LNG加气站有哪些…

手把手教你使用媲美人声的AI配音神器

1、找到微信小程序界面 2、搜索番茄AI配音&#xff0c;找到番茄AI配音图标&#xff0c;点击进入小程序 3、复制粘贴文本到最上方的界面 4、在选择主播的地方&#xff0c;选择自己喜欢的主播声音 5、然后点击制作语音图标&#xff0c;制作成功之后可以试听一下 6、觉得满意之后&…

台灯太亮会导致近视吗?精选高品质的台灯

台灯相信很多家庭都会备上一台&#xff0c;用于办公、休闲或者给孩子学习使用&#xff0c;如果使用的台灯亮度过高的话&#xff0c;可能会对视力造成一定的影响&#xff0c;尤其是夜晚的时候。建议是选择带有亮度调节功能的台灯会比较好一点&#xff0c;可以自行根据周围环境的…

Winclone pro v10.2(Boot Camp分区备份还原工具)

Winclone Pro 10是一款专为Mac用户设计的高级Windows系统备份和迁移工具。它提供了强大而易于使用的功能&#xff0c;让用户能够轻松地创建、克隆和还原Windows系统&#xff0c;并在Mac上运行。 Winclone Pro 10的主要特点包括&#xff1a; 完整的系统备份和还原&#xff1a;…

右击文件或者文件夹使用vscode打开

平常我们在打开项目时&#xff0c;经常会需要快捷打开方式&#xff0c;直接使右键使用编辑器打开&#xff0c;但是有时在安装时忘记了选择 “Add “Open with Code” action to Windows Explorer file context menu” 在Windows资源管理器文件上下文菜单中添加“用代码打开”操…

初识Vue 输出Hello World 及注意事项

在我们还没接触Vue之前&#xff0c;我同学常说我可以直接在元素里输出JS的表达式吗&#xff1f;肯定是不太行。当我们接触vue.js后&#xff0c;这个想法成了现实。 每当我们学习一门新的语言或者框架时&#xff0c;我们都习惯打印一个“hello world”&#xff0c;在我们vue当中…

找到数组中出现一种/两种奇数——异或运算

找到数组中出现一种/两种奇数 题目&#xff1a;一个数组有一种数出现了奇数次&#xff0c;其他数都出现了偶数次&#xff0c;怎么找到并打印这个数&#xff1f; trick 因为异或运算有个特点&#xff0c;满足交换律和结合律&#xff0c;同时有两个重要的特点&#xff1a; n^n…