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);
}

Надеюсь, что это поможет кому-то еще там!

Другие вопросы по тегам