Usage and example of SerialPort class

Usage and example of SerialPort class


Since Microsoft .Net 2.0 version, the System.IO.Ports.SerialPort class is provided by default. Users can easily write a small amount of code to complete the serial port information sending and receiving program. This article will introduce how to use C# .Net to develop serial port applications on the PC side.

1. Serial hardware signal definition

DB9 Connector signal definitions

Image 1

pin Signal definition effect
1 DCD carrier detect Received Line Signal Detector(Data Carrier Detect)
2 RXD Receive data Received Data
3 TXD send data Transmit Data
4 DTR data terminal ready Data Terminal Ready
5 SGND Signal area Signal Ground
6 DSR data ready Data Set Ready
7 RTS request to send Request To Send
8 CTS clear to send Clear To Send
9 RI Ring alert Ring Indicator

2. Serial port number search

One of the easiest way:

string[] portList = System.IO.Ports.SerialPort.GetPortNames();  // 静态方法

There is also a method to obtain the implementation by calling the API, which can obtain the detailed and complete serial port name, which is especially suitable for the USB-to-COM virtual serial port. It can get the same name as in the device manager, such as "Prolific USB-to-Serial Comm Port(COM34)", while the above method can only get "COM34".

3. Serial port attribute parameter setting

The properties contained in the SerialPort class are shown in the following table:

name illustrate
BaseStream Gets the underlying SerialPort object for the Stream object.
BaudRate Get or set the serial baud rate.
BreakState Gets or sets the interrupt signal state.
BytesToRead Get the number of bytes of data in the receive buffer.
BytesToWrite Get the number of bytes of data in the send buffer.
CanRaiseEvents Gets a value indicating whether the component can raise an event. (Inherited from Component.)
CDHolding Gets the status of the carrier detect line of the port.
Container Get the IContainer, which contains the Component. (Inherited from Component.)
CtsHolding Get the status of the "ready to send" row.
DataBits Gets or sets the standard data bit length per byte.
DesignMode Gets a value indicating whether the Component is currently in design mode. (Inherited from Component.)
DiscardNull Gets or sets a value indicating whether null bytes are ignored when transferring between the port and the receive buffer.
DsrHolding Gets the status of the Data Set Ready (DSR) signal.
DtrEnable Gets or sets a value that enables the data terminal ready (DTR) signal during serial communication.
Encoding Gets or sets the byte encoding for text conversion before and after transmission.
Events Get event handlers attached to this list Component. (Inherited from Component.)
Handshake Use the value in Handshake to get or set the handshake protocol for serial port data transfers.
IsOpen Gets a value indicating the open or closed state of the SerialPort object.
NewLine Gets or sets the value that interprets the end of calls to the ReadLine and WriteLine methods.
Parity Gets or sets the parity check protocol.
ParityReplace Gets or sets a byte that replaces invalid bytes in the data stream in the event of a parity error.
PortName Gets or sets communication ports, including but not limited to all available COM ports.
ReadBufferSize Gets or sets the size of the SerialPort input buffer.
ReadTimeout Gets or sets the number of milliseconds before a timeout occurs when a read operation is not complete.
ReceivedBytesThreshold Gets or sets the number of bytes in the internal input buffer before the DataReceived event occurs.
RtsEnable Gets or sets a value indicating whether request-to-send (RTS) signaling is enabled in serial communications.
Site 获取或设置 ISite 的 Component。(继承自 Component。)
StopBits 获取或设置每个字节的标准停止位数。
WriteBufferSize 获取或设置串行端口输出缓冲区的大小。
WriteTimeout 获取或设置写入操作未完成时发生超时之前的毫秒数。

简单初始化串口参数的示例程序:

SerialPort mySerialPort = new SerialPort("COM2"); // 构造参数指定串口名
//serialPort.PortName = "COM2";
mySerialPort.BaudRate = 9600;
mySerialPort.Parity=Parity.None;
mySerialPort.StopBits = StopBits.One;
mySerialPort.DataBits = 8;

mySerialPort.Open();  // 打开串口

// DataReceived是串口类的一个事件,通过+=运算符订阅事件,如果串口接收到数据就触发事件,调用DataReceive_Method事件处理方法
mySerialPort.DataReceived += new SerialDataReceivedEvenHandler(DataReceive_Method);
 
// 事件处理方法
public static void DataReceive_Method(object sender, SerialDataReceivedEventArgs e)
{
    //sender是事件触发对象,通过它可以获取到mySerialPort
    SerialPort mySerialPort = (SerialPort)sender;
}

mySerialPort.Close();  // 关闭串口

4. 串口发送信息

SerialPort类定义了多种方法用于串口发送信息。

Write(Byte[], Int32, Int32)使用缓冲区中的数据将指定数量的字节写入串行端口

注意一字节(Byte)是8个比特(bit),而一个十六进制数是4个比特,两个十六进制数就是8个比特,即一个字节。所以要想发送十六进制数就得用写入字节的函数。

byte[] t = new byte[2];
t[0] = 0xAA;
t[1] = 0xBB;
串口对象.Write(t,0,2);

==Write(Char[], Int32, Int32)==使用缓冲区中的数据将指定数量的字符写入串行端口
==Write(String)==将指定的字符串写入串行端口
==WriteLine(String)==将指定的字符串和NewLine值写入输出缓冲区

下面是一个简单的例子说明如何通过串口发送字符串和字节数据:

private static void SendSampleData()
{
    SerialPort port = new SerialPort(
        "COM1", 9600, Parity.None, 8, StopBits.One);

    port.Open();

    port.Write("Hello World");

    port.Write(new byte[] { 0x0A, 0xE2, 0xFF }, 0, 3);

    port.Close();
}

下面是如何发送一个文本文件的例子:

private static void SendTextFile(SerialPort port, string FileName)
{
    port.Write(File.OpenText(FileName).ReadToEnd());
}

下面是如何发送一个二进制文件的例子:

private static void SendBinaryFile(SerialPort port, string FileName)
{
    using (FileStream fs = File.OpenRead(FileName))
        port.Write((new BinaryReader(fs)).ReadBytes((int)fs.Length), 0, (int)fs.Length);
}

5. 串口接收信息

SerialPort类定义了多种方法用于串口接收信息。

Read(Byte[], Int32, Int32) 从SerialPort输入缓冲区读取一些字节,并将那些字节写入字节数组中指定的偏移量处
Read(Byte[], Int32, Int32)从SerialPort输入缓冲区读取一些字符,并将那些字符写入字符数组中指定的偏移量处
ReadByte() 从SerialPort输入缓冲区中同步读取一个字节
ReadChar() 从SerialPort输入缓冲区中同步读取一个字符
ReadExisting() 在编码的基础上,读取SerialPort对象的流和输入缓冲区中所有立即可用的字节
ReadLine()一直读取到输入缓冲区中的NewLine值
ReadTo(String)一直读取到输入缓冲区中的指定value的字符串

通常一个比较常见的用法就是将串口里面立即能用的字符或数据读取然后打印在textbox等控件中显示。

private SerialPort port = new SerialPort("COM1",
                                         9600, Parity.None, 8, StopBits.One);

port.DataReceived += new
    SerialDataReceivedEventHandler(port_DataReceived);

port.Open();

private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    Console.WriteLine(port.ReadExisting());
}

In addition, there is another application that needs to cache a serial port to receive data, and then find useful information in the cached data. In this case, the method used in the following example can be used.

SerialPort com = new SerialPort(SerialPort.GetPortNames()[0],
                                9600, Parity.None, 8, StopBits.One);

List<byte> bBuffer = new List<byte>();
string sBuffer = String.Empty;

com.DataReceived += new SerialDataReceivedEventHandler(com_DataReceived);

com.Open();

void com_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    while (com.BytesToRead > 0)
        bBuffer.Add((byte)com.ReadByte());
    ProcessBuffer(bBuffer);

    sBuffer += com.ReadExisting();
    ProcessBuffer(sBuffer);
}

private void ProcessBuffer(string sBuffer)
{}

private void ProcessBuffer(List<byte> bBuffer)
{}

serial port tools

public class SerialPortUtils
{
    public static SerialPort serialPort = null;   // 定义一个静态的串口对象

    /// <summary>
    /// 搜索串口
    /// </summary>
    /// <returns></returns>
    public static string[] GetPortNames() 
    {
        return SerialPort.GetPortNames();
    }

    /// <summary>
    /// 打开或者关闭串口
    /// </summary>
    /// <param name="comName"></param>
    /// <param name="baud"></param>
    /// <returns></returns>
    public static SerialPort OpenClosePort(string comName, int baud)
    {
        //串口未打开
        if (serialPort == null || !serialPort.IsOpen)
        {
            serialPort = new SerialPort();
            //串口名称
            serialPort.PortName = comName;
            //波特率
            serialPort.BaudRate = baud;
            //数据位
            serialPort.DataBits = 8;
            //停止位
            serialPort.StopBits = StopBits.One;
            //校验位
            serialPort.Parity = Parity.None;
            //打开串口
            serialPort.Open();
            //串口数据接收事件实现
            serialPort.DataReceived += new SerialDataReceivedEventHandler(ReceiveData);

            return serialPort;
        }
        //串口已经打开
        else
        {
            serialPort.Close();
            return serialPort;
        }
    }

    /// <summary>
    /// 接收数据
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public static void ReceiveData(object sender, SerialDataReceivedEventArgs e)
    {
        SerialPort _SerialPort = (SerialPort)sender;

        int _bytesToRead = _SerialPort.BytesToRead;
        byte[] recvData = new byte[_bytesToRead];

        _SerialPort.Read(recvData, 0, _bytesToRead);

        //向控制台打印数据
        Debug.WriteLine("收到数据:" + recvData);
    }

    /// <summary>
    /// 发送数据
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    public static bool SendData(byte[] data)
    {
        if (serialPort != null && serialPort.IsOpen)
        {
            serialPort.Write(data, 0, data.Length);
            Debug.WriteLine("发送数据:" + data);
            return true;
        }
        else
        {
            return false;
        }
    }
}

Notes on serial port usage

Reference: https://blog.csdn.net/wuyazhe/article/details/5606276

When Open() opens the serial port, a listening thread ListenThread is created, and the DataReceived event is processed in this thread. At the same time, there is also a main thread, UIThread. At this time, attention should be paid to thread synchronization.

In the DataReceived event processing function, generally call the Read() method to read the serial port buffer data, perform fault-tolerant processing after reading, then parse the data, and update the UI after the parsing is completed. The UI of the main thread cannot be updated in the listening thread, so the Invoke method must be called back to the main thread to update the UI. But since Invoke is a synchronous function, the listening thread stays in the DataReceived event handler until the UI is updated. At this time, if the main thread calls the Close() function to close the serial port, it will cause a deadlock because the resources of the listening thread have not been released in time.

The solution is to use the BeginInvoke asynchronous call to update the UI .

Guess you like

Origin blog.csdn.net/xiaoshihd/article/details/115246773