Files
maszyna/Console/PoKeys55.cpp
2015-04-03 22:10:22 +08:00

366 lines
18 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//---------------------------------------------------------------------------
#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 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 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 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 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 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 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 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 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
};