2012-12-26 11 views
7

Ich versuche, mit dem Enttec USB DMX Pro zu kommunizieren. Hauptsächlich Empfang von DMX.FTDI-Kommunikation mit USB-Gerät - Objective C

Sie veröffentlichten eine Visual C++ - Version here, aber ich bin ein wenig ratlos, was zu tun ist, um Obj-c zu konvertieren. Enttec schreibt: "Sprechen Sie mit dem PRO, indem Sie die FTDI-Bibliothek für Mac verwenden, und lesen Sie den D2XX-Programmierleitfaden, um das Gerät zu öffnen und mit ihm zu sprechen." Gibt es irgendwelche Beispiel-Apps für Objective-C? Gibt es eine einfache Möglichkeit, mit dem Enttec DMX USB Pro zu kommunizieren?

+0

Die "Treiber", von denen Sie sprechen, [scheinen auf der FTDI Chip-Website verfügbar zu sein] (http://www.ftdichip.com/Drivers/VCP.htm) und ich kenne kein "Ziel" C "spezifische Beispiel-Apps, aber wenn Sie einen MacOS-Beispielcode verwenden können, der C oder C++ verwendet, sollte das auch in einer Objective-C-App funktionieren. –

+0

Ja, ich habe versucht, einen MacOS-Beispielcode in C oder C++ zu finden .... – objectiveccoder001

+0

Nicht sicher, ob es hilft, aber vielleicht lohnt sich [oxDmx] (https://github.com/kylemcdonald/ofxDmx) oder der [DMXusbPro] (https://github.com/q-depot/Cinder-DMXusbPro) Schlackenblock –

Antwort

25

Ich habe eine Menge Arbeit mit den FTDI-Chips auf dem Mac gemacht, so kann ich hier einen kleinen Einblick geben. Ich habe die Einkanal- und Zweikanal-Varianten ihrer USB-Seriell-Wandler verwendet, und sie verhalten sich alle gleich.

FTDI verfügt über ihre virtuellen COM-Port-Treiber, die einen seriellen COM-Port auf Ihrem System erstellen, der die an ihren Chip angeschlossene serielle Verbindung und ihre D2XX-Bibliotheken für direkte Kommunikation darstellt. Du wirst mit letzterem arbeiten wollen, welches can be downloaded from their site for various platforms.

Die D2XX-Bibliotheken für den Mac werden in einer eigenständigen .dylib (die neueste Version ist libftd2xx.1.2.2.dylib) oder einer neuen statischen Bibliothek geliefert, die sie vor kurzem ausgeliefert haben. In diesem Paket enthalten sind die entsprechenden Header-Dateien, die Sie benötigen (ftd2xx.h und WinTypes.h).

Fügen Sie in Ihrem Xcode-Projekt die .dylib als zu verknüpfendes Framework hinzu und fügen Sie die Dateien ftd2xx.h, WinTypes.h und ftd2xx.cfg zu Ihrem Projekt hinzu. Stellen Sie in Ihrer Build-Build-Phase für das Build-Paket sicher, dass libftd2xx.1.2.2.dylib und ftd2xx.cfg in dieser Phase vorhanden sind. Sie können auch den relativen Pfad anpassen müssen, dass diese Bibliothek, um erwartet für sie in Ihrem App-Bundle funktionieren, so müssen Sie möglicherweise den folgenden Befehl gegen sie in der Befehlszeile auszuführen:

install_name_tool -id @executable_path/../Frameworks/libftd2xx.1.2.2.dylib libftd2xx.1.2.2.dylib 

Sobald Ihre Projekt wird alle richtig konfiguriert ist, sollten Sie die FTDI-Header importieren:

#import "ftd2xx.h" 

und starten Sie zu Ihren seriellen Geräten zu verbinden. Das Beispiel, auf das Sie in Ihrer Frage verlinken, enthält ein herunterladbares C++ - Beispiel, das zeigt, wie sie mit ihrem Gerät kommunizieren. Sie können fast den gesamten C-Code, der dort verwendet wird, mitbringen und ihn in Ihre Objective-C-Anwendung einfügen. Sie scheinen nur die Standard-FTDI-D2XX-Befehle zu verwenden, die im herunterladbaren D2XX Programmer's Guide ausführlich beschrieben sind.

Dies ist ein Code, den ich von einem meiner Anwendungen angehoben haben, verwendet, um eines dieser Geräte zu verbinden:

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

Disconnection ist ziemlich einfach:

 ftdiPortStatus = FT_Close(*electronicsPointer); 
     *electronicsPointer = 0; 
     if (ftdiPortStatus != FT_OK) 
     { 
      return; 
     } 

auf das Schreiben serielles Gerät ist dann recht einfach:

__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 ist ein NSData Instanz, und runOnMainQueueWithoutDeadlocking() ist nur a convenience function I use to guarantee execution of a block on the main queue).

Sie können roh Bytes von der seriellen Schnittstelle mit so etwas wie die folgenden lesen:

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

Am Ende der obigen Ausführungen werden response eine NSData Instanz sein, den Bytes enthält, die Sie vom Hafen gelesen habe.

Außerdem würde ich vorschlagen, dass Sie immer auf das FTDI-Gerät aus dem Hauptthread zugreifen sollten. Obwohl sie sagen, dass sie Multithread-Zugriffe unterstützen, habe ich festgestellt, dass jede Art von Nicht-Haupt-Thread-Zugriff (sogar garantierte exklusive Zugriffe von einem einzelnen Thread) zu zeitweiligen Abstürzen auf dem Mac führt.

Neben den oben beschriebenen Fällen können Sie den D2XX-Programmierleitfaden für die anderen Funktionen von FTDI in der C-Bibliothek konsultieren. Auch hier sollten Sie nur den entsprechenden Code aus den Beispielen, die Ihnen von Ihrem Gerätehersteller zur Verfügung gestellt wurden, verschieben.

+0

WOW! Vielen Dank, Brad. Das ist wahnsinnig hilfreich. +1 für sicher! Ich schätze wirklich die Antwort – objectiveccoder001

+0

@Brad: Wirklich große Info. Ich versuche das gleiche mit J2DXX, ich bin fähig zur Kommunikation mit dem Gerät und zum Abrufen von Geräteinformationen und so weiter. Aber ich bin nur verwirrt, weil ich keine Methode sehe, um zu sagen, in welchem ​​Block der Mifare-Karte ich Daten schreiben soll oder so weiter ... diese API liest oder schreibt nicht auf die Mifare-Karte richtig ?, aber nur für das Lesegerät –

+0

@inba - Ich habe keine Ahnung, wie die MIFARE funktioniert, aber das obige ist nur wie Sie senden und empfangen Daten über die FTDI USB-Seriell-Konverter. Sie müssen alle Befehle, die MIFARE benötigt, selbst senden. –

0

Ich stieß auf ein ähnliches Problem (versucht, auf die EntTec Open DMX mit Objective-C zu schreiben), ohne Erfolg. Nachdem ich die große Antwort von @ Brad befolgt hatte, erkannte ich, dass Sie auch jedes Mal, wenn Sie ein DMX-Paket senden, den BREAK-Status wechseln müssen.

Hier ist ein Beispiel für meine Schleife in einem Testcode, die Pakete mit einer Verzögerung von 20 Millisekunden zwischen Frames sendet.

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

Hoffe das hilft jemand anderen da draußen!