/* Pulse generator for IGBT test equipment, in conjunction with
* an Agilent arbitrary waveform generator HP33120A
* Source tabsize is 8 of course!
* Matthias Olescher
080107 BASIC program rewritten as C program (without runtime library)
-080903 small fixes, conditional help button, HTML help, less size
-130404 Bound to msvcrt-light.lib, much easier to handle
Unlimited & fast COM port enumeration via SetupDI functions
*/
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <windowsx.h> // useful macros
#include <shlwapi.h>
#include <commctrl.h>
#include <uxtheme.h> // extra question-mark button
#include <tmschema.h> // even more XP stuff
#include <htmlhelp.h>
#include <setupapi.h>
#include <cfgmgr32.h>
#include <ntddser.h>
//#define MAXCOMSEARCH 32
#define HELPBUTTON // enable fourth caption bar button
#define elemof(x) (sizeof(x)/sizeof((x)[0]))
#define T(x) TEXT(x)
#define nobreak
typedef enum {false,true} bool;
static HINSTANCE ghInstance;
static TCHAR MBoxTitle[64];
static TCHAR sDecimal[2];
static TCHAR gIniFileName[MAX_PATH];
static const TCHAR gHelpFileName[]=T("PulseGen.chm");
static HWND ghMainWnd;
static LPCTSTR gDlgCtlAtom; // for colored dialog controls (via SetProp)
static bool gHelpAvail;
static bool gDirty;
static struct _g {
int Pulses; // pulse count, 1..5
float Volt; // Voltage Ucc [V]
float Curr; // Current Ic [A]
float Indu; // Inductance [H]
float Toff; // off time (between pulses) [s]
float Ton[5]; // on times (gTon[0] will be calculated) [s]
int SerialNo; // Number of serial port, 1 = COM1 etc.
int Samples; // number of samples for waveform
}g; // No initialization here to create an empty .data section
static const struct _g gDefaults={
2, // Pulses
1800, // Volt
1200, // Curr
3E-4F, // Indu
1E-5F, // Toff
{0,1E-5F,1E-5F,1E-5F,1E-5F}, // Ton[5]
1, // SerialNo
4096}; // Samples
// for colored dialog controls:
typedef struct {
// COLORREF ForeColor;
COLORREF BackColor;
HBRUSH BackBrush;
}DLGCTLPROP;
static DLGCTLPROP gEditErrorProp; // here: only one color modification
/**********************************
* general routines from WUTILS.H *
**********************************/
static void _fastcall InitStruct(LPVOID p, UINT len) {
*((LPUINT)p)=len; len/=sizeof(UINT); len--;
if (len) do *++((LPUINT)p)=0; while (--len);
}
// message box with string ressource identifier
static int vMBox(HWND Wnd, UINT id, UINT style, va_list arglist) {
TCHAR buf1[256],buf2[1024];
LoadString(ghInstance,id,buf1,elemof(buf1));
wvnsprintf(buf2,elemof(buf2),buf1,arglist);
return MessageBox(Wnd,buf2,MBoxTitle,style);
}
static int _cdecl MBox(HWND Wnd, UINT id, UINT style,...) {
return vMBox(Wnd,id,style,(va_list)(&style+1));
}
static void EnableDlgItem(HWND Wnd, UINT id, BOOL state) {
Wnd=GetDlgItem(Wnd,id);
if (Wnd) EnableWindow(Wnd,state);
}
static void ShowDlgItem(HWND Wnd, UINT id, int state) {
Wnd=GetDlgItem(Wnd,id);
if (Wnd) ShowWindow(Wnd,state);
}
// As SetDlgItemText() but keeping text selection
// and reset the "dirty" bit.
static void SetEditText(HWND Wnd, UINT id, LPTSTR s) {
DWORD SelStart,SelEnd;
if (id!=(UINT)-1) Wnd=GetDlgItem(Wnd,id);
SendMessage(Wnd,EM_GETSEL,(WPARAM)&SelStart,(LPARAM)&SelEnd);
SetWindowText(Wnd,s);
Edit_SetSel(Wnd,SelStart,SelEnd);
Edit_SetModify(Wnd,FALSE);
}
/***************
* contexthelp *
***************/
static void SendWmHelp(HWND Wnd, HWND Child, int MouseX, int MouseY) {
HELPINFO hi;
hi.cbSize=sizeof(hi); // fill-up HELPINFO
hi.iContextType=HELPINFO_WINDOW;
hi.iCtrlId=GetDlgCtrlID(Child);
hi.hItemHandle=Child;
hi.dwContextId=GetWindowContextHelpId(Child);
hi.MousePos.x=MouseX;
hi.MousePos.y=MouseY;
SendMessage(Wnd,WM_HELP,0,(LPARAM)&hi);
}
// Handler for WM_CONTEXTMENU: creates a pop-up menu
// and then sends WM_HELP
static bool HandleContextMenu(HWND Wnd, HWND Child, LPARAM lParam) {
HMENU m;
TCHAR s[64];
if (Child==Wnd) return false;
m=CreatePopupMenu();
LoadString(ghInstance,40/*contexthelp*/,s,elemof(s));
AppendMenu(m,0,101,s);
if ((DWORD)lParam==(DWORD)-1) { // per keyboard
RECT r;
GetWindowRect(Child,&r);
lParam=MAKELONG(r.left,r.bottom);
}
if (TrackPopupMenu(m,TPM_LEFTALIGN|TPM_TOPALIGN|TPM_RIGHTBUTTON|TPM_RETURNCMD,
GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam),0,Wnd,NULL)==101) {
SendWmHelp(Wnd,Child,GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));
}
DestroyMenu(m);
return true;
}
#ifdef HELPBUTTON
static void GetQuestionButtonRect(HWND Wnd, LPRECT r) {
NONCLIENTMETRICS ncm;
ncm.cbSize=sizeof(ncm);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS,sizeof(ncm),&ncm,0);
GetWindowRect(Wnd,r); // here: measure X extension
r->left=r->right-r->left
-GetSystemMetrics(SM_CYFRAME)
-ncm.iCaptionHeight*4
+1;
r->right=r->left+ncm.iCaptionHeight-2;
r->top=GetSystemMetrics(SM_CXFRAME)+1; // frame-type dependent
r->bottom=r->top+ncm.iCaptionHeight-4;
}
// volatile: The optimizer must not remove the <y> funtion parameter!
static BOOL _stdcall NcInQuestionRect(HWND Wnd, int x, volatile int y) {
RECT r;
GetWindowRect(Wnd,&r);
x-=r.left;
y-=r.top;
GetQuestionButtonRect(Wnd,&r);
return PtInRect(&r,*(POINT*)&x);
}
static BOOL _stdcall InQuestionRect(HWND Wnd, int x, int y) {
ClientToScreen(Wnd,(POINT*)&x);
return NcInQuestionRect(Wnd,x,y);
}
static void DrawQuestionButton(HWND Wnd, WPARAM wParam/*Region*/, bool Pushed, bool Hot) {
HDC dc=wParam==1
?GetWindowDC(Wnd)
:GetDCEx(Wnd,(HRGN)wParam,DCX_WINDOW|DCX_INTERSECTRGN);
RECT r;
HTHEME th=0;
HANDLE hLib;
GetQuestionButtonRect(Wnd,&r);
hLib=LoadLibraryA("uxtheme");
if (hLib) {
th=(HTHEME(_stdcall*)(HWND,LPCWSTR))GetProcAddress(hLib,"OpenThemeData")(Wnd,L"WINDOW");
}
if (th) { // Windows XP "luna" style
HRESULT (_stdcall*pDrawThemeBackground)(HTHEME,HDC,int,int,const RECT*,const RECT*);
(FARPROC)pDrawThemeBackground=GetProcAddress(hLib,"DrawThemeBackground");
pDrawThemeBackground(th,dc,WP_HELPBUTTON,Pushed?HBS_PUSHED:Hot?HBS_HOT:HBS_NORMAL,&r,NULL);
(HRESULT(_stdcall*)(HTHEME))GetProcAddress(hLib,"CloseThemeData")(th);
}else{
DrawFrameControl(dc,&r,DFC_CAPTION,Pushed?DFCS_CAPTIONHELP|DFCS_PUSHED:DFCS_CAPTIONHELP);
}
if (hLib) FreeLibrary(hLib);
ReleaseDC(Wnd,dc);
}
static void QuestionModalLoop(HWND Wnd) {
MSG Msg;
bool Pushed=true;
SetCapture(Wnd);
DrawQuestionButton(Wnd,1,true,false);
while (GetMessage(&Msg,0,0,0)) {
switch (Msg.message) {
case WM_MOUSEMOVE: {
bool InQuest=InQuestionRect(Wnd,
GET_X_LPARAM(Msg.lParam),GET_Y_LPARAM(Msg.lParam))!=0;
if (Pushed ^ InQuest) {
Pushed=InQuest;
DrawQuestionButton(Wnd,1,Pushed,false);
}
}break;
case WM_LBUTTONUP: {
if (Pushed) {
PostMessage(Wnd,WM_SYSCOMMAND,SC_CONTEXTHELP,Msg.lParam);
DrawQuestionButton(Wnd,1,false,false);
}
ReleaseCapture();
}return; // exit modal loop
}
DispatchMessage(&Msg);
}
}
// Filters various non-client messages to draw and handle question-mark button
// Only for dialogs! This routine is not suitable for normal windows.
// The dialog handler must propagate TRUE return code.
static BOOL HandleDlgNcMessagesForHelp(HWND Wnd,UINT Msg,WPARAM wParam,LPARAM lParam) {
switch (Msg) {
case WM_NCPAINT:
case WM_NCACTIVATE: {
SetWindowLong(Wnd,DWL_MSGRESULT,DefWindowProc(Wnd,Msg,wParam,lParam));
DrawQuestionButton(Wnd,1,false,false);
}return TRUE;
case WM_NCHITTEST: {
// 1. Suppress dragging window when hitting the question mark
// 2. deliver wParam==HTHELP at WM_NCLBUTTONDOWN
if (NcInQuestionRect(Wnd,GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)))
return SetDlgMsgResult(Wnd,WM_NCHITTEST,HTHELP);
}break;
case WM_NCMOUSEMOVE: { // hot tracking (luna style only)
static bool Hot;
if (Hot!=(wParam==HTHELP)) {
Hot=!Hot;
DrawQuestionButton(Wnd,1,false,Hot);
}
}break;
case WM_NCLBUTTONDOWN:
case WM_NCLBUTTONDBLCLK: {
if (wParam==HTHELP) {QuestionModalLoop(Wnd); return TRUE;} // swallow result
}break;
case 0xAE:
case 0xAF: return TRUE; // unknown XP messages must not reach DefWindowProc
}
return FALSE;
}
#endif//HELPBUTTON
/********************************
* float-point support routines *
********************************/
int _fltused; // against linker error
// Load MSVCRT.DLL and use only the sprintf and sscanf routines
extern int _declspec(dllimport) _cdecl _snwprintf(PWSTR,int,PCWSTR,...);
extern int _declspec(dllimport) _cdecl _snprintf(PSTR,int,PCSTR,...);
extern int _declspec(dllimport) _cdecl swscanf(PCWSTR,PCWSTR,...);
extern int _declspec(dllimport) _cdecl sscanf(PCSTR,PCSTR,...);
#ifdef UNICODE
# define _stprintf _snwprintf
# define _stscanf swscanf
#else
# define _stprintf _snprintf
# define _stscanf sscanf
#endif
// typecast to int: the float value sits on ST(0)
long _declspec(naked) _cdecl _ftol2_sse(void) {
_asm{ push eax
fistp dword ptr [esp]
fwait
pop eax
ret
}
}
static void Float2String(PTSTR s, int slen, float z) {
_stprintf(s,slen,T("%.6G"),z);
s=StrChr(s,T('.'));
if (s) *s=sDecimal[0];
}
static bool String2Float(PTSTR s, float*z) {
PTSTR p;
float f;
int n;
p=StrChr(s,sDecimal[0]);
if (!p) p=StrChr(s,T(','));
if (p) *p=T('.');
if (_stscanf(s,T("%g%n"),&f,&n)!=1) return false;
while (s[n]==' ') n++; // ignore trailing spaces
if (s[n]) return false; // following characters: wrong!
if (z) *z=f;
return true;
}
/**********************
* scrollable numbers *
**********************/
static void HandleScroll(HWND Wnd, int dir, int min, int max) {
HWND Parent=GetParent(Wnd);
UINT id=GetWindowLong(Wnd,GWL_ID);
BOOL ok;
int k;
int i=GetDlgItemInt(Parent,id,&ok,TRUE);
if (!ok) {
MessageBeep(MB_ICONEXCLAMATION);
return; // no number
}
k=i;
i+=dir;
if (i>max) i=max; // limit
if (i<min) i=min;
if (i==k) { // changed?
MessageBeep(MB_ICONEXCLAMATION);
return; // limit reached
}
SetDlgItemInt(Parent,id,i,TRUE); // let EN_CHANGE message do the rest
}
// Instead of using a buddy, scroll bars are equally useful, and requires much less coding!
// Unfortunately, scroll bars must be inserted manually into .RC file (when using MSVC)
// (The same inconenience applies to default edit texts: MSVC is bullshit for resources!)
static WNDPROC DefEditProc;
static LRESULT CALLBACK UpDownEditProc(HWND Wnd,UINT Msg,WPARAM wParam,LPARAM lParam) {
switch (Msg) {
case WM_VSCROLL: switch (LOWORD(wParam)) {
case SB_LINEUP:
case SB_PAGEUP: HandleScroll(Wnd,1,1,5); break;
case SB_LINEDOWN:
case SB_PAGEDOWN: HandleScroll(Wnd,-1,1,5); break;
}break;
case WM_KEYDOWN: switch (wParam) {
case VK_UP:
case VK_PRIOR: HandleScroll(Wnd,1,1,5); return 0; // eat keypress
case VK_DOWN:
case VK_NEXT: HandleScroll(Wnd,-1,1,5); return 0;
}break;
}
return CallWindowProc(DefEditProc,Wnd,Msg,wParam,lParam);
}
/*****************************
* Calculations and updating *
*****************************/
// Get the float value out of edit control.
// On erraneous input, set the window property.
static bool GetEditFloat(UINT id, float*val) {
TCHAR s[32];
bool ret;
HWND w=GetDlgItem(ghMainWnd,id);
if (!Edit_GetModify(w)) return false;
Edit_SetModify(w,FALSE);
GetWindowText(w,s,elemof(s));
ret=String2Float(s,val);
if (ret) RemoveProp(w,gDlgCtlAtom);
else SetProp(w,gDlgCtlAtom,(HANDLE)&gEditErrorProp);
InvalidateRect(w,NULL,TRUE);
return ret;
}
static BYTE *gWaveform; // pointer to dynamic allocated array
static float gFullTime; // time for entire waveform [s]
__forceinline int RoundInt(float f) {
int i;
_asm fld f
_asm fistp i
return i;
}
static void SetPulse(float from, float to) {
int a=RoundInt(from/gFullTime*g.Samples);
int e=RoundInt(to/gFullTime*g.Samples);
if (e>g.Samples) e=g.Samples;
if (e>a) memset(gWaveform+a,TRUE,e-a);
}
// As name implies, this routine recalculates the (binary) waveform
// out of timing parameters and desired sample count.
static void CalcWaveform() {
int i;
float t;
gFullTime=g.Ton[0];
if (gWaveform) LocalFree(gWaveform);
gWaveform=LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT,g.Samples);
for (i=1; i<g.Pulses; i++) {
gFullTime+=g.Toff+g.Ton[i];
}
for (t=0, i=0; i<g.Pulses; i++) {
SetPulse(t,t+g.Ton[i]);
t+=g.Toff+g.Ton[i];
}
InvalidateRect(GetDlgItem(ghMainWnd,12),NULL,TRUE); // draw curve
gDirty=true;
}
// Don't optimize parameter passing here!
static BOOL _stdcall Line(HDC dc, int x1, int y1, int x2, int y2) {
int a=y1+x2+y2;
return Polyline(dc,(POINT*)&x1,2)+a;
}
static void DrawCurve(PDRAWITEMSTRUCT dis) {
POINT *pt=LocalAlloc(LMEM_FIXED,(g.Samples+4)*sizeof(POINT));
POINT *ptp;
RECT r;
HBRUSH br;
HPEN pen;
int x,y;
CopyRect(&r,&dis->rcItem);
br=CreateSolidBrush(RGB(255,204,153));
SelectBrush(dis->hDC,br);
SelectPen(dis->hDC,GetStockPen(BLACK_PEN));
Rectangle(dis->hDC,r.left,r.top,r.right,r.bottom);
DeleteBrush(br);
InflateRect(&r,-15,-10); // area for curve
pen=CreatePen(PS_SOLID,0,RGB(128,128,128));
SelectPen(dis->hDC,pen);
Line(dis->hDC,dis->rcItem.left+1,r.top,dis->rcItem.right-1,r.top);
Line(dis->hDC,dis->rcItem.left+1,r.bottom,dis->rcItem.right-1,r.bottom);
Line(dis->hDC,r.left,dis->rcItem.top+1,r.left,dis->rcItem.bottom-1);
Line(dis->hDC,r.right,dis->rcItem.top+1,r.right,dis->rcItem.bottom-1);
DeletePen(pen);
pen=CreatePen(PS_DOT,0,RGB(0,0,0));
SelectPen(dis->hDC,pen);
y=(r.top+r.bottom)/2; // middle
Line(dis->hDC,dis->rcItem.left+1,y,dis->rcItem.right-1,y);
DeletePen(pen);
pen=CreatePen(PS_SOLID,2,RGB(0,0,0));
SelectPen(dis->hDC,pen);
if (!pt) return;
ptp=pt;
ptp->x=r.left-15; // show leading LOW level
ptp->y=r.bottom;
ptp++;
ptp->x=r.left;
ptp->y=r.bottom;
ptp++;
if (gWaveform) for (x=0; x<g.Samples; x++,ptp++) {
ptp->x=MulDiv(x+1,r.right-r.left-1,g.Samples+1)+r.left;
ptp->y=gWaveform[x]?r.top:r.bottom;
}
ptp->x=r.right; // show trailing LOW level
ptp->y=r.bottom;
ptp++;
ptp->x=r.right+15;
ptp->y=r.bottom;
Polyline(dis->hDC,pt,g.Samples+4);
DeletePen(pen);
LocalFree(pt);
}
static void PulsesChanged() {
int i;
HWND w=GetDlgItem(ghMainWnd,18); // don't show Toff in case of only one pulse
BOOL b=g.Pulses>1;
ShowWindow(GetPrevSibling(w),b); // Label (Toff)
ShowWindow(w,b); // Edit control
ShowWindow(GetNextSibling(w),b); // Unit (µs)
for (i=2; i<=5; i++) {
w=GetDlgItem(ghMainWnd,20+i-2);
b=i<=g.Pulses;
ShowWindow(GetPrevSibling(w),b); // Label (TonX)
ShowWindow(w,b); // Edit control
ShowWindow(GetNextSibling(w),b); // Unit (µs)
}
CalcWaveform();
}
static void CalcTon0(void) {
if (g.Volt) {
TCHAR buf[32];
g.Ton[0]=g.Indu*g.Curr/g.Volt;
Float2String(buf,elemof(buf),g.Ton[0]*1E6F); SetEditText(ghMainWnd,19,buf); // show micro-seconds
CalcWaveform();
}
}
static BOOL GetString(PTSTR key, PTSTR value, int vlen) {
return GetPrivateProfileString(T("PulseGen"),key,T(""),value,vlen,gIniFileName);
}
static BOOL PutString(PTSTR key, PTSTR value) {
return WritePrivateProfileString(T("PulseGen"),key,value,gIniFileName);
}
static void LoadConfig(void) {
TCHAR buf[32];
int i;
g.Pulses=GetPrivateProfileInt(T("PulseGen"),T("Pulses"),g.Pulses,gIniFileName);
SetDlgItemInt(ghMainWnd,14,g.Pulses,FALSE);
PulsesChanged();
GetString(T("Voltage(V)"),buf,elemof(buf)); String2Float(buf,&g.Volt);
GetString(T("Current(A)"),buf,elemof(buf)); String2Float(buf,&g.Curr);
GetString(T("Inductance(H)"),buf,elemof(buf));String2Float(buf,&g.Indu);
GetString(T("TimeOff(s)"),buf,elemof(buf)); String2Float(buf,&g.Toff);
Float2String(buf,elemof(buf),g.Volt); SetEditText(ghMainWnd,15,buf);
Float2String(buf,elemof(buf),g.Curr); SetEditText(ghMainWnd,16,buf);
Float2String(buf,elemof(buf),g.Indu*1E6F); SetEditText(ghMainWnd,17,buf); // show micro-henry
Float2String(buf,elemof(buf),g.Toff*1E6F); SetEditText(ghMainWnd,18,buf); // show micro-seconds
for (i=1; i<elemof(g.Ton); i++) {
TCHAR key[32];
wnsprintf(key,elemof(key),T("TimeOn%d(s)"),i+1);
GetString(key,buf,elemof(buf)); String2Float(buf,g.Ton+i);
Float2String(buf,elemof(buf),g.Ton[i]*1E6F); SetEditText(ghMainWnd,19+i,buf); // show micro-seconds
}
g.SerialNo=GetPrivateProfileInt(T("PulseGen"),T("COM"),g.SerialNo,gIniFileName);
g.Samples=GetPrivateProfileInt(T("PulseGen"),T("Samples"),g.Samples,gIniFileName);
SetDlgItemInt(ghMainWnd,32,g.Samples,FALSE);
CalcTon0(); // calls CalcWaveform() too
}
static void SaveConfig(void) {
TCHAR buf[32];
int i;
wnsprintf(buf,elemof(buf),T("%d"),g.Pulses); PutString(T("Pulses"),buf);
_stprintf(buf,elemof(buf),T("%.6G"),g.Volt); PutString(T("Voltage(V)"),buf);
_stprintf(buf,elemof(buf),T("%.6G"),g.Curr); PutString(T("Current(A)"),buf);
_stprintf(buf,elemof(buf),T("%.6G"),g.Indu); PutString(T("Inductance(H)"),buf);
_stprintf(buf,elemof(buf),T("%.6G"),g.Toff); PutString(T("TimeOff(s)"),buf);
for (i=1; i<elemof(g.Ton); i++) {
TCHAR key[32];
wnsprintf(key,elemof(key),T("TimeOn%d(s)"),i+1);
_stprintf(buf,elemof(buf),T("%.6G"),g.Ton[i]); PutString(key,buf);
}
wnsprintf(buf,elemof(buf),T("%d"),g.SerialNo); PutString(T("COM"),buf);
wnsprintf(buf,elemof(buf),T("%d"),g.Samples); PutString(T("Samples"),buf);
}
/************************
* serial communication *
************************/
static HANDLE ghCom;
static OVERLAPPED gOvl;
// This routine may process messages!
static BOOL OpenSerial(void) {
TCHAR ComName[12];
DCB dcb;
static const COMMTIMEOUTS to={0,0,100,2,1000};
wnsprintf(ComName,elemof(ComName),T("\\\\.\\COM%u"),g.SerialNo);
ghCom=CreateFile(ComName,GENERIC_READ|GENERIC_WRITE,
0,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,0);
if (ghCom==INVALID_HANDLE_VALUE) {
MBox(ghMainWnd,20,MB_OK,ComName);
return FALSE;
}
gOvl.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
InitStruct(&dcb,sizeof(dcb));
BuildCommDCB(T("9600,n,8,2"),&dcb);
dcb.fOutxDsrFlow=TRUE;
dcb.fDtrControl=DTR_CONTROL_HANDSHAKE; // full handshake (required by HP33120A for 9600 baud)
dcb.fRtsControl=RTS_CONTROL_ENABLE;
return SetCommState(ghCom,&dcb) && SetCommTimeouts(ghCom,(LPCOMMTIMEOUTS)&to);
}
static void CloseSerial(bool DoFlush) {
if (DoFlush) FlushFileBuffers(ghCom);
CloseHandle(ghCom);
CloseHandle(gOvl.hEvent);
}
// Waiting for gOvl.hEvent
static void WaitOvl(void) {
while (MsgWaitForMultipleObjects(1,&gOvl.hEvent,TRUE,1000,QS_ALLINPUT)==1) {
MSG Msg;
while (PeekMessage(&Msg,0,0,0,PM_REMOVE)) {
if (Msg.message==WM_QUIT) return; // break loop in WM_QUIT
if (Msg.message!=WM_COMMAND) { // don't process WM_COMMAND
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}
}
}
static int SendSerial(LPSTR data,int len) {
if (len==-1) len=lstrlenA(data);
WriteFile(ghCom,data,len,&len,&gOvl);
WaitOvl();
return len;
}
static int RecvSerial(LPSTR data, int len) {
ReadFile(ghCom,data,len,&len,&gOvl);
WaitOvl();
return len;
}
// prepare and transmit waveform in one chunk (easier to debug)
static void TransmitWaveform(void) {
char *buf;
PWORD p;
int i;
buf=LocalAlloc(LMEM_FIXED,30+g.Samples*2);
i=wnsprintfA(buf,30,"%u",g.Samples*2); // number of characters (buf unused)
i=wnsprintfA(buf,30,"DATA:DAC VOLATILE,#%u%u",i,(g.Samples+2)*2);
p=(PWORD)(buf+i);
*p++=0; // first sample must be 0 (otherwise 5V will be steady before sending first wavefoem)
for (i=0; i<g.Samples; i++) {
*p++ = gWaveform[i] ? 2047 : 0; // append binary data
}
*p++=0; // last sample must be 0 (otherwise 5V will be steady after sending first waveform)
*(char*)p='\n';
SendSerial(buf,(char*)p-buf+1);
LocalFree(buf);
}
static void Unremote(void) {
if (!OpenSerial()) return;
SendSerial("SYST:LOC\n",-1); // enable device's pushbuttons
Sleep(100);
CloseSerial(false);
}
// This routine may process messages!
static void SendWaveform(void) {
CHAR buf[64];
if (!OpenSerial()) return;
SendSerial("*RST\nSYST:REM\n",-1); // enable remote control, disable local pushbuttons
RecvSerial(buf,sizeof(buf)); // clear answer buffer
SendSerial("*IDN?\n",-1); // should return "HEWLETT-PACKARD,33120A,..."
RecvSerial(buf,sizeof(buf));
buf[23]=0;
if (lstrcmp(buf,"HEWLETT-PACKARD,33120A,")) {
CloseSerial(false);
MBox(ghMainWnd,21,MB_OK);
return;
}
SendSerial(
"DISP:TEXT \"Loading\"\n"
"TRIG:SOUR BUS\n" // trigger source: BUS (i.e. this serial port)
"BM:STAT ON\n"
"FORM:BORD SWAP\n",-1); // binary data in "Intel" byte order
_stprintf(buf,elemof(buf),"FREQ %f\n\n",1/gFullTime*(g.Samples+2)/g.Samples);
SendSerial(buf,-1);
TransmitWaveform();
SendSerial(
"DISP:TEXT:CLE\n"
"FUNC:USER VOLATILE\n"
"FUNC:SHAP USER\n"
//"APPL:USER %f,5,0\n" // set waveform, frequency, voltage, and offset using one instruction!
"OUTP:LOAD 50\n" // output resistance: 50 Ohm
"VOLT 5\n" // output voltage: 5 V (for maximum DAC value)
"BM:NCYC 1\n",-1); // number of cycles per trigger: 1
CloseSerial(true);
gDirty=false;
}
// This routine may process messages!
static void StartPulse(void) {
if (!OpenSerial()) return;
SendSerial("*TRG\n",-1);
CloseSerial(true);
}
/*************************
* main window procedure *
*************************/
static RECT gMaxRect;
static BOOL CALLBACK MainDlgProc(HWND Wnd,UINT Msg,WPARAM wParam,LPARAM lParam) {
#ifdef HELPBUTTON
if (gHelpAvail && IsZoomed(Wnd) && HandleDlgNcMessagesForHelp(Wnd,Msg,wParam,lParam)) return TRUE;
#endif
switch (Msg) {
case WM_INITDIALOG: {
HDC dc;
WINDOWPLACEMENT wp;
RECT r;
ghMainWnd=Wnd;
GetWindowText(Wnd,MBoxTitle,elemof(MBoxTitle));
DefEditProc=SubclassWindow(GetDlgItem(Wnd,14),UpDownEditProc);
dc=GetDC(Wnd);
gEditErrorProp.BackColor=GetNearestColor(dc,RGB(255,128,128)); // light red
gEditErrorProp.BackBrush=CreateSolidBrush(gEditErrorProp.BackColor);
ReleaseDC(Wnd,dc);
GetWindowRect(Wnd,&gMaxRect);
GetWindowRect(GetDlgItem(Wnd,11),&r); // <<</>>> button
wp.length=sizeof(wp);
GetWindowPlacement(Wnd,&wp);
wp.ptMaxPosition.x=gMaxRect.left;
wp.ptMaxPosition.y=gMaxRect.top;
wp.rcNormalPosition.right=r.right+5;
wp.rcNormalPosition.bottom=r.bottom+5;
wp.showCmd=SW_SHOWMAXIMIZED;
SetWindowPlacement(Wnd,&wp);
SetTimer(Wnd,1,10,NULL); // do further initializations later
}return TRUE;
case WM_TIMER: {
KillTimer(Wnd,wParam); // all timers are non-periodic
switch (wParam) {
case 1: { // continued initialization
SendMessage(Wnd,WM_SETTINGCHANGE,0,0);
LoadConfig();
SendMessage(Wnd,WM_DEVICECHANGE,0,0);
}break;
case 15: { // deferred voltage edit change
float f;
if (GetEditFloat(wParam,&f) && 1<=f && f<=1E5) {
g.Volt=f;
CalcTon0();
}
}break;
case 16: { // deferred current edit change
float f;
if (GetEditFloat(wParam,&f) && 0.1<=f && f<=1E5) {
g.Curr=f;
CalcTon0();
}
}break;
case 17: { // deferred inductance edit change
float f;
if (GetEditFloat(wParam,&f) && 1<=f && f<=1E5) {
g.Indu=f*1E-6F;
CalcTon0();
}
}break;
case 18: { // deferred Toff edit change
float f;
if (GetEditFloat(wParam,&f) && 0.5<=f && f<=1E5) {
g.Toff=f*1E-6F;
CalcWaveform();
}
}break;
case 20:
case 21:
case 22:
case 23: { // deferred Ton edit changes
float f;
if (GetEditFloat(wParam,&f) && 0.5<=f && f<=1E5) {
g.Ton[wParam-19]=f*1E-6F;
CalcWaveform();
}
}
}
}break;
case WM_GETMINMAXINFO: {
PMINMAXINFO pmmi=(PMINMAXINFO)lParam;
pmmi->ptMaxSize.x=gMaxRect.right-gMaxRect.left;
pmmi->ptMaxSize.y=gMaxRect.bottom-gMaxRect.top;
}break;
case WM_DEVICECHANGE: {
HWND hCombo;
int i;
// re-read available serial ports
hCombo=GetDlgItem(Wnd,30);
ComboBox_ResetContent(hCombo);
#if 0
{HMODULE hLib;
hLib=LoadLibraryA("cfgmgr32.dll"); // speed-up W2K, XP
for (i=1; i<MAXCOMSEARCH; i++) {
DWORD cclen;
TCHAR ComName[8];
COMMCONFIG cc;
InitStruct(&cc,cclen=sizeof(cc));
wnsprintf(ComName,elemof(ComName),T("COM%u"),i);
if (GetDefaultCommConfig(ComName,&cc,&cclen)) {
int idx=ComboBox_AddString(hCombo,ComName);
ComboBox_SetItemData(hCombo,idx,i);
if (i==g.SerialNo) ComboBox_SetCurSel(hCombo,idx);
}
}
if (hLib) FreeLibrary(hLib);
}
#else
{HDEVINFO devs;
devs=SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT,0,0,DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
if (devs!=INVALID_HANDLE_VALUE) {
// SP_DEVICE_INTERFACE_DATA devinterface;
SP_DEVINFO_DATA devinfo;
// devinterface.cbSize=sizeof devinterface;
devinfo.cbSize=sizeof devinfo;
for (i=0; SetupDiEnumDeviceInfo(devs,i,&devinfo); i++) {
TCHAR s[16];
DWORD size=sizeof s;
int k;
HKEY hKey;
if (CM_Open_DevNode_Key(devinfo.DevInst,KEY_QUERY_VALUE,0,RegDisposition_OpenExisting,&hKey,CM_REGISTRY_HARDWARE)) continue;
if (RegQueryValueEx(hKey,T("PortName"),NULL,NULL,(LPBYTE)s,&size)
|| RegCloseKey(hKey)) continue;
if (_stscanf(s,T("COM%d"),&k)==1) {
int idx=ComboBox_AddString(hCombo,s);
ComboBox_SetItemData(hCombo,idx,k);
if (k==g.SerialNo) ComboBox_SetCurSel(hCombo,i);
}
}
SetupDiDestroyDeviceInfoList(devs);
}
}
#endif
}break;
case WM_WININICHANGE: { // Wenn in Systemsteuerung Punkt/Komma wechselt
GetProfileString(T("intl"),T("sDecimal"),T("."),sDecimal,elemof(sDecimal));
}break;
case WM_COMMAND: switch (LOWORD(wParam)) {
case 10: { // "Go" button - start pulse output
if (gDirty) switch (MBox(Wnd,22,MB_YESNOCANCEL)) {
case IDYES: SendMessage(Wnd,WM_COMMAND,13,(LPARAM)GetDlgItem(Wnd,13)); break;
case IDCANCEL: goto raus;
}
StartPulse();
raus:;
}break;
case 11: { // normal/maximize button
ShowWindow(Wnd,IsZoomed(Wnd)?SW_RESTORE:SW_MAXIMIZE);
}break;
case 13: {
EnableWindow((HWND)lParam,FALSE); // show activity
SetCursor(LoadCursor(0,IDC_WAIT));
SendWaveform();
EnableWindow((HWND)lParam,TRUE);
}break;
case 14: switch (HIWORD(wParam)) {
case EN_CHANGE: {
int i=GetDlgItemInt(Wnd,14,NULL,FALSE);
if (0<i && i<=5) {
RemoveProp((HWND)lParam,gDlgCtlAtom);
g.Pulses=i;
PulsesChanged();
}else SetProp((HWND)lParam,gDlgCtlAtom,&gEditErrorProp);
InvalidateRect((HWND)lParam,NULL,TRUE);
}break;
}break;
case 15: // Voltage Edit
case 16: // Current Edit
case 17: // Inductance Edit
case 18: // Toff Edit
case 20: // Ton2 Edit
case 21: // Ton3 Edit
case 22: // Ton4 Edit
case 23: // Ton5 Edit
switch (HIWORD(wParam)) {
case EN_CHANGE: {
// on change, defer updating of internal variables
SetTimer(Wnd,LOWORD(wParam),200,NULL);
}break;
}break;
case 30: switch (HIWORD(wParam)) {
case CBN_SELCHANGE: {
g.SerialNo=ComboBox_GetItemData((HWND)lParam,ComboBox_GetCurSel((HWND)lParam));
}break;
}break;
case 32: switch (HIWORD(wParam)) {
case EN_CHANGE: {
int i=GetDlgItemInt(Wnd,32,NULL,FALSE);
if (8<=i && i<=8190) { // limits given by HP33120A;
// for more samples, the maximum frequency greatly reduces
// The HP33120A documentation does not explain output behaviour
// for small sample counts, i.e. whether linear approximation is done,
// or the output holds the sample the entire sampling time.
// In the latter case, the graphic does not show real waveform.
RemoveProp((HWND)lParam,gDlgCtlAtom);
g.Samples=i;
CalcWaveform();
}else SetProp((HWND)lParam,gDlgCtlAtom,&gEditErrorProp);
InvalidateRect((HWND)lParam,NULL,TRUE);
}break;
}break;
}break;
case WM_CTLCOLOREDIT: {
DLGCTLPROP *pep=GetProp((HWND)lParam,gDlgCtlAtom);
if (!pep) break; // do nothing if no property is set
SetBkMode((HDC)wParam,TRANSPARENT);
SetBkColor((HDC)wParam,pep->BackColor);
// SetTextColor((HDC)wParam,pep->Forecolor);
return (BOOL)pep->BackBrush;
}
case WM_HELP: if (gHelpAvail) {
LPHELPINFO hi=(LPHELPINFO)lParam;
HtmlHelp(hi->hItemHandle,gHelpFileName,HH_HELP_CONTEXT,hi->iCtrlId);
}break;
case WM_CONTEXTMENU: if (HandleContextMenu(Wnd,(HWND)wParam,lParam)) return TRUE; break;
case WM_SIZE: {
SetDlgItemTextA(Wnd,11,wParam==SIZE_MAXIMIZED?"<&<<":">&>>");
}break;
case WM_DRAWITEM: DrawCurve((PDRAWITEMSTRUCT)lParam); break;
case WM_ENDSESSION: if (wParam) {
SaveConfig();
if (!gDirty) Unremote();
DeleteBrush(gEditErrorProp.BackBrush);
}break;
case WM_SYSCOMMAND: switch (wParam&0xFFF0) {
case SC_CLOSE: {
SendMessage(Wnd,WM_ENDSESSION,TRUE,0);
EndDialog(Wnd,0);
}break;
}break;
}
return FALSE;
}
/******************************
* main program (entry point) *
******************************/
void WinMainCRTStartup(void) {
HANDLE f;
UINT ret;
ghInstance=GetModuleHandle(NULL);
InitCommonControls();
GetModuleFileName(NULL,gIniFileName,elemof(gIniFileName));
lstrcpy(PathFindExtension(gIniFileName),T(".ini"));
gDlgCtlAtom=(LPCTSTR)GlobalAddAtomA("h#s?Color");
g=gDefaults;
f=CreateFile(gHelpFileName,0,FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,0,0);
if (f!=INVALID_HANDLE_VALUE) {
CloseHandle(f);
gHelpAvail=true;
}
ret=DialogBox(0,MAKEINTRESOURCE(100),0,MainDlgProc);
GlobalDeleteAtom((ATOM)gDlgCtlAtom);
ExitProcess(ret);
}
Detected encoding: ANSI (CP1252) | 4
|
|