简体   繁体   English

如何解决 SerialPort.Read() 在 C# 中总是返回 0?

[英]How do I troubleshoot SerialPort.Read() always return 0 in C#?

I have communicating to Display monitor using RS-232C 9-Pin D-Sub to Stereo Cable to control power ON/OFF, Volume Up/Down etc. I created a sample program based on Microsoft Docs System.IO.Ports.我使用 RS-232C 9 针 D-Sub 到立体声电缆与显示器进行通信,以控制电源开/关、音量增大/减小等。我创建了一个基于 Microsoft Docs System.IO.Ports 的示例程序。 I can open COM PORT, write some byte arrays (based on protocol document) and no issue.我可以打开 COM PORT,写入一些字节数组(基于协议文档)并且没有问题。 But, reading in another thread always time out and receives '0' only.但是,在另一个线程中读取总是超时并且只接收“0”。 According to document, I should receive ACK or NAK (which will send back to me as byte array starting with 0xAA as well).根据文档,我应该收到 ACK 或 NAK(这也将作为以 0xAA 开头的字节数组发回给我)。 I call Write() method in driver main class.我在驱动程序主类中调用 Write() 方法。

How can I troubleshoot for this issue?如何解决此问题? My reading byte array method is wrong?我的读取字节数组方法有误? Thank you all for the input.谢谢大家的意见。

public class ComPortDemo
{
    private static bool _continue;
    private static SerialPort _serialPort;

    public ComPortDemo()
    {
        _serialPort = new SerialPort
        {
            PortName = "COM1",
            Parity = Parity.None,
            StopBits = StopBits.One,
            BaudRate = 9600,
            DataBits = 8,
            WriteTimeout = 2000,
            ReadTimeout = 5000,
            Handshake = Handshake.None,
        };

        _serialPort.DataReceived += SerialPortOnDataReceived;
        _serialPort.ErrorReceived += SerialPortOnErrorReceived;
        _serialPort.PinChanged += SerialPortOnPinChanged;
        _serialPort.DtrEnable = true;
        _serialPort.RtsEnable = true;
    }

    private void SerialPortOnPinChanged(object sender, SerialPinChangedEventArgs e)
    {
        Console.WriteLine($"Pin changed event occurred. " + e.EventType);
    }

    public void Write()
    {
        int bufferLength = 6;
        byte[] commands = new byte[bufferLength];

        commands[0] = 0xAA;
        commands[1] = 0x11;
        commands[2] = 0x02;
        commands[3] = 0x01;
        commands[4] = 0x01;
        commands[5] = 0x15;

        if (_serialPort.IsOpen)
            _serialPort.Close();

        _serialPort.Open();
        _continue = true;
        Thread readThread = new Thread(Read);
        readThread.Start();
        Console.WriteLine("Reader thread started and wait for response..");

        Console.Write("Sent: ");
        foreach (var command in commands)
        {
            Console.Write($"0x{command:X2} ");
        }

        Console.WriteLine();

        _serialPort.DiscardInBuffer();
        _serialPort.DiscardOutBuffer();
        _serialPort.Write(commands, 0, commands.Length);

        Console.WriteLine("ENTER to end reading thread..");
        Console.ReadLine();
        _continue = false;
        readThread.Join();
    }

    private static void SerialPortOnErrorReceived(object sender, SerialErrorReceivedEventArgs e)
    {

        Console.WriteLine("serial port on error : " + e.EventType);
    }

    private static void SerialPortOnDataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        //var serialPort = (SerialPort) sender;
        //string data = serialPort.ReadExisting();
        //Console.WriteLine($"{data}");
        Console.WriteLine("data received event");
    }

    public static void Read()
    {

        while (_continue)
        {
            try
            {
                if (_serialPort.BytesToRead <= 0)
                {
                    Console.WriteLine("No data to read");
                }

                byte[] buffer = new byte[7];
                Console.WriteLine("trying to read..");
                int readByte = _serialPort.Read(buffer, 0, buffer.Length);
                Console.WriteLine($"Read byte : {readByte}");
                //if (readByte > 0)
                foreach (var b in buffer)
                    Console.Write($"0x{b:X2} ");

            }
            catch (TimeoutException toe)
            {
                Console.WriteLine(toe.Message);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine(e.StackTrace);
            }

            Thread.Sleep(2000);
        }
    }

    public void Close()
    {
        _continue = false;
        if(_serialPort.IsOpen)
            _serialPort.Close();
    }
}

If You can, use my simple wrapper of COM port - it uses base streams and TPL library, therefore should eliminate some, maybe even most problems with threading work needed.如果可以,请使用我对 COM 端口的简单包装器 - 它使用基本流和 TPL 库,因此应该消除一些,甚至可能是大多数线程工作所需的问题。

You will have to create a custom ICommunicationDeviceSettings implementation that will hold typical serial port settings (take a look at SetupDeviceOptions method, You will know what exactly is needed).您必须创建一个自定义的 ICommunicationDeviceSettings 实现来保存典型的串行端口设置(查看 SetupDeviceOptions 方法,您将知道到底需要什么)。

i hope that it will help You.我希望它会帮助你。 Bare in mind that this implementation was made for long message transfer, but still - should work for You.请记住,此实现是为长消息传输而设计的,但仍然 - 应该对您有用。

using System;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SerialPortWrapper
{
    /// <summary>
    /// Wraps SerialPort for future use with interfaces
    /// </summary>
    public class SerialPortAdapter : ICommunicationDeviceAsync
    {
        private readonly SerialPort _port = new SerialPort();
        private Encoding _encoding;
        private const int ByteBufferSize = 4092;    // Optimal size of max single data portion received buffer
        private byte[] _buffer = new byte[ByteBufferSize];

        private readonly object _lock = new object();
        private bool _disposed;

        public event EventHandler<string> DataReceived;

        public SerialPortAdapter(ICommunicationDeviceSettings settings) => Setup(settings);

        public bool IsOpen => _port?.IsOpen == true;
        public int ConfirmationTimeout { get; private set; }
        public int CompletitionTimeout { get; private set; }
        public string Name { get; private set; }

        /// <summary>
        /// Sets up all of the necessary parameters for this instance of the device
        /// </summary>
        /// <param name="settings">Configuration from AppSettings for this type of device</param>
        /// <returns>True if successful</returns>
        public void Setup(ICommunicationDeviceSettings settings)
        {
            if (!(settings is SerialPortSettings set))
                throw new ArgumentNullException(nameof(settings));

            SetUpDeviceOptions(set);
        }

        private bool PortNumberExists(int portNumber) =>
            SerialPort.GetPortNames().Contains($"COM{portNumber}");

        private void SetUpDeviceOptions(SerialPortSettings settings)
        {
            _port.PortName = $"COM{settings.PortNumber}";
            _port.BaudRate = settings.BaudRate;
            _port.DataBits = settings.DataBits;
            _port.Parity = settings.Parity;
            _port.StopBits = settings.StopBits;
            _port.ReadTimeout = settings.ReadTimeout;
            _port.WriteTimeout = settings.WriteTimeout;
            _encoding = _port.Encoding = Encoding.GetEncoding(settings.Encoding);
            _port.DtrEnable = false;
            _port.RtsEnable = false;
            _port.DiscardNull = true;
            CompletitionTimeout = settings.CompletitionTimeout;
            ConfirmationTimeout = settings.ConfirmationTimeout;
            Name = settings.Name;
        }

        public void Open()
        {
            lock (_lock)
            {
                if (!_port.IsOpen)
                    _port.Open();
            }
        }

        public void Close()
        {
            DiscardInBuffer();
            _port.Close();
        }

        public async Task CloseAsync()
        {
            await Task.Run(() =>
            {
                DiscardInBuffer();
                _port.Close();
            })
            .ConfigureAwait(false);
        }

        public string Receive()
        {
            if (!_port.IsOpen)
                return string.Empty;

            try
            {
                int bytesRead = _port.Read(_buffer, 0, _buffer.Length);
                string rawData = _encoding.GetString(_buffer, 0, bytesRead);
                OnDataReceived(rawData);
                return rawData;
            }
            catch (IOException)
            {
                return string.Empty; // Receiving failed - port closed or disconnected
            }
        }

        /// <summary>
        /// Constantly monitors incoming communication data, filters it and generates Commands (ICommandModel)
        /// </summary>
        public async Task<string> ReceiveAsync(CancellationToken ct)
        {
            if (!_port.IsOpen)
                return string.Empty;

            try
            {
                ct.ThrowIfCancellationRequested();
                int bytesRead = await _port.BaseStream.ReadAsync(_buffer, 0, _buffer.Length, ct).ConfigureAwait(false);
                string rawData = _encoding.GetString(_buffer, 0, bytesRead);
                OnDataReceived(rawData);
                return rawData;
            }
            catch (Exception ex) when (ex is IOException || ex is OperationCanceledException)
            {
                // Only known way to handle sudden port failure AND cancellation - known SerialPort implementation hiccup.
                throw new OperationCanceledException("System IO exception in BaseStream.ReadAsync - handled, expected, re-thrown. Either task was canceled or port has been closed", ex, ct);
            }
        }

        /// <summary>
        /// Sends a raw data string.
        /// </summary>
        /// <param name="data">Single data string to transfer</param>
        /// <returns>True if sending was successful</returns>
        public bool Send(string data)
        {
            if (!CanSend(data))
                return false;

            byte[] buffer = _encoding.GetBytes(data);
            try
            {
                _port.Write(buffer, 0, buffer.Length);
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Sends a raw data string asynchronously.
        /// </summary>
        /// <param name="data">Single data string to transfer</param>
        /// <returns>True if sending was successful</returns>
        public async Task<bool> SendAsync(string data, CancellationToken ct)
        {
            if (!CanSend(data))
                return false;

            byte[] buffer = _encoding.GetBytes(data);
            try
            {
                ct.ThrowIfCancellationRequested();
                await _port.BaseStream.WriteAsync(buffer, 0, buffer.Length, ct).ConfigureAwait(false);
                return true;
            }
            catch
            {
                return false;
            }
        }

        private bool CanSend(string data) => !IsOpen || string.IsNullOrEmpty(data);

        /// <summary>
        /// Clears data in 'received' buffer
        /// </summary>
        public void DiscardInBuffer()
        {
            _port.BaseStream.Flush();
            _port.DiscardInBuffer();
            _buffer = new byte[ByteBufferSize];
        }

        protected virtual void OnDataReceived(string rawData) => DataReceived?.Invoke(this, rawData);

        #region IDisposable implementation

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
                return;

            if (disposing)
            {
                if (_port?.IsOpen ?? false)
                    _port.Close();
            }
            _disposed = true;
        }

        #endregion IDisposable implementation
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM