Программирование PC Доступ к устройствам FTDI из C# Mon, November 20 2017  

Поделиться

нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.


Доступ к устройствам FTDI из C# Печать
Добавил(а) microsin   

Здесь описываются примеры кода, в котором осуществляется обмен с устройствами FTDI через драйверы D2XX и FTD2XX.DLL на языке C#.

Компания FTDI предоставляет на платформе Windows управляемый класс-обертку (managed .NET wrapper class) для функций FTD2XX DLL. Эта managed wrapper DLL (FTD2XX_NET.DLL) предоставляется для свободной загрузки вместе с документацией Intellisense, имеющейся в файле FTD2XX_NET.XML - его можно просмотреть через Visual Studio Object Browser, и также предоставляются подсказки-хинты при написании кода.

Примеры 3 и 4 ниже оба показывают, как использовать интерфейс FTD2XX_NET DLL. В Solution Explorer нужно добавить ссылку на файл FTD2XX_NET.DLL. Для этого просто сделайте правый клик на элемент References Solution Explorer в Вашем проекте Visual Studio, выберите "Add Reference", затем "Browse", и укажите на файл DLL. Файл XML должен быть размещен в том же месте, что и DLL.

FTD2XX_NET_v1.0.14.zip FTD2XX_NET.DLL

FTD2XX_NETclass.zip - исходный код класса .NET Wrapper

CDM v2.12.00 WHQL Certified.exe - инсталлятор для драйверов D2XX и VCP для Windows.

Обратите внимание, что в инсталляционном пакете CDM v2.12.00 WHQL Certified.exe (подходящем для всех версий Windows, кроме Windows 95 и Windows 98) находятся сразу и драйверы VCP, и драйверы D2XX.

Также для загрузки исходного кода .NET Wrapper см. страничку, посвященную примерам кода [1].

[Прием и передача данных через последовательный порт]

USBTransfer.zip. Компания KOPF GmbH разработала этот пример на C# (автор Oliver Kopf). Пример требует дополнительной DLL (AID.DLL) стиля C#. Кроме приведенных здесь ссылок, Вы можете скачать исходный код проекта и DLL с сайта KOPF GmbH, http://www.kopfweb.de (см. раздел сайта DOWNLOADS).

FTDI Csharp example1 USBTransfer

В примере демонстрируются многие функции библиотеки. Однако недостаток примера в том, что весь код, работающий с функциями библиотеки FTDI, спрятан в отдельной AID.DLL, которая в примере поставляется в двоичном виде. Так что если Вы хотите получить пример использования функций, то придется отдельно найти исходный код для библиотеки AID.DLL на сайте разработчика (можно также найти проект для AID.DLL вместе с исходным кодом в архиве по ссылке [7]).

Еще одно неудобство в том, что DLL скомпилирована под 32-битную Windows, поэтому если Ваше приложение работает на платформе x64, то нужно его перекомпилировать на платформу x86, и возможно появятся проблемы несовместимости.

D2XXAccess_csharp.zip. Этот вариант приложения на C# изначально предназначен для Windows CE - в том виде, как его можно скачать с сайта FTDI. Я его перекомпилировал на Windows 7 x64, пример заработал нормально. ИМХО этот проект намного полезнее предыдущего, потому что в нем функции FTDI запускаются непосредственно, без использования промежуточной вспомогательной DLL. Показано, как получить список устройств, прочитать строки дескрипторов, открыть устройство, установить скорость, передать и принять данные.

FTDI Csharp example2 D2XXAccess

Для запуска на Windows CE пример требует наличия установленных драйверов D2XX (эти драйверы можно скачать на страничке D2XX Drivers сайта FTDI).

LoopBack.zip. Этот код показывает пример приема и передачи данных самому себе (loopback переводится как "обратная петля"), т. е. происходит запись в устройство и чтение из этого же устройства с использованием DLL-интерфейса FTD2XX_NET. В устройство будет записана легендарная фраза "Hello World!" в виде строки, и эта же строка будет прочитана из устройства обратно - имейте в виду, что нужно замкнуть сигнал Tx на сигнал Rx, чтобы получилась "обратная петля", и пример смог работать.

Код требует, чтобы в системе Windows были установлены драйверы D2XX компании FTDI. Драйверы можно загрузить со странички Drivers [2] сайта FTDI.

Основной код примера LoopBack находится в файле Program.cs:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using FTD2XX_NET;
 
namespace LoopBack
{
   class Program
   {
      static void Main(string[] args)
      {
         UInt32 ftdiDeviceCount = 0;
         FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OK;
// Создание нового экземпляр класса устройства FTDI: FTDI myFtdiDevice = new FTDI(); // Определение количества устройств FTDI, подключенных сейчас к компьютеру: ftStatus = myFtdiDevice.GetNumberOfDevices(ref ftdiDeviceCount); // Проверка результата вызова GetNumberOfDevices: if (ftStatus == FTDI.FT_STATUS.FT_OK) { Console.WriteLine("Количество устройств FTDI: " + ftdiDeviceCount.ToString()); Console.WriteLine(""); } else { // Ожидание нажатия на клавишу: Console.WriteLine("Ошибка получения количества устройств (ошибка " + ftStatus.ToString() + ")"); Console.ReadKey(); return; }
// Если нет ни одного подключенного устройства, то возврат: if (ftdiDeviceCount == 0) { // Ожидание нажатия на клавишу: Console.WriteLine("Ошибка получения количества устройств (ошибка " + ftStatus.ToString() + ")"); Console.ReadKey(); return; }
// Создание хранилища для списка информации об устройствах // (device info list): FTDI.FT_DEVICE_INFO_NODE[] ftdiDeviceList = new FTDI.FT_DEVICE_INFO_NODE[ftdiDeviceCount];
// Заполнение списка информацией: ftStatus = myFtdiDevice.GetDeviceList(ftdiDeviceList);
if (ftStatus == FTDI.FT_STATUS.FT_OK) { for (UInt32 i = 0; i < ftdiDeviceCount; i++) { Console.WriteLine("Device Index: " + i.ToString()); Console.WriteLine("Flags: " + String.Format("{0:x}", ftdiDeviceList[i].Flags)); Console.WriteLine("Type: " + ftdiDeviceList[i].Type.ToString()); Console.WriteLine("ID: " + String.Format("{0:x}", ftdiDeviceList[i].ID)); Console.WriteLine("Location ID: " + String.Format("{0:x}", ftdiDeviceList[i].LocId)); Console.WriteLine("Serial Number: " + ftdiDeviceList[i].SerialNumber.ToString()); Console.WriteLine("Description: " + ftdiDeviceList[i].Description.ToString()); Console.WriteLine(""); } }
// Открыть первое устройство в нашем списке по серийному номеру: ftStatus = myFtdiDevice.OpenBySerialNumber(ftdiDeviceList[0].SerialNumber); if (ftStatus != FTDI.FT_STATUS.FT_OK) { // Ожидание нажатия на клавишу: Console.WriteLine("Не получилось открыть устройство (ошибка " + ftStatus.ToString() + ")"); Console.ReadKey(); return; }
// Настройка параметров устройства. // Установить скорость на 9600 бод: ftStatus = myFtdiDevice.SetBaudRate(9600); if (ftStatus != FTDI.FT_STATUS.FT_OK) { // Ожидание нажатия на клавишу: Console.WriteLine("Проблема в установке скорости (ошибка " + ftStatus.ToString() + ")"); Console.ReadKey(); return; }
// Установить формат фрейма - сколько бит данных, стоп-битов, четность: ftStatus = myFtdiDevice.SetDataCharacteristics(FTDI.FT_DATA_BITS.FT_BITS_8, FTDI.FT_STOP_BITS.FT_STOP_BITS_1, FTDI.FT_PARITY.FT_PARITY_NONE); if (ftStatus != FTDI.FT_STATUS.FT_OK) { // Ожидание нажатия на клавишу: Console.WriteLine("Не получилось настроить параметры фрейма (ошибка " + ftStatus.ToString() + ")"); Console.ReadKey(); return; }
// Настроить управление потоком (flow control) на использование // сигналов RTS/CTS: //ftStatus = myFtdiDevice.SetFlowControl(FTDI.FT_FLOW_CONTROL.FT_FLOW_RTS_CTS, // 0x11, 0x13); //if (ftStatus != FTDI.FT_STATUS.FT_OK) //{ // // Ожидание нажатия на клавишу: // Console.WriteLine("Не получилось настроить flow control (ошибка " // + ftStatus.ToString() + ")"); // Console.ReadKey(); // return; //}
// Установить таймаут чтения на 5 секунд, таймаут записи на бесконечность: ftStatus = myFtdiDevice.SetTimeouts(5000, 0); if (ftStatus != FTDI.FT_STATUS.FT_OK) { // Ожидание нажатия на клавишу: Console.WriteLine("Не получилось установить таймауты (ошибка " + ftStatus.ToString() + ")"); Console.ReadKey(); return; }
// Начало теста loop back - убедитесь, что вход устройства соединен с его // выходом. // Запись строки в устройство: string dataToWrite = "Hello world!"; UInt32 numBytesWritten = 0; // Обратите внимание, что метод Write перезагружен, благодаря чему можно // записать строку или массив данных: ftStatus = myFtdiDevice.Write(dataToWrite, dataToWrite.Length, ref numBytesWritten); if (ftStatus != FTDI.FT_STATUS.FT_OK) { // Ожидание нажатия на клавишу: Console.WriteLine("Неудачная запись в устройство (ошибка " + ftStatus.ToString() + ")"); Console.ReadKey(); return; }
// Проверка количества данных, доступных для чтения. // В этом случае мы точно знаем, сколько данных должно поступить, // поэтому просто ждем, пока не получим все отправленные данные. UInt32 numBytesAvailable = 0; do { ftStatus = myFtdiDevice.GetRxBytesAvailable(ref numBytesAvailable); if (ftStatus != FTDI.FT_STATUS.FT_OK) { // Ожидание нажатия на клавишу: Console.WriteLine("Не получилось проверить количество доступных " + "для чтения байт (ошибка " + ftStatus.ToString() + ")"); Console.ReadKey(); return; } Thread.Sleep(10); } while (numBytesAvailable < dataToWrite.Length);
// Теперь, когда у нас есть нужное количество байт, прочитаем их: string readData; UInt32 numBytesRead = 0; // Обратите внимание, что метод Read перезагружен, так что можно // прочитать строку или массив байт: ftStatus = myFtdiDevice.Read(out readData, numBytesAvailable, ref numBytesRead); if (ftStatus != FTDI.FT_STATUS.FT_OK) { // Ожидание нажатия на клавишу: Console.WriteLine("Не получилось прочитать данные (ошибка " + ftStatus.ToString() + ")"); Console.ReadKey(); return; }
Console.WriteLine(readData); // Закроем наше устройство: ftStatus = myFtdiDevice.Close(); // Ожидание нажатия на клавишу: Console.WriteLine("Нажмите любую клавишу для продолжения."); Console.ReadKey(); return; } } }

Для проверки работы примера я использовал плату FT232R-Breakout [3].

D2XX Csharp FT232R Breakout LoopBack

D2XX Csharp LoopBack running

Обратите внимание, что я в коде примера закомментировал настройку управления потоком (flow control) через сигналы RTS и CTS. Если этого не сделать, то для работы примера LoopBack придется замкнуть друг на друга не только сигналы RXD и TXD, но и еще и сигналы квитирования RTS и CTS.

В примерах 2 и 3 рассмотрена простая передача и прием данных через виртуальный COM-порт, который получается при подключении через USB микросхемы FTDI, но сделано это через вызовы функций библиотеки D2XX. Возникает резонный вопрос - зачем нужна дополнительная библиотека, когда для работы с COM-портом можно воспользоваться традиционными способами программирования, как например класс SerialPort библиотеки .NET (см. пример 5, а также ссылку [4])?

Да, все это так, но в случае использования библиотеки D2XX добавляются преимущества: можно открыть канал связи не по его COM-имени (COM1, COM2...) а по серийному номеру, или можно просто выбрать устройство по нужным параметрам. Кроме того, можно использовать повышенные (и/или нестандартные) скорости обмена, и настраивать параметры устройства, которые другим способом настроить нельзя. Еще очень важно, что в случае сбоя связи с устройством (например, некачественный коннектор USB, который привел к аварийному переподключению устройства) можно программно восстановить работу без необходимости полного перезапуска приложения (см. описание функций библиотеки D2XX в [5]).

В следующем примере показан один из примеров программного доступа к памяти EEPROM, позволяющий настраивать энергонезависимые параметры устройства - доступ к этим возможностям происходит исключительно через библиотеку D2XX.

VCPTestCENET_Source.zip (исходный код), VCPTestCENET.zip (исполняемый код). В этом примере используется компонент .NET SerialPort.  Приложение VCPTestCENET ждет события приема символа, чтобы прочитать доступные данные из входного буфера. Можно записывать текст в устройство путем ручного ввода в текстовое поле.

FTDI Csharp example5 VCPTestCENETa FTDI Csharp example5 VCPTestCENETb

С точки зрения изучения интерфейса D2XX этот пример совсем не интересен, потому что функции FTDI из FTD2XX_NET.dll здесь совсем не используются.

[Настройка параметров устройства, работа с EEPROM]

EEPROM.zip. Этот код дает пример изменения серийного номера в EEPROM устройства. Серийный номер вычитывается из EEPROM и изменяется на пустую строку (что позволяет для FTD2XX DLL сгенерировать серийный номер) перед программированием EEPROM новым содержимым.

Код требует, чтобы в системе Windows были установлены драйверы D2XX компании FTDI. Драйверы можно загрузить со странички Drivers [2] сайта FTDI.

Основной код примера EEPROM находится в файле Program.cs:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using FTD2XX_NET;
 
namespace EEPROM
{
   class Program
   {
      static void Main(string[] args)
      {
         UInt32 ftdiDeviceCount = 0;
         FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OK;
         // Создание нового экземпляра для класса устройства FTDI:
         FTDI myFtdiDevice = new FTDI();
         // Определение количества устройств FTDI, подключенных к компьютеру:
         ftStatus = myFtdiDevice.GetNumberOfDevices(ref ftdiDeviceCount);
         // Проверка результата вызова GetNumberOfDevices:
         if (ftStatus == FTDI.FT_STATUS.FT_OK)
         {
            Console.WriteLine("Количество устройств FTDI: "
                            + ftdiDeviceCount.ToString());
            Console.WriteLine("");
         }
         else
         {
            // Ожидание нажатия на клавишу:
            Console.WriteLine("Ошибка получения количества устройств (ошибка " 
                            + ftStatus.ToString() + ")");
            Console.ReadKey();
            return;
         }
// Если нет ни одного подключенного устройства, то возврат: if (ftdiDeviceCount == 0) { // Ожидание нажатия на клавишу: Console.WriteLine("Ошибка получения количества устройств (ошибка " + ftStatus.ToString() + ")"); Console.ReadKey(); return; }
// Создание хранилища для списка информации об устройствах // (device info list): FTDI.FT_DEVICE_INFO_NODE[] ftdiDeviceList = new FTDI.FT_DEVICE_INFO_NODE[ftdiDeviceCount];
// Заполнение списка информацией: ftStatus = myFtdiDevice.GetDeviceList(ftdiDeviceList); if (ftStatus == FTDI.FT_STATUS.FT_OK) { for (UInt32 i = 0; i < ftdiDeviceCount; i++) { Console.WriteLine("Device Index: " + i.ToString()); Console.WriteLine("Flags: " + String.Format("{0:x}", ftdiDeviceList[i].Flags)); Console.WriteLine("Type: " + ftdiDeviceList[i].Type.ToString()); Console.WriteLine("ID: " + String.Format("{0:x}", ftdiDeviceList[i].ID)); Console.WriteLine("Location ID: " + String.Format("{0:x}", ftdiDeviceList[i].LocId)); Console.WriteLine("Serial Number: " + ftdiDeviceList[i].SerialNumber.ToString()); Console.WriteLine("Description: " + ftdiDeviceList[i].Description.ToString()); Console.WriteLine(""); } }
// Открыть первое устройство в нашем списке по серийному номеру: ftStatus = myFtdiDevice.OpenBySerialNumber(ftdiDeviceList[0].SerialNumber); if (ftStatus != FTDI.FT_STATUS.FT_OK) { // Ожидание нажатия на клавишу: Console.WriteLine("Не получилось открыть устройство (ошибка " + ftStatus.ToString() + ")"); Console.ReadKey(); return; }
// Создание структуры EEPROM нашего устройства, // базируясь на типе устройства, которое мы открыли: if (ftdiDeviceList[0].Type == FTDI.FT_DEVICE.FT_DEVICE_232R) { // У нас FT232R или FT245R, поэтому используем структуру FT232R EEPROM: FTDI.FT232R_EEPROM_STRUCTURE myEEData = new FTDI.FT232R_EEPROM_STRUCTURE(); // Чтение EEPROM устройства. // При попытке чтения устройства, тип которого не соответствует // используемой структуре, может произойти ошибка (выброс исключения, // throw an exception), поэтому всегда используйте в этом месте // блок try - catch. try { ftStatus = myFtdiDevice.ReadFT232REEPROM(myEEData); } catch (FTDI.FT_EXCEPTION) { Console.WriteLine("При вызове ReadFT232REEPROM сработало исключение"); } if (ftStatus != FTDI.FT_STATUS.FT_OK) { // Ожидание нажатия на клавишу: Console.WriteLine("Не получилось прочитать EEPROM устройства (ошибка " + ftStatus.ToString() + ")"); Console.ReadKey(); // Закрытие устройства и возврат: myFtdiDevice.Close(); return; }
// Печать общих элементов EEPROM в консоль приложения: Console.WriteLine("Содержимое EEPROM для устройства по индексу 0:"); Console.WriteLine("Vendor ID: " + String.Format("{0:x}", myEEData.VendorID)); Console.WriteLine("Product ID: " + String.Format("{0:x}", myEEData.ProductID)); Console.WriteLine("Manufacturer: " + myEEData.Manufacturer.ToString()); Console.WriteLine("Manufacturer ID: " + myEEData.ManufacturerID.ToString()); Console.WriteLine("Description: " + myEEData.Description.ToString()); Console.WriteLine("Serial Number: " + myEEData.SerialNumber.ToString()); Console.WriteLine("Max Power: " + myEEData.MaxPower.ToString() + "mA"); Console.WriteLine("Self Powered: " + myEEData.SelfPowered.ToString()); Console.WriteLine("Remote Wakeup Enabled: " + myEEData.RemoteWakeup.ToString()); Console.WriteLine(""); // Изменим серийный номер и запишем обратно в устройство. // Путем установки серийного номера в пустую строку мы // тем самым разрешаем FTD2XX DLL сгенерировать серийный номер. myEEData.SerialNumber = String.Empty;
// Запись нашей модифицированной структуры обратно в EEPROM устройства. // При попытке записи устройства, тип которого не соответствует // используемой структуре, может произойти ошибка (выброс исключения, // throw an exception), поэтому всегда используйте в этом месте // блок try - catch. try { ftStatus = myFtdiDevice.WriteFT232REEPROM(myEEData); } catch (FTDI.FT_EXCEPTION) { Console.WriteLine("При вызове WriteFT232REEPROM сработало исключение"); } if (ftStatus != FTDI.FT_STATUS.FT_OK) { // Ожидание нажатия на клавишу: Console.WriteLine("Не получилось записать EEPROM устройства (ошибка " + ftStatus.ToString() + ")"); Console.ReadKey(); // Закрытие устройства и возврат: myFtdiDevice.Close(); return; } } else if (ftdiDeviceList[0].Type == FTDI.FT_DEVICE.FT_DEVICE_2232) { // У нас FT2232, поэтому используем структуру EEPROM для FT2232: FTDI.FT2232_EEPROM_STRUCTURE myEEData = new FTDI.FT2232_EEPROM_STRUCTURE(); // Чтение EEPROM устройства. // При попытке чтения устройства, тип которого не соответствует // используемой структуре, может произойти ошибка (выброс исключения, // throw an exception), поэтому всегда используйте в этом месте // блок try - catch. try { ftStatus = myFtdiDevice.ReadFT2232EEPROM(myEEData); } catch (FTDI.FT_EXCEPTION) { Console.WriteLine("При вызове ReadFT2232EEPROM сработало исключение"); } if (ftStatus != FTDI.FT_STATUS.FT_OK) { // Ожидание нажатия на клавишу: Console.WriteLine("Не получилось прочитать EEPROM устройства (ошибка " + ftStatus.ToString() + ")"); Console.ReadKey(); // Закрытие устройства и возврат: myFtdiDevice.Close(); return; }
// Печать общих элементов EEPROM в консоль приложения: Console.WriteLine("Содержимое EEPROM для устройства по индексу 0:"); Console.WriteLine("Vendor ID: " + String.Format("{0:x}", myEEData.VendorID)); Console.WriteLine("Product ID: " + String.Format("{0:x}", myEEData.ProductID)); Console.WriteLine("Manufacturer: " + myEEData.Manufacturer.ToString()); Console.WriteLine("Manufacturer ID: " + myEEData.ManufacturerID.ToString()); Console.WriteLine("Description: " + myEEData.Description.ToString()); Console.WriteLine("Serial Number: " + myEEData.SerialNumber.ToString()); Console.WriteLine("Max Power: " + myEEData.MaxPower.ToString() + "mA"); Console.WriteLine("Self Powered: " + myEEData.SelfPowered.ToString()); Console.WriteLine("Remote Wakeup Enabled: " + myEEData.RemoteWakeup.ToString()); Console.WriteLine(""); // Изменим серийный номер и запишем обратно в устройство. // Путем установки серийного номера в пустую строку мы // тем самым разрешаем FTD2XX DLL сгенерировать серийный номер. myEEData.SerialNumber = String.Empty; // Запись нашей модифицированной структуры обратно в EEPROM устройства. // При попытке записи устройства, тип которого не соответствует // используемой структуре, может произойти ошибка (выброс исключения, // throw an exception), поэтому всегда используйте в этом месте // блок try - catch. try { ftStatus = myFtdiDevice.WriteFT2232EEPROM(myEEData); } catch (FTDI.FT_EXCEPTION) { Console.WriteLine("При вызове WriteFT2232EEPROM сработало исключение"); } if (ftStatus != FTDI.FT_STATUS.FT_OK) { // Ожидание нажатия на клавишу: Console.WriteLine("Не получилось записать EEPROM устройства (ошибка " + ftStatus.ToString() + ")"); Console.ReadKey(); // Закрытие устройства и возврат: myFtdiDevice.Close(); return; } } else if (ftdiDeviceList[0].Type == FTDI.FT_DEVICE.FT_DEVICE_BM) { // У нас FT232B или FT245B, поэтому используем структуру EEPROM FT232B: FTDI.FT232B_EEPROM_STRUCTURE myEEData = new FTDI.FT232B_EEPROM_STRUCTURE(); // Чтение EEPROM устройства. // При попытке чтения устройства, тип которого не соответствует // используемой структуре, может произойти ошибка (выброс исключения, // throw an exception), поэтому всегда используйте в этом месте // блок try - catch. try { ftStatus = myFtdiDevice.ReadFT232BEEPROM(myEEData); } catch (FTDI.FT_EXCEPTION) { Console.WriteLine("При вызове ReadFT2232EEPROM сработало исключение"); } if (ftStatus != FTDI.FT_STATUS.FT_OK) { // Ожидание нажатия на клавишу: Console.WriteLine("Failed to read device EEPROM (error " + ftStatus.ToString() + ")"); Console.ReadKey(); // Закрытие устройства и возврат: myFtdiDevice.Close(); return; }
// Печать общих элементов EEPROM в консоль приложения: Console.WriteLine("Содержимое EEPROM для устройства по индексу 0:"); Console.WriteLine("Vendor ID: " + String.Format("{0:x}", myEEData.VendorID)); Console.WriteLine("Product ID: " + String.Format("{0:x}", myEEData.ProductID)); Console.WriteLine("Manufacturer: " + myEEData.Manufacturer.ToString()); Console.WriteLine("Manufacturer ID: " + myEEData.ManufacturerID.ToString()); Console.WriteLine("Description: " + myEEData.Description.ToString()); Console.WriteLine("Serial Number: " + myEEData.SerialNumber.ToString()); Console.WriteLine("Max Power: " + myEEData.MaxPower.ToString() + "mA"); Console.WriteLine("Self Powered: " + myEEData.SelfPowered.ToString()); Console.WriteLine("Remote Wakeup Enabled: " + myEEData.RemoteWakeup.ToString()); Console.WriteLine(""); // Изменим серийный номер и запишем обратно в устройство. // Путем установки серийного номера в пустую строку мы // тем самым разрешаем FTD2XX DLL сгенерировать серийный номер. myEEData.SerialNumber = String.Empty; // Запись нашей модифицированной структуры обратно в EEPROM устройства. // При попытке записи устройства, тип которого не соответствует // используемой структуре, может произойти ошибка (выброс исключения, // throw an exception), поэтому всегда используйте в этом месте // блок try - catch. try { ftStatus = myFtdiDevice.WriteFT232BEEPROM(myEEData); } catch (FTDI.FT_EXCEPTION) { Console.WriteLine("При вызове WriteFT232BEEPROM сработало исключение"); } if (ftStatus != FTDI.FT_STATUS.FT_OK) { // Ожидание нажатия на клавишу: Console.WriteLine("Не получилось записать EEPROM устройства (ошибка " + ftStatus.ToString() + ")"); Console.ReadKey(); // Закрытие устройства и возврат: myFtdiDevice.Close(); return; } } // Использование фичи "cycle port" (эмуляция переподключения устройства), // чтобы вызвать принудительную повторную энумерацию устройства. // В библиотеке класса FTD2XX_NET, метод cycle port также закрывает // открытый хендл устройства, поэтому не нужно вызывать отдельно // метод Close. ftStatus = myFtdiDevice.CyclePort(); UInt32 newFtdiDeviceCount = 0; do { // Ожидание завершения переэнумерации устройства. // У устройства будет то же самое место положения на шине (location) // потому что оно не извлекалось физически, поэтому будем пробовать // открыть до успешного завершения операции. ftStatus = myFtdiDevice.OpenByLocation(ftdiDeviceList[0].LocId); Thread.Sleep(1000); } while (ftStatus != FTDI.FT_STATUS.FT_OK);
// Закрыть устройство. myFtdiDevice.Close(); // Заново создадим наш список устройств: ftStatus = myFtdiDevice.GetNumberOfDevices(ref newFtdiDeviceCount); if (ftStatus != FTDI.FT_STATUS.FT_OK) { // Ожидание нажатия на клавишу: Console.WriteLine("Не получилось определить количество устройств (ошибка " + ftStatus.ToString() + ")"); Console.ReadKey(); return; }
// Заново заполним список информацией: ftStatus = myFtdiDevice.GetDeviceList(ftdiDeviceList); if (ftStatus == FTDI.FT_STATUS.FT_OK) { for (UInt32 i = 0; i < ftdiDeviceCount; i++) { Console.WriteLine("Device Index: " + i.ToString()); Console.WriteLine("Flags: " + String.Format("{0:x}", ftdiDeviceList[i].Flags)); Console.WriteLine("Type: " + ftdiDeviceList[i].Type.ToString()); Console.WriteLine("ID: " + String.Format("{0:x}", ftdiDeviceList[i].ID)); Console.WriteLine("Location ID: " + String.Format("{0:x}", ftdiDeviceList[i].LocId)); Console.WriteLine("Serial Number: " + ftdiDeviceList[i].SerialNumber.ToString()); Console.WriteLine("Description: " + ftdiDeviceList[i].Description.ToString()); Console.WriteLine(""); } } // Ожидание нажатия на клавишу: Console.WriteLine("Для продолжения нажмите любую клавишу."); Console.ReadKey(); return; } } }

Тестировался пример EEPROM также на плате FT232R-Board [3]. Ниже на скриншотах показано, как это работает. До запуска программы плата была определена в системе как COM3, а после запуска программы серийный номер поменялся, и поэтому после повторной энумерации плата стала определяться как порт COM4.

FTDI device before SN change

D2XX Csharp EEPROM running

FTDI device after SN change

Программа очень простая, несмотря на большой размер листинга, потому что многие блоки кода повторяются (для того, чтобы обслуживать устройства разных типов, и потому, что некоторые операции выполняются повторно).

Обратите внимание, что некоторая часть кода Примеров 3 и 4 совпадает (это шаги, касающиеся создания списка устройств, заполнения списка информацией, открытия устройства, вывода его параметров).

[BitBang]

BitBang - это использование ножек чипа FTDI как портов ввода/вывода (GPIO), подключенных к компьютеру через USB. С помощью библиотеки D2XX можно настраивать порты как вход или выход, считывать их значение, устанавливать логический уровень на выходе.

В режиме асинхронного BitBang любые данные, которые Вы записали в устройство, попадут на выводы чипа, которые настроены как выходы. Скорость, с которой записанные данные попадут на выход, определяется генератором скорости устройства (Baud rate generator). Если в устройство не записываются данные, то выводы остаются в последнем состоянии, установившемся после последней записи.

С режимом BitBang работают функции FT_SetBitMode, FT_SetBaudRate, FT_Write, FT_GetBitMode, FT_Read (подробнее см. [5, 6]). На C# для асинхронного управления выводами следует пользоваться готовым классом FTDI и его соответствующими методами SetBitMode, Write, Read.

using FTD2XX_NET;
...
FTDI myFtdiDevice = new FTDI(); FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OK;
byte[] data = new byte[1] { 0 }; UInt32 numBytesWritten = 0;

while (true) { //Открыть первое попавшееся устройство FTDI: ftStatus = myFtdiDevice.OpenByIndex(0); //Настроить все ножки порта как выходы (передана маска 0xFF), установить // асинхронный режим управления выходами: ftStatus = myFtdiDevice.SetBitMode(0xFF, FTDI.FT_BIT_MODES.FT_BIT_MODE_ASYNC_BITBANG); //Вывод байта данных в ножки FT232R (управление выходами): ftStatus = myFtdiDevice.Write(data, data.Length, ref numBytesWritten); //Закрыть устройство, если оно открыто: if (myFtdiDevice.IsOpen) ftStatus = myFtdiDevice.Close(); //Изменение значений всех бит байта на противоположное: data[0] = (data[0] == 0) ? (byte)0xFF : (byte)0x00; //Задержка 0.5 секунды: System.Threading.Thread.Sleep(500); }

Для упрощения здесь опущены проверки ошибок и наличия на шине USB нужного устройства. Если подключить светодиод к любой ножке платы FT232R-Breakout [3], то он будет мигать с частотой 1 Гц.

Этот код может быть вставлен в отдельный поток (например BackgroundWorker).

BitBangCsharp output example6

using FTD2XX_NET;
...
FTDI myFtdiDevice = new FTDI(); FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OK;
byte[] data = new byte[1] { 0 }; UInt32 numBytesWritten = 0;

while (true) { //Открыть первое попавшееся устройство FTDI: ftStatus = myFtdiDevice.OpenByIndex(0); //Настроить все ножки порта как входы (передана маска 0x00), установить // асинхронный режим управления: ftStatus = myFtdiDevice.SetBitMode(0x00, FTDI.FT_BIT_MODES.FT_BIT_MODE_ASYNC_BITBANG); //Ввод байта данных из ножек FT232R (D7..D0), байт попадет в data[0]: ftStatus = myFtdiDevice.Read(data, data.Length, ref numBytesWritten); //Закрыть устройство, если оно открыто: if (myFtdiDevice.IsOpen) ftStatus = myFtdiDevice.Close(); //Задержка 10 мс: System.Threading.Thread.Sleep(10); }

Для упрощения здесь опущены проверки ошибок и наличия на шине USB нужного устройства.

FT232R-Breakout-v36-pinout-D7-D0

Я создал проект Sample6.BitBang (см. [7]), в котором демонстрируется определение подключения и отключения устройства, возможность чтения входов и управления выходами. Если устройство не подключено, то на форме отображается серая картинка платы FT232R-Breakout. Если устройство подключено, то картинка становится полноцветной, и доступно тестирование функций чтения/записи BitBang в асинхронном режиме.

FTDI Csharp example6 BitBang disconnected FTDI Csharp example6 BitBang input FTDI Csharp example6 BitBang output

Если подключить плату FT2232H Board, то будет осуществляться работа с портами ADBUS7..ADBUS0.

[Ссылки]

1. Code Examples site:ftdichip.com.
2. D2XX Drivers site:ftdichip.com.
3Плата FT232R-Breakout.
4. Visual Studio C#: работа с последовательным портом.
5. FTDI: справочник по функциям библиотеки D2XX.
6. Режимы BitBang для микросхем FT232R и FT245R.
7. 150607FTDIexamples.zip - примеры кода, описанные в статье (проекты Visual C#).

 

Добавить комментарий


Защитный код
Обновить

Top of Page