SerialPort串口通信

 C#数据类型及字节长度和取值范围:

类型名称 类型说明 字节长度(bytes数) 取值范围 .NET框架类型(包装类) 默认值
sbyte 8位有符号整数 1bytes -128~127 System.SByte 0
byte 8位无符号整数 1bytes 0~255 System.Byte 0
short 16位有符号整数 2bytes -32768~32767 System.Int16 0
ushort 16位无符号整数 2bytes 0~65535 System.UInt16 0
int 32位有符号整数 4bytes -2,147,483,648~2,147,483,647 System.Int32 0
uint 32位无符号整数 4bytes 0~4,294,967,295 System.UInt32 0
long 64位有符号整数 8bytes

-9,223,372,036,854,775,808

~9,223,372,036,854,775,807

System.Int64 0
ulong 64位无符号整数 8bytes 0~18,446,744,073,709,551,615 System.UInt64 0
nint 带符号的32位或64位整数 (在32位进程中是4bytes,在64位进程中是8bytes) 取决于(在运行时计算的)平台 System.IntPtr -
nuint 无符号的32位或64位整数 (在32位进程中是4bytes,在64位进程中是8bytes) 取决于(在运行时计算的)平台 System.UIntPtr -
float 单精度浮点数
(精度为大约6-9位有效小数位)
4bytes 1.5×10 -45 ~3.4×10 38 System.Single 0.0f
double 双精度浮点数
(精度为大约15-17位有效小数位)
8bytes ±5.0×10 -324 ~±1.7×10 308 System.Double 0.0d
decimal 实数类型
(精度为28-29位有效小数位)
16bytes ±1.0×10 28 ~±7.9228×10 28 System.Decimal 0m
bool 布尔值 1bytes(实际仅需1bit) true /false System.Boolean false
char Unicode字符串 U+0000~U+ffff System.Char \x0000
object 所有其他类型的基类,包括简单类型     System.Object  
string 0个或多个Unicode字符所组成的序列     System.String  
dynamic 使用动态类型语言编写的程序集时使用     无相应的.NET类型  

 

串口通信(Serial Communication)是一种点对点的通信方式,通过串行传输逐位发送数据。常见的串口协议包括 RS-232、RS-485 和 RS-422。
RS-232:
特点:早期串口标准,通信距离短(15 米以内),点对点连接。
适用场景:低速短距离通信,如老式工业设备或 PC 与设备的直接连接。
RS-485
特点:支持多点通信,最大支持 32 个设备,通信距离长(1,200 米)。
适用场景:多设备总线通信,如传感器网络和工业总线。
RS-422
特点:点对点连接,抗干扰能力强,适合长距离高速传输。
适用场景:设备间长距离高速通信

优点:

(1) 稳定可靠:通信协议简单,干扰少。
(2) 硬件成本低:硬件接口通用,广泛支持。
(3) 通信距离可达 1,200 米(RS-485/RS-422),适合长距离场景。

缺点:

(1) 带宽低:数据传输速率较低,最高通常不超过 10Mbps。
(2) 点对点限制(RS-232):难以实现多设备通信。
(3) 协议缺乏灵活性:需要额外开发应用层协议。

应用场景:

(1) 工业设备调试(通过 RS-232)。
(2) 传感器网络通信(通过 RS-485)。
(3) 长距离数据采集和监控。

 

NuGet包:System.IO.Ports

常用属性:

属性名类型/说明
PortName string — 串口名称(如 COM1、COM3)
BaudRate int — 波特率,表示数据传输速率。常用有 9600、19200、38400、57600、115200 等
Parity Parity — 奇偶校验类型(如 None 无奇偶校验、Even 偶校验、Odd 奇校验)
DataBits int — 数据位数,常见为 8 位;也可设置为 5、6、7 位
StopBits StopBits — 停止位(如 None 不使用、One 1 个、Two 2 个、OnePointFive 1.5 个)
Handshake Handshake — 握手协议(如 None、RequestToSend、XOnXOff)
Encoding Encoding — 数据编码方式(如 Encoding.UTF8)
ReadTimeout int — 读取操作超时时间(毫秒),默认为 InfiniteTimeout
WriteTimeout int — 写入操作超时时间(毫秒),默认为 InfiniteTimeout
IsOpen bool — 表示串口是否已打开
NewLine string — 定义行结束符(如 "\r\n"),用于 ReadLine() 和 WriteLine()
DtrEnable bool — 控制数据终端就绪(DTR)信号状态
RtsEnable bool — 控制请求发送(RTS)信号状态
ReceivedBytesThreshold int — 触发 DataReceived 事件的最小字节数

常用方法:

方法名说明
Open() 打开串口。
Close() 关闭已打开的串口。
Read(byte[] buffer, int offset, int count) 从串口读取指定字节数到缓冲区。
ReadByte() 读取单个字节(返回 int,范围 0-255,失败返回 -1)。
ReadLine() 读取一行数据,直到遇到 NewLine 定义的结束符。
ReadExisting() 读取接收缓冲区中所有可用数据(不阻塞)。
Write(string text) 写入字符串到串口(自动按 Encoding 编码)。
Write(byte[] buffer, int offset, int count) 写入字节数组到串口。
DiscardInBuffer() 清空输入缓冲区中的数据。
DiscardOutBuffer() 清空输出缓冲区中的数据。

常用事件:

事件名说明
DataReceived 当接收到数据且字节数达到 ReceivedBytesThreshold 时触发。
ErrorReceived 当串口发生错误(如奇偶校验错误)时触发。

其它重要扩展属性:

属性名类型/说明
BytesToRead int — 接收缓冲区中已接收的字节数。
BytesToWrite int — 输出缓冲区中待发送的字节数。
ReadBufferSize int — 设置输入缓冲区大小(默认 4096)。
WriteBufferSize int — 设置输出缓冲区大小(默认 2048)。

 

.xaml代码 

<Window x:Class="StudySerialPort.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:StudySerialPort"
        mc:Ignorable="d"
        Title="串口案例" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"></ColumnDefinition>
            <ColumnDefinition Width="4*"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <!--左边-->
        <Grid Background="Azure">
            <Grid.RowDefinitions>
                <RowDefinition Height="auto"></RowDefinition>
                <RowDefinition Height="auto"></RowDefinition>
                <RowDefinition Height="auto"></RowDefinition>
                <RowDefinition Height="auto"></RowDefinition>
                <RowDefinition Height="auto"></RowDefinition>
                <RowDefinition Height="auto"></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="1.5*"></ColumnDefinition>
                <ColumnDefinition Width="2.5*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <TextBlock  VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,10,5">串口</TextBlock>
            <ComboBox x:Name="cbbSpName" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,0,10,5"></ComboBox>

            <TextBlock  Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Right"  Margin="0,0,10,5">波特率</TextBlock>
            <ComboBox x:Name="cbbBaud" SelectedIndex="0" Grid.Row="1"  Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,0,10,5">
                <ComboBoxItem>9600</ComboBoxItem>
                <ComboBoxItem>19200</ComboBoxItem>
                <ComboBoxItem>38400</ComboBoxItem>
            </ComboBox>

            <TextBlock Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,10,5">数据位</TextBlock>
            <ComboBox x:Name="cbbDataBit" SelectedIndex="0" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,0,10,5">
                <ComboBoxItem>8</ComboBoxItem>
                <ComboBoxItem>7</ComboBoxItem>
                <ComboBoxItem>6</ComboBoxItem>
            </ComboBox>

            <TextBlock  Grid.Row="3" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,10,5">校验位</TextBlock>
            <ComboBox x:Name="cbbParity" SelectedIndex="0" Grid.Row="3" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,0,10,5">
                <ComboBoxItem></ComboBoxItem>
                <ComboBoxItem>奇校验</ComboBoxItem>
                <ComboBoxItem>偶校验</ComboBoxItem>
            </ComboBox>

            <TextBlock  Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,10,5">停止位</TextBlock>
            <ComboBox Grid.Row="4" x:Name="cbbStopBit" SelectedIndex="0" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,0,10,5">
                <ComboBoxItem>1</ComboBoxItem>
                <ComboBoxItem>1.5</ComboBoxItem>
                <ComboBoxItem>2</ComboBoxItem>
            </ComboBox>
            <Button  x:Name="BtnOpen"  Grid.Row="5"  Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" 
             Margin="0,0,10,5" Click="BtnOpen_Click">打开串口</Button>

            <Button  x:Name="BtnClose"  Grid.Row="5"  Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" 
  Margin="0,0,10,5" Visibility="Collapsed" Click="BtnClose_Click">关闭串口</Button>
        </Grid>

        <!--右边-->
        <Grid Grid.Column="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="auto"></RowDefinition>
                <RowDefinition Height="auto"></RowDefinition>
            </Grid.RowDefinitions>

            <RichTextBox Height="300" x:Name="txtShowContent"  VerticalScrollBarVisibility="Visible"></RichTextBox>

            <Grid Grid.Row="1">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="2*"></ColumnDefinition>
                    <ColumnDefinition Width="1*"></ColumnDefinition>
                </Grid.ColumnDefinitions>

                <TextBox Height="80" x:Name="txtSendContent"></TextBox>
                <Button x:Name="BtnSend" Grid.Column="1" Click="BtnSend_Click">发送</Button>
            </Grid>
        </Grid>
    </Grid>
</Window>
View Code

 

.xaml.cs代码

using System.IO.Ports;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace StudySerialPort;


public partial class MainWindow : Window
{
    //创建串口
    SerialPort serialPort = new SerialPort();
    public MainWindow()
    {
        InitializeComponent();

        BindPort();//绑定串口到下拉 
        //异步读取
        Task.Run(async () =>
        {
            while (true)
            {
                _ = Dispatcher.Invoke(async () =>
                {
                    await ReadAsync();
                });

                await Task.Delay(1000);
            }
        });
    }

    /// <summary>
    /// 绑定串口到下拉
    /// </summary>
    private void BindPort()
    {
        //绑定串口到下拉
        string[] portList = SerialPort.GetPortNames();
        cbbSpName.ItemsSource = portList;
        if (portList.Length > 0)
        {
            cbbSpName.SelectedIndex = 0;//默认选择第一个
        }
    }

    /// <summary>
    /// 打开串口
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void BtnOpen_Click(object sender, RoutedEventArgs e)
    {
        //核心代码。输入验证
        serialPort.PortName = cbbSpName.Text;//串口名称
        serialPort.BaudRate = Convert.ToInt32(cbbBaud.Text);//波特率
        serialPort.DataBits = Convert.ToInt32(cbbDataBit.Text);//数据位 

        //校验位
        serialPort.Parity = Parity.None;
        if (cbbParity.SelectedIndex == 1)
        {
            serialPort.Parity = Parity.Odd;
        }
        if (cbbParity.SelectedIndex == 2)
        {
            serialPort.Parity = Parity.Even;
        }

        //停止位
        serialPort.StopBits = StopBits.One;
        if (cbbStopBit.SelectedIndex == 1)
        {
            serialPort.StopBits = StopBits.OnePointFive;
        }
        if (cbbStopBit.SelectedIndex == 2)
        {
            serialPort.StopBits = StopBits.Two;
        }

        //打开串口
        try
        {
            serialPort.Open();
            //隐藏打开按钮,显示关闭按钮
            BtnOpen.Visibility = Visibility.Collapsed;//隐藏打开按钮
            BtnClose.Visibility = Visibility.Visible;//显示关闭按钮

            //显示打开消息
            AppendTxt($"{DateTime.Now.ToString("yyyy-MM-dd")} 打开串口{cbbSpName.Text}成功", Colors.Green);
        }
        catch (Exception)
        {
            BtnOpen.Visibility = Visibility.Visible;
            BtnClose.Visibility = Visibility.Collapsed;

            //显示打开异常消息
            AppendTxt($"{DateTime.Now.ToString("yyyy-MM-dd")} 打开串口{cbbSpName.Text}失败", Colors.Red);
        }

    }

    /// <summary>
    /// 关闭串口
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void BtnClose_Click(object sender, RoutedEventArgs e)
    {
        if (serialPort.IsOpen)
        {
            serialPort.Close();
            BtnOpen.Visibility = Visibility.Visible;
            BtnClose.Visibility = Visibility.Collapsed;

            //显示关闭信息
            AppendTxt($"{DateTime.Now.ToString("yyyy-MM-dd")} 关闭串口{cbbSpName.Text}", Colors.Red);
        }
    }

    /// <summary>
    /// 发送数据
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void BtnSend_Click(object sender, RoutedEventArgs e)
    {
        if (serialPort.IsOpen)//打开了才能发送数据
        {
            byte[] bytes = Encoding.UTF8.GetBytes(txtSendContent.Text);
            serialPort.Write(bytes, 0, bytes.Length);

            //显示
            AppendTxt($"{DateTime.Now.ToString("yyyy-MM-dd")} 发送消息 {txtSendContent.Text}成功", Colors.Black);
        }
        else
        {
            MessageBox.Show("请先打开串口");
        }
    }

    /// <summary>
    /// 异步读取并显示在richtextbox里
    /// </summary>
    private async Task ReadAsync()
    {
        if (serialPort.IsOpen)
        {
            byte[] readBytes = new byte[1024];//存放读的结果

            if (serialPort.BytesToRead>0)//接收缓冲区有数据
            {
                int readCount = await serialPort.BaseStream.ReadAsync(readBytes, 0, readBytes.Length);
                if (readCount > 0)
                {
                    string data = Encoding.UTF8.GetString(readBytes);//转成字符串

                    //ui操作
                    AppendTxt($"{DateTime.Now.ToString("yyyy-MM-dd")} 读取数据 {data}", Colors.Blue);
                }
            }
        }
    }

    FlowDocument doc = new FlowDocument();
    /// <summary>
    /// 将string追加richtextbox里
    /// </summary>
    /// <param name="txt"></param>
    /// <param name="txtColor">颜色</param>
    private void AppendTxt(string txt, Color txtColor)
    {
        var p = new Paragraph(); // Paragraph 类似于 html 的 P 标签
        var r = new Run(txt); // Run 是一个 Inline 的标签
        p.Inlines.Add(r);
        p.Foreground = new SolidColorBrush(txtColor);//设置字体颜色
        doc.Blocks.Add(p);
        txtShowContent.Document = doc;
        txtShowContent.ScrollToEnd();
    }
}

 

posted @ 2026-03-23 15:36  野码  阅读(1)  评论(0)    收藏  举报