欢迎来到代码驿站!

.NET代码

当前位置:首页 > 软件编程 > .NET代码

C#实现简单串口通信

时间:2022-06-24 08:59:06|栏目:.NET代码|点击:

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

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

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();
            }
        }

上一篇:C#中按引用传递与按值传递的区别,以及ref与out关键字的用法详解

栏    目:.NET代码

下一篇:深入理解C#指针之美

本文标题:C#实现简单串口通信

本文地址:http://www.codeinn.net/misctech/205751.html

推荐教程

广告投放 | 联系我们 | 版权申明

重要申明:本站所有的文章、图片、评论等,均由网友发表或上传并维护或收集自网络,属个人行为,与本站立场无关。

如果侵犯了您的权利,请与我们联系,我们将在24小时内进行处理、任何非本站因素导致的法律后果,本站均不负任何责任。

联系QQ:914707363 | 邮箱:codeinn#126.com(#换成@)

Copyright © 2020 代码驿站 版权所有