FTDI与USB设备的通信 - 目标C.

时间:2012-12-26 01:06:01

标签: objective-c ftdi dmx512

我正在尝试与Enttec USB DMX Pro进行通信。主要是接收DMX。

他们发布了一个Visual C ++版本here,但我对如何转换为Obj-c感到有点困惑。 Enttec写道,“使用适用于Mac的FTDI库与PRO交谈,并参考D2XX编程指南打开并与设备通信。” Objective-C的所有示例应用程序都在那里?有没有简单的方法与Enttec DMX USB Pro进行通信?

2 个答案:

答案 0 :(得分:25)

我在Mac上使用FTDI芯片做了大量工作,所以我可以在这里提供一点见解。我使用了USB串行转换器的单通道和双通道变体,它们的行为方式相同。

FTDI有两个虚拟COM端口驱动程序,它们在您的系统上创建一个串行COM端口,代表连接到其芯片的串行连接,以及它们的D2XX直接通信库。你会想要使用后者,can be downloaded from their site for various platforms

Mac的D2XX库有一个独立的.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 Programmer's Guide中有详细描述。

这是我从我的一个应用程序中提取的一些代码,用于连接到其中一个设备:

    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()仅仅是a convenience function I use to guarantee execution of a block on the main queue)。

您可以使用以下内容从串行接口读取原始字节:

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库中提供的其他功能。同样,您只需要移动设备制造商提供给您的样本中的相应代码。

答案 1 :(得分:0)

我遇到了类似的问题(尝试使用Objective-C写入EntTec Open DMX),但没有任何成功。按照@Brad的好答案,我意识到每次发送DMX数据包时都需要切换BREAK状态。

以下是我在某些测试代码中循环的示例,该代码在帧之间发送延迟为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);
}

希望这可以帮助其他人!