簡體   English   中英

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

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

我使用 RS-232C 9 針 D-Sub 到立體聲電纜與顯示器進行通信,以控制電源開/關、音量增大/減小等。我創建了一個基於 Microsoft Docs System.IO.Ports 的示例程序。 我可以打開 COM PORT,寫入一些字節數組(基於協議文檔)並且沒有問題。 但是,在另一個線程中讀取總是超時並且只接收“0”。 根據文檔,我應該收到 ACK 或 NAK(這也將作為以 0xAA 開頭的字節數組發回給我)。 我在驅動程序主類中調用 Write() 方法。

如何解決此問題? 我的讀取字節數組方法有誤? 謝謝大家的意見。

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();
    }
}

如果可以,請使用我對 COM 端口的簡單包裝器 - 它使用基本流和 TPL 庫,因此應該消除一些,甚至可能是大多數線程工作所需的問題。

您必須創建一個自定義的 ICommunicationDeviceSettings 實現來保存典型的串行端口設置(查看 SetupDeviceOptions 方法,您將知道到底需要什么)。

我希望它會幫助你。 請記住,此實現是為長消息傳輸而設計的,但仍然 - 應該對您有用。

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