Я создаю приложение, которое связывается с устройством через преобразователь FT2232H USB/RS232. Для коммуникации я пользуюсь библиотекой FTD2XX_NET.dll с веб-сайта FTDI.
Я использую два потока:
private void receiverLoop()
{
if (this.DataReceivedHandler == null)
{
throw new BackendException("dataReceived delegate is not set");
}
FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OK;
byte[] readBytes = new byte[this.ReadBufferSize];
while (true)
{
lock (FtdiPort.threadLocker)
{
UInt32 numBytesRead = 0;
ftStatus = ftdiDevice.Read(readBytes, this.ReadBufferSize, ref numBytesRead);
if (ftStatus == FTDI.FT_STATUS.FT_OK)
{
this.DataReceivedHandler(readBytes, numBytesRead);
}
else
{
Trace.WriteLine(String.Format("Couldn't read data from ftdi: status {0}", ftStatus));
Thread.Sleep(10);
}
}
Thread.Sleep(this.RXThreadDelay);
}
}
public void Write(byte[] data, int length)
{
if (this.IsOpened)
{
uint i = 0;
lock (FtdiPort.threadLocker)
{
this.ftdiDevice.Write(data, length, ref i);
}
Thread.Sleep(1);
if (i != (int)length)
{
throw new BackendException("Couldnt send all data");
}
}
else
{
throw new BackendException("Port is closed");
}
}
static Object threadLocker = new Object();
private void startReceiver()
{
if (this.DataReceivedHandler == null)
{
return;
}
if (this.IsOpened == false)
{
throw new BackendException("Trying to start listening for raw data while disconnected");
}
this.receiverThread = new Thread(this.receiverLoop);
//this.receiverThread.Name = "protocolListener";
this.receiverThread.IsBackground = true;
this.receiverThread.Start();
}
ftdiDevice. Запишите, что функция не зависает, если я комментирую следующую строку: ftStatus = ftdiDevice.Read(readBytes, this.ReadBufferSize, ref numBytesRead);
Несколько вещей:
Проверьте, не блокируется ли ваш вызов Read. Если это так, возможно, вы не сможете вызвать Write, пока Read блокируется в ожидании ответа. В документации по API может быть более подробная информация об этом.
Некоторые API-интерфейсы не очень хорошо поддерживают несколько потоков даже при синхронизации доступа. В этом случае вы можете использовать дизайн, в котором вы делегируете свои команды Write своему потоку связи. Когда я использовал этот шаблон в прошлом, я обычно ставлю в очередь какой-то класс Command, содержащий информацию, которую я хочу записать, и либо использую класс потокового сигнала, чтобы позволить моим вызывающим `` командным '' методам блокироваться, либо предоставлять какие-то асинхронное уведомление.
При проверке API мне показалось, что драйвер способен эмулировать COM-порт. Я вижу, как метод GetComPort () возвращает строку «COMx». Это делает весьма вероятным использование класса System.IO.Ports.SerialPort. Что уже делает то, что пытается сделать ваша оболочка, она поддерживает событие DataReceived. Стоит попробовать.
Я нашел более подробную документацию по API. Действительно, функция ftdiDevice.read блокируется, если не установить значение readTimeout, отличное от 0. Установка этого значения таймаута решила проблему.
Спасибо за быстрый ответ.
С уважением
Альтернативой является использование механизма уведомления о событиях от FTDI, таким образом вам не понадобится блокирующий поток для считывания данных:
public FTDISample()
{
private AutoResetEvent receivedDataEvent;
private BackgroundWorker dataReceivedHandler;
private FTDI ftdi;
public FTDISample(string serialNumber){
ftdi = new FTDI();
FTDI.FT_STATUS status = ftdi.OpenBySerialNumber(serialNumber);
receivedDataEvent = new AutoResetEvent(false);
status = mFTDI.SetEventNotification(FTDI.FT_EVENTS.FT_EVENT_RXCHAR, receivedDataEvent);
dataReceivedHandler = new BackgroundWorker();
dataReceivedHandler.DoWork += ReadData;
if (!dataReceivedHandler.IsBusy)
{
dataReceivedHandler.RunWorkerAsync();
}
}
private void ReadData(object pSender, DoWorkEventArgs pEventArgs)
{
UInt32 nrOfBytesAvailable = 0;
while (true)
{
// wait until event is fired
this.receivedDataEvent.WaitOne();
// try to recieve data now
FTDI.FT_STATUS status = ftdi.GetRxBytesAvailable(ref nrOfBytesAvailable);
if (status != FTDI.FT_STATUS.FT_OK)
{
break;
}
if (nrOfBytesAvailable > 0)
{
byte[] readData = new byte[nrOfBytesAvailable];
UInt32 numBytesRead = 0;
status = mFTDI.Read(readData, nrOfBytesAvailable, ref numBytesRead);
// invoke your own event handler for data received...
//InvokeCharacterReceivedEvent(fParsedData);
}
}
}
public bool Write(string data)
{
UInt32 numBytesWritten = 0;
ASCIIEncoding enconding = new ASCIIEncoding();
byte[] bytes = enconding.GetBytes(data);
FTDI.FT_STATUS status = ftdi.Write(bytes, bytes.Length, ref numBytesWritten);
if (status != FTDI.FT_STATUS.FT_OK)
{
Debug.WriteLine("FTDI Write Status ERROR: " + status);
return false;
}
if (numBytesWritten < data.Length)
{
Debug.WriteLine("FTDI Write Length ERROR: " + status + " length " + data.Length +
" written " + numBytesWritten);
return false;
}
return true;
}