#include <windows.h>
#include <windowsx.h>
#include <shlwapi.h>
#include <commctrl.h>
#include "usbcalls.h"
#define nobreak
#define elemof(x) (sizeof(x)/sizeof(*(x)))
#define T(x) TEXT(x)
HINSTANCE ghInst;
HANDLE ghHID;
HWND ghMainWnd;
struct{
char id1;
char Hell;
char id2;
char Prog;
char id3;
char lht[100];
char id4;
char bootloadjmp;
}HidFeatures;
static WORD randseed;
static _declspec(naked) BYTE random() { _asm{
mov ah,8
mov ecx,dword ptr[randseed]
l1: mov al,ch
and al,0xB4
setpo al
shr al,1
rcl ecx,1
dec ah
jnz l1
xchg eax,ecx
mov [randseed],ax
ret
}}
static BYTE random(BYTE max) {
BYTE r;
do r=random(); while (r>max);
return r;
}
static BYTE random(BYTE min, BYTE max) {
return random(max-min)+min;
}
static BYTE random(BYTE min, BYTE max, const BYTE*na, BYTE nalen) {
BYTE r;
do r=random(min,max); while (memchr(na,r,nalen));
return r;
}
// Zuordnung einer LED zu einem Sternbild
static const BYTE Sternbild[100]={
0x00,0x00,0x01,0x00,0x0B,0x00,0x09,0x03,0x0A,0x03,
0x0E,0x0E,0x0D,0x03,0x01,0x0B,0x03,0x0A,0x0C,0x0C,
0x0D,0x00,0x00,0x08,0x09,0x03,0x00,0x0A,0x0E,0x0B,
0x01,0x03,0x0E,0x09,0x08,0x0A,0x0B,0x00,0x00,0x0A,
0x00,0x02,0x00,0x02,0x0D,0x01,0x0C,0x06,0x0C,0x0B,
0x02,0x08,0x09,0x03,0x01,0x0A,0x0C,0x06,0x0D,0x02,
0x0E,0x0D,0x08,0x01,0x04,0x02,0x06,0x06,0x06,0x01,
0x0D,0x09,0x04,0x07,0x05,0x04,0x05,0x06,0x05,0x00,
0x09,0x04,0x07,0x07,0x07,0x05,0x0F,0x05,0x0F,0x00,
0x04,0x00,0x04,0x07,0x0F,0x01,0x02,0x0F,0x0F,0x02};
/* Low-Nibble = Sternbild-Zuordnung, High-Nibble = relative Helligkeit?
0 = frei
1 = Orion
2 = großer Bär
3 = kleiner Bär
4 = großer Hund
5 = Himmels-W
6 = Schwan
7 = Kreuz des Südens
8 = Zwillinge
9 = Schlange
A = Löwe
B = Stier
C = Perseus
D = Pegasus
E = Zentaur
F = Skorpion
*/
static struct{
WORD phase;
BYTE bild;
BYTE bild0; // vorhergehendes Bild (zum Ausblenden bei kleinen Phasenwerten)
BYTE quasar[8];
BYTE super; // Stern mit Supernova, 100..255 = kein Stern
BYTE quasph[8];// Phasen (Frequenz gleich)
BYTE quasi; // Index für auszuwechselnden Quasar
BYTE disko; // Phase für Disko, Helligkeitssteller = Freuenz
BYTE flimmer[16]; // 16 Flimmer-Frequenzen
BYTE langsam[16]; // 16 langsame Frequenzen
BYTE wasser; // Richtung der Wellenfront (0..7);
BYTE automode; // Automatischer Themenwechsel
BYTE prog1; // Programm-Nummer, 0 = gleiche Helligkeit, 1 = Sternbilder zeigen usw.
BYTE teil1; // Anteil der Helligkeit bei der Mischung (1..4)
BYTE prog2; // Sekundär-Programm
BYTE teil2; // Anteil der Helligkeit bei der Mischung (0..3)
}s;
/* Prog prog1 teil1 prog2 teil2
0 0 4 - 0
1 0 3 1 1
2 0 2 1 2
3 0 1 1 3
4 1 4 - 0
...
28 7 4 - 0
29 7 3 0 1
30 7 2 0 2
31 + 4 // schaltet prog1 0..7, teil1=4, bei Programmwechsel Sekundärprogramm = 0?
*/
// Gleichmäßige Helligkeit
static BYTE ProgHell0(BYTE) {
return HidFeatures.Hell;
}
// Sternbilder zeigen
static BYTE ProgHell1(BYTE i) {
BYTE anteil=s.phase&255;
if (anteil>=64) anteil=64;
if ((Sternbild[i]^s.bild)&0x0F) { // nicht aktuelles Sternbild
if (anteil>=64 || (Sternbild[i]^s.bild0)&0x0F) return 0; // vorhergehendes? nein!
else anteil=63-anteil;
}
return (HidFeatures.Hell*anteil)>>6;
}
// Quasare
static BYTE ProgHell2(BYTE i) {
for (int j=0; j<8; j++) if (s.quasar[j]==i) break;
if (j==8) return 0; // kein Quasar
BYTE ph=BYTE(s.phase)+s.quasph[j];
BYTE anteil=ph&7; // 0..7
if (ph&8) anteil=8-anteil; // 8..1
return (HidFeatures.Hell*anteil)>>3;
}
// Supernova
// Problem: - Sollte auch gemischt mit maximaler Helligkeit leuchten
static BYTE ProgHell3(BYTE i) {
if (i!=s.super) return HidFeatures.Hell>>1; // halbe Grundhelligkeit
if (s.phase&0x200) return 31; // Maximum
BYTE anteil=BYTE(s.phase>>1);
return anteil>>3; // 0..31;
}
// Disko
static BYTE ProgHell4(BYTE) {
return ~s.disko>>3; // Fallende Intensität
}
// Flimmern
static BYTE ProgHell5(BYTE i) {
BYTE posrand=(i^Sternbild[i])&0x0F; // 16 Frequenzen, determiniert aber verstreut verteilt
BYTE anteil=s.flimmer[posrand];
if (anteil&0x80) anteil=-anteil; // 0..128
return (HidFeatures.Hell*anteil)>>7; // Zwei Flanken
}
// Langsam
static BYTE ProgHell6(BYTE i) {
BYTE posrand=(~i^Sternbild[i])&0x0F; // 16 Frequenzen, determiniert aber verstreut verteilt
BYTE anteil=s.langsam[posrand];
if (anteil&0x80) anteil=-anteil; // 0..128
return (HidFeatures.Hell*anteil)>>7; // Zwei Flanken
}
// Wasserfront (hell oder dunkel je nach Helligkeitssteller)
static BYTE ProgHell7(BYTE i) {
div_t d=div(i,10); // Mikrocontroller-Implementierung per Subtraktion!
signed char logpos;
switch (s.wasser&3) {
case 0: logpos=d.quot; break; // gerade (0..9)
case 1: logpos=d.rem; break;
case 2: logpos=d.quot+d.rem; break; // diagonal (0..18)
case 3: logpos=d.quot-d.rem; break; // andersherum diagonal (-9..9)
}
if (s.wasser&4) logpos=-logpos; // Gegenrichtung (-18..18 möglich)
signed char h=0;
if ((s.phase&255)<64) {
h=(s.phase&255)-32; // -32 .. 31
h+=logpos; // -50 .. 49
if (h>=0) h=~h; // Bei Null maximale Helligkeit, neg. Betrag bilden (-50..0)
h+=31; // -19 .. 31
if (h<0) h=0; // alles Negative wegschneiden
}
if (HidFeatures.Hell>=16) {
h=31-h;
if (h>HidFeatures.Hell) h=HidFeatures.Hell;
}else{
if (h<HidFeatures.Hell) h=HidFeatures.Hell;
}
return h;
}
static BYTE(*ProgHell[8])(BYTE)={
ProgHell0,
ProgHell1,
ProgHell2,
ProgHell3,
ProgHell4,
ProgHell5,
ProgHell6,
ProgHell7};
static void PeriodicAction() {
if (HidFeatures.Prog==31) { // variable Programme
s.automode=1;
}else{ // feste Programme und Anteile
s.automode=0;
s.prog1=HidFeatures.Prog>>2; // 0..7
s.teil2=HidFeatures.Prog&3; // 0..3
s.prog2=(s.prog1+1)&7; // 0..7
}
s.teil1=4-s.teil2; // 1..4
for (int i=0; i<100; i++) {
BYTE h=ProgHell[s.prog1](i)*s.teil1;
if (s.teil2) h+=ProgHell[s.prog2](i)*s.teil2;
h>>=2;
if ((HidFeatures.lht[i]^h)&0x1F) HidFeatures.lht[i]=h;
}
s.phase++; // 10 ms * 256 = 2,56 Sekunden pro Sternbild (vorerst)
if (!(s.phase&255)) {
s.bild0=s.bild;
s.bild=random(1,15,&s.bild0,1);
}
if (!(s.phase&63)) {
s.quasar[s.quasi]=random(0,99,s.quasar,9);
s.quasph[s.quasi]=random();
s.quasi=(s.quasi+1)&7;
}
if (!(s.phase&1023)) {
s.super=random(0,99,s.quasar,9); // nicht immer ist eine Supernova zu sehen?
if (s.automode) {
s.prog2=random(0,7,&s.teil1,2); // die "4" (Disko), die in s.teil1 steht, trickreich ausschließen!
}
}
s.disko+=16+HidFeatures.Hell; // Stellverhältnis 3:1
for (i=0; i<16; i++) s.flimmer[i]+=i+3;
if (!(s.phase&15)) for (i=0; i<16; i++) s.langsam[i]+=(i>>2)+1; // 4 Geschwindigkeiten
if (!(s.phase&255)) s.wasser=random(0,7,&s.wasser,1);
if (s.automode && (s.phase&1023)<64 && !(s.phase&15)) {
s.teil2=((s.phase>>4)+1)&3; // 1-2-3-0 (Intensität des neuen Motivs)
if (!s.teil2) s.prog1=s.prog2; // neues Motiv = erstes Motiv
}
}
HBRUSH brushes[32];
bool DrawNumber;
static void DrawLed(HDC dc, const RECT*R, int index) {
TCHAR buf[3];
int mx=((R->right+R->left)>>1)+1;
if (DrawNumber)
ExtTextOut(dc,mx,R->top,0,R,buf,wnsprintf(buf,elemof(buf),T("%d"),index),NULL);
index=HidFeatures.lht[index]&0x1F;
SelectBrush(dc,brushes[index]);
Ellipse(dc,R->left+1,R->top+12,R->right-1,R->bottom);
if (DrawNumber) {
COLORREF oc=SetTextColor(dc,index>=24?0x000000:0xFFFFFF);
ExtTextOut(dc,mx,R->top+15,0,R,buf,wnsprintf(buf,elemof(buf),T("%d"),index),NULL);
SetTextColor(dc,oc);
}
}
struct DIS:DRAWITEMSTRUCT {
void DrawLeds() const;
};
void DIS::DrawLeds() const {
if (DrawNumber) {
SetTextAlign(hDC,TA_TOP|TA_CENTER);
SetBkMode(hDC,TRANSPARENT);
}
// gleichmäßiges Gitter machen
SIZE delta;
delta.cx=(rcItem.right-rcItem.left)/10;
delta.cy=(rcItem.bottom-rcItem.top)/10;
// LEDs einzeln malen
int i,xx,yy;
RECT R;
for (i=yy=0,R.bottom=rcItem.bottom; yy<10; yy++,R.bottom=R.top) {
R.top=R.bottom-delta.cy;
for (xx=0,R.left=0; xx<10; xx++,i++,R.left=R.right) {
R.right=R.left+delta.cx;
if (!(HidFeatures.lht[i]&0x80)) {
DrawLed(hDC,&R,i);
HidFeatures.lht[i]^=0x80;
}
}
}
}
static void RedrawLeds(void) {
InvalidateRect(GetDlgItem(ghMainWnd,103),NULL,FALSE);
}
static LONG_PTR CALLBACK MainDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_INITDIALOG: {
ghMainWnd=Wnd;
do; while (!(randseed=(WORD)GetTickCount()));
HidFeatures.id1=1;
HidFeatures.id2=2;
HidFeatures.id3=3;
HidFeatures.id4=4;
ghHID=usbOpenDevice(0,0x16c0,NULL,1503,T("h#s Sternhimmel"));
brushes[0]=GetStockBrush(BLACK_BRUSH);
brushes[31]=GetStockBrush(WHITE_BRUSH);
for (int i=1; i<31; i++) {
// const BYTE gamma[30]={16,32,48,64,74,84,94,104,110,116,122,128,134,140,146,152,158,164,170,176,182,198,202,206,210,214,218,222,226,230,234,238,242,248,252};
BYTE v=(i<<3)|(i>>2);
brushes[i]=CreateSolidBrush(RGB(v,v,v));
}
SendDlgItemMessage(Wnd,101,TBM_SETRANGE,FALSE,MAKELONG(0,31));
SendDlgItemMessage(Wnd,102,TBM_SETRANGE,FALSE,MAKELONG(0,31));
HidFeatures.Hell=HidFeatures.Prog=31;
for (i=0; i<32; i++) s.flimmer[i]=random();
SetTimer(Wnd,1,10,NULL);
}return TRUE;
case WM_TIMER: {
PeriodicAction();
RedrawLeds();
}break;
case WM_ERASEBKGND: {
for (int i=0; i<100; i++) HidFeatures.lht[i]&=0x1F;
}break;
case WM_DRAWITEM: {
((DIS*)lParam)->DrawLeds();
}break;
case WM_VSCROLL: switch (LOWORD(wParam)) {
case TB_THUMBTRACK:
case TB_ENDTRACK: {
int i=31-(int)SendMessage((HWND)lParam,TBM_GETPOS,0,0);
switch (GetDlgCtrlID((HWND)lParam)) {
case 101: HidFeatures.Hell=i; break;
case 102: HidFeatures.Prog=i; break;
}
HidFeatures.lht[98]=HidFeatures.Hell;
HidFeatures.lht[99]=HidFeatures.Prog;
RedrawLeds();
}break;
}break;
case WM_COMMAND: switch (LOWORD(wParam)) {
case IDCANCEL: EndDialog(Wnd,wParam); break;
}break;
}
return FALSE;
}
void CALLBACK WinMainCRTStartup() {
ghInst=GetModuleHandle(NULL);
InitCommonControls();
ExitProcess((UINT)DialogBox(ghInst,MAKEINTRESOURCE(100),0,(DLGPROC)MainDlgProc));
}
/*
Programm-Ideen für Programm-Wähler
* Sternbilder zeigen (ca. 10 s)
* Quasare (ca. 1 Hz, bis zu 10 Sterne)
* Supernovas (1 Stern, ca. 30 s)
* Disko (alle Sterne synchron, Frequenz einstellbar)
* Flimmern (wie Disko, jedoch verschiedene Frequenzen)
* langsame Helligkeitänderung (wie Flimmern)
* "Wasserfront" (helle Welle), Wolken (dunkle Welle)
* alles (außer Disko)
*/
Vorgefundene Kodierung: UTF-8 | 0
|