c#串口通讯详解(附源码)

串口通讯

	串口通讯是计算机传输数据的一种通讯方式,只不过是以比特位来传输数据的

串口通讯的相关参数

波特率:
波特率的大小代表每秒钟可以传输多少个二进制位,如波特率是9600,能每秒传输9600二进制位
起始位、停止位
数据包从起始位开始,到停止位结束。起始信号用逻辑0的数据位表示,停止信号由0.5、1、1.5或2个逻辑1的数据位表示,只要双方约定一致即可。标识这本次通讯单元的结束
数据位
起始位之后便是传输的主体数据内容了,也称为有效数据,其长度一般被约定为5、6、7或8位长。表示一次通讯真正的要发送的有效数据
奇偶校验位
由于在通讯过程中易受到外部干扰导致传输数据出现偏差,所以在有效数据之后加上校验位解决。是串口通讯中的一周检错方式

界面展示

发送跟接收支持ASCII跟Hex格式
在这里插入图片描述
代码部分如下:
后端代码

    public partial class Comm : Form
    {

        public Comm()
        {
            InitializeComponent();
            ComHelper.Data += AddContent;
            ComHelper.ReceiveNum += AddNums;
            ComHelper.SendNum += AddNum;
        }

        //页面启动事件
        private void Comm_Load(object sender, EventArgs e)
        {
            //读取计算机所有串口
            string[] strCom = SerialPort.GetPortNames();
            if (strCom == null)
            {
                MessageBox.Show("本机没有串口!", "Error");
                return;
            }
            //GetPortNames()方法:获取当前计算机的串行端口名的数组
            foreach (string com in System.IO.Ports.SerialPort.GetPortNames())
            {
                com_port.Items.Add(com);

            }
            //默认选中第一个
            com_port.SelectedIndex = 0;
            com_jyw.SelectedIndex = 0;
            com_data.Text = "8";
            com_stop.SelectedIndex = 0;
            com_bps.Text = "4800";
            receive_ascii.Checked = true;
            send_ascii.Checked = true;
        }

        //选中保存目录
        private void receive_file_Click(object sender, EventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Multiselect = true;//该值确定是否可以选择多个文件
            dialog.Title = "请选择文件夹";
            dialog.Filter = "所有文件(*.*)|*.*";
            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                receive_FileSrc.Text = dialog.FileName;
            }
        }

        private void AddNum(object content)
        {
            int sendnum = int.Parse(content.ToString()); ;
            num1.Text = (int.Parse(num1.Text) + sendnum).ToString();
        }
        private void AddNums(object content)
        {
            int sendnum = int.Parse(content.ToString()); ;
            num2.Text = (int.Parse(num2.Text) + sendnum).ToString();
        }
        //将接受到的内容显示出来
        private void AddContent(object content)
        {
            this.BeginInvoke(new MethodInvoker(delegate
             {

                 string str = content as string;
                 if (IsTime.Checked == true)
                 {

                     string Title = DateTime.Now.ToString();
                     bt_ReceiveData.AppendText("[" + Title + "]# RECV " + (receive_ascii.Checked == true ? "ASCII" : "HEX") + "\r\n");
                 }
                 bt_ReceiveData.AppendText(str + " ");
                 if (IsLine.Checked == true)
                 {
                     bt_ReceiveData.AppendText("\r\n");
                 }
                 bt_ReceiveData.Focus();//先获取焦点
                 bt_ReceiveData.Select(bt_ReceiveData.TextLength, 0);//选中数据末尾0个字符
                 bt_ReceiveData.ScrollToCaret();//将滚动条移动到当前位置
                 //记录收到的字符个数
                 //toolStripStatusLabel1.Text += (int.Parse(toolStripStatusLabel1.Text) + content.Length).ToString();
             }));
        }

        //清空接收区
        private void receive_clean_Click(object sender, EventArgs e)
        {
            this.bt_ReceiveData.Text = "";
        }

        //清空发送区
        private void send_clean_Click(object sender, EventArgs e)
        {
            this.bt_SendData.Text = "";
        }

        /// 打开串口
        private void com_open_Click(object sender, EventArgs e)
        {
            //实例化串口配置
            ComClass com = new ComClass()
            {
                PortName = com_port.Text,
                BaudRate = int.Parse(this.com_bps.Text),
                Databit = int.Parse(com_data.Text),
                Stopbit = float.Parse(com_stop.Text),
                Paritybit = this.com_jyw.Text
            };
            //判断端口是否打开
            if (!ComHelper.port.IsOpen)
            {
                ComHelper.SerialClass(com);
                //判断是否已经打开串口控件禁用
                if (ComHelper.OpenPart())
                {
                    this.com_state.Checked = true;
                    this.com_port.Enabled = false;
                    this.com_bps.Enabled = false;
                    this.com_data.Enabled = false;
                    this.com_stop.Enabled = false;
                    this.com_jyw.Enabled = false;
                    this.com_open.Text = "关闭串口";
                }
            }
            else
            {
                ComHelper.closePort();
                this.com_port.Enabled = true;
                this.com_bps.Enabled = true;
                this.com_data.Enabled = true;
                this.com_stop.Enabled = true;
                this.com_jyw.Enabled = true;
                this.com_state.Checked = false;
                this.com_open.Text = "打开串口";
            }
        }

        //发送数据
        private void bt_send_Click(object sender, EventArgs e)
        {
            string SendData = this.bt_SendData.Text.Trim();
            //判断是否是HEX发送
            if (send_hex.Checked == true)
            {
                if (!ComHelper.IsHexadecimal(SendData)) { MessageBox.Show("输入非16进制数据"); }
                ComHelper.SendData(SendData, true);
            }
            else
            {
                ComHelper.SendData(SendData, false);
            }

        }

        //选中HEX时判断输入内容是否为HEX数据,如果不是将其转换成Hex
        private void send_hex_Click(object sender, EventArgs e)
        {
            if (send_hex.Checked == true && !ComHelper.IsHexadecimal(this.bt_SendData.Text))
            {
                this.bt_SendData.Text = ComHelper.GetHexFromChs(bt_SendData.Text);
            }
        }

        //接收是否为Hex格式
        private void receive_hex_CheckedChanged(object sender, EventArgs e)
        {
            if (receive_hex.Checked == true)
            {
                ComHelper.ReceiveIsHex = true;
            }
            else
            {
                ComHelper.ReceiveIsHex = false;
            }
        }

        private void send_file_Click(object sender, EventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Multiselect = true;//该值确定是否可以选择多个文件
            dialog.Title = "请选择文件夹";
            dialog.Filter = "所有文件(*.*)|*.*";
            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                this.bt_SendData.Text = ComHelper.fileToString(dialog.FileName);
            }
        }
    }
}


ComClass实体类

namespace MSComm
{
   public class ComClass
    {
        private string _portName;
        private int _baud;
        private float _stopbit;
        private int _databit;
        private string _paritybit;

        public string PortName { get => _portName; set => _portName = value; }//获取串口名
        public int BaudRate { get => _baud; set => _baud = value; }//波特率
        public float Stopbit { get => _stopbit; set => _stopbit = value; }//停止位
        public int Databit { get => _databit; set => _databit = value; }//数据位
        public string Paritybit { get => _paritybit; set => _paritybit = value; }//校验位
    }
}

ComHelper类

namespace MSComm
{
    public class ComHelper
    {
        public delegate void ReceiveData(object str);

        public static event ReceiveData Data;
        public static event ReceiveData ReceiveNum;
        public static event ReceiveData SendNum;

        //是否Hex接收
        public static bool ReceiveIsHex = false;
        //实例化串口
        public static SerialPort port = new SerialPort();
        //读取缓存区数据
        static   StringBuilder Datas = new StringBuilder("");
        static bool rest = true;
        //打开串口配置串口
        public static void SerialClass(ComClass p)
        {
            port.PortName = p.PortName;
            port.BaudRate = p.BaudRate;
            port.DataBits = p.Databit;
            switch (p.Stopbit)
            {
                case 1:
                    port.StopBits = StopBits.One; //使用一个停止位
                    break;
                case 1.5f:
                    port.StopBits = StopBits.OnePointFive; //使用一个停止位
                    break;
                case 2:
                    port.StopBits = StopBits.Two; //使用一个停止位
                    break;
                default:

                    break;
            }
            switch (p.Paritybit)
            {
                case "NONE":
                    port.Parity = Parity.None;
                    break;
                case "ODD":
                    port.Parity = Parity.Odd;
                    break;
                case "EVEN":
                    port.Parity = Parity.Even;
                    break;
                case "MARK":
                    port.Parity = Parity.Mark;
                    break;
                case "SPACE":
                    port.Parity = Parity.Space;
                    break;
                default:

                    break;
            }
            //port.Handshake = Handshake.None;
            port.RtsEnable = true;
            port.DtrEnable = true;//获取或设置一个值,改值在串行通讯过程中启用数据终端就绪(DTR)信号。
            port.ReadTimeout = 2000;
            port.DataReceived += Sp1_DataReceived;
        }

        /// <summary>
        /// 打开串口
        /// </summary>
        /// <returns></returns>
        public static bool OpenPart()
        {
            bool ok = false;
            //如果串口是打开的,先关闭
            if (port.IsOpen) port.Close();
            try
            {
                //打开串口
                port.Open();
                ok = true;
            }
            catch (Exception Ex)
            {
                throw Ex;
            }
            return ok;
        }

        /// <summary>
        /// 关闭串口
        /// </summary>
        public static void closePort()
        {
            if (port.IsOpen)
            {
                port.Close();
            }
        }

        /// <summary>
        /// 数据发送
        /// </summary>
        /// <param name="Data">数据</param>
        /// <param name="IsHEX">是否Hex发送</param>
        public static void SendData(string Data, bool IsHEX)
        {
            byte[] sendData = null;
            if (!port.IsOpen) //如果没打开
            {
                return;
            }
            String strSend = Data;
            try
            {
                if (IsHEX)
                {
                    sendData = ConvertHexStringToBytes(strSend.Trim());
                }
                else
                {
                    sendData = Encoding.Default.GetBytes(strSend.Trim());
                }
                //sp1.WriteLine(txtSendStr.Text);    //写入数据
                SendNum(sendData.Length);
                port.Write(sendData, 0, sendData.Length);
            }
            catch (Exception ex)
            {
            }

        }

      
        /// <summary>
        /// 监听返回值
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void Sp1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (ComHelper.port.IsOpen)     //判断是否打开串口
            {
                string content = "";
                int Lengths = port.BaudRate/10;
                //输出当前时间
                DateTime dt = DateTime.Now;
                try
                {
                    Byte[] receivedData = new Byte[port.BytesToRead];        //创建接收字节数组
                    port.Read(receivedData, 0, receivedData.Length);         //读取数据
                    ReceiveNum(receivedData.Length);
                    //foreach (byte item in receivedData)
                    //{
                    //    Datas.Append(Convert.ToChar(item));
                    //}
                    //是否解析成16进制
                    if (ReceiveIsHex)
                    {
                        content = bytetohex(receivedData, receivedData.Length).Trim();
                    }
                    else
                    {
                        content = Encoding.Default.GetString(receivedData);
                    }
                    //委托传值
                    Data(content);
                }
                catch (Exception ex)
                {

                }
            }
        }

        /// <summary>
        /// 从汉字转换到16进制
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static string GetHexFromChs(string s)
        {
            if ((s.Length % 2) != 0)
            {
                s += " ";//空格
                         //throw new ArgumentException("s is not valid chinese string!");
            }

            System.Text.Encoding chs = System.Text.Encoding.GetEncoding("gb2312");

            byte[] bytes = chs.GetBytes(s);

            string str = "";

            for (int i = 0; i < bytes.Length; i++)
            {
                str += string.Format("{0:X}", bytes[i]) + " ";
            }
            return str;
        }

        /// <summary>
        /// byte[]转HEXString
        /// </summary>
        /// <param name="byteArray">byte数组</param>
        /// <param name="lenght">长度</param>
        /// <returns></returns>
        public static string bytetohex(byte[] byteArray, int lenght)
        {
            // string str = "";
            var str = new System.Text.StringBuilder();
            int start = Environment.TickCount;//计时器
            Console.WriteLine("wait...");
            for (int i = 0; i < lenght; i++)
            {
                str.Append(String.Format("{0:X2} ", byteArray[i]));//var拼接
            }
            string s = str.ToString();
            Console.WriteLine(Math.Abs(Environment.TickCount - start));//在调试窗口可查用时
            return s;
        }

        /// 判断是否十六进制格式字符串
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static bool IsHexadecimal(string str)
        {
            str = str.Replace(" ", "");
            bool IsHex = System.Text.RegularExpressions.Regex.IsMatch(str, "^[0-9A-Fa-f]+$");
            return IsHex;
        }

        /// <summary>
        /// 16进制原码字符串转字节数组
        /// </summary>
        /// <param name="hexString">"AABBCC"或"AA BB CC"格式的字符串</param>
        /// <returns></returns>
        public static byte[] ConvertHexStringToBytes(string hexString)
        {
            hexString = hexString.Replace(" ", "");
            if (hexString.Length % 2 != 0)
            {
                throw new ArgumentException("参数长度不正确");
            }

            byte[] returnBytes = new byte[hexString.Length / 2];
            for (int i = 0; i < returnBytes.Length; i++)
            {
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            }

            return returnBytes;
        }


        /// <summary>
        /// 获取文件中的数据
        /// </summary>
        /// <param name="args"></param>
        public static string fileToString(String filePath)
        {
            StringBuilder strData = new StringBuilder("");
            try
            {
                string line;
                // 创建一个 StreamReader 的实例来读取文件 ,using 语句也能关闭 StreamReader
                using (System.IO.StreamReader sr = new System.IO.StreamReader(filePath, System.Text.Encoding.GetEncoding("gb2312")))
                {
                    // 从文件读取并显示行,直到文件的末尾 
                    while ((line = sr.ReadLine()) != null)
                    {
                        //Console.WriteLine(line);
                        strData.Append( line);
                    }
                }
            }
            catch (Exception e)
            {
                // 向用户显示出错消息
                Console.WriteLine("The file could not be read:");
                Console.WriteLine(e.Message);
            }
            return strData.ToString();
        }

        public static void WriteLog(string FileName)
        {

        }
    }
}

源码链接

以下是C#串口通讯断线重连的示例代码: ```csharp using System.IO.Ports; using System.Threading; public class SerialPortManager { private SerialPort serialPort; private Thread reconnectThread; private bool isReconnecting = false; public SerialPortManager(string portName, int baudRate) { serialPort = new SerialPort(portName, baudRate); serialPort.DataReceived += SerialPort_DataReceived; serialPort.ErrorReceived += SerialPort_ErrorReceived; serialPort.Open(); } private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { // 处理接收到的数据 } private void SerialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e) { if (e.EventType == SerialError.RXOver || e.EventType == SerialError.Overrun || e.EventType == SerialError.Frame || e.EventType == SerialError.RXParity) { // 发生错误,断开连接并尝试重新连接 Reconnect(); } } private void Reconnect() { if (isReconnecting) { return; } isReconnecting = true; // 关闭串口 serialPort.Close(); // 开启重连线程 reconnectThread = new Thread(() => { while (true) { try { // 尝试重新连接 serialPort.Open(); // 重新连接成功,退出重连线程 isReconnecting = false; break; } catch { // 重新连接失败,等待一段时间后继续尝试 Thread.Sleep(1000); } } }); reconnectThread.Start(); } } ``` 该示例代码中,SerialPortManager类封装了串口通讯的相关操作,包括初始化串口、处理接收到的数据、处理错误事件、断线重连等。当发生错误事件时,会调用Reconnect方法进行断线重连,该方法会关闭串口并开启一个新的线程进行重连操作,直到重连成功为止。
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值