#c# #pinvoke #dmx512
#c# #pinvoke #dmx512
Вопрос:
Пара быстрых вопросов. У меня есть USB-контроллер освещения DMX King, которым я пытаюсь управлять.
Он основан на открытом протоколе DMX (от Entec), который делает доступным класс c #. Я подключил устройство к RGB can, и если я протестирую USB-устройство с их драйвером, оно подключится к COM4, и когда я переключу их программное обеспечение в режим передачи, я смогу настроить отдельные каналы DMX.
Используя их класс OpenDMX, с несколькими изменениями (ядро осталось тем же, я просто добавил дополнительную проверку ошибок, я могу найти устройство, запросить его информацию и т.д.). Когда я открываю устройство, я получаю дескриптор. Я могу записать на это устройство с помощью FT_Write, но независимо от того, что я делаю, индикаторы на самом деле не загораются.
Вот несколько соответствующих фрагментов кода:
public static byte[] buffer;
[DllImport("FTD2XX.dll")]
public static extern FT_STATUS FT_Open(UInt32 uiPort, ref uint ftHandle);
[DllImport("FTD2XX.dll")]
public static extern FT_STATUS FT_Write(uint ftHandle, IntPtr lpBuffer, UInt32 dwBytesToRead, ref UInt32 lpdwBytesWritten);
public static void writeData()
{
while (!done)
{
try
{
initOpenDMX();
status = FT_SetBreakOn(handle);
status = FT_SetBreakOff(handle);
bytesWritten = write(handle, buffer, buffer.Length);
if (bytesWritten == 0)
{
break;
}
System.Threading.Thread.Sleep(25);
}
catch (Exception)
{
break;
}
}
Connected = false;
done = false;
}
Все состояния возвращаются как FT_Ok, а записанные байты возвращаются как 512 (количество каналов на этом USB-контроллере).
Я продолжаю думать, что пропустил что-то вроде настройки устройства в режим передачи или аналогичный (у него только один разъем DMX)
public static void initOpenDMX()
{
status = FT_ResetDevice(handle);
status = FT_SetDivisor(handle, (char)12); // set baud rate
status = FT_SetDataCharacteristics(handle, BITS_8, STOP_BITS_2, PARITY_NONE);
status = FT_SetFlowControl(handle, (char)FLOW_NONE, 0, 0);
status = FT_ClrRts(handle);
status = FT_SetLatencyTimer(handle, (byte)40);
status = FT_Purge(handle, PURGE_TX);
status = FT_Purge(handle, PURGE_RX);
}
Я также пробовал класс Entec OpenDMX без каких-либо изменений с моей стороны, и, похоже, он тоже ничего не делает.
Просто хочу подчеркнуть, что их управляющее программное обеспечение работает нормально, поэтому светильник и контроллер совместимы. Я думаю, что чего-то не хватает в том, как я использую FTD2xx. Ошибок не поступает (все в FT_OK), так что это говорит о том, что DLL работает — тем более, что я могу запросить устройство, используя методы FT_ListDevices и FT_GetDeviceInfo.
Есть идеи?
Гарет
Ответ №1:
Чтобы решить эту проблему, я отправил электронное письмо производителю. Оказалось, что устройство не было OpenDMX, на самом деле это был DMXProUSB Протокол был довольно похож, и он был основан на чипе FTDI, поэтому код частично работал, но в нем есть микроконтроллер. Я преобразовал файл примера контроллера C в C # и все это заработало. Если это когда-нибудь повторится, я буду рад поделиться полученным кодом c # для DMXProUSB, однако без поддержки. Я отправил код по электронной почте производителю (dmxking) и разместил копию на github:https://github.com/agrath/Sniper .Освещение.Dmx
Спасибо за вашу помощь
Комментарии:
1. Рад, что это помогло вам. Наверное, мне стоит выложить это на github
2. Мы добавили исходный код на github и удалили ссылку на dropbox выше. github.com/agrath/Sniper . Освещение.Dmx
3. Я смог загрузить ваш код и скомпилировать его. Это библиотека, и мне было интересно, есть ли у вас простое полноценное решение. У меня есть проект на VB .NET, и я попытался преобразовать код с помощью SharpDevelop 4.4, но он не был полностью преобразован автоматически.
4. @asylumax вы должны иметь возможность просто скомпилировать библиотеку как c #, а затем добавить ее в качестве ссылки на свой vb.net проект и вызов в нем
5. @asylumax в дополнение к моему комментарию выше, есть старый пример, который я написал некоторое время назад для решения проблемы с github: github.com/agrath/Sniper . Lighting.Dmx /issues / 2 Я также только что разместил свежий образец в wiki github.com/agrath/Sniper . Освещение.Dmx/wiki/Образец-c#-использование
Ответ №2:
Я прогнал версию Hippy на VB через механический переводчик VB на C # и обнаружил одно ключевое отличие. FT_WRITE использует строку для передачи данных в неуправляемый код. Класс C # использует IPtr, указывающий на массив байтов.
Эта версия работает для меня:
using System;
using System.Runtime.InteropServices;
using System.Threading;
// based on Hippy's VB Example
// http://members.westnet.com.au/rowanmac/opendmx.html#tx
// Working link: https://web.archive.org/web/20150217155014/http://members.westnet.com.au:80/rowanmac/opendmx.html
namespace Test
{
class Program
{
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_Open(short intDeviceNumber, ref int lngHandle);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_Close(int lngHandle);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetDivisor(int lngHandle, int div);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_Read(int lngHandle, string lpszBuffer, int lngBufferSize, ref int lngBytesReturned);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_Write(int lngHandle, string lpszBuffer, int lngBufferSize, ref int lngBytesWritten);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_Write(int lngHandle, IntPtr lpBuffer, int lngBufferSize, ref int lngBytesWritten);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetBaudRate(int lngHandle, int lngBaudRate);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetDataCharacteristics(int lngHandle, byte byWordLength, byte byStopBits, byte byParity);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetFlowControl(int lngHandle, short intFlowControl, byte byXonChar, byte byXoffChar);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_ResetDevice(int lngHandle);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetDtr(int lngHandle);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_ClrDtr(int lngHandle);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetRts(int lngHandle);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_ClrRts(int lngHandle);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_GetModemStatus(int lngHandle, ref int lngModemStatus);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_Purge(int lngHandle, int lngMask);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_GetStatus(int lngHandle, ref int lngRxBytes, ref int lngTxBytes, ref int lngEventsDWord);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_GetQueueStatus(int lngHandle, ref int lngRxBytes);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_GetEventStatus(int lngHandle, ref int lngEventsDWord);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetChars(int lngHandle, byte byEventChar, byte byEventCharEnabled, byte byErrorChar, byte byErrorCharEnabled);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetTimeouts(int lngHandle, int lngReadTimeout, int lngWriteTimeout);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetBreakOn(int lngHandle);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetBreakOff(int lngHandle);
// FTDI Constants
const short FT_OK = 0;
const short FT_INVALID_HANDLE = 1;
const short FT_DEVICE_NOT_FOUND = 2;
const short FT_DEVICE_NOT_OPENED = 3;
const short FT_IO_ERROR = 4;
const short FT_INSUFFICIENT_RESOURCES = 5;
// Word Lengths
const byte FT_BITS_8 = 8;
// Stop Bits
const byte FT_STOP_BITS_2 = 2;
// Parity
const byte FT_PARITY_NONE = 0;
// Flow Control
const byte FT_FLOW_NONE = 0x0;
// Purge rx and tx buffers
const byte FT_PURGE_RX = 1;
const byte FT_PURGE_TX = 2;
public static int handle=0;
public static byte[] buffer = new byte[4]; // can be up to 512, shorter is faster
private static string lpszBuffer="" (char) 0 (char) 64 (char) 64 (char) 0;
static void Main(string[] args)
{
init();
}
public static string init()
{
short n = 0;
// ==== ATTEMPT TO OPEN DEVICE ====
if (FT_Open(n, ref handle) != FT_OK)
{
return "FTTD Not Found";
}
// ==== PREPARE DEVICE FOR DMX TRANSMISSION ====
// reset the device
if (FT_ResetDevice(handle) != FT_OK)
{
return "Failed To Reset Device!";
}
// get an ID from the widget from jumpers
// GetID(ref n);
// set the baud rate
if (FT_SetDivisor(handle, 12) != FT_OK)
{
return "Failed To Set Baud Rate!";
}
// shape the line
if (FT_SetDataCharacteristics(handle, FT_BITS_8, FT_STOP_BITS_2, FT_PARITY_NONE) != FT_OK)
{
return "Failed To Set Data Characteristics!";
}
// no flow control
if (FT_SetFlowControl(handle, FT_FLOW_NONE, 0, 0) != FT_OK)
{
return "Failed to set flow control!";
}
// set bus transiever to transmit enable
if (FT_ClrRts(handle) != FT_OK)
{
return "Failed to set RS485 to send!";
}
// Clear TX amp; RX buffers
if (FT_Purge(handle, FT_PURGE_TX) != FT_OK)
{
return "Failed to purge TX buffer!";
}
// empty buffers
if (FT_Purge(handle, FT_PURGE_RX) != FT_OK)
{
return "Failed to purge RX buffer!";
}
setDmxValue(0, 0); // should always be zero
setDmxValue(1, 64);
setDmxValue(2, 64);
setDmxValue(3, 0);
Thread thread = new Thread(new ThreadStart(writeDataThread));
thread.Start();
return "Ok";
}
// init
public static void setDmxValue(int channel, byte value)
{
buffer[channel] = value;
lpszBuffer="";
for (int i = 0; i < buffer.Length; i)
{
lpszBuffer = (char)buffer[i];
}
}
public static void writeDataThread()
{
bool done = false;
int lngBytesWritten=0;
while (!done)
{
FT_SetBreakOn(handle);
FT_SetBreakOff(handle);
FT_Write(handle, lpszBuffer, buffer.Length, ref lngBytesWritten);
System.Threading.Thread.Sleep(50);
}
}
}
}
Ответ №3:
Я написал класс C # на сайте open dmx. Он был основан на драйвере Open DMX от Hippy, написанном на VB.
// Старая неработающая ссылка:
http://members.westnet.com.au/rowanmac/opendmx.html#tx
// Рабочая ссылка: https://web.archive.org/web/20150217155014/http://members.westnet.com.au:80/rowanmac/opendmx.html
Класс C # в том, что он не инициализирует что-либо на чипе FDDI.
Это мучило меня годами.
У меня есть подозрения по поводу FT_SetFlowControl. В приложении VB вторым параметром является короткое значение int. Но любой вызов его эквивалента в классе C # будет работать только в том случае, если второй параметр приведен к символу.
[DllImport("FTD2XX.dll")]
public static extern FT_STATUS FT_SetFlowControl(uint ftHandle, UInt16 usFlowControl, byte uXon, byte uXoff);
Ответ №4:
Я исправляю проблему с помощью Marshal.Copy
public static void writeDataThread(int Length)
{
int lngBytesWritten = 0;
IntPtr pnt = Marshal.AllocHGlobal(Length);
Marshal.Copy(buffer, 0, pnt, Length);
FT_SetBreakOn(handle);
FT_SetBreakOff(handle);
string StartCode = null;
FT_Write(handle, StartCode, 1, ref lngBytesWritten);
FT_Write(handle, pnt, Length, ref lngBytesWritten);
}
Комментарии:
1. Было ли это в моем коде? Если это так, я должен исправить свою версию
Ответ №5:
Чипы FTDI имеют контакты GPIO в дополнение к последовательным портам. Надеюсь, в документации указано, нужно ли их устанавливать. Функция является FT_SetBitMode
. Здесь документация.