mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
366 lines
18 KiB
C++
366 lines
18 KiB
C++
//---------------------------------------------------------------------------
|
||
#include <vcl.h>
|
||
#pragma hdrstop
|
||
|
||
#include <setupapi.h>
|
||
#include "PoKeys55.h"
|
||
//---------------------------------------------------------------------------
|
||
#pragma package(smart_init)
|
||
// HIDscaner: http://forum.simflight.com/topic/68257-latest-lua-package-for-fsuipc-and-wideclient/
|
||
//#define MY_DEVICE_ID "Vid_04d8&Pid_003F"
|
||
//#define MY_DEVICE_ID "Vid_1dc3&Pid_1001&Rev_1000&MI_01"
|
||
// HID\Vid_1dc3&Pid_1001&Rev_1000&MI_01 - MI_01 to jest interfejs komunikacyjny (00-joystick,
|
||
// 02-klawiatura)
|
||
|
||
HANDLE WriteHandle = INVALID_HANDLE_VALUE;
|
||
HANDLE ReadHandle = INVALID_HANDLE_VALUE;
|
||
// GUID InterfaceClassGuid={0x4d1e55b2,0xf16f,0x11cf,0x88,0xcb,0x00,0x11,0x11,0x00,0x00,0x30};
|
||
//{4d1e55b2-f16f-11cf-88cb-001111000030}
|
||
|
||
__fastcall TPoKeys55::TPoKeys55()
|
||
{
|
||
cRequest = 0;
|
||
iPWMbits = 1;
|
||
iFaza = 0;
|
||
iLastCommand = 0;
|
||
fAnalog[0] = fAnalog[1] = fAnalog[2] = fAnalog[3] = fAnalog[4] = fAnalog[5] = fAnalog[6] = -1.0;
|
||
iPWM[0] = iPWM[1] = iPWM[2] = iPWM[3] = iPWM[4] = iPWM[5] = iPWM[6] = 0;
|
||
iPWM[7] = 4096;
|
||
iInputs[0] = 0; // czy normalnie s¹ w stanie wysokim?
|
||
iRepeats = 0;
|
||
bNoError = true;
|
||
};
|
||
//---------------------------------------------------------------------------
|
||
__fastcall TPoKeys55::~TPoKeys55() { Close(); };
|
||
//---------------------------------------------------------------------------
|
||
bool __fastcall TPoKeys55::Close()
|
||
{ // roz³¹czenie komunikacji
|
||
if (WriteHandle != INVALID_HANDLE_VALUE)
|
||
CloseHandle(WriteHandle);
|
||
WriteHandle = INVALID_HANDLE_VALUE;
|
||
if (ReadHandle != INVALID_HANDLE_VALUE)
|
||
CloseHandle(ReadHandle);
|
||
ReadHandle = INVALID_HANDLE_VALUE;
|
||
};
|
||
//---------------------------------------------------------------------------
|
||
bool __fastcall TPoKeys55::Connect()
|
||
{ // Ra: to jest do wyczyszcznia z niepotrzebnych zmiennych i komunikatów
|
||
Close();
|
||
GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00,
|
||
0x11, 0x11, 0x00, 0x00, 0x30}; // wszystkie HID tak maj¹
|
||
HDEVINFO DeviceInfoTable;
|
||
PSP_DEVICE_INTERFACE_DATA InterfaceDataStructure = new SP_DEVICE_INTERFACE_DATA;
|
||
PSP_DEVICE_INTERFACE_DETAIL_DATA DetailedInterfaceDataStructure =
|
||
new SP_DEVICE_INTERFACE_DETAIL_DATA;
|
||
SP_DEVINFO_DATA DevInfoData;
|
||
DWORD InterfaceIndex = 0;
|
||
// DWORD StatusLastError=0;
|
||
DWORD dwRegType;
|
||
DWORD dwRegSize;
|
||
DWORD StructureSize = 0;
|
||
PBYTE PropertyValueBuffer;
|
||
bool MatchFound;
|
||
DWORD ErrorStatus;
|
||
HDEVINFO hDevInfo;
|
||
String DeviceIDFromRegistry;
|
||
String DeviceIDToFind = "Vid_1dc3&Pid_1001&Rev_1000&MI_01";
|
||
// First populate a list of plugged in devices (by specifying "DIGCF_PRESENT"), which are of the
|
||
// specified class GUID.
|
||
DeviceInfoTable =
|
||
SetupDiGetClassDevs(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||
// Now look through the list we just populated. We are trying to see if any of them match our
|
||
// device.
|
||
while (true)
|
||
{
|
||
InterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||
if (SetupDiEnumDeviceInterfaces(DeviceInfoTable, NULL, &InterfaceClassGuid, InterfaceIndex,
|
||
InterfaceDataStructure))
|
||
{
|
||
ErrorStatus = GetLastError();
|
||
if (ERROR_NO_MORE_ITEMS == ErrorStatus) // Did we reach the end of the list of matching
|
||
// devices in the DeviceInfoTable?
|
||
{ // Cound not find the device. Must not have been attached.
|
||
SetupDiDestroyDeviceInfoList(
|
||
DeviceInfoTable); // Clean up the old structure we no longer need.
|
||
// ShowMessage("Erreur: Sortie1");
|
||
return false;
|
||
}
|
||
}
|
||
else // Else some other kind of unknown error ocurred...
|
||
{
|
||
ErrorStatus = GetLastError();
|
||
SetupDiDestroyDeviceInfoList(
|
||
DeviceInfoTable); // Clean up the old structure we no longer need.
|
||
// ShowMessage("Erreur: Sortie2");
|
||
return false;
|
||
}
|
||
// Now retrieve the hardware ID from the registry. The hardware ID contains the VID and PID,
|
||
// which we will then
|
||
// check to see if it is the correct device or not.
|
||
// Initialize an appropriate SP_DEVINFO_DATA structure. We need this structure for
|
||
// SetupDiGetDeviceRegistryProperty().
|
||
DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
||
SetupDiEnumDeviceInfo(DeviceInfoTable, InterfaceIndex, &DevInfoData);
|
||
// First query for the size of the hardware ID, so we can know how big a buffer to allocate
|
||
// for the data.
|
||
SetupDiGetDeviceRegistryProperty(DeviceInfoTable, &DevInfoData, SPDRP_HARDWAREID,
|
||
&dwRegType, NULL, 0, &dwRegSize);
|
||
// Allocate a buffer for the hardware ID.
|
||
// PropertyValueBuffer=(BYTE*)malloc(dwRegSize);
|
||
PropertyValueBuffer = new char[dwRegSize];
|
||
if (PropertyValueBuffer == NULL) // if null,error,couldn't allocate enough memory
|
||
{ // Can't really recover from this situation,just exit instead.
|
||
// ShowMessage("Allocation PropertyValueBuffer impossible");
|
||
SetupDiDestroyDeviceInfoList(
|
||
DeviceInfoTable); // Clean up the old structure we no longer need.
|
||
return false;
|
||
}
|
||
// Retrieve the hardware IDs for the current device we are looking at. PropertyValueBuffer
|
||
// gets filled with a
|
||
// REG_MULTI_SZ (array of null terminated strings). To find a device,we only care about the
|
||
// very first string in the
|
||
// buffer,which will be the "device ID". The device ID is a string which contains the VID
|
||
// and PID,in the example
|
||
// format "Vid_04d8&Pid_003f".
|
||
SetupDiGetDeviceRegistryProperty(DeviceInfoTable, &DevInfoData, SPDRP_HARDWAREID,
|
||
&dwRegType, PropertyValueBuffer, dwRegSize, NULL);
|
||
// Now check if the first string in the hardware ID matches the device ID of my USB device.
|
||
// ListBox1->Items->Add((char*)PropertyValueBuffer);
|
||
DeviceIDFromRegistry = StrPas((char *)PropertyValueBuffer);
|
||
// free(PropertyValueBuffer); //No longer need the PropertyValueBuffer,free the memory to
|
||
// prevent potential memory leaks
|
||
delete PropertyValueBuffer; // No longer need the PropertyValueBuffer,free the memory to
|
||
// prevent potential memory leaks
|
||
// Convert both strings to lower case. This makes the code more robust/portable accross OS
|
||
// Versions
|
||
DeviceIDFromRegistry = DeviceIDFromRegistry.LowerCase();
|
||
DeviceIDToFind = DeviceIDToFind.LowerCase();
|
||
// Now check if the hardware ID we are looking at contains the correct VID/PID
|
||
MatchFound = (DeviceIDFromRegistry.AnsiPos(DeviceIDToFind) > 0);
|
||
if (MatchFound == true)
|
||
{
|
||
// Device must have been found. Open read and write handles. In order to do this,we
|
||
// will need the actual device path first.
|
||
// We can get the path by calling SetupDiGetDeviceInterfaceDetail(),however,we have to
|
||
// call this function twice: The first
|
||
// time to get the size of the required structure/buffer to hold the detailed interface
|
||
// data,then a second time to actually
|
||
// get the structure (after we have allocated enough memory for the structure.)
|
||
DetailedInterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||
// First call populates "StructureSize" with the correct value
|
||
SetupDiGetDeviceInterfaceDetail(DeviceInfoTable, InterfaceDataStructure, NULL, NULL,
|
||
&StructureSize, NULL);
|
||
DetailedInterfaceDataStructure =
|
||
(PSP_DEVICE_INTERFACE_DETAIL_DATA)(malloc(StructureSize)); // Allocate enough memory
|
||
if (DetailedInterfaceDataStructure ==
|
||
NULL) // if null,error,couldn't allocate enough memory
|
||
{ // Can't really recover from this situation,just exit instead.
|
||
SetupDiDestroyDeviceInfoList(
|
||
DeviceInfoTable); // Clean up the old structure we no longer need.
|
||
return false;
|
||
}
|
||
DetailedInterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||
// Now call SetupDiGetDeviceInterfaceDetail() a second time to receive the goods.
|
||
SetupDiGetDeviceInterfaceDetail(DeviceInfoTable, InterfaceDataStructure,
|
||
DetailedInterfaceDataStructure, StructureSize, NULL,
|
||
NULL);
|
||
// We now have the proper device path,and we can finally open read and write handles to
|
||
// the device.
|
||
// We store the handles in the global variables "WriteHandle" and "ReadHandle",which we
|
||
// will use later to actually communicate.
|
||
WriteHandle = CreateFile((DetailedInterfaceDataStructure->DevicePath), GENERIC_WRITE,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
|
||
ErrorStatus = GetLastError();
|
||
// if (ErrorStatus==ERROR_SUCCESS)
|
||
// ToggleLedBtn->Enabled=true;//Make button no longer greyed out
|
||
ReadHandle = CreateFile((DetailedInterfaceDataStructure->DevicePath), GENERIC_READ,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
|
||
ErrorStatus = GetLastError();
|
||
if (ErrorStatus == ERROR_SUCCESS)
|
||
{
|
||
// GetPushbuttonState->Enabled=true;//Make button no longer greyed out
|
||
// StateLabel->Enabled=true;//Make label no longer greyed out
|
||
}
|
||
SetupDiDestroyDeviceInfoList(
|
||
DeviceInfoTable); // Clean up the old structure we no longer need.
|
||
iRepeats = 0; // nowe szanse na pod³¹czenie
|
||
return true;
|
||
}
|
||
InterfaceIndex++;
|
||
// Keep looping until we either find a device with matching VID and PID,or until we run out
|
||
// of items.
|
||
} // end of while(true)
|
||
// ShowMessage("Sortie");
|
||
return false;
|
||
}
|
||
//---------------------------------------------------------------------------
|
||
bool __fastcall TPoKeys55::Write(unsigned char c, unsigned char b3, unsigned char b4,
|
||
unsigned char b5)
|
||
{
|
||
DWORD BytesWritten = 0;
|
||
OutputBuffer[0] = 0; // The first byte is the "Report ID" and does not get transmitted over the
|
||
// USB bus. Always set=0.
|
||
OutputBuffer[1] = 0xBB; // 0xBB - bajt rozpoznawczy dla PoKeys55
|
||
OutputBuffer[2] = iLastCommand = c; // operacja: 0x31: blokowy odczyt wejϾ
|
||
OutputBuffer[3] = b3; // np. numer pinu (o 1 mniej ni¿ numer na p³ytce)
|
||
OutputBuffer[4] = b4;
|
||
OutputBuffer[5] = b5;
|
||
OutputBuffer[6] = 0;
|
||
OutputBuffer[7] = ++cRequest; // numer ¿¹dania
|
||
OutputBuffer[8] = 0;
|
||
for (int i = 0; i < 8; ++i)
|
||
OutputBuffer[8] += OutputBuffer[i]; // czy sumowaæ te¿ od 9 do 64?
|
||
// The basic Windows I/O functions WriteFile() and ReadFile() can be used to read and write to
|
||
// HID class USB devices
|
||
//(once we have the read and write handles to the device, which are obtained with CreateFile()).
|
||
// The following call to WriteFile() sends 64 bytes of data to the USB device.
|
||
WriteFile(WriteHandle, &OutputBuffer, 65, &BytesWritten,
|
||
0); // Blocking function, unless an "overlapped" structure is used
|
||
return (BytesWritten == 65);
|
||
// Read(); //odczyt trzeba zrobiæ inaczej - w tym miejscu bêdzie za szybko i nic siê nie odczyta
|
||
}
|
||
//---------------------------------------------------------------------------
|
||
|
||
bool __fastcall TPoKeys55::Read()
|
||
{
|
||
DWORD BytesRead = 0;
|
||
InputBuffer[0] = 0; // The first byte is the "Report ID" and does not get transmitted over the
|
||
// USB bus. Always set=0.
|
||
// Now get the response packet from the firmware.
|
||
// The following call to ReadFIle() retrieves 64 bytes of data from the USB device.
|
||
ReadFile(ReadHandle, &InputBuffer, 65, &BytesRead,
|
||
0); // Blocking function,unless an "overlapped" structure is used
|
||
// InputPacketBuffer[0] is the report ID, which we don't care about.
|
||
// InputPacketBuffer[1] is an echo back of the command.
|
||
// InputPacketBuffer[2] contains the I/O port pin value for the pushbutton.
|
||
return (BytesRead == 65) ? InputBuffer[7] == cRequest : false;
|
||
}
|
||
//---------------------------------------------------------------------------
|
||
bool __fastcall TPoKeys55::ReadLoop(int i)
|
||
{ // próbuje odczytaæ (i) razy
|
||
do
|
||
{
|
||
if (Read())
|
||
return true;
|
||
Sleep(1); // trochê poczekaæ, a¿ odpowie
|
||
} while (--i);
|
||
return false;
|
||
}
|
||
//---------------------------------------------------------------------------
|
||
AnsiString __fastcall TPoKeys55::Version()
|
||
{ // zwraca numer wersji, funkcja nieoptymalna czasowo (czeka na odpowiedŸ)
|
||
if (!WriteHandle)
|
||
return "";
|
||
Write(0x00, 0); // 0x00 - Read serial number, version
|
||
if (ReadLoop(10))
|
||
{ // 3: serial MSB; 4: serial LSB; 5: software version (v(1+[4-7]).([0-3])); 6: revision number
|
||
AnsiString s = "PoKeys55 #" + AnsiString((InputBuffer[3] << 8) + InputBuffer[4]);
|
||
s += " v" + AnsiString(1 + (InputBuffer[5] >> 4)) + "." + AnsiString(InputBuffer[5] & 15) +
|
||
"." + AnsiString(InputBuffer[6]);
|
||
/* //Ra: pozyskiwanie daty mo¿na sobie darowaæ, jest poniek¹d bez sensu
|
||
Write(0x04,0); //0x04 - Read build date: drugi argument zmieniaæ od 0 do 2, uzyskuj¹c
|
||
kolejno po 4 znaki
|
||
if (ReadLoop(5))
|
||
{//2: 0x04; 3-6: char 1-4, 5-8, 9-11; (83-65-112-32-32-49-32-50-48-49-49-0=="Sep 1 2011")
|
||
s+=" ("+AnsiString((char*)InputBuffer+3,4);
|
||
Write(0x04,1); //0x04 - Read build date: drugi argument zmieniaæ od 0 do 2, uzyskuj¹c
|
||
kolejno po 4 znaki
|
||
if (ReadLoop(5))
|
||
{s+=AnsiString((char*)InputBuffer+3,4);
|
||
Write(0x04,2); //0x04 - Read build date: drugi argument zmieniaæ od 0 do 2, uzyskuj¹c
|
||
kolejno po 4 znaki
|
||
if (ReadLoop(5))
|
||
s+=AnsiString((char*)InputBuffer+3,3);
|
||
}
|
||
s+=")";
|
||
}
|
||
*/
|
||
return s;
|
||
}
|
||
return "";
|
||
};
|
||
|
||
bool __fastcall TPoKeys55::PWM(int x, float y)
|
||
{ // ustawienie wskazanego PWM (@12Mhz: 12000=1ms=1000Hz)
|
||
// iPWM[7]=1024; //1024==85333.3333333333ns=11718.75Hz
|
||
iPWM[x] = int(0.5f + 0x0FFF * y) & 0x0FFF; // 0x0FFF=4095
|
||
return true;
|
||
}
|
||
|
||
bool __fastcall TPoKeys55::Update(bool pause)
|
||
{ // funkcja powinna byæ wywo³ywana regularnie, np. raz w ka¿dej ramce ekranowej
|
||
if (pause)
|
||
{ // specjalna procedura, jeœli utracone po³¹czenie spowodowa³o pauzê
|
||
iLastCommand = 0; // po³¹czenie zosta³o na nowo otwarte
|
||
// iFaza=0; //jeden b³¹d i podtrzymanie pauzy jest kontynuowane
|
||
}
|
||
switch (iFaza)
|
||
{
|
||
case 0: // uaktualnienie PWM raz na jakiœ czas
|
||
OutputBuffer[9] = 0x3F; // maska u¿ytych PWM
|
||
*((int *)(OutputBuffer + 10)) = iPWM[0]; // PWM1 (pin 22)
|
||
*((int *)(OutputBuffer + 14)) = iPWM[1]; // PWM2 (pin 21)
|
||
*((int *)(OutputBuffer + 18)) = iPWM[2]; // PWM3 (pin 20)
|
||
*((int *)(OutputBuffer + 22)) = iPWM[3]; // PWM4 (pin 19)
|
||
*((int *)(OutputBuffer + 26)) = iPWM[4]; // PWM5 (pin 18)
|
||
*((int *)(OutputBuffer + 30)) = iPWM[5]; // PWM6 (pin 17)
|
||
*((int *)(OutputBuffer + 34)) = iPWM[7]; // PWM period
|
||
if (Write(0xCB, 1)) // wys³anie ustawieñ (1-ustaw, 0-odczyt)
|
||
iRepeats = 0; // informacja, ¿e posz³o dobrze
|
||
++iFaza; // ta faza zosta³a zakoñczona
|
||
// iRepeats=0;
|
||
break;
|
||
case 1: // odczyt wejϾ analogowych - komenda i przetwarzanie
|
||
if (iLastCommand != 0x3A) // asynchroniczne ustawienie kontrolki mo¿e namieszaæ
|
||
Write(0x3A, 0); // 0x3A - Analog inputs reading – all analog inputs in one command
|
||
else if (Read())
|
||
{ // jest odebrana ramka i zgodnoœæ numeru ¿¹dania
|
||
fAnalog[0] = ((InputBuffer[21] << 8) + InputBuffer[22]) / 4095.0f; // pin 47
|
||
fAnalog[1] = ((InputBuffer[19] << 8) + InputBuffer[20]) / 4095.0f; // pin 46
|
||
fAnalog[2] = ((InputBuffer[17] << 8) + InputBuffer[18]) / 4095.0f; // pin 45
|
||
fAnalog[3] = ((InputBuffer[15] << 8) + InputBuffer[16]) / 4095.0f; // pin 44
|
||
fAnalog[4] = ((InputBuffer[13] << 8) + InputBuffer[14]) / 4095.0f; // pin 43
|
||
fAnalog[5] = ((InputBuffer[11] << 8) + InputBuffer[12]) / 4095.0f; // pin 42
|
||
fAnalog[6] = ((InputBuffer[9] << 8) + InputBuffer[10]) / 4095.0f; // pin 41
|
||
++iFaza; // skoro odczytano, mo¿na przejœæ do kolejnej fazy
|
||
iRepeats = 0; // zerowanie licznika prób
|
||
}
|
||
else
|
||
++iRepeats; // licznik nieudanych prób
|
||
break;
|
||
case 2: // odczyt wejϾ cyfrowych - komenda i przetwarzanie
|
||
if (iLastCommand != 0x31) // asynchroniczne ustawienie kontrolki mo¿e namieszaæ
|
||
Write(0x31, 0); // 0x31: blokowy odczyt wejϾ
|
||
else if (Read())
|
||
{ // jest odebrana ramka i zgodnoœæ numeru ¿¹dania
|
||
iInputs[0] = *((int *)(InputBuffer + 3)); // odczyt 32 bitów
|
||
iFaza = 3; // skoro odczytano, mo¿na kolejny cykl
|
||
iRepeats = 0; // zerowanie licznika prób
|
||
}
|
||
else
|
||
++iRepeats; // licznik nieudanych prób
|
||
break;
|
||
case 3: // ustawienie wyjœæ analogowych, 0..4095 mapowaæ na 0..65520 (<<4)
|
||
if (Write(0x41, 43 - 1, (iPWM[6] >> 4), (iPWM[6] << 4))) // wys³anie ustawieñ
|
||
iRepeats = 0; // informacja, ¿e posz³o dobrze
|
||
iFaza = 0; //++iFaza; //ta faza zosta³a zakoñczona
|
||
// powinno jeszcze przyjϾ potwierdzenie o kodzie 0x41
|
||
break;
|
||
default:
|
||
iFaza = 0; // na wypadek, gdyby zb³¹dzi³o po jakichœ zmianach w kodzie
|
||
// iRepeats=0;
|
||
}
|
||
if (!iRepeats)
|
||
bNoError = true; // jest OK
|
||
else if (iRepeats >= 10) // youBy 2014-07: przy 5 powtórzeniach sieje mi pauz¹ po 2 razy na
|
||
// sekundê, a przy 10 jest ok
|
||
{ // przekroczenie liczby prób wymusza kolejn¹ fazê
|
||
++iFaza;
|
||
iRepeats = 1; // w nowej fazie nowe szanse, ale nie od 0!
|
||
bNoError = false; // zg³osiæ b³¹d
|
||
}
|
||
return (bNoError); // true oznacza prawid³owe dzia³anie
|
||
// czy w przypadku b³êdu komunikacji z PoKeys w³¹czaæ pauzê?
|
||
// dopiero poprawne pod³¹czenie zeruje licznik prób
|
||
};
|