/* Ansteuerung einer Kette von RGB-Leuchtdioden mit WS2812-Protokoll via HID-USB
* h#s 10/15
*/
#include <windows.h>
#include <windowsx.h>
#include <shlwapi.h>
#include <commctrl.h>
#include <setupapi.h>
#include <devguid.h>
extern "C"{
#include <hidsdi.h>
#include <hidpi.h>
}
#pragma intrinsic(memcpy)
static struct TConfig{
POINTS WinPos; // Fensterposition des Dialogs
BYTE HidNo; // Nummer des HID-USB, 0 = erstes
BYTE Idx; // LED-Nummer
COLORREF custcolors[16];
}Config;
HINSTANCE hInstance;
#define elemof(x) (sizeof(x)/sizeof((x)[0]))
#define T(x) TEXT(x)
/******************************
* Hardwarenahe Kommunikation *
******************************/
static SP_CLASSIMAGELIST_DATA ild;
static struct WS2812{
HANDLE hDev;
PHIDP_PREPARSED_DATA pd;
HIDP_CAPS hidcaps;
BYTE *report; // genügend großer Transferpuffer
void Enum(HWND hCombo);
void Open() {Enum(0);getfeature();}
void Close();
int getfeature();
int setfeature();
int setfeature(int);
COLORREF getRGB(int);
BYTE getRGB(int,int);
void setRGB(int,COLORREF);
void setRGB(int,int,BYTE);
int end() const;
void limit(BYTE&) const;
}ws2812;
void WS2812::Enum(HWND hCombo) {
COMBOBOXEXITEM cbei;
TCHAR buf[12];
int n;
if (hCombo) {
cbei.mask=CBEIF_TEXT|CBEIF_IMAGE|CBEIF_SELECTEDIMAGE;
cbei.pszText=buf;
cbei.iItem=0;
SetupDiGetClassImageIndex(&ild,(LPGUID)&GUID_DEVCLASS_HIDCLASS,&cbei.iImage);
cbei.iSelectedImage=cbei.iImage;
ComboBox_ResetContent(hCombo);
SetWindowText(hCombo,NULL);
}else{
n=Config.HidNo+1;
}
GUID hidGuid;
HidD_GetHidGuid(&hidGuid);
HDEVINFO devs=SetupDiGetClassDevs(&hidGuid,NULL,0,DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
if (devs!=INVALID_HANDLE_VALUE) {
SP_DEVICE_INTERFACE_DATA di;
for (DWORD i=0; di.cbSize=sizeof di,SetupDiEnumDeviceInterfaces(devs,0,&hidGuid,i,&di); i++) {
struct{
SP_DEVICE_INTERFACE_DETAIL_DATA d;
TCHAR space[MAX_PATH];
}d;
d.d.cbSize=sizeof d.d;
if (SetupDiGetDeviceInterfaceDetail(devs,&di,&d.d,sizeof d,NULL,NULL)) {
HANDLE h=CreateFile(d.d.DevicePath,GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
if (h!=INVALID_HANDLE_VALUE) {
PHIDP_PREPARSED_DATA pd;
if (HidD_GetPreparsedData(h,&pd)) {
HIDP_CAPS hidcaps;
if ((int)HidP_GetCaps(pd,&hidcaps)>=0
&& hidcaps.InputReportByteLength==0
&& hidcaps.OutputReportByteLength==5
&& hidcaps.FeatureReportByteLength>=8) {
if (hCombo) {
wsprintf(buf,T("HID%u"),cbei.iItem);
SendMessage(hCombo,CBEM_INSERTITEM,0,(LPARAM)&cbei);
if (cbei.iItem==Config.HidNo) ComboBox_SetCurSel(hCombo,cbei.iItem);
cbei.iItem++;
}else if (!--n) { // gewünschtes passendes PnPSA gefunden
hDev=h;
this->pd=pd;
this->hidcaps=hidcaps;
report=(BYTE*)LocalAlloc(LPTR,hidcaps.FeatureReportByteLength);
goto raus;
}
}
HidD_FreePreparsedData(pd);
}
CloseHandle(h);
}
}
}
raus:
SetupDiDestroyDeviceInfoList(devs);
}
}
int WS2812::getfeature() {
return HidD_GetFeature(hDev,report,hidcaps.FeatureReportByteLength);
}
int WS2812::setfeature() {
return HidD_SetFeature(hDev,report,hidcaps.FeatureReportByteLength);
}
int WS2812::setfeature(int idx) {
BYTE buf[5];
buf[0]=0;
memcpy(1+buf,1+report+idx*3,3);
buf[4]=idx;
return HidD_SetOutputReport(hDev,buf,5);
}
void WS2812::Close() {
if (report) {LocalFree(report); report=NULL;}
if (pd) {HidD_FreePreparsedData(pd); pd=NULL;}
if (hDev) {CloseHandle(hDev); hDev=0;}
}
COLORREF WS2812::getRGB(int i) {
return RGB(getRGB(i,0),getRGB(i,1),getRGB(i,2));
}
BYTE WS2812::getRGB(int i,int c) {
if (!(c&2)) c^=1; // Reihenfolge GRB
return report?report[1+i*3+c]:0;
}
void WS2812::setRGB(int i,COLORREF c) {
setRGB(i,0,GetRValue(c));
setRGB(i,1,GetGValue(c));
setRGB(i,2,GetBValue(c));
}
void WS2812::setRGB(int i,int c,BYTE b) {
if (!(c&2)) c^=1; // Reihenfolge GRB
if (report) report[1+i*3+c]=b;
}
int WS2812::end() const{
return hidcaps.FeatureReportByteLength/3-1;
}
void WS2812::limit(BYTE&b) const{
if (b>end()) b=end();
}
/************************
* Konfigurationsdialog *
************************/
// Lädt Konfigurationsdaten aus Registrierung
static void LoadConfig() {
HKEY key;
if (!RegOpenKeyEx(HKEY_CURRENT_USER,T("Software\\h#s\\WS2812"),
0,KEY_QUERY_VALUE,&key)) {
DWORD size=sizeof(Config);
RegQueryValueEx(key,T("Config"),NULL,NULL,(LPBYTE)&Config,&size);
RegCloseKey(key);
}
}
// Speichert Konfigurationsdaten in Registrierung
static void SaveConfig(HWND Wnd) {
HKEY key;
if (RegCreateKeyEx(HKEY_CURRENT_USER,T("Software\\h#s\\WS2812"),
0,NULL,REG_OPTION_NON_VOLATILE,KEY_SET_VALUE,NULL,&key,NULL)) return;
TCHAR s[128];
RegSetValue(key,NULL,REG_SZ,s,GetWindowText(Wnd,s,elemof(s))*sizeof(TCHAR));
RegSetValueEx(key,T("Config"),0,REG_BINARY,(LPBYTE)&Config,sizeof(Config));
RegCloseKey(key);
}
static INT_PTR CALLBACK SetupDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_INITDIALOG: {
ild.cbSize=sizeof(ild);
SetupDiGetClassImageList(&ild);
SendDlgItemMessage(Wnd,110,CBEM_SETIMAGELIST,0,(LPARAM)ild.ImageList);
SendMessage(Wnd,WM_TIMER,2,0);
}return TRUE;
case WM_COMMAND: switch (LOBYTE(wParam)) {
case IDOK:
Config.HidNo=(BYTE)SendDlgItemMessage(Wnd,110,CB_GETCURSEL,0,0);
case IDCANCEL: {
EndDialog(Wnd,wParam);
}break;
}break;
case WM_TIMER: {
KillTimer(Wnd,wParam);
ws2812.Enum(GetDlgItem(Wnd,110));
}break;
case WM_DEVICECHANGE: {
SetTimer(Wnd,1,1500,NULL);
}break;
case WM_DESTROY: {
SetupDiDestroyClassImageList(&ild);
}break;
}
return FALSE;
}
/***************
* Haupfenster *
***************/
static void RetrieveWinPos(HWND Wnd) {
WINDOWPLACEMENT wp;
wp.length=sizeof(wp);
GetWindowPlacement(Wnd,&wp);
Config.WinPos.x=(short)wp.rcNormalPosition.left;
Config.WinPos.y=(short)wp.rcNormalPosition.top;
}
static void setRGB(HWND Wnd, int i, BYTE b, BYTE mask=0x83) {
if (mask&1) // Slider
SendDlgItemMessage(Wnd,20+i,TBM_SETPOS,TRUE,255-b);
if (mask&2) // Edit + UpDown
SetDlgItemInt(Wnd,24+i,b,FALSE);
if (mask&0x80 && ws2812.report) { // Ausgabe
ws2812.setRGB(Config.Idx,i,b);
ws2812.setfeature(Config.Idx);
}
}
static void open(HWND Wnd) {
ws2812.Open();
SendDlgItemMessage(Wnd,15,UDM_SETRANGE32,0,ws2812.end());
ws2812.limit(Config.Idx);
SetDlgItemInt(Wnd,14,Config.Idx,FALSE);
}
INT_PTR CALLBACK MainDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam){
switch (Msg) {
case WM_INITDIALOG: {
LoadConfig();
HICON ico=LoadIcon(hInstance,MAKEINTRESOURCE(100));
SetClassLongPtr(Wnd,GCLP_HICON,(LONG_PTR)ico);
SetClassLongPtr(Wnd,GCLP_HICONSM,(LONG_PTR)ico);
SetWindowPos(Wnd,0,Config.WinPos.x,Config.WinPos.y,0,0,SWP_NOSIZE|SWP_NOZORDER);
STARTUPINFO si;
si.cb=sizeof(si);
GetStartupInfo(&si);
ShowWindow(Wnd,si.wShowWindow);
for (UINT i=0; i<3; i++) {
SendDlgItemMessage(Wnd,20+i,TBM_SETRANGE,FALSE,MAKELONG(0,255)); // Slider-Bereich
SendDlgItemMessage(Wnd,20+i,TBM_SETTICFREQ,15,0);
SendDlgItemMessage(Wnd,28+i,UDM_SETRANGE32,0,255);
}
open(Wnd);
}return TRUE;
case WM_VSCROLL: {
UINT id=GetWindowID((HWND)lParam);
if (id<16) break;
if (id<24) setRGB(Wnd,id&3,(BYTE)(255-SendMessage((HWND)lParam,TBM_GETPOS,0,0)),0x82);
}break;
case WM_NOTIFY: {
NMHDR*h=(NMHDR*)lParam;
if (h->idFrom>=16 && h->code==UDN_DELTAPOS) {
NMUPDOWN*ud=(NMUPDOWN*)h;
setRGB(Wnd,h->idFrom&3,ud->iPos+ud->iDelta,0x81);
}
}break;
case WM_COMMAND: switch (wParam) {
case MAKELONG(14,EN_UPDATE): {
Config.Idx=(BYTE)GetDlgItemInt(Wnd,14,NULL,FALSE);
ws2812.limit(Config.Idx);
for (UINT i=0; i<3; i++) setRGB(Wnd,i,ws2812.getRGB(Config.Idx,i),0x03);
}break;
case MAKELONG(24,EN_UPDATE):
case MAKELONG(25,EN_UPDATE):
case MAKELONG(26,EN_UPDATE): if (SendMessage((HWND)lParam,EM_GETMODIFY,0,0)) {
BYTE b=(BYTE)GetDlgItemInt(Wnd,LOBYTE(wParam),NULL,FALSE);
setRGB(Wnd,LOBYTE(wParam)&3,b,0x81);
SendMessage((HWND)lParam,EM_SETMODIFY,FALSE,0);
}break;
case 1: {
CHOOSECOLOR cs;
cs.lStructSize=sizeof cs;
cs.hwndOwner=Wnd;
cs.lpCustColors=Config.custcolors;
cs.Flags=CC_ANYCOLOR|CC_FULLOPEN;
if (ws2812.report) {
cs.rgbResult=ws2812.getRGB(Config.Idx);
cs.Flags|=CC_RGBINIT;
}
if (ChooseColor(&cs) && ws2812.report) {
ws2812.setRGB(Config.Idx,cs.rgbResult);
SendMessage(Wnd,WM_COMMAND,MAKELONG(14,EN_UPDATE),0);
ws2812.setfeature(Config.Idx);
}
}break;
case 11: if (DialogBox(hInstance,MAKEINTRESOURCE(101),Wnd,SetupDlgProc)==IDOK) {
ws2812.Close();
open(Wnd);
}break;
case 2: {
ws2812.Close();
RetrieveWinPos(Wnd);
SaveConfig(Wnd);
EndDialog(Wnd,wParam);
}break;
}break;
case WM_ENDSESSION: if (wParam) {
RetrieveWinPos(Wnd);
SaveConfig(Wnd);
}break;
}
return FALSE;
}
INT_PTR CALLBACK WinMainCRTStartup() {
hInstance=GetModuleHandle(NULL);
InitCommonControls();
ExitProcess((UINT)DialogBox(hInstance,MAKEINTRESOURCE(100),0,MainDlgProc));
}
| Detected encoding: ANSI (CP1252) | 4
|
|
|