7

I'm trying to communicate with the Enttec USB DMX Pro. Mainly receiving DMX.

They released a Visual C++ version here, but I'm a little stumped on what to do to convert to Obj-c. Enttec writes, "Talk to the PRO using FTDI library for Mac, and refer to D2XX programming guide to open and talk to the device." Any example apps for Objective-C out there? Is there an easy way to communicate with the Enttec DMX USB Pro?

4

2 回答 2

25

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

FTDI 有他们的虚拟 COM 端口驱动程序,它在您的系统上创建一个串行 COM 端口,代表连接到他们的芯片的串行连接,以及他们的 D2XX 直接通信库。您将要使用后者,可以从他们的网站上为各种平台下载

适用于 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 程序员指南中详细描述了这些命令。

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

    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 库中提供的其他功能。同样,您应该只需要从设备制造商提供给您的示例中转移适当的代码。

于 2013-01-21T20:42:41.817 回答
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);
}

希望这对其他人有帮助!

于 2016-02-11T21:17:33.873 回答