/* Evaluation software for Analog Devices AD9834 DDS generator
* (Direct Digital Synthesis) for nearly random frequencies.
* Replaces the silly Analog Devices software.
*
*
* Siehe http://www.tu-chemnitz.de/~heha/hs_freeware/mein_msvc.htm
*
* Änderungen (~=Bugfix, +=neues Feature, -=Feature entfernt, *=Änderung)
100701 Erstausgabe
~100803 Kleinkram, Icon (leider ohne Wirkung)
+100814 Sweep-Funktion
-140505 COM1..256 per SetupDi-Funktionen; Bruch mit Windows 95.
Einstellungen nach HKEY_CURRENT_USER
UpDownControl-Verhalten mit Cursor (d.h. Schrittweite einstellbar)
+140507 Echte UpDownControls (sehen unter Windows 7 besser aus)
Umstellung float->double (sonst lästige Rundungsfehler)
*
* TODO
+ DDE-Interface
*/
#define WIN32_LEAN_AND_MEAN
#include <windowsx.h>
#include <shlwapi.h>
#include <shellapi.h>
#include <setupapi.h>
#include <devguid.h>
//#include "../gnul/gnul.h"
#include "AD9834.h"
#include <stdio.h>
#include <tchar.h>
void _fastcall InitStruct(void*p, size_t len) {
__stosd(p,0,len>>2);
*(size_t*)p=len;
}
TCHAR MBoxTitle[64];
int vMBox(HWND Wnd, UINT id, UINT style, va_list arglist) {
TCHAR buf1[256],buf2[1024];
LoadString(0,id,buf1,elemof(buf1));
wvnsprintf(buf2,elemof(buf2),buf1,arglist);
return MessageBox(Wnd,buf2,MBoxTitle,style);
}
int _cdecl MBox(HWND Wnd, UINT id, UINT style,...) {
return vMBox(Wnd,id,style,(va_list)(&style+1));
}
static void EnableDlgItem(HWND w, UINT id, BOOL ena) {
EnableWindow(GetDlgItem(w,id),ena);
}
HINSTANCE hInstance; // Quelle der Ressourcen
int nInstance; // Laufende Instanz eines AD9834-Fensters, 0..31
HWND hMainWnd;
static DWORD mInstances; // Beim Start bereits vorgefundene Instanzen
static char sDecimal[4]; // Set, 1. Zeichen je nach Windows-Einstellung
EXTERN_C void _declspec(naked) _cdecl _fltused() {} // Linker ruhig stellen
// All constant Unicode strings are listed here for conversion by GNUL
const LPCTSTR S[]={
T("Software\\h#s"),
T("AD9834"),
T("Config%d"),
T("%Xh (%u, LPT%u)"), // [3]
T("PortName"), // [4] Registry-Schlüssel
T("AD9834.lng"), // [5] LangFileName
T("\\\\.\\COM%u"), // [6] zum Öffnen
T("%03Xh"),
T("UsbPrn%u"), // [8]
NULL};
#define LangFileName S[5]
#define sizeofTCHAR sizeof(TCHAR)
TConfig Config;
// Lädt Konfiguration. Bei Erfolg sollte Config.iomode != 0 sein.
static void LoadSettings(void) {
HKEY hKey0,hKey1;
TCHAR n[16];
DWORD l;
// Schlüssel-Zweig stufenweise öffnen
if (!RegOpenKeyEx(HKEY_CURRENT_USER,S[0],0,KEY_QUERY_VALUE,&hKey0)) {
if (!RegOpenKeyEx(hKey0,S[1],0,KEY_QUERY_VALUE,&hKey1)) {
wnsprintf(n,elemof(n),S[2],nInstance);
l=sizeof(Config);
RegQueryValueEx(hKey1,n,NULL,NULL,(PBYTE)&Config,&l);
RegCloseKey(hKey1);
}
RegCloseKey(hKey0);
}
}
// Speichert Konfiguration. Die Fensterposition sollte bereits eingesetzt sein.
static void SaveSettings(void) {
HKEY hKey0, hKey1;
DWORD dispo;
TCHAR n[16];
TCHAR v[64];
int l;
// Schlüssel-Zweig stufenweise erzeugen
if (!RegCreateKeyEx(HKEY_CURRENT_USER,S[0],0,
NULL,REG_OPTION_NON_VOLATILE,KEY_SET_VALUE,NULL,&hKey0,&dispo)) {
if (dispo==REG_CREATED_NEW_KEY) {
l=LoadString(0,0,v,elemof(v)); // Beschreibung: Hersteller
RegSetValueEx(hKey0,NULL,0,REG_SZ,(PBYTE)v,(l+1)*sizeofTCHAR);
}
if (!RegCreateKeyEx(hKey0,S[1],0,
NULL,REG_OPTION_NON_VOLATILE,KEY_SET_VALUE,NULL,&hKey1,&dispo)) {
if (dispo==REG_CREATED_NEW_KEY) {
l=LoadString(hInstance,1,v,elemof(v)); // Beschreibung: Produkt
RegSetValueEx(hKey1,NULL,0,REG_SZ,(PBYTE)v,(l+1)*sizeofTCHAR);
}
wnsprintf(n,elemof(n),S[2],nInstance);
RegSetValueEx(hKey1,n,0,REG_BINARY,(PBYTE)&Config,sizeof(Config));
RegCloseKey(hKey1);
}
RegCloseKey(hKey0);
}
}
/*******************
* Einstell-Dialog *
*******************/
static const WORD DefAddrs[]={0x378,0x278,0x3BC};
INT_PTR CALLBACK SetupDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_INITDIALOG: {
// Parallele Schnittstellen listen
int i;
bool set=false;
HWND w=GetDlgItem(Wnd,15);
TCHAR buf[20];
for (i=0; i<elemof(DefAddrs); i++) {
wnsprintf(buf,elemof(buf),S[3],DefAddrs[i],DefAddrs[i],i+1);
ComboBox_AddString(w,buf);
if (Config.paradr==DefAddrs[i]) {
ComboBox_SetCurSel(w,i);
set=true;
}
}
if (!set) {
wnsprintf(buf,elemof(buf),S[7],Config.paradr);
SetWindowText(w,buf);
}
CheckRadioButton(Wnd,10,13,9+Config.iomode);
SendMessage(Wnd,WM_COMMAND,10,0);
SendMessage(Wnd,WM_TIMER,1,0);
}return TRUE;
case WM_DEVICECHANGE: SetTimer(Wnd,1,2500,NULL); break;
case WM_TIMER: if (wParam==1) {
HWND w;
UINT i,j;
DWORD NeedBytes=0;
HANDLE devs;
KillTimer(Wnd,wParam);
// serielle Schnittstellen (neu) listen (bei jedem WM_DEVICECHANGE bspw. für USB)
w=GetDlgItem(Wnd,14);
ComboBox_ResetContent(w);
devs=SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS,NULL,0,DIGCF_PRESENT);
if (devs!=INVALID_HANDLE_VALUE) {
SP_DEVINFO_DATA devInfo;
devInfo.cbSize=sizeof devInfo;
for (i=0; SetupDiEnumDeviceInfo(devs,i,&devInfo); i++) {
HKEY hKey;
char s[16]; // Kann ANSI oder Unicode sein!
DWORD slen=sizeof s;
*s=0;
if ((hKey=SetupDiOpenDevRegKey(devs,&devInfo,DICS_FLAG_GLOBAL,0,DIREG_DEV,KEY_READ))
==INVALID_HANDLE_VALUE) continue;
RegQueryValueEx(hKey,S[4],NULL,NULL,(LPBYTE)s,&slen);
RegCloseKey(hKey);
if (*s=='C') {
int idx=ComboBox_AddString(w,(PTSTR)s);
int num=StrToInt((PTSTR)s+3)-1; // nullbasiert
ComboBox_SetItemData(w,idx,num);
if (num==Config.comnum) ComboBox_SetCurSel(w,idx);
}
}
SetupDiDestroyDeviceInfoList(devs);
}
// USB-Drucker-Adapter listen
w=GetDlgItem(Wnd,16);
ComboBox_ResetContent(w);
j=GetNumUsbPorts();
for (i=0; i<j; i++) {
TCHAR UsbName[16];
wnsprintf(UsbName,elemof(UsbName),S[8],i);
ComboBox_AddString(w,UsbName);
if (Config.u2pnum==i) ComboBox_SetCurSel(w,i);
}
}break;
case WM_COMMAND: switch (LOWORD(wParam)) {
case 10:
case 11:
case 12: {
int i;
for (i=10; i<=12; i++) EnableDlgItem(Wnd,i+4,IsDlgButtonChecked(Wnd,i));
}break;
case 9: {
TCHAR fn[MAX_PATH]; // Textdatei
LoadString(hInstance,3,fn,elemof(fn));
ShellExecute(Wnd,NULL,fn,NULL,NULL,SW_SHOW);
}break;
case IDOK: {
int i;
if (IsDlgButtonChecked(Wnd,10)) {
HWND w=GetDlgItem(Wnd,14);
i=ComboBox_GetCurSel(w);
if (i<0) {
MBox(Wnd,17,MB_OK);
SetFocus(w);
break; // nicht OK!
}
Config.comnum=(BYTE)ComboBox_GetItemData(w,i);
Config.iomode=1;
}else if (IsDlgButtonChecked(Wnd,11)) {
HWND w=GetDlgItem(Wnd,15);
char s[16];
DWORD a;
GetWindowTextA(w,s,sizeof(s));
a=strtoul(s,NULL,16);
if (a<0x100 || a>=0x10000) {
MBox(Wnd,18,MB_OK);
SetFocus(w);
break; // nicht OK!
}
Config.paradr=(WORD)a;
Config.iomode=2;
}else if (IsDlgButtonChecked(Wnd,12)) {
HWND w=GetDlgItem(Wnd,16);
i=ComboBox_GetCurSel(w);
if (i<0) {
MBox(Wnd,17,MB_OK);
SetFocus(w);
break; // nicht OK!
}
Config.u2pnum=i;
Config.iomode=3;
}else{
MBox(Wnd,16,MB_OK);
SetFocus(GetDlgItem(Wnd,10));
break;
}
if (hMutex) WaitForSingleObject(hMutex,INFINITE);
SpiClose();
i=SpiOpen(Wnd);
ReleaseMutex(hMutex);
if (!i) break;
}nobreak;
case IDCANCEL: {
EndDialog(Wnd,LOWORD(wParam));
}break;
}break;
}
return FALSE;
}
static void SpiSendFreq(DWORD f, WORD m) {
SpiSend((WORD)f&0x3FFF|m);
f>>=14;
SpiSend((WORD)f|m);
}
// (Serielle) Daten auf AD9834-Chip ausgeben
// m = Aktualisierungs-Maske
// Bit 0 = Control-Register
// Bit 1 = volle Frequenz-Aktualisierung
// Bit 2,3 = Frequenz-Register 0 und 1
// Bit 4,5 = Phasen-Register 0 und 1
// Bit 6 = Zustand der RESET-Leitung
// Zum Setzen der vollen Frequenz _muss_ vorher das Control-Register geschrieben werden,
// sonst "springt" die Frequenz zwischen Low- und High-Word.
// (Murks, siehe Datenblatt Seite 18/32)
/*** Aufruf von beiden Threads, per Mutex abgesichert ***/
void UpdateChip(BYTE m) {
static WORD ctl; // Zuletzt geschriebenes Control-Register
if (hMutex) WaitForSingleObject(hMutex,INFINITE);
if (m&3 || m&12 && ctl&0x2000) {
ctl=Config.control;
if (m&2) ctl|=0x2000; // Mindestens eine volle Frequenz wird gesetzt
SpiSend(ctl);
}
if (m&4) {
if (m&2) SpiSendFreq(Config.freq[0],0x4000);
else SpiSend((WORD)Config.freq[0]&0x3FFF|0x4000);
}
if (m&8) {
if (m&2) SpiSendFreq(Config.freq[1],0x8000);
else SpiSend((WORD)Config.freq[1]&0x3FFF|0x8000);
}
if (m&1<<4) SpiSend(Config.phase[0]|0xC000);
if (m&1<<5) SpiSend(Config.phase[1]|0xE000);
if (m&1<<6) SetResetPin();
SpiFlush();
if (hMutex) ReleaseMutex(hMutex);
}
// Hier: als Festkommawert ausgeben
static int FloatToString(double v, int nk, char*buf, int buflen) {
int i=_snprintf(buf,buflen,"%.*f",nk,v);
char*p=StrChrA(buf,'.');
if (p) *p=*sDecimal;
return i;
}
int StringToFloat(char*s, double*v) {
char*p,*q;
if (!s || !*s) return 0;
p=StrPBrkA(s,sDecimal);
if (p) *p='.';
*v=strtod(s,&q);
if (*q) return 0; // OK nur wenn String-Ende erreicht
return (p?p:q)-s; // Index des Dezimaltrenners
}
/****************
* Hauptfenster *
****************/
// Aktuelle Verbindungsart (links oben) anzeigen
static void ShowConn(void) {
TCHAR s[32];
DWORD p;
PCTSTR t=S[5+Config.iomode];
switch (Config.iomode) {
case 1: p=Config.comnum+1; t+=4; break;
case 2: p=Config.paradr; break;
case 3: p=Config.u2pnum; break;
default: return;
}
wnsprintf(s,elemof(s),t,p);
SetDlgItemText(hMainWnd,126,s);
}
// Zwei sich ausschließende Radiobuttons schalten
static void CheckRadio(HWND w, int controlbitnr) {
UINT id=128+(controlbitnr<<1);
CheckRadioButton(w,id,id+1,id+((Config.control>>controlbitnr)&1));
}
static double ToHz(DWORD freq) {
return Config.mclk/(1<<28)*freq;
}
// Liefert Skalierungswert des Einheitenpräfix' (nur "k" und "M")
double E10(int e) {
double ret=1;
if (e>0 && e<=32) do ret*=10; while(--e);
return ret;
}
static double ScaleHz(DWORD freq) {
return ToHz(freq)/E10(Config.unitpre); // Hz (unverändert)
}
static double ScaleGrd(WORD pha) {
return 360.0/(1<<12)*pha; // Grad
}
// Edit-Text setzen, dabei die Auswirkungen von EN_CHANGE
// (nämlich den Timer) zunichte machen und Markierung (Kursorposition) rechtsbündig behalten
static void ChangeEdit(HWND w, UINT id, const char*s) {
HWND ed=GetDlgItem(w,id);
int a,e,grow;
SendMessage(ed,EM_GETSEL,(WPARAM)&a,(LPARAM)&e);
grow=lstrlen(s)-GetWindowTextLength(ed);
if (a||!e) a+=grow; // Markierung darf nach links wachsen / von links schrumpfen (Wenn Anfang=0 bleibt's 0)
e+=grow; // Ende stets rechtsbündig
SetWindowTextA(ed,s);
SendMessage(ed,EM_SETSEL,a,e);
KillTimer(w,id);
}
void FloatToEdit(double v, int nk, HWND w, UINT id) {
char s[32];
FloatToString(v,nk,s,sizeof(s));
ChangeEdit(w,id,s);
}
static void BitsToEdit(DWORD v, int radix, HWND w, UINT id) {
char s[33];
if (radix==16) _snprintf(s,sizeof(s),"%X",v);
else _itoa(v,s,radix);
ChangeEdit(w,id,s);
}
// Aktualisierung der Dialogelemente, NICHT der Hardware
void UpdateValues(DWORD m) {
HWND w=hMainWnd;
// Bits
if (m&1<<1) CheckRadio(w,1); // MODE (Analogausgang)
if (m&1<<3) { // DIV2, DIGN/PIB, OPBITEN (Digitalausgang)
static const BYTE cases[]={0,0,0,0,2,1,3,3};
CheckRadioButton(w,48,51,48+cases[Config.control>>3&7]);
EnableDlgItem(w,131,!(Config.control&(1<<5|1<<6))); // Dreieck nicht auswählbar machen (reservierte Kombination)
}
if (m&1<<6) { // SLEEP12
BOOL ena=~Config.control&1<<6;
CheckRadio(w,6);
EnableDlgItem(w,45,ena);
EnableDlgItem(w,130,ena);
EnableDlgItem(w,131,!(Config.control&(1<<5|1<<6)));
}
if (m&1<<7) CheckRadio(w,7); // SLEEP1
if (m&1<<8) CheckRadio(w,8); // RESET
if (m&1<<9) { // PIN/SW
static const BYTE list[]={42,150,151,43,148,149,140,141,142,143,144,145,44};
int i,e=elemof(list);
BOOL ena=~Config.control&1<<9;
if (Config.iomode>=2) e-=3; // RESET kann weiter geschaltet werden!
CheckRadio(w,9);
for (i=0; i<e; i++) EnableDlgItem(w,list[i],ena);
}
if (m&1<<10) CheckRadio(w,10);
if (m&1<<11) CheckRadio(w,11);
if (m&1<<15) BitsToEdit(Config.control,2,w,15);
// Frequenzregister
if (m&1<<16) FloatToEdit(ScaleHz(Config.freq[0]),3,w,16);
if (m&1<<17) BitsToEdit(Config.freq[0],Config.radix,w,17);
if (m&1<<18) FloatToEdit(ScaleHz(Config.freq[1]),3,w,18);
if (m&1<<19) BitsToEdit(Config.freq[1],Config.radix,w,19);
// Phasenregister
if (m&1<<20) FloatToEdit(ScaleGrd(Config.phase[0]),2,w,20);
if (m&1<<21) BitsToEdit(Config.phase[0],Config.radix,w,21);
if (m&1<<22) FloatToEdit(ScaleGrd(Config.phase[1]),2,w,22);
if (m&1<<23) BitsToEdit(Config.phase[1],Config.radix,w,23);
//Speisefrequenz
if (m&1<<24) FloatToEdit(Config.mclk/E10(6),4,w,24);
// Schalter
if (m&1<<30) CheckRadioButton(w,32,38,32+Config.unitpre);
if (m&1<<31) CheckRadioButton(w,80,96,80+Config.radix);
}
static void HandleBitButton(BYTE id) {
BYTE bitno=(id-128)>>1;
if ((id^Config.control>>bitno)&1) { // wenn das Bit kippt ...
DWORD mask=1<<bitno;
Config.control^=mask;
UpdateValues(mask|1<<15);
UpdateChip(bitno==8 ? 0x41 : 1); // ggf. mit RESET-Steuerung
}
}
// aus Frequenz (in Hz) den Programmierwert (28 bit) berechnen
// Typ <double> nur für Sweep-Funktion (das Benutzerinterface benutzt durchweg <float>)
DWORD FromHz(double f) {
DWORD d=(DWORD)(f*(1<<28)/Config.mclk+0.5);
if (d>=1<<28) d=(1<<28)-1; // kann beim Runden passieren
return d;
}
// Aufruf wenn Editfenster geändert (nach 500 ms, dir==0)
// oder beim Betätigen einer Scrollfunktion (sofort, dir!=0)
// Zulässige IDs: 15..24
static bool HandleEditChange(BYTE id, int dir) {
DWORD d;
int i=id>>1&1; // Index Frequenz- oder Phasenregister
char s[32];
int a,e,pot;
GetDlgItemTextA(hMainWnd,id,s,elemof(s));
if (!*s) return false;
SendDlgItemMessage(hMainWnd,id,EM_GETSEL,(WPARAM)&a,(LPARAM)&e);
if (id&1) { // ungerade IDs sind Integer-Eingaben
int radix=2; // stets binär für Control-Bits
char*p;
DWORD dv;
if (id>=16) radix=Config.radix;
dv=d=strtoul(s,&p,radix);
if (*p) return false;
pot=lstrlen(s)-e; // Anzahl Zeichen hinter Selektions-Ende
if (pot) do dir*=radix; while (--pot);
d+=dir;
switch (id) {
case 15: { // Control-Bits
if (d>=1<<12) return false; // obere Bits (B28 und HLB) müssen hier 0 sein
if (Config.control!=d) {
BYTE m=1;
if ((Config.control^d)&1<<8) m=0x41; // wenn sich das RESET-Bit ändert
Config.control=(WORD)d;
UpdateValues(0x7FFF); // (einfach) alle Radiobuttons aktualisieren
UpdateChip(m);
}
}break;
case 17:
case 19: { // Frequenz-Bits
if (d>=1L<<28) {
if (dir) d=(long)d<0?0:(1L<<28)-1; // begrenzen
if (d==dv) return false; // unverändert: Piep!
}
if (Config.freq[i]!=d) {
BYTE m=4<<i;
if ((Config.freq[i]^d)>>14) m|=2; // High-Teil ändert sich?
Config.freq[i]=d;
UpdateValues(i?1L<<18:1L<<16); // Frequenzangabe aktualisieren
UpdateChip(m);
}
}break;
case 21:
case 23: { // Phasen-Bits
if (d>=1<<12) {
if (dir) d&=(1<<12)-1; // Wrapping ausführen
else return false; // nur 12 Bits erlaubt
}
if (Config.phase[i]!=d) {
Config.phase[i]=(WORD)d;
UpdateValues(i?1L<<22:1L<<20); // Phasenangabe aktualisieren
UpdateChip(16<<i);
}
}break;
}
if (dir) BitsToEdit(d,radix,hMainWnd,id);
}else{ // gerade IDs sind Gleitkomma-Eingaben
double f,fv,fs=1,add=dir;
int nk=3,dp=StringToFloat(s,&f);
if (!dp) return false;
if (a || e!=lstrlen(s)) { // Bei Vollmarkierung bei Einerschritten bleiben
pot=dp-e;
if (pot<0) add/=E10(-1-pot); // Dezimalpunkt als solchen ignorieren
else add*=E10(pot);
}
fv=f; // Wert vorher
f+=add;
switch (id) {
case 16:
case 18: { // Frequenz-Angabe
if (f<0) {
if (dir) f=0; // begrenzen
if (f==fv) return false; // unverändert: Piep!
}
fs=E10(Config.unitpre);
f*=fs;
fv*=fs;
if (f>=Config.mclk) {
if (dir) f=Config.mclk-1; // 1 Hz drunter
if (f==fv) return false; // unverändert: Piep!
}
d=FromHz(f);
if (Config.freq[i]!=d) {
BYTE m=4<<i;
if ((Config.freq[i]^d)>>14) m|=2; // High-Teil ändert sich?
Config.freq[i]=d;
UpdateValues(i?1L<<19:1L<<17); // Frequenz-Bits aktualisieren
UpdateChip(m);
}
}break;
case 20:
case 22: { // Phasenwinkel-Angabe
if (f>+1E5) return false; // die folgenden Schleifen sinnvoll eingrenzen!
if (f<-1E5) return false;
while (f>=360) f-=360;
while (f<0) f+=360;
d=(DWORD)(f*(1<<12)/360+0.5);
d&=(1<<12)-1; // Überlauf kann (nur) durch Runden passieren
if (Config.phase[i]!=d) {
Config.phase[i]=(WORD)d;
UpdateValues(i?1L<<23:1L<<21); // Phasen-Bits aktualisieren
UpdateChip(16<<i);
}
nk=2;
}break;
case 24: { // Speisefrequenz-Angabe
if (f<0) {
if (dir) f=0; // begrenzen
if (f==fv) return false; // unverändert: Piep!
}
fs=E10(6);
f*=fs; fv*=fs; // in Hz umrechnen und speichern
if (f>100E6) { // nicht mit mehr als 100 MHz (über)takten
if (dir) f=100E6; // begrenzen
if (f==fv) return false; // unverändert: Piep!
}
if (Config.mclk!=f) {
Config.mclk=(float)f;
UpdateValues(0x50000L); // beide Frequenzen neu anzeigen (kein Chip-Update)
}
nk=4;
}break;
}
if (dir) FloatToEdit(f/fs,nk,hMainWnd,id);
}
return true;
}
static void HandleScroll(HWND Wnd, int dir) {
BYTE id=(BYTE)GetWindowID(Wnd);
int sw=10;
if (id&1 && Config.radix!=10) { // nicht-dezimale Zahlen: hexadezimale Schrittweite!
sw=16;
if (dir==10) dir=sw;
if (dir==-10) dir=-sw;
}
if (GetKeyState(VK_CONTROL)<0) dir*=sw; // Schrittweite bei Strg-Taste potenzieren
if (!HandleEditChange(id,dir))
MessageBeep(MB_ICONEXCLAMATION);
}
static WNDPROC DefEditProc;
static LRESULT CALLBACK UpDownEditProc(HWND Wnd,UINT Msg,WPARAM wParam,LPARAM lParam) {
switch (Msg) {
case EM_SETSEL: {
if (!wParam && (long)lParam==-1) return 0; // kommt nach dem Klicken aufs UpDownControl vorbei: abwürgen!
}break;
case WM_VSCROLL: switch (LOWORD(wParam)) {
case SB_LINEUP: HandleScroll(Wnd,1); break;
case SB_LINEDOWN: HandleScroll(Wnd,-1); break;
case SB_PAGEUP: HandleScroll(Wnd,10); break;
case SB_PAGEDOWN: HandleScroll(Wnd,-10); break;
}break;
case WM_KEYDOWN: switch (wParam) {
case VK_UP: HandleScroll(Wnd,1); return 0; // Taste verschlucken
case VK_DOWN: HandleScroll(Wnd,-1); return 0;
case VK_PRIOR: HandleScroll(Wnd,10); return 0;
case VK_NEXT: HandleScroll(Wnd,-10); return 0;
}break;
}
return CallWindowProc(DefEditProc,Wnd,Msg,wParam,lParam);
}
BOOL HandleUpDownNotify(HWND Wnd, LPARAM lParam) {
NMHDR *nm=(NMHDR*)lParam;
switch (nm->code) {
case UDN_DELTAPOS: {
NMUPDOWN *ud=(NMUPDOWN*)nm;
HWND hEdit=(HWND)SendMessage(nm->hwndFrom,UDM_GETBUDDY,0,0);
int n=abs(ud->iDelta);
while (n) {
int wp=ud->iDelta<0?SB_LINEDOWN:SB_LINEUP;
if (n>=10) {wp|=SB_PAGEUP; n-=10;} else --n;
SendMessage(hEdit,WM_VSCROLL,wp,0);
}
return SetDlgMsgResult(Wnd,WM_NOTIFY,TRUE);
}
}
return FALSE;
}
static bool TitleExpanded;
static void ExpandTitle(void) {
if (!TitleExpanded) {
TCHAR s[64];
int l=GetWindowText(hMainWnd,s,elemof(s));
wnsprintf(s+l,elemof(s)-l,T(" #%d"),nInstance);
SetWindowText(hMainWnd,s);
TitleExpanded=true;
}
}
static INT_PTR CALLBACK MainDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_INITDIALOG: {
int i;
hMainWnd=Wnd;
if (Config.iomode) SetWindowPos(Wnd,0,Config.winpos.x,Config.winpos.y,0,0,SWP_NOSIZE|SWP_NOZORDER);
for (i=16; i<=24; i++) {
HWND hEdit=GetDlgItem(Wnd,i);
DefEditProc=SubclassWindow(hEdit,UpDownEditProc);
CreateUpDownControl(WS_VISIBLE|WS_CHILD|UDS_ALIGNRIGHT|UDS_HOTTRACK,0,0,0,0,Wnd,-1,hInstance,hEdit,100,-100,0);
}
SendMessage(Wnd,WM_WININICHANGE,0,0);
UpdateValues((UINT)-1); // alles setzen
if (nInstance || mInstances) ExpandTitle();
SetTimer(Wnd,1,200,NULL); // Initialisierung fortsetzen
}return TRUE;
case WM_WININICHANGE: {
GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SDECIMAL,sDecimal,elemof(sDecimal));
sDecimal[1]='.';
sDecimal[2]=',';
sDecimal[3]=0;
if (lParam) UpdateValues(0x1550000L); // alles neu anzeigen, was Dezimaltrenner enthält
}break;
case WM_IOERROR: {
if (MBox(Wnd,10,MB_YESNO|MB_ICONEXCLAMATION)==IDYES) {
SpiClose();
if (SpiOpen(Wnd)) break;
}
SendMessage(Wnd,WM_COMMAND,127,0); // Hardwaredialog zeigen
}break;
case WM_GETINST: {
SetWindowLong(Wnd,DWL_MSGRESULT,nInstance); // diese Nachricht liefert die Instanz-Nummer
ExpandTitle(); // Wenn jemand fragt, ggf. "#0" dazusetzen (damit man's sieht)
}return TRUE;
case WM_COMMAND: {
if (LOBYTE(wParam)>=128) HandleBitButton(LOBYTE(wParam));
else switch (LOBYTE(wParam)) {
case 15:
case 16:
case 17:
case 18:
case 19:
case 20:
case 21:
case 22:
case 23:
case 24: { // Editfenster
if (HIWORD(wParam)==EN_CHANGE) SetTimer(Wnd,LOBYTE(wParam),500,NULL);
}break;
case 32+0:
case 32+3:
case 32+6: { // Einheiten-Auswahl
Config.unitpre=LOBYTE(wParam)-32;
UpdateValues(0x50000L); // Frequenzen neu anzeigen
}break;
case 80+2:
case 80+10:
case 80+16: { // Radix-Auswahl
Config.radix=LOBYTE(wParam)-80;
UpdateValues(0xAA0000L); // FSELx und PSELx neu anzeigen
}break;
case 48:
case 49:
case 50:
case 51: {
static const BYTE cases[]={0,0x28,0x20,0x30};
WORD c=Config.control&~0x38|cases[wParam-48]; // hier: HIBYTE(wParam)=0
if (Config.control!=c) {
Config.control=c;
UpdateValues(1L<<3|1L<<15);
UpdateChip(1);
}
}break;
case 102: {
if (hSweep) SetFocus(hSweep);
else hSweep=CreateDialog(hInstance,MAKEINTRESOURCE(3),Wnd,SweepDlgProc);
}break;
case 127: {
if (DialogBox(hInstance,MAKEINTRESOURCE(2),Wnd,SetupDlgProc)==IDOK) { // ruft selber SpiClose() und SpiOpen()
UpdateChip((BYTE)-1);
}
ShowConn(); // auch bei IDCANCEL zeigen (jaja, unsauber!)
}break;
}
}break;
case WM_NOTIFY: {
if (HandleUpDownNotify(Wnd,lParam)) return TRUE;
}break;
case WM_TIMER: {
KillTimer(Wnd,wParam); // alles _einmalige_ Timer!
switch (LOBYTE(wParam)) {
case 1: {
if (!Config.iomode) {
if (!Config.mclk) Config.mclk=50000000L; // beim ersten Start 50 MHz Oszillatorfrequenz annehmen
if (!Config.freq[0]) Config.freq[0]=(1L<<28)/50; // beim ersten Start 1 MHz ausgeben lassen
UpdateValues(0x01030000);
SendMessage(Wnd,WM_COMMAND,127,0);
}else{
ShowConn();
if (SpiOpen(Wnd)) UpdateChip((BYTE)-1);
}
if (Config.swflag&0x80) {
SendMessage(Wnd,WM_COMMAND,102,0); // Sweep-Fenster wieder öffnen
if (Config.swflag&0x40) {
PostMessage(hSweep,WM_COMMAND,IDOK,0); // Sweep starten
}
}
Config.swflag&=~0xC0;
}break;
default: if (!HandleEditChange(LOBYTE(wParam),0)) MessageBeep(MB_ICONEXCLAMATION);
}
}break;
case WM_CLOSE: {
if (!IsIconic(Wnd)) { // Unsauber: Bei minimiertem Fenster wird keine (restaurierte) Position gespeichert
RECT R;
GetWindowRect(Wnd,&R);
Config.winpos.x=(short)R.left;
Config.winpos.y=(short)R.top;
}
if (hSweep) Config.swflag|=0x80; // Sweep-Dialog war sichtbar
if (hMutex) Config.swflag|=0x40; // Sweep lief noch
EndDialog(Wnd,0);
}break;
case WM_DESTROY: {
SpiClose();
}break;
}
return FALSE;
}
// Alle (max. 32 möglichen) AD9834-Fenster finden
static BOOL CALLBACK EnumWindowsProc(HWND Wnd,LPARAM lParam) {
TCHAR cn[32];
GetClassName(Wnd,cn,elemof(cn));
if (!lstrcmpi(cn,T("AD9834"))) {
int i=SendMessage(Wnd,WM_GETINST,0,0);
mInstances|=1<<i;
}
return TRUE;
}
// Gewünschte Instanz herausfinden
// Diese bleibt dann für die gesamte Prozesslebensdauer konstant.
static bool FindInstance(void) {
int i=0;
LPTSTR Args=PathGetArgs(GetCommandLine());
EnumWindows(EnumWindowsProc,0);
if (!_BitScanForward(&nInstance,~mInstances)) { // erstes freies Bit
MBox(0,9,MB_ICONSTOP|MB_OK);
return false;
}
while (*Args==' ') Args++;
if (*Args) {
i--;
if (*Args=='#') i=StrToInt(Args+1);
if ((unsigned)i>=32) {
if (MBox(0,8,MB_ICONEXCLAMATION|MB_YESNO,Args,nInstance)!=IDYES) return false;
}else if (mInstances&1<<i) {
if (MBox(0,7,MB_ICONQUESTION|MB_YESNO,i,nInstance)!=IDYES) return false;
}else nInstance=i;
}
return true;
}
int WinMainCRTStartup() {
HINSTANCE inst;
WNDCLASSEX wc;
hInstance=inst=GetModuleHandle(NULL);
// GnulInit(inst,NULL,S);
InitCommonControls();
InitStruct(&wc,sizeof(wc));
wc.style=CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
wc.lpfnWndProc=DefDlgProc;
wc.cbWndExtra=DLGWINDOWEXTRA;
wc.hInstance=hInstance;
wc.hCursor=LoadCursor(0,IDC_ARROW);
wc.hIcon=LoadIcon(hInstance,MAKEINTRESOURCE(1));
wc.hIconSm=LoadImage(hInstance,MAKEINTRESOURCE(1),IMAGE_ICON,16,16,0);
wc.lpszClassName=T("AD9834");
RegisterClassEx(&wc);
inst=LoadLibrary(LangFileName);
if (inst) hInstance=inst;
LoadString(hInstance,1,MBoxTitle,elemof(MBoxTitle));
if (!FindInstance()) return IDCANCEL;
// Vorgaben laden
LoadSettings();
if (Config.radix<2) Config.radix=10;
DialogBox(hInstance,MAKEINTRESOURCE(1),0,MainDlgProc);
SaveSettings();
return IDOK;
}
Detected encoding: UTF-8 | 0
|