Я создаю приложение, которое общается с устройством через FT2232H конвертер USB / RS232. Для общения я использую библиотеку FTD2XX_NET.dll с сайта FTDI.
Я использую два streamа:
У меня проблема, когда я пытаюсь записать какие-либо данные на устройство во время работы streamа получателя. Основной stream просто зависает на функции ftdiDevice.Write.
Я попытался синхронизировать оба streamа, так что только один stream может одновременно использовать функцию чтения / записи, но это не помогло.
Ниже код, ответственный за сообщение. Обратите внимание, что следующие функции – это методы classа FtdiPort.
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.Write не зависает, если я прокомментирую следующую строку:
ftStatus = ftdiDevice.Read(readBytes, this.ReadBufferSize, ref numBytesRead);
Несколько вещей:
Проверьте, заблокирован ли ваш запрос на чтение. Если это так, возможно, вы не сможете позвонить в «Запись», когда «Чтение» блокирует ожидание ответа. В документации по API вы можете получить более подробную информацию об этом.
Некоторые API-интерфейсы не поддерживают множество streamов очень хорошо, даже при синхронизации доступа. Если это так, вы можете использовать дизайн, в котором вы делегируете свои команды Write в ваш коммитный stream. Когда я использовал этот шаблон в прошлом, я обычно помещаю в очередь какой-то class Command, содержащий информацию, которую я хочу написать, и либо использую class Threading Signal, чтобы позволить моим вызовам «командные» методы блокировать или предоставлять какие-то асинхронное уведомление.
Альтернативой является использование механизма уведомления о событиях из FTDI, так что вам не нужен блокирующий stream для считывания данных:
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; }
Я нашел более подробную документацию по API. Действительно, функция ftdiDevice.read блокируется, если вы не установили другое значение readTimeout 0. Установка этого значения таймаута решила проблему.
Спасибо за ваш быстрый ответ.
С уважением
Проверяя API, мне кажется, что драйвер способен имитировать COM-порт. Я вижу, что метод GetComPort () возвращает строку «COMx». Это довольно вероятно, что вы можете использовать class System.IO.Ports.SerialPort. Что уже делает то, что пытается сделать ваша shell, оно поддерживает событие DataReceived. Стоит сделать снимок.