WinFrom 串口通讯的简单使用【包括RCR校验】

搞嵌入式开发难免会使用串口通信 ,现有一个项目需要使用C#,借此机会来开发一个串口供大家参考

一、UI布局

数据位、校验位、停止位  均写死,各个按钮的 Name值已经标注未标注的基本没有用

二、接收数据报文

这里我们主要对

从机响应

字节数

返回的信息

备  注

从机地址

1

XX

来自地址为XX的从机

功能码

1

    33H  

读取寄存器(0x33 = 51)

数据字节数

1

02H  

XX字节(2倍数据个数)

寄存器数据1

2

DAT1

传感器参数1数据内容

CRC码

2

XXXX 

由从机计算得到CRC码

进行分析,不难发现报文一帧发送 7 个16进制的数据

 

 

由此我们准备以下代码

using ComHelp;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace UnlockProject
{
    public partial class Form1 : Form
    {
        //被打开的COM
        public static SerialPort mySerialPort;


        public Form1()
        {
            CheckForIllegalCrossThreadCalls = false;

            //初始化
            InitializeComponent();
            //加载COM
            GetComList();
            //配置初始化
            InitralConfig();
        }






        public SerialPort com = new SerialPort();
        //定义端口类
        private SerialPort ComDevice = new SerialPort();
 
        /// <summary>
        /// 从注册表获取系统串口列表
        /// </summary>
        public string[] GetComList()
        {
            RegistryKey keyCom = Registry.LocalMachine.OpenSubKey("Hardware\\DeviceMap\\SerialComm");
            string[] sSubKeys = keyCom.GetValueNames();
            string[] str = new string[sSubKeys.Length];
            for (int i = 0; i < sSubKeys.Length; i++)
            {
                str[i] = (string)keyCom.GetValue(sSubKeys[i]);
            }
            return str;
        }

        /// <summary>
        /// 配置初始化
        /// </summary>
        private void InitralConfig()
        {
            Boolean open = false;
            string[] coms = GetComList();
            for (int i = 0; i < coms.Length; i++)
            {
                open = false;
                if (com.IsOpen)
                {
                    com.Close();
                }
                cmbPort.Items.Add(coms[i]);
            }
            //向ComDevice.DataReceived(是一个事件)注册一个方法Com_DataReceived,当端口类接收到信息时时会自动调用Com_DataReceived方法
            ComDevice.DataReceived += new SerialDataReceivedEventHandler(Com_DataReceived);
        }


        int i = 0;
        /// <summary>
        /// 一旦ComDevice.DataReceived事件发生,就将从串口接收到的数据显示到接收端对话框
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="sender"></param>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Com_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {

            //开辟接收缓冲区
            byte[] ReDatas = new byte[ComDevice.BytesToRead];

            //从串口读取数据
            ComDevice.Read(ReDatas, 0, ReDatas.Length);
            //实现数据的解码与显示
            AddData(ReDatas);
        }


        int i_data = 0;//接收位数
        List<byte> i_data_count = new List<byte>();//不符合位数的数据暂存
        /// <summary>
        /// 解码过程
        /// </summary>
        /// <param name="data">串口通信的数据编码方式因串口而异,需要查询串口相关信息以获取</param>
        public void AddData(byte[] data)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var b in data)
            {
                sb.Append(Convert.ToString(b, 16).PadLeft(2, '0')+" ");
            }


            i_data += data.Length;//记录位长

            if (i_data == 7)//满足位长
            {
                //追写历史记录的数据
                StringBuilder i_data_str = new StringBuilder();
                foreach (var item in i_data_count)
                {
                    i_data_str.Append(Convert.ToString(item, 16).PadLeft(2, '0') + " ");
                }

                //CRC校验值是否正确
                List<byte> deCRC = new List<byte>();
                foreach (var item in i_data_count)
                {
                    deCRC.Add(item);
                }
                foreach (var item in data)
                {
                    deCRC.Add(item);
                }

                //得到校验码
                long CRC = new CRCHelp().deCRC(5, deCRC.ToArray());

                //得到低位在前高位在后的值
                string strCRC = CRC.ToString("x8");

                //低位  
                string low = strCRC.Substring(4, 1) + strCRC.Substring(5, 1);
                //高位
                string heigt = strCRC.Substring(6, 1) + strCRC.Substring(7, 1);
                //校验状态
                string crc_flag = "[错误]";


                //CRC值与数据进行校验是否正确
                if (low == deCRC[5].ToString("x8").ToString().Replace("0", "") && heigt == deCRC[6].ToString("x8").ToString().Replace("0", "")) {
                    crc_flag = "[正确]";
                }

                //进行打印  
                AddContent(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss "+ crc_flag + ":") + i_data_str + sb.ToString() + "\r\n");//.ToUpper()



                //重置位长
                i_data = 0;
                //重置位数据缓存
                i_data_count = new List<byte>(); 
            }
            else {
                //不满足位长时记录缓存数据
                foreach (var item in data)
                {
                    i_data_count.Add(item);
                }


            }
        }



        List<string> data = new List<string>();
        /// <summary>
        /// 接收端对话框显示消息
        /// </summary>
        /// <param name="content"></param>
        private void AddContent(string content)
        {
            BeginInvoke(new MethodInvoker(delegate
            {
                datalog.AppendText(content); 
            }));
 
        }
        /// <summary>
        /// 串口开关
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_Switch_Click(object sender, EventArgs e)
        {
            if (cmbPort.Items.Count <= 0)
            {
                MessageBox.Show("未发现可用串口,请检查硬件设备");
                return;
            }
            if (ComDevice.IsOpen == false)
            {
                //设置串口相关属性
                ComDevice.PortName = cmbPort.Text;//cmbPort.SelectedItem.ToString();
                                                  //波特率
                ComDevice.BaudRate = Convert.ToInt32(btl.Text); 
                //校验位
                ComDevice.Parity = Parity.None;
                //停止位
                ComDevice.StopBits = StopBits.One;
                //数据位位
                ComDevice.DataBits = 8;

              try
                {
                    //开启串口
                    ComDevice.Open(); 
                    //while (true)
                    {
                        //接收数据
                        ComDevice.DataReceived += new SerialDataReceivedEventHandler(Com_DataReceived);
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "未能成功开启串口", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
            }
            else
            {
                try
                {
                    ComDevice.Close();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "串口关闭错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            cmbPort.Enabled = !ComDevice.IsOpen; 
        }
        /// <summary>
        /// 将消息编码并发送
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_Send_Click(object sender, EventArgs e)
        {
            //if (datalog.Text.Length > 0)
            //{
            // datalog.AppendText("\n");
            //}

            byte[] sendData = null;
            //sendData = Encoding.UTF8.GetBytes(datalog.Text);
            sendData = Hex16StringToHex16Byte(datalog.Text);
            SendData(sendData);
        }
        /// <summary>
        /// 此方法用于将16进制的字符串转换成16进制的字节数组
        /// </summary>
        /// <param name="_hex16ToString">要转换的16进制的字符串。</param>
        public static byte[] Hex16StringToHex16Byte(string _hex16String)
        {
            //去掉字符串中的空格。
            _hex16String = _hex16String.Replace(" ", "");
            if (_hex16String.Length / 2 == 0)
            {
                _hex16String += " ";
            }
            //声明一个字节数组,其长度等于字符串长度的一半。
            byte[] buffer = new byte[_hex16String.Length / 2];
            for (int i = 0; i < buffer.Length; i++)
            {
                //为字节数组的元素赋值。
                buffer[i] = Convert.ToByte((_hex16String.Substring(i * 2, 2)), 16);
            }
            //返回字节数组。
            return buffer;
        }
        /// <summary>
        /// 此函数将编码后的消息传递给串口
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool SendData(byte[] data)
        {
            if (ComDevice.IsOpen)
            {
                try
                {
                    //将消息传递给串口
                    ComDevice.Write(data, 0, data.Length);
                    return true;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "发送失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            else
            {
                MessageBox.Show("串口未开启", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            return false;
        }

        /// <summary>
        /// 16进制编码
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        private byte[] strToHexByte(string hexString)
        {
            hexString = hexString.Replace(" ", "");
            if ((hexString.Length % 2) != 0) hexString += " ";
            byte[] returnBytes = new byte[hexString.Length / 2];
            for (int i = 0; i < returnBytes.Length; i++)
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2).Replace(" ", ""), 16);
            return returnBytes;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            i_data = 0;
            i_data_count = new List<byte>();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            datalog.Text="";
        }
    }
}

CRC计算类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UnlockProject
{
   public class CRCHelp
    {

        #region CRC计算
        public Int64 deCRC(int length, byte[] data)
        {
            Int64 CRCtemp = 65535;
            int j = 0;
            int chr = 0;
            int chr1 = 0;
            for (int y = 0; y < length; y++)
            {
                chr = (int)CRCtemp & 255;
                chr = chr ^ data[j];
                CRCtemp = CRCtemp & 0xff00;
                CRCtemp = CRCtemp + chr;
                for (int i = 0; i < 8; i++)
                {
                    if ((CRCtemp & 0x01) == 1)
                    {
                        CRCtemp = CRCtemp >> 1;
                        CRCtemp = CRCtemp ^ 0xA001;

                    }
                    else
                    {
                        CRCtemp = CRCtemp >> 1;
                    }
                }
                j += 1;
            }
            chr = (int)CRCtemp & 0xff;
            chr1 = (int)CRCtemp & 0xff00;
            CRCtemp = chr << 8 | chr1 >> 8;
            return CRCtemp;
        }
        #endregion
    }
}

 

 

三、代码详细解释

3.1我们只对关键代码解释:

   

        int i_data = 0;//接收位数
        List<byte> i_data_count = new List<byte>();//不符合位数的数据暂存
/// <summary>
        /// 解码过程
        /// </summary>
        /// <param name="data">串口通信的数据编码方式因串口而异,需要查询串口相关信息以获取</param>
        public void AddData(byte[] data)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var b in data)
            {
                sb.Append(Convert.ToString(b, 16).PadLeft(2, '0')+" ");
            }


            i_data += data.Length;//记录位长

            if (i_data == 7)//满足位长
            {
                //追写历史记录的数据
                StringBuilder i_data_str = new StringBuilder();
                foreach (var item in i_data_count)
                {
                    i_data_str.Append(Convert.ToString(item, 16).PadLeft(2, '0') + " ");
                }

                //CRC校验值是否正确
                List<byte> deCRC = new List<byte>();
                foreach (var item in i_data_count)
                {
                    deCRC.Add(item);
                }
                foreach (var item in data)
                {
                    deCRC.Add(item);
                }

                //得到校验码
                long CRC = new CRCHelp().deCRC(5, deCRC.ToArray());

                //得到低位在前高位在后的值
                string strCRC = CRC.ToString("x8");

                //低位  
                string low = strCRC.Substring(4, 1) + strCRC.Substring(5, 1);
                //高位
                string heigt = strCRC.Substring(6, 1) + strCRC.Substring(7, 1);
                //校验状态
                string crc_flag = "[错误]";


                //CRC值与数据进行校验是否正确
                if (low == deCRC[5].ToString("x8").ToString().Replace("0", "") && heigt == deCRC[6].ToString("x8").ToString().Replace("0", "")) {
                    crc_flag = "[正确]";
                }

                //进行打印  
                AddContent(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss "+ crc_flag + ":") + i_data_str + sb.ToString() + "\r\n");//.ToUpper()



                //重置位长
                i_data = 0;
                //重置位数据缓存
                i_data_count = new List<byte>(); 
            }
            else {
                //不满足位长时记录缓存数据
                foreach (var item in data)
                {
                    i_data_count.Add(item);
                }


            }
        }

四、运行结果

五、其他工具

串口通讯助手:SerialPort2.exe

串口仿生助手:Configure Virtual Serial Port Driver

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值