C# Winform串口助手

界面设置

在这里插入图片描述

  • 修改控件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;
}

在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

互联网底层民工

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

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

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

打赏作者

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

抵扣说明:

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

余额充值