[英]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.