C#实现简单串口通信

作者:RunnerDNA 时间:2023-05-14 08:15:27 

串口通信(Serial Communications)是指外设和计算机间通过数据信号线、地线等按位(bit)进行传输数据的一种通信方式,属于串行通信方式,能够实现远距离通信,长度可达1200米。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配。

串口通信的接口标准有很多,有 RS-232C、RS-232、RS-422A、RS-485 等。常用的就是 RS-232 和 RS-485。串口通信使用的大多都是 DB9 接口,如下图。

C#实现简单串口通信

1 载波检测(DCD) 2 接受数据(RXD) 3 发出数据(TXD) 4 数据终端准备好(DTR) 5 信号地线(SG) 6 数据准备好(DSR) 7 请求发送(RTS) 8 清除发送(CTS) 9 振铃指示(RI)

这里我们以 RS-232 接口进行演示。

1、数据包格式定为(10bytes):

帧头(0xAA,0x55),命令字(1byte),地址位(2bytes),数据位(2bytes),校验位(1byte,和校验),帧尾(0xFE,0xFE)

地址位和数据位都是高位在前。

数据封装方法:

//数据打包
private byte[] DataPackage(byte cmd, int addr, int data)
        {
            byte[] package = new byte[10];
            package[0] = 0xAA;//帧头
            package[1] = 0x55;
            package[2] = cmd;//命令字    
            byte[] dataaddr = IntToByteArray(addr);
            package[3] = dataaddr[0];//地址高字节
            package[4] = dataaddr[1];//地址低字节
            byte[] value = IntToByteArray(data);
            package[5] = value[0];//数据高字节
            package[6] = value[1];//数据低字节
            package[7] = CheckSum(package);//校验位
            package[8] = 0xFE;//帧尾
            package[9] = 0xFE;
            return package;
        }
 
        //将int转换成2位数组
        private static byte[] IntToByteArray(int value)
        {
            int hvalue = (value >> 8) & 0xFF;
            int lValue = value & 0xFF;
            byte[] arr = new byte[] { (byte)hvalue, (byte)lValue };
            return arr;
        }
 
        //得到和校验码
        private byte CheckSum(byte[] package)
        {
            byte checksum = 0;
            for (int i = 0; i < package.Length; i++)
            {
                checksum += package[i];
            }
            return checksum;
        }

2、串口调用封装类CommHelper.cs

internal class CommHelper
    {
        //委托
        public delegate void EventHandle(byte[] readBuffer);
        public event EventHandle DataReceived;
 
        public SerialPort serialPort;
        private Thread thread;
        volatile bool _keepReading;
 
        public CommHelper()
        {
            serialPort = new SerialPort();
            
            thread = null;
            _keepReading = false;
 
            serialPort.ReadTimeout = -1;
            serialPort.WriteTimeout = 1000;
        }
 
        //获取串口打开状态
        public bool IsOpen
        {
            get
            {
                return serialPort.IsOpen;
            }
        }
 
        //开始读取
        private void StartReading()
        {
            if (!_keepReading)
            {
                _keepReading = true;
                thread = new Thread(new ThreadStart(ReadPort));
                thread.Start();
            }
        }
 
        //停止读取
        private void StopReading()
        {
            if (_keepReading)
            {
                _keepReading = false;
                thread.Join();
                thread = null;
            }
        }
 
        //读数据
        private void ReadPort()
        {
            while (_keepReading)
            {
                if (serialPort.IsOpen)
                {
                    int count = serialPort.BytesToRead;
                    if (count > 0)
                    {
                        byte[] readBuffer = new byte[count];
                        try
                        {
                            Application.DoEvents();
                            serialPort.Read(readBuffer, 0, count);
                            DataReceived?.Invoke(readBuffer);
                            Thread.Sleep(100);
                        }
                        catch (TimeoutException)
                        {
                        }
                    }
                }
            }
        }
 
        //写数据
        public void WritePort(byte[] send, int offSet, int count)
        {
            if (IsOpen)
            {
                serialPort.Write(send, offSet, count);
            }
        }
 
        //打开
        public void Open()
        {
            Close();
            try
            {
                serialPort.Open();
                serialPort.RtsEnable = true;
                if (serialPort.IsOpen)
                {
                    StartReading();
                }
                else
                {
                    MessageBox.Show("串口打开失败!");
                }
            }
            catch
            {
 
            }
        }
 
        //关闭
        public void Close()
        {
            StopReading();
            serialPort.Close();            
        }
    }

3、调用(模拟测试读写硬件版本号)

//地址存储信息定义
private static int HARD_VERSION = 0;            //硬件版本号
       
//命令定义
private static byte RD_HV = 0;                //读硬件版本
private static byte WR_HV = 1;                //写硬件版本
 
private CommHelper comm = new CommHelper();
private bool GetCorrect = false; //用来标识是否已经接收到返回值
 
private List<byte> buffer = new List<byte>(1024);//默认分配1M内存,限制不能超过
private byte[] binary_data = new byte[10];//指定格式的某个完整数据
 
//串口初始化
private void InitComm()
        {
            comm.serialPort.PortName = "COM1";
            comm.serialPort.BaudRate = 19200;
            try
            {
                comm.Open();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            if (comm.IsOpen)
            {
                comm.DataReceived += new CommHelper.EventHandle(comm_DataReceived);
            }
        }
 
        //数据接收后处理
        private void comm_DataReceived(byte[] readBuffer)
        {
            try
            {
                buffer.AddRange(readBuffer);//将数据放到缓存中
                //完整性判断
                while (buffer.Count >= 10)
                {
                    //查找数据头
                    if (buffer[0] == 0xAA && buffer[1] == 0x55)
                    {
                        //和校验
                        byte checksum = 0;
                        for (int i = 0; i < 7; i++)
                        {
                            checksum += buffer[i];
                        }
                        if (checksum != buffer[7])
                        {
                            buffer.RemoveRange(0, 10);//如果校验失败,从缓存中删除这一包数据
                            continue;
                        }
                        buffer.CopyTo(0, binary_data, 0, 10);//复制一条完整的数据到具体的数据缓存
 
                        //读取硬件版本号
                        if (binary_data[2] == RD_HV && ByteArrayToInt(binary_data[3], binary_data[4]) == HARD_VERSION)//命令字为读RD_HV,地址HARD_VERSION
                        {
                            GetCorrect = true;
                            string data = ByteArrayToString(binary_data[5], binary_data[6]);
                            //这里可以处理数据更新界面展示
                        }
 
                        buffer.RemoveRange(0, 10);
                    }
                    else
                    {
                        GetCorrect = false;
                        buffer.RemoveAt(0);//如果数据开始不是头,则删除数据
                    }
                }
            }
            catch
            {
 
            }
        }
 
        //将2位数组转换成字符串
        private static string ByteArrayToString(byte arr1, byte arr2)
        {
            int value = ByteArrayToInt(arr1, arr2);
            string str = Convert.ToString(value, 10);
            return str;
        }
 
        //将2位数组转换成10进制数
        private static int ByteArrayToInt(byte arr1, byte arr2)
        {
            byte[] arr = new byte[] { arr1, arr2 };
            int value = (int)((arr[0] & 0xFF) << 8)
                      | (arr[1] & 0xFF);
            return value;
        }
 
        //读取硬件版本号
        private void ReadHV()
        {
            GetCorrect = false;
            try
            {
                comm.WritePort(DataPackage(RD_HV, HARD_VERSION, 0), 0, 10);//发送RD_E2P命令,地址HARD_VERSION
                Delay(100);
                if (GetCorrect == true)
                {
                    MessageBox.Show("硬件版本号读取成功!");
                }
                else
                {
                    MessageBox.Show("硬件版本号读取失败!");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
 
        //写入硬件版本号
        private void WriteHV()
        {
            if (tbVersion.Text.Length == 4 && isLegal(tbVersion.Text))//限定只能写入4个字符且输入合法
            {
                try
                {
                    int buf = Convert.ToInt32(tbVersion.Text);
                    GetCorrect = false;
                    comm.WritePort(DataPackage(WR_HV, HARD_VERSION, buf), 0, 10);//发送WR_HV命令,地址HARD_VERSION
                    Delay(100);
                    if (GetCorrect == true)
                    {
                        MessageBox.Show("硬件版本号写入成功!");
                    }
                    else
                    {
                        MessageBox.Show("硬件版本号写入失败!");
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
            else
            {
                MessageBox.Show("硬件版本号数据格式错误!请重新写入!");
            }
        }
 
        //判断输入是否合法,必须是数字
        private const string PATTERN = @"^[0-9]*$";
        private bool isLegal(string hex)
        {
            return System.Text.RegularExpressions.Regex.IsMatch(hex, PATTERN);
        }
 
        //延时函数
        [DllImport("kernel32.dll")]
        static extern uint GetTickCount();
        static void Delay(uint ms)
        {
            uint start = GetTickCount();
            while (GetTickCount() - start < ms)
            {
                Application.DoEvents();
            }
        }

来源:https://blog.csdn.net/dnazhd/article/details/88327160

标签:C#,串口通信
0
投稿

猜你喜欢

  • Java实现TFIDF算法代码分享

    2023-12-23 20:54:45
  • JAVA中实现原生的 socket 通信机制原理

    2021-08-08 03:06:01
  • JavaMail与Spring整合过程解析

    2022-07-06 23:53:54
  • Android蓝牙服务启动流程分析探索

    2023-12-20 17:45:35
  • 登陆验证码kaptcha结合spring boot的用法详解

    2023-02-19 15:56:32
  • Java数据结构之优先级队列(PriorityQueue)用法详解

    2023-11-18 13:00:50
  • java开发之MD5加密算法的实现

    2022-05-13 23:44:35
  • 浅析C#静态类,静态构造函数,静态变量

    2022-04-10 03:33:14
  • Java将Date日期类型字段转换成json字符串的方法

    2023-02-18 19:57:09
  • Swing常用组件之单选按钮和复选框

    2023-02-15 13:35:05
  • Android 内存优化知识点梳理总结

    2021-05-24 02:54:19
  • java开发中遇到的异常汇总详解

    2023-03-21 02:54:15
  • java 使用foreach遍历集合元素的实例

    2022-11-17 09:24:58
  • 关于Java中修饰符的总结(fina除外)

    2023-11-22 23:15:57
  • Android实现应用内置语言切换功能

    2021-11-14 13:19:50
  • 在Unity中使用全局变量的操作

    2022-12-30 13:18:47
  • Java截取字符串的几种方法示例

    2023-11-29 12:36:32
  • Java深度复制功能与用法实例分析

    2023-07-05 11:23:17
  • C# 参数按照ASCII码从小到大排序(字典序)

    2023-03-14 22:14:11
  • Android ExpandableListView双层嵌套实现三级树形菜单

    2023-11-05 23:04:55
  • asp之家 软件编程 m.aspxhome.com