FTDI Связь с USB-устройством - Цель C
Я пытаюсь связаться с Enttec USB DMX Pro. В основном получение DMX.
Они выпустили версию Visual C++ здесь, но я немного озадачен тем, что делать, чтобы преобразовать в Obj-c. Enttec пишет: "Поговорите с PRO, использующим библиотеку FTDI для Mac, и обратитесь к руководству по программированию D2XX, чтобы открыть и поговорить с устройством". Какие-нибудь примеры приложений для Objective-C там? Существует ли простой способ связи с Enttec DMX USB Pro?
2 ответа
Я проделал значительную работу с микросхемами FTDI на Mac, поэтому могу немного рассказать о них. Я использовал одноканальные и двухканальные варианты их USB-последовательных преобразователей, и все они ведут себя одинаково.
FTDI имеет как свои драйверы виртуального COM-порта, которые создают последовательный COM-порт в вашей системе, представляющий последовательное соединение, подключенное к их чипу, так и их библиотеки прямой связи D2XX. Вы захотите поработать с последними, которые можно скачать с их сайта для разных платформ.
Библиотеки D2XX для Mac поставляются в виде отдельного файла.dylib (последним является libftd2xx.1.2.2.dylib) или новой статической библиотеки, которую они недавно начали поставлять. В этот пакет также будут включены нужные вам заголовочные файлы (ftd2xx.h и WinTypes.h).
В своем проекте XCode добавьте.dylib в качестве фреймворка, с которым нужно связать, и добавьте файлы ftd2xx.h, WinTypes.h и ftd2xx.cfg в ваш проект. На этапе сборки Copy Bundled Frameworks убедитесь, что на этом этапе присутствуют libftd2xx.1.2.2.dylib и ftd2xx.cfg. Вам также может понадобиться настроить относительный путь, который ожидает эта библиотека, для того, чтобы она функционировала в вашем комплекте приложений, поэтому вам может потребоваться выполнить следующую команду для нее в командной строке:
install_name_tool -id @executable_path/../Frameworks/libftd2xx.1.2.2.dylib libftd2xx.1.2.2.dylib
Как только ваш проект будет правильно настроен, вы захотите импортировать заголовки FTDI:
#import "ftd2xx.h"
и начать подключаться к вашим последовательным устройствам. Пример, на который вы ссылаетесь в своем вопросе, содержит загружаемый образец C++, который показывает, как они общаются со своим устройством. Вы можете перенести почти весь код C, используемый там, и поместить его в свое приложение Objective C. Они просто используют стандартные команды FTDI D2XX, которые подробно описаны в загружаемом Руководстве по программированию D2XX.
Вот некоторый код, который я взял из одного из моих приложений и использовал для подключения к одному из этих устройств:
DWORD numDevs = 0;
// Grab the number of attached devices
ftdiPortStatus = FT_ListDevices(&numDevs, NULL, FT_LIST_NUMBER_ONLY);
if (ftdiPortStatus != FT_OK)
{
NSLog(@"Electronics error: Unable to list devices");
return;
}
// Find the device number of the electronics
for (int currentDevice = 0; currentDevice < numDevs; currentDevice++)
{
char Buffer[64];
ftdiPortStatus = FT_ListDevices((PVOID)currentDevice,Buffer,FT_LIST_BY_INDEX|FT_OPEN_BY_DESCRIPTION);
NSString *portDescription = [NSString stringWithCString:Buffer encoding:NSASCIIStringEncoding];
if ( ([portDescription isEqualToString:@"FT232R USB UART"]) && (usbRelayPointer != NULL))
{
// Open the communication with the USB device
ftdiPortStatus = FT_OpenEx("FT232R USB UART",FT_OPEN_BY_DESCRIPTION,usbRelayPointer);
if (ftdiPortStatus != FT_OK)
{
NSLog(@"Electronics error: Can't open USB relay device: %d", (int)ftdiPortStatus);
return;
}
//Turn off bit bang mode
ftdiPortStatus = FT_SetBitMode(*usbRelayPointer, 0x00,0);
if (ftdiPortStatus != FT_OK)
{
NSLog(@"Electronics error: Can't set bit bang mode");
return;
}
// Reset the device
ftdiPortStatus = FT_ResetDevice(*usbRelayPointer);
// Purge transmit and receive buffers
ftdiPortStatus = FT_Purge(*usbRelayPointer, FT_PURGE_RX | FT_PURGE_TX);
// Set the baud rate
ftdiPortStatus = FT_SetBaudRate(*usbRelayPointer, 9600);
// 1 s timeouts on read / write
ftdiPortStatus = FT_SetTimeouts(*usbRelayPointer, 1000, 1000);
// Set to communicate at 8N1
ftdiPortStatus = FT_SetDataCharacteristics(*usbRelayPointer, FT_BITS_8, FT_STOP_BITS_1, FT_PARITY_NONE); // 8N1
// Disable hardware / software flow control
ftdiPortStatus = FT_SetFlowControl(*usbRelayPointer, FT_FLOW_NONE, 0, 0);
// Set the latency of the receive buffer way down (2 ms) to facilitate speedy transmission
ftdiPortStatus = FT_SetLatencyTimer(*usbRelayPointer,2);
if (ftdiPortStatus != FT_OK)
{
NSLog(@"Electronics error: Can't set latency timer");
return;
}
}
}
Отключение довольно просто:
ftdiPortStatus = FT_Close(*electronicsPointer);
*electronicsPointer = 0;
if (ftdiPortStatus != FT_OK)
{
return;
}
Запись на последовательное устройство довольно проста:
__block DWORD bytesWrittenOrRead;
unsigned char * dataBuffer = (unsigned char *)[command bytes];
//[command getBytes:dataBuffer];
runOnMainQueueWithoutDeadlocking(^{
ftdiPortStatus = FT_Write(electronicsCommPort, dataBuffer, (DWORD)[command length], &bytesWrittenOrRead);
});
if((bytesWrittenOrRead < [command length]) || (ftdiPortStatus != FT_OK))
{
NSLog(@"Bytes written: %d, should be:%d, error: %d", bytesWrittenOrRead, (unsigned int)[command length], ftdiPortStatus);
return NO;
}
(command
является экземпляром NSData, и runOnMainQueueWithoutDeadlocking()
это просто удобная функция, которую я использую, чтобы гарантировать выполнение блока в главной очереди).
Вы можете прочитать необработанные байты из последовательного интерфейса, используя что-то вроде следующего:
NSData *response = nil;
DWORD numberOfCharactersToRead = size;
__block DWORD bytesWrittenOrRead;
__block unsigned char *serialCommunicationBuffer = malloc(numberOfCharactersToRead);
runOnMainQueueWithoutDeadlocking(^{
ftdiPortStatus = FT_Read(electronicsCommPort, serialCommunicationBuffer, (DWORD)numberOfCharactersToRead, &bytesWrittenOrRead);
});
if ((bytesWrittenOrRead < numberOfCharactersToRead) || (ftdiPortStatus != FT_OK))
{
free(serialCommunicationBuffer);
return nil;
}
response = [[NSData alloc] initWithBytes:serialCommunicationBuffer length:numberOfCharactersToRead];
free(serialCommunicationBuffer);
В конце вышесказанного, response
будет экземпляром NSData, содержащим байты, которые вы прочитали из порта.
Кроме того, я бы посоветовал вам всегда обращаться к устройству FTDI из основного потока. Несмотря на то, что они говорят, что поддерживают многопоточный доступ, я обнаружил, что любой вид доступа не из основного потока (даже гарантированный эксклюзивный доступ из одного потока) вызывает периодические сбои на Mac.
Помимо описанных выше случаев, вы можете обратиться к руководству по программированию D2XX для других функций, которые FTDI предоставляет в своей библиотеке C. Опять же, вам просто нужно перейти к соответствующему коду из примеров, предоставленных вам производителем вашего устройства.
Я столкнулся с подобной проблемой (пытаясь записать в EntTec Open DMX с помощью Objective-C), но безуспешно. После того, как вы ответили на замечательный ответ @Brad, я понял, что вам также нужно переключать состояние BREAK при каждой отправке пакета DMX.
Вот пример моего цикла в некотором тестовом коде, который отправляет пакеты с 20-миллисекундной задержкой между кадрами.
while (1) {
FT_SetBreakOn(usbRelayPointer);
FT_SetBreakOff(usbRelayPointer);
ftdiPortStatus = FT_Write(usbRelayPointer, startCode, 1, &bytesWrittenOrRead);
ftdiPortStatus = FT_Write(usbRelayPointer, dataBuffer, (DWORD)[command length], &bytesWrittenOrRead);
usleep(20000);
}
Надеюсь, что это поможет кому-то еще там!