一、串口通信简介

        串行接口(串口)是一种可以将接受来自CPU的并行数据字符转换为连续的串行数据流发送出去,同时可将接受的串行数据流转换为并行的数据字符供给CPU的器件。一般完成这种功能的电路,我们称为串行接口电路。

        串口通信(Serial Communications)的概念非常简单,串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配。

        1.波特率:这是一个衡量符号传输速率的参数。指的是信号被调制以后在单位时间内的变化,即单位时间内载波参数变化的次数,如每秒钟传送960个字符,而每个字符格式包含10位(1个起始位,1个停止位,8个数据位),这时的波特率为960Bd,比特率为10位*960个/秒=9600bps。

        2.数据位:这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据往往不会是8位的,标准的值是6、7和8位。标准的ASCII码是0~127(7位),扩展的ASCII码是0~255(8位)。

        3.停止位:用于表示单个包的最后几位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。

        4.校验位:在串口通信中一种简单的检错方式。常规种检错方式:无校验、偶校验、奇校验。

二、C#串口编程类

        从.NET Framework 2.0开始,C#提供了SerialPort类用于实现串口控制。命名空间:System.IO.Ports下面介绍其常用的字段、方法和事件。

1、常用字段:

名称

说明

PortName

获取或设置通信端口

BaudRate

获取或设置串行波特率

DataBits

获取或设置每个字节的标准数据位长度

Parity

获取或设置奇偶校验检查协议

StopBits

获取或设置每个字节的标准停止位数

2、常用方法:

名称

说明

Close

关闭端口连接,将IsOpen属性设置为false,并释放内部Stream对象

GetPortNames

获取当前计算机的串行端口名称数组

Open

打开一个新的串行端口连接

Read

SerialPort输入缓冲区中读取

Write

将数据写入串行端口输出缓冲区

3、常用事件:

名称

说明

DataReceived

表示将处理 SerialPort 对象的数据接收事件的方法

三、C#开发串口实例

1、创建项目

     此实例基于VS 2022开发,其他版本根据步骤来。

2、窗体设计

2.1、串口操作区

2.1.1、GroupBox组容器控件

        a、在工具箱中,输入GroupBox ,拖拽到窗体界面中,调节合适的大小,把箭头指向GroupBox的框架;

        b、右键打开属性界面,在Text中改写显示在界面的文字“串口参数”,然后在Font中改变字体的大小“宋体、四号”;

2.1.2、ladel文本控件

        打开工具箱,输入ladel,拖拽5个过来界面上,依次按上面的操作把Text改成对应的名字。

2.1.3、comboBox选择控件

        a、打开工具箱,输入comboBox,拖拽5个过来界面上,依次对应每一个。

        b、波特率的comboBox2,右键打开属性,找到ltems(集合)点击右边的(...)打开编辑器,在里面输入下面的数据(复制就行,他会自动排序)

波特率内容:

1200

4800

9600

14400

19200

38400

43000

57600

76800

115200

128000

230400

256000

460800

921600

1382400

        c、数据位的comboBox3,右键打开属性,找到ltems(集合)点击右边的(...)打开编辑器,在里面输入下面的数据(复制就行,他会自动排序)

数据位内容:

8
7
6
5

        d、停止位的comboBox4,右键打开属性,找到ltems(集合)点击右边的(...)打开编辑器,在里面输入下面的数据(复制就行,他会自动排序)

停止位内容:


1.5
2

        e、校验位的comboBox5,右键打开属性,找到ltems(集合)点击右边的(...)打开编辑器,在里面输入下面的数据(复制就行,他会自动排序)

校验位内容:


奇校验
偶校验

        f、全部输入后,运行以下画面

2.1.4、serialPort串口控件

        a、在工具箱中输入serialPort ,拖拽过来界面上,就会出现一下的界面,用来获取串口的信息。

        b、双击窗口空白处,进入代码界面,添加代码。

 添加方法text_Serial_port()来获取电脑的串口;

//添加方法text_Serial_port()来获取电脑的串口
private void text_Serial_port()
        {
            string[] ports = System.IO.Ports.SerialPort.GetPortNames(); //获得可用的串口
            for (int i = 0; i < ports.Length; i++)
            {
                comboBox1.Items.Add(ports[i]);
            }
            comboBox1.SelectedIndex = comboBox1.Items.Count > 0 ? 0 : -1;//如果里面有数据,显示第0个
        }

在Form1_load中调用方法text_Serial_port();

//然后在Form1_load中调用方法text_Serial_port()
private void Form1_Load(object sender, EventArgs e)
        {
            text_Serial_port();
        }

        程序如下:

        c、运行一下就有以下界面,如果出现右边的界面,说明电脑上没有虚拟串口,可以查询虚拟串口,有相关的资源;

2.1.5、串口初始化

        a、在代码中添加初始化方法,并在然后在Form1_load中调用方法;

串口初始化方法

//添加串口初始化方法
        private void Initialization()
        {
            comboBox2.Text = "9600";//波特率设置为9600
            comboBox3.Text = "1";//数据位设置为1
            comboBox4.Text = "8";//停止位设置为8
            comboBox5.Text = "无";//校验位设置为无
        }

在Form1_load中调用串口初始化方法

private void Form1_Load(object sender, EventArgs e)
        {
            text_Serial_port();//调用方法text_Serial_port()来获取电脑的串口
            Initialization();//调用初始化串口
        }

 程序如下:

        b、运行以下结果

 2.1.6、Button按钮控件

        a、打开工具箱,输入Button,找到并拖进界面中,调整大小,并在属性中的Text中改写成“打开串口”;

        b、双击Button控件进入代码界面,在其中添加Button方法,即可打开串口;

串口的开、关

//串口的开、关
private void button1_Click(object sender, EventArgs e)
{
    if (button1.Text == "打开串口")
    {
        try
        {
            serialPort1.PortName = comboBox1.Text;//获取要打开的串口
            serialPort1.BaudRate = int.Parse(comboBox2.Text);//获得波特率
            serialPort1.DataBits = int.Parse(comboBox4.Text);//获得数据位
            //设置停止位
            if (comboBox3.Text == "1")
            {
                serialPort1.StopBits = StopBits.One;
            }
            else if (comboBox3.Text == "1.5")
            {
                serialPort1.StopBits = StopBits.OnePointFive;
            }
            else if (comboBox3.Text == "2")
            {
                serialPort1.StopBits = StopBits.Two;
            }
            //设置校验位
            if (comboBox5.Text == "无")
            {
                serialPort1.Parity = Parity.None;
            }
            else if (comboBox5.Text == "奇校验")
            {
                serialPort1.Parity = Parity.Odd;
            }
            else if (comboBox5.Text == "偶校验")
            {
                serialPort1.Parity = Parity.Even;
            }
            serialPort1.Open();//打开串口
            button1.Text = "关闭串口";
        }
        catch (Exception err)
        {
            MessageBox.Show("打开失败" + err.ToString(), "提示!");
        }
    }
    else
    {
        //关闭串口
        try
        {
            serialPort1.Close();//关闭串口
        }
        catch (Exception) { }
        button1.Text = "打开串口"; //按钮显示打开
    }
}

 程序如下:

        b、运行以下结果

2.2、数据接收区

2.2.1、GroupBox组容器控件

        a、在工具箱中,输入GroupBox ,拖拽到窗体界面中,调节合适的大小,把箭头指向GroupBox的框架;

          b、右键打开属性界面,在Text中改写显示在界面的文字“数据接收”,然后在Font中改变字体的大小“宋体、四号”;

2.2.2、TextBox文本框控件

         a、在工具箱中输入TextBox,拖到界面中,点击组件上面的小箭头,把MultiLine勾上,然后调节大小;

        b、打开TextBox的属性,在BackColor中调节画面的颜色,在ForeColor中调节字体的颜色,这里我界面选择的是黑色,字体选择的是绿色;

2.2.3、DataReceived数据接收事件

        a、将鼠标箭头指向serialPort右键打开属性,在属性页面点击那个闪电一样,在下面找到DataReceived;

        b、双击“DataReceived”,进入程序界面;

读取并显示接收的信息;

//读取并显示接收的信息
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    int len = serialPort1.BytesToRead; //获取可以读取的字节数

    byte[] buff = new byte[len];
    serialPort1.Read(buff, 0, len);//把数据读取到数组中
    string reslut = Encoding.Default.GetString(buff);//将byte值根据为ASCII值转为string
    Invoke((new Action(() =>
    {
        //还应该添加16制转化
        textBox1.AppendText(" " + reslut);//追加显示在界面上        
    }
    )));
}

 程序如下:

2.3、数据发送区 

2.3.1、GroupBox组容器控件

        a、在工具箱中,输入GroupBox ,拖拽到窗体界面中,调节合适的大小,把箭头指向GroupBox的框架;

        b、右键打开属性界面,在Text中改写显示在界面的文字“数据接收”,然后在Font中改变字体的大小“宋体、四号”;

2.3.2、TextBox文本框控件

         在工具箱中输入TextBox,拖到界面中,点击组件上面的小箭头,把MultiLine勾上,然后调节大小;

2.4、数据处理

2.4.1、GroupBox组容器控件

        a、在工具箱中,输入GroupBox ,拖拽到窗体界面中,调节合适的大小,把箭头指向GroupBox的框架;

        b、右键打开属性界面,在Text中改写显示在界面的文字“数据设置”,然后在Font中改变字体的大小“宋体、四号”;

2.4.2、16进制显示处理

        a、工具箱中查找CheckBox,拖拽到界面中,并且把Text改成“16进制显示”;

        b、添加字符串转换16进制显示方法

字符串转换16进制显示方法;

//字符串转换16进制显示方法
private string byteToHexstr(byte[] buff)
 {
     string str = "";
     try
     {
         if (buff != null)
         {

             for (int i = 0; i < buff.Length; i++)
             {
                 //char a = (char)buff[i];
                 //str += a.ToString();
                 str += buff[i].ToString("x2");
                 str += " ";//两个之间用空格
             }
             //str = new string(buff);
             return str;
         }
     }
     catch (Exception)
     {
         return str;
     }
     return str;
 }

程序运行:

 2.4.3、数据显示区清除

        a、在工具箱中找到Button拖拽过来界面,将Text改成“清除数据”;

        b、双击清除按钮,添加代码;

private void button2_Click(object sender, EventArgs e)
{
    textBox1.Clear();//清除数据显示区
}

程序如下:

  2.4.4、发送数据处理

        a、从工具箱中找到CheckBox并拖拽到界面,将Text改成“16进制发送”;

        b、添加发送字符串转换16进制方法

字符串发送转16进制

//字符串转16进制
private byte[] strToHexbytes(string str)
{
    str = str.Replace(" ", "");//清除空格
    byte[] buff;
    if ((str.Length % 2) != 0)
    {
        buff = new byte[(str.Length + 1) / 2];
        try
        {
            for (int i = 0; i < buff.Length; i++)
            {
                buff[i] = Convert.ToByte(str.Substring(i * 2, 2), 16);
            }
            buff[buff.Length - 1] = Convert.ToByte(str.Substring(str.Length - 1, 1).PadLeft(2, '0'), 16);
            return buff;
        }
        catch (Exception err)
        {
            MessageBox.Show("含有f非16进制的字符", "提示");
            return null;
        }
    }
    else
    {
        buff = new byte[str.Length / 2];
        try
        {
            for (int i = 0; i < buff.Length; i++)
            {
                buff[i] = Convert.ToByte(str.Substring(i * 2, 2), 16);
            }
            //buff[buff.Length - 1] = Convert.ToByte(str.Substring(str.Length - 1, 1).PadLeft(2, '0'), 16);
        }
        catch (Exception err)
        {
            {
                MessageBox.Show("含有非16进制的字符", "提示");
                return null;
            }
        }
    }

    return buff;
}
  2.4.5、发送数据

        从工具箱中找到Button,将Text改成“发送信息”,双击按钮进入程序界面;

private void button3_Click(object sender, EventArgs e)
{
    //创建发送数据
    Task.Run(() => {
        send_();
    });
}

//发送数据
string data_;
private void send_()
{
    data_ = textBox2.Text.ToString();
    try
    {
        if (data_.Length != 0)
        {
            data_ += " ";
            if (checkBox2.Checked) //16进制发送
            {
                serialPort1.Write(Encoding.Default.GetString(strToHexbytes(data_)));
            }
            else
            {
                serialPort1.Write(data_);
                //byte[] byteArray = Encoding.Default.GetBytes(shuju);//Str 转为 Byte值
                //serialPort1.Write(byteArray, 0, byteArray.Length, 0, null, null); //发送数据         
            }
        }

    }
    catch (Exception) 
    { 
            
    }
}
2.4.6、清除发送数据

        从工具箱中找到Button,将Text改成“清除数据”,双击按钮进入程序界面;

 private void button4_Click(object sender, EventArgs e)
 {
     textBox2.Clear();//清除发送数据区
 }

3、完整设计

3.1、窗体界面

3.2、完整程序

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;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace 串口简易程序实例
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //添加方法text_Serial_port()来获取电脑的串口
        private void text_Serial_port()
        {
            string[] ports = System.IO.Ports.SerialPort.GetPortNames(); //获得可用的串口
            for (int i = 0; i < ports.Length; i++)
            {
                comboBox1.Items.Add(ports[i]);
            }
            comboBox1.SelectedIndex = comboBox1.Items.Count > 0 ? 0 : -1;//如果里面有数据,显示第0个
        }

        //添加串口初始化方法
        private void Initialization()
        {
            comboBox2.Text = "9600";//波特率设置为9600
            comboBox3.Text = "1";//停止位设置为1
            comboBox4.Text = "8";//数据位设置为8
            comboBox5.Text = "无";//校验位设置为无
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            text_Serial_port();//调用方法text_Serial_port()来获取电脑的串口
            Initialization();//调用初始化串口
        }

        串口的开、关
        private void button1_Click(object sender, EventArgs e)
        {
            if (button1.Text == "打开串口")
            {
                try
                {
                    serialPort1.PortName = comboBox1.Text;//获取要打开的串口
                    serialPort1.BaudRate = int.Parse(comboBox2.Text);//获得波特率
                    serialPort1.DataBits = int.Parse(comboBox4.Text);//获得数据位
                    //设置停止位
                    if (comboBox3.Text == "1")
                    {
                        serialPort1.StopBits = StopBits.One;
                    }
                    else if (comboBox3.Text == "1.5")
                    {
                        serialPort1.StopBits = StopBits.OnePointFive;
                    }
                    else if (comboBox3.Text == "2")
                    {
                        serialPort1.StopBits = StopBits.Two;
                    }
                    //设置校验位
                    if (comboBox5.Text == "无")
                    {
                        serialPort1.Parity = Parity.None;
                    }
                    else if (comboBox5.Text == "奇校验")
                    {
                        serialPort1.Parity = Parity.Odd;
                    }
                    else if (comboBox5.Text == "偶校验")
                    {
                        serialPort1.Parity = Parity.Even;
                    }
                    serialPort1.Open();//打开串口
                    button1.Text = "关闭串口";
                }
                catch (Exception err)
                {
                    MessageBox.Show("打开失败" + err.ToString(), "提示!");
                }
            }
            else
            {
                //关闭串口
                try
                {
                    serialPort1.Close();//关闭串口
                }
                catch (Exception) { }
                button1.Text = "打开串口"; //按钮显示打开
            }
        }

        //字符串转换16进制显示方法
        private string byteToHexstr(byte[] buff)
        {
            string str = "";
            try
            {
                if (buff != null)
                {

                    for (int i = 0; i < buff.Length; i++)
                    {
                        //char a = (char)buff[i];
                        //str += a.ToString();
                        str += buff[i].ToString("x2");
                        str += " ";//两个之间用空格
                    }
                    //str = new string(buff);
                    return str;
                }
            }
            catch (Exception)
            {
                return str;
            }
            return str;
        }

        //读取并显示接收的信息
        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            int len = serialPort1.BytesToRead; //获取可以读取的字节数

            byte[] buff = new byte[len];
            serialPort1.Read(buff, 0, len);//把数据读取到数组中
            string reslut = Encoding.Default.GetString(buff);//将byte值根据为ASCII值转为string
            Invoke((new Action(() =>
            {
                if (checkBox1.Checked)//16进制显示选择按钮为真,转化为16进制显示
                {
                    textBox1.AppendText(" " + byteToHexstr(buff) + Environment.NewLine);//16进制显示并换行
                }
                else
                {
                    textBox1.AppendText(" " + reslut + Environment.NewLine);//字符串显示在界面上并换行
                }    
            }
            )));
        }

        private void button2_Click(object sender, EventArgs e)
        {
            textBox1.Clear();//清除数据显示区
        }

        //字符串转16进制
        private byte[] strToHexbytes(string str)
        {
            str = str.Replace(" ", "");//清除空格
            byte[] buff;
            if ((str.Length % 2) != 0)
            {
                buff = new byte[(str.Length + 1) / 2];
                try
                {
                    for (int i = 0; i < buff.Length; i++)
                    {
                        buff[i] = Convert.ToByte(str.Substring(i * 2, 2), 16);
                    }
                    buff[buff.Length - 1] = Convert.ToByte(str.Substring(str.Length - 1, 1).PadLeft(2, '0'), 16);
                    return buff;
                }
                catch (Exception err)
                {
                    MessageBox.Show("含有f非16进制的字符", "提示");
                    return null;
                }
            }
            else
            {
                buff = new byte[str.Length / 2];
                try
                {
                    for (int i = 0; i < buff.Length; i++)
                    {
                        buff[i] = Convert.ToByte(str.Substring(i * 2, 2), 16);
                    }
                    //buff[buff.Length - 1] = Convert.ToByte(str.Substring(str.Length - 1, 1).PadLeft(2, '0'), 16);
                }
                catch (Exception err)
                {
                    {
                        MessageBox.Show("含有非16进制的字符", "提示");
                        return null;
                    }
                }
            }

            return buff;
        }

        private void button3_Click(object sender, EventArgs e)
        {
            //创建发送数据
            Task.Run(() => {
                send_();
            });
        }

        //发送数据
        string data_;
        private void send_()
        {
            data_ = textBox2.Text.ToString();
            try
            {
                if (data_.Length != 0)
                {
                    data_ += " ";
                    if (checkBox2.Checked) //16进制发送
                    {
                        serialPort1.Write(Encoding.Default.GetString(strToHexbytes(data_)));
                    }
                    else
                    {
                        serialPort1.Write(data_);
                        //byte[] byteArray = Encoding.Default.GetBytes(shuju);//Str 转为 Byte值
                        //serialPort1.Write(byteArray, 0, byteArray.Length, 0, null, null); //发送数据         
                    }
                }

            }
            catch (Exception) 
            { 
            
            }
        }
     private void button4_Click(object sender, EventArgs e)
     {
         textBox2.Clear();//清除发送数据区
     }
    }
}

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐