Source file: /~heha/hs/ADS121x.zip/src/ads121x.c

/* Ansteuerung des A/D-Wandlerchips ADS1210, 1211, 1212, 1213
 * mit dem PC am Parallelport
 * h#s 07/05  Nur Win32!
 */

#include "wutils.h"
#include "../inpout32/inpout32.h"
#include <stdio.h>
#include <math.h>	// floor
#include <shlwapi.h>

UINT LptBase;

FILE*f;	// Log-Datei
TCHAR LogName[MAX_PATH];
UINT  LogZaehler;	// Anzahl zu schreibender Messwerte, dann Schluss
DWORD CommandReg=0x43002000L+1952;	// Abbild des Kommando-Registers im ADS121x
UINT  KanalMaske=0xF;	// Welche Kanäle benutzt werden, Default: alle vier

void outbyte(DWORD a, BYTE b) {
 Out32((short)(LptBase+a),b);
}

BYTE inbyte(DWORD a) {
 return Inp32((short)(LptBase+a));
}
/**************************************************************************
Die Hardware:
	SubD	Bit
D0..D7	2..9	+0:0..7	Stromversorgung für Optokoppler (alle High setzen!)
ACK	10	+1:6	-DRDY	Fertigmeldung ADU	(ADU -> PC)
BSY	11	+1:/7	SDOUT	Serielle Ausgabe	(ADU -> PC)
STB	1	+2:/0	SCLK	Serieller Takt		(PC -> ADU)
DSL	17	+2:/3	SDIO	Serielle Eingabe	(PC -> ADU)
/**************************************************************************/

void SendBits(unsigned x, unsigned bits) {	// <bits> Bits aus <x> zum ADU senden
 bits=1<<(bits-1);	// nun Maske, MSB zuerst
 while (bits) {
  BYTE b=(BYTE)(x&bits ? 0x00 : 0x08);	// Takt HIGH, Daten je nach Bit
  outbyte(2,b);
  b|=0x01;				// Takt LOW, Daten werden vom ADU übernommen
  outbyte(2,b);
  bits>>=1;
 }
}

unsigned RecvBits(int bits) {
 unsigned r=0;
 while (bits) {
  r<<=1;
  outbyte(2,0x08);	// Daten LOW, Takt HIGH
  if (!(inbyte(1)&0x80)) r|=1;	// Wenn SDOUT=HIGH, ein Datenbit in <r> setzen
  outbyte(2,0x09);	// Takt und Daten auf LOW
  bits--;
 }
 return r;
}

bool WaitDRDY(void) {	// Wartet High-Low-Flanke von /DRDY ab
 DWORD tic=GetTickCount();
 while (!(inbyte(1)&0x40)) {	// abfragen bis High
  if (GetTickCount()-tic>200) return false;
 }
 while (inbyte(1)&0x40) {	// abfragen bis Low
  if (GetTickCount()-tic>200) return false;
 }
 return true;
}

bool InitADU(void) {
 outbyte(0,0xFF);	// Optokoppler mit Betriebsspannung versorgen
 outbyte(2,0x09);	// Takt und Daten auf LOW
 if (!WaitDRDY()) return false;
 SendBits(0x64,8); SendBits(CommandReg|0x00200000L,32);	// SDL=1 - SDOUT aktivieren
			// Zugleich 10 Hz Abtastrate wählen und Self-Calibration
 SendBits(0x84,8);	// Prüfen, ob der gleiche Wert rückgelesen werden kann
 return (RecvBits(8)>>1)==(CommandReg>>25);
}

HINSTANCE HInstance;
HANDLE hPorttalk=INVALID_HANDLE_VALUE;	// PORTTALK.SYS-Handle (nur NT)
HWND MainWnd;		// Hauptfenster
HFONT LargeFont;
HBRUSH BlueBrush;
HBRUSH YellowBrush;	// für DDE-Advise-Variablen
ATOM AtomBack;
TCHAR sDecimal[2];
TCHAR IniFileName[MAX_PATH];
TCHAR HelpFileName[]=T("ADS121x.HLP");
DWORD DdeInst;
HSZ hszService;
#define hszTopic hszService
#define WM_CONTINUEINIT (WM_USER+100)
typedef struct {
 long wert;		// Wert vom ADU
 float anz;		// Anzeigewert
 float skale;		// Faktor für <wert>
 float offset;		// dimensionsbehaftet!
 int ziffern;		// Kommastellen
 TCHAR einheit[8];	// Einheit (zur Anzeige)
 HSZ hsz;		// DDE-String-Handle
 UINT idx;		// (Rückwärtiger) Index
}TADWert;

TADWert ADWert[4];
UINT Millisek;		// Default: 500

HSZ CreateStringHandle(PCTSTR Str) {
 return DdeCreateStringHandle(DdeInst,Str,CP_WINNEUTRAL);
}

void FreeStringHandle(HSZ hsz) {
 if (hsz) DdeFreeStringHandle(DdeInst,hsz);
}

int CheckHsz(HSZ hsz) {
 int i;
 for (i=0; i<4; i++) {
  if (!DdeCmpStringHandles(hsz,ADWert[i].hsz)) return 108+8*i;
 }
 return 0;			// alles andere ist Fehler
}

#ifdef __BORLANDC__
# pragma argsused
#endif
HDDEDATA CALLBACK DdeCallback(UINT type, UINT fmt, HCONV conv,
 HSZ hsz1, HSZ hsz2, HDDEDATA data, DWORD data1, DWORD data2) {
 switch (type) {
  case XTYP_CONNECT: {
   if (hsz1==hszTopic) {
    SetDlgItemText(MainWnd,111,T("DDE-Verbindung gestartet"));
    return (HDDEDATA)TRUE;
   }
  }break;
  case XTYP_DISCONNECT: {
   SetDlgItemText(MainWnd,111,T("DDE-Verbindung beendet"));
  }break;
  case XTYP_REQUEST:
  case XTYP_ADVREQ: {
   int id=CheckHsz(hsz2);
   char s[16];
   if (fmt!=CF_TEXT) break;
   if (!id) break;
   return DdeCreateDataHandle(DdeInst,(LPBYTE)s,
     GetDlgItemTextA(MainWnd,id,s,elemof(s))+1,
     0,hsz2,fmt,0);
  }
  case XTYP_ADVSTART:
  case XTYP_ADVSTOP: {
   int id=CheckHsz(hsz2);
   HWND w;
   if (!id) break;
   w=GetDlgItem(MainWnd,id);
   id=(int)GetProp(w,(LPTSTR)AtomBack);
   id+=type==XTYP_ADVSTART?+1:-1;
   if (id) SetProp(w,(LPTSTR)AtomBack,(HANDLE)id);
   else RemoveProp(w,(LPTSTR)AtomBack);
   InvalidateRect(w,NULL,TRUE);
   return (HDDEDATA)TRUE;
  }
 }
 return (HDDEDATA)FALSE;
}

void Float2String(PTSTR s, float z) {
 _stprintf(s,T("%.6G"),z);
 s=_tcschr(s,T('.'));
 if (s) *s=sDecimal[0];
}
bool String2Float(PTSTR s, float*z) {
 PTSTR p;
 p=_tcschr(s,sDecimal[0]);
 if (!p) p=_tcschr(s,T(','));
 if (p) *p=T('.');
 if (_stscanf(s,T("%G"),z)!=1) return false;
 return true;
}

void LoadADWert(TADWert*w) {
 TCHAR s[32];
 TCHAR sektion[2];
 _stprintf(sektion,T("%u"),w->idx+1);
 GetPrivateProfileString(sektion,T("Skalierung"),T("1"),s,elemof(s),IniFileName);
 String2Float(s,&w->skale);
 GetPrivateProfileString(sektion,T("Offset"),T("0"),s,elemof(s),IniFileName);
 String2Float(s,&w->offset);
 w->ziffern=GetPrivateProfileInt(sektion,T("Kommastellen"),0,IniFileName);
 GetPrivateProfileString(sektion,T("Einheit"),T(""),w->einheit,elemof(w->einheit),IniFileName);
}

void SetLptBase(UINT a) {
 LptBase=a;
}

void LoadConfig(void) {
 TCHAR s[32];
 UINT i=0;
 if (GetPrivateProfileString(T("ADS121x"),T("PortBase"),T(""),s,elemof(s),IniFileName))
   _stscanf(s,T("%X"),&i);
 if (i<0x100) i=0x378;
 SetLptBase(i);
 Millisek=GetPrivateProfileInt(T("ADS121x"),T("Millisek"),500,IniFileName);
 if (GetPrivateProfileString(T("ADS121x"),T("CommandReg"),T(""),s,elemof(s),IniFileName))
   _stscanf(s,T("%lX"),&CommandReg);
 if (GetPrivateProfileString(T("ADS121x"),T("KanalMaske"),T(""),s,elemof(s),IniFileName))
   _stscanf(s,T("%X"),&KanalMaske);
 GetPrivateProfileString(T("ADS121x"),T("LogName"),T("ADS121x.log"),
   LogName,elemof(LogName),IniFileName);
 for (i=0; i<elemof(ADWert); i++) LoadADWert(ADWert+i);
 InitADU();
}

void SaveADWert(TADWert*w) {
 TCHAR s[32];
 TCHAR sektion[2];
 _stprintf(sektion,T("%u"),w->idx+1);
 Float2String(s,w->skale);
 WritePrivateProfileString(sektion,T("Skalierung"),s,IniFileName);
 Float2String(s,w->offset);
 WritePrivateProfileString(sektion,T("Offset"),s,IniFileName);
 _stprintf(s,T("%i"),w->ziffern);
 WritePrivateProfileString(sektion,T("Kommastellen"),s,IniFileName);
 WritePrivateProfileString(sektion,T("Einheit"),w->einheit,IniFileName);
}

void SaveConfig(void) {
 TCHAR s[32];
 UINT i;
 _stprintf(s,T("0x%X"),LptBase);
 WritePrivateProfileString(T("ADS121x"),T("PortBase"),s,IniFileName);
 _stprintf(s,T("%i"),Millisek);
 WritePrivateProfileString(T("ADS121x"),T("Millisek"),s,IniFileName);
 _stprintf(s,T("0x%lX"),CommandReg);
 WritePrivateProfileString(T("ADS121x"),T("CommandReg"),s,IniFileName);
 _stprintf(s,T("0x%X"),KanalMaske);
 WritePrivateProfileString(T("ADS121x"),T("KanalMaske"),s,IniFileName);
 WritePrivateProfileString(T("ADS121x"),T("LogName"),LogName,IniFileName);
 for (i=0; i<elemof(ADWert); i++) SaveADWert(ADWert+i);
 WritePrivateProfileString(NULL,NULL,NULL,IniFileName);
}

void DdeStart(void) {
 DdeInitialize(&DdeInst,DdeCallback,
   CBF_FAIL_POKES|CBF_FAIL_EXECUTES|
   CBF_SKIP_REGISTRATIONS|CBF_SKIP_UNREGISTRATIONS,0);
 hszService=CreateStringHandle(T("ADS121x"));
 ADWert[0].hsz=CreateStringHandle(T("1"));
 ADWert[1].hsz=CreateStringHandle(T("2"));
 ADWert[2].hsz=CreateStringHandle(T("3"));
 ADWert[3].hsz=CreateStringHandle(T("4"));
 AtomBack=AddAtom(T("DdeAdvises"));
 YellowBrush=CreateSolidBrush(0xC0FFFFL);
 DdeNameService(DdeInst,hszService,0,DNS_REGISTER);
}

void DdeStop(void) {
 DdeUninitialize(DdeInst);
 DeleteObject(YellowBrush);
}

void PorttalkStart(void) {}

void PorttalkStop(void) {}

void SetWert(TADWert *w,long wert) {
 TCHAR buf[32];
 PTSTR p;
 if (w->wert==wert) return;
 w->wert=wert;
 w->anz=w->wert*w->skale-w->offset;
 _stprintf(buf,T("%.*f %s"),
   w->ziffern,
   w->anz,
   w->einheit);
 p=_tcschr(buf,T('.'));
 if (p) *p=sDecimal[0];
 SetDlgItemText(MainWnd,108+2*w->idx,buf);
 DdePostAdvise(DdeInst,hszTopic,w->hsz);
}

bool GetMesswert(TADWert*w,long*v) {	// Messwert vom A/D-Wandler holen
 if (!WaitDRDY()) return false;	// Warten bis fertig
 SendBits(0xC0,8);		// 3 Bytes von Adresse 0 lesen wollen
 *v=(long)(RecvBits(24)<<8)>>8;	// 3 Bytes lesen und Vorzeichen "aufblasen"
 return true;
}

bool GetMesswert3x(TADWert*w,long*v) {	// dito, mehrfach probieren
 int i=3;
 do if (GetMesswert(w,v) /*&& *v>-0x7FFFF0 && *v<0x7FFFF0*/) return true; while(--i);
 return false;
}

void StartMessung(TADWert*w) {	// Messung auf Kanal anstoßen
 CommandReg=(CommandReg&~0x30000)|(w->idx<<16);
 SendBits(0x24,8);
 SendBits(CommandReg>>16,16);	// Kanal setzen, ADU starten
}

#ifdef __BORLANDC__
# pragma argsused
#endif
UINT CALLBACK OfnHook(HWND Wnd,UINT Msg,WPARAM wParam,LPARAM lParam) {
 switch (Msg) {
  case WM_INITDIALOG: {
   SetWindowLong(Wnd,DWL_USER,lParam);
  }break;
  case WM_COMMAND: switch (LOWORD(wParam)) {
   case 101: EnableDlgItem(Wnd,102,IsDlgButtonChecked(Wnd,101)); break;
   case 103: {	// Je nach Schalter Überschreibwarnung aktivieren
    LPDWORD f=&(((LPOPENFILENAME)GetWindowLong(Wnd,DWL_USER))->Flags);
    if (IsDlgButtonChecked(Wnd,103)) *f&=~OFN_OVERWRITEPROMPT;
    else *f|=OFN_OVERWRITEPROMPT;
   }break;
  }break;
  case WM_NOTIFY: switch (((LPOFNOTIFY)lParam)->hdr.code) {
   case CDN_FILEOK: {
    if (IsDlgButtonChecked(Wnd,101))	// Wenn EIN dann Rückgabewert setzen (?)
      ((LPOFNOTIFY)lParam)->lpOFN->lCustData=GetDlgItemInt(Wnd,102,NULL,FALSE);
   }break;
  }break;
 }
 return 0;
}

void SwitchLog(void) {	// Schalte Log-Funktion ein/aus
 if (f) {
  fclose(f); f=NULL;
 }else{
  OPENFILENAME ofn;
  TCHAR sFilter[128];
  sFilter[LoadString(HInstance,104,sFilter,elemof(sFilter))+1]=0;
  InitStruct(&ofn,sizeof(ofn));
  ofn.hwndOwner=MainWnd;
  ofn.hInstance=HInstance;
  ofn.lpstrFilter=sFilter;
  ofn.lpstrFile=LogName;
  ofn.nMaxFile=elemof(LogName);
  ofn.Flags=OFN_PATHMUSTEXIST|OFN_OVERWRITEPROMPT|OFN_HIDEREADONLY
    |OFN_EXPLORER|OFN_ENABLEHOOK|OFN_ENABLESIZING|OFN_ENABLETEMPLATE;
  ofn.lpfnHook=OfnHook;
  ofn.lpTemplateName=MAKEINTRESOURCE(0x11);	// enthält Zusatz-Dialogelemente
  if (GetSaveFileName(&ofn)) {
   LogZaehler=ofn.lCustData;	// Null wenn ohne Begrenzung
   f=_tfopen(LogName,ofn.Flags&OFN_OVERWRITEPROMPT?T("wt"):T("at"));
   if (!f) MBox(MainWnd,106,MB_ICONSTOP,LogName);
  }
 }
 CheckMenuItem(GetMenu(MainWnd),0x11,f?MF_CHECKED:MF_UNCHECKED);
}

#ifdef __BORLANDC__
# pragma argsused
#endif
BOOL CALLBACK PortDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam){
 const static unsigned Portadressen[]={0x378,0x278,0x3BC};
 switch (Msg) {
  case WM_INITDIALOG: {
   int i;
   for (i=0; i<elemof(Portadressen); i++) {
    TCHAR s[16];
    _stprintf(s,T("LPT%i (0x%X)"),i+1,Portadressen[i]);
    SendDlgItemMessage(Wnd,101,CB_ADDSTRING,0,(LPARAM)(LPSTR)s);
    if (LptBase==Portadressen[i]) SendDlgItemMessage(Wnd,101,CB_SETCURSEL,i,0);
   }	// unter Win32 ist kein Rankommen an die wahren Portadressen, oder??
  }return TRUE;
  case WM_COMMAND: switch (LOWORD(wParam)) {
   case IDHELP: WinHelp(Wnd,HelpFileName,HELP_CONTEXT,0x21); break;
   case IDOK: {
    UINT pa;
    pa=(UINT)SendDlgItemMessage(Wnd,101,CB_GETCURSEL,0,0);
    if (pa>=elemof(Portadressen)) {
     MBox(Wnd,101,MB_ICONEXCLAMATION|MB_OK);
     SetFocus(GetDlgItem(Wnd,101));
     break;
    }
    SetLptBase(Portadressen[pa]);
    if (!InitADU() && MBox(Wnd,103,MB_ICONEXCLAMATION|MB_OKCANCEL)!=IDOK) break;
   }nobreak;
   case IDCANCEL: EndDialog(Wnd,wParam);
  }
 }
 return FALSE;
}

void SetEdits(TADWert*w, HWND wnd) {
 TCHAR s[32];
 UINT idbase=100+4*w->idx;
 Float2String(s,w->skale); SetDlgItemText(wnd,  idbase,s);
 Float2String(s,w->offset);SetDlgItemText(wnd,++idbase,s);
 SetDlgItemInt (wnd,++idbase,w->ziffern,FALSE);
 SetDlgItemText(wnd,++idbase,w->einheit);
}

bool GetEdits(TADWert*w, HWND wnd) {
 TCHAR s[32];
 BOOL b;
 UINT idbase=100+4*w->idx;
 GetDlgItemText(wnd,idbase,s,elemof(s));
 if (!String2Float(s,&w->skale)) goto err;
 GetDlgItemText(wnd,++idbase,s,elemof(s));
 if (!String2Float(s,&w->offset)) goto err;
 w->ziffern=GetDlgItemInt(wnd,++idbase,&b,FALSE);
 if (!b || w->ziffern>6) goto err;
 if ((unsigned)SendDlgItemMessage(wnd,++idbase,WM_GETTEXTLENGTH,0,0)
   >=elemof(w->einheit)) goto err;	// zu viele Zeichen
 GetDlgItemText(wnd,idbase,w->einheit,elemof(w->einheit));
 return true;
err:
 MBox(wnd,102,MB_ICONEXCLAMATION|MB_OK);
 SetEditFocus(wnd,idbase);
 return false;
}

bool iWaitDRDY(HWND w) {	// interaktive Version von WaitDRDY()
 do{
  if (WaitDRDY()) return true;
 }while (MBox(w,103,MB_RETRYCANCEL)==IDRETRY);
 return false;
}

#ifdef __BORLANDC__
# pragma argsused
#endif
BOOL CALLBACK KoeffDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam){
 switch (Msg) {
  case WM_INITDIALOG: {
   int i;
   for (i=0; i<4; i++) SetEdits(ADWert+i,Wnd);
  }return TRUE;
  case WM_COMMAND: switch (LOWORD(wParam)) {
   case 120: EnableDlgItem(Wnd,121,IsDlgButtonChecked(Wnd,120)); break;
   case IDHELP: WinHelp(Wnd,HelpFileName,HELP_CONTEXT,0x22); break;
   case IDOK: {
    if (!GetEdits(ADWert+0,Wnd)) break;
    if (!GetEdits(ADWert+1,Wnd)) break;
    if (!GetEdits(ADWert+2,Wnd)) break;
    if (!GetEdits(ADWert+3,Wnd)) break;
   }nobreak;
   case IDCANCEL: EndDialog(Wnd,wParam);
  }
 }
 return FALSE;
}

void SetKanalMaske(UINT km) {	// macht Hauptfenster-Rähmchen grau
 if (KanalMaske!=km) {
  int i;
  for (i=0; i<elemof(ADWert); i++) {
   BOOL e=(1<<i)&km;
   HWND w=GetDlgItem(MainWnd,108+2*i);
   EnableWindow(w,e);			// Static (de)aktivieren
   EnableWindow(GetPrevSibling(w),e);	// Rahmen (de)aktivieren
   if (!e) SetWindowText(w,T("--"));
  }
  KanalMaske=km;
 }
}

#ifdef __BORLANDC__
# pragma argsused
#endif
BOOL CALLBACK KonfDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam){
 switch (Msg) {
  case WM_INITDIALOG: {
   unsigned i;
   HWND w;
   TCHAR s[256],*p;

   for (i=0,w=GetDlgItem(Wnd,101); i<5; i++) {	// Verstärkung
    _stprintf(s,T("%ux"),1<<i);
    ComboBox_AddString(w,s);
    if (i==((CommandReg>>18)&7)) ComboBox_SetCurSel(w,i);
   }
   for (i=0,w=GetDlgItem(Wnd,102); i<5; i++) {	// Turbo
    _stprintf(s,T("%ux"),1<<i);
    ComboBox_AddString(w,s);
    if (i==((CommandReg>>13)&7)) ComboBox_SetCurSel(w,i);
   }
   Float2String(s,(float)(1E7*(1<<((CommandReg>>13)&7))/(((CommandReg&0x1FFF)+1)<<9)));
   SetDlgItemText(Wnd,103,s);
   KillTimer(Wnd,103);
   SetDlgItemInt(Wnd,104,CommandReg&0x1FFF,FALSE);
   SetDlgItemText(Wnd,105,s);
   s[LoadString(HInstance,211,s,elemof(s))+1]=0;
   for (p=s,w=GetDlgItem(Wnd,106),i=0; *p; p+=lstrlen(p)+1,i++) {
    ComboBox_AddString(w,p);
    if (i==((CommandReg>>21)&7)) ComboBox_SetCurSel(w,i);
   }
   CheckDlgButton(Wnd,107,(CommandReg>>30)&1);
   SetCheckboxGroup(Wnd,110,113,KanalMaske);
   SetDlgItemInt(Wnd,114,Millisek,FALSE);
  }return TRUE;
  case WM_TIMER: switch (wParam) {
   case 103: {
    float z;
    TCHAR s[32];
    int dr,tr;
    SetDlgItemText(Wnd,104,T(""));
    SetDlgItemText(Wnd,105,T(""));
    GetDlgItemText(Wnd,103,s,elemof(s));
    if (!String2Float(s,&z)) break;
    if (z<=0) break;			// Notbremse
    tr=1<<SendDlgItemMessage(Wnd,102,CB_GETCURSEL,0,0);	//Turbo-Rate
    dr=(int)floor(1E7*tr/512/z-0.5);	// Dezimierungsverhältnis
    SetDlgItemInt(Wnd,104,dr,FALSE);
    Float2String(s,(float)(1E7*tr/(dr<<9)));
    SetDlgItemText(Wnd,105,s);
   }break;
  }break;
  case WM_COMMAND: switch (LOWORD(wParam)) {
   case 102: if (GET_WM_COMMAND_CMD(wParam,lParam)==CBN_SELCHANGE)
     SetTimer(Wnd,103,200,NULL); break;
   case 103: if (GET_WM_COMMAND_CMD(wParam,lParam)==EN_CHANGE)
     SetTimer(Wnd,103,200,NULL); break;
   case IDHELP: WinHelp(Wnd,HelpFileName,HELP_CONTEXT,0x23); break;
   case 3: // Knopf "Übernehmen"
   case IDOK: {
    UINT km,ms,dr,vv,tr;
    km=GetCheckboxGroup(Wnd,110,113);
    if (!km) {
     MBox(Wnd,107,MB_ICONEXCLAMATION|MB_OK);	// Mindestens 1 Kanal!
     SetFocus(GetDlgItem(Wnd,110));
     break;
    }
    ms=GetDlgItemInt(Wnd,114,NULL,FALSE);
    if (!ms) {
     MBox(Wnd,102,MB_ICONEXCLAMATION|MB_OK);	// Eingabefehler!
     SetEditFocus(Wnd,114);
     break;
    }
    dr=GetDlgItemInt(Wnd,104,NULL,FALSE);
    if (dr<19 || dr>8000) {
     MBox(Wnd,108,MB_ICONEXCLAMATION|MB_OK,dr);	// Ungültiges Dezimierunsverhältnis!
     SetEditFocus(Wnd,103);
     break;
    }
    vv=SendDlgItemMessage(Wnd,101,CB_GETCURSEL,0,0);
    tr=SendDlgItemMessage(Wnd,102,CB_GETCURSEL,0,0);
    if (vv+tr>4) {
     MBox(Wnd,109,MB_ICONEXCLAMATION|MB_OK,1<<(vv+tr));	// Produkt max. 16!
     SetFocus(GetDlgItem(Wnd,101));
     break;
    }
    CommandReg=(CommandReg&0xBF030000)
      |((DWORD)vv<<18)
      |(tr<<13)
      |dr
      |(SendDlgItemMessage(Wnd,106,CB_GETCURSEL,0,0)<<21)
      |(IsDlgButtonChecked(Wnd,107)<<30);
    if (!iWaitDRDY(Wnd)) break;
    SendBits(0x64,8); SendBits(CommandReg,32);
    SetKanalMaske(km);
    Millisek=ms;
    SetTimer(MainWnd,1,ms,NULL);
   }if (wParam!=IDOK) break;	// (nur) bei OK Dialog schließen
   case IDCANCEL: EndDialog(Wnd,wParam);
  }
 }
 return FALSE;
}

#ifdef __BORLANDC__
# pragma argsused
#endif
BOOL CALLBACK AboutDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam){
 switch (Msg) {
  case WM_INITDIALOG: return TRUE;
  case WM_COMMAND: EndDialog(Wnd,wParam);
 }
 return FALSE;
}

int KanalNext(int k) {	// Ermittelt nächsten Kanal-Index
 if (!KanalMaske) KanalMaske=1;	// Gefahr einer Endlosschleife abwehren
 do{
  k=(k+1)&3;
 }while (!((1<<k)&KanalMaske));	// Suche bis Bit gesetzt ist
 return k;
}

float U2R(float U) {
// U wird normiert übergeben, als ob der Spannungsteiler mit 1 V gespeist würde
// Liefert Widerstandswert in Ohm
 return (float)(2*620E3*U/(1-U));	// Thermistor-Widerstand, mit 2 x 620k Vorwiderstand
}

float R2T(float R) {
// R (Widerstand) in Ohm wird übergeben
// Liefert Temperatur in °C (hoffentlich!)
 static long RT[]={		//°C
/*
   274590,208350,159390,122910,	//-20..-5
   95490,74730,58890,46710,	//0..15
   37320,30000,24250,19720,	//20..35
   16120,13250,10950,9090,	//40..55
   7581,6354,5349,4524,		//60..75
   3840,3273,2799,2405,2073};	//80..100
*/
   274590L,261050L,247250L,233600L,220500L,208350L,197250L,186900L,177250L,168500L,	// -20..-11
   159390L,151150L,144200L,136250L,129800L,122910L,116750L,110950L,105550L,100400L,	// -10.. -1
   95490L,90850L,86540L,82350L,78450L,74730L,71200L,67850L,64700L,61750L,
   58890L,56175L,53625L,51200L,48900L,46710L,44620L,42650L,40800L,39025L,
   37320L,35700L,34170L,32715L,31330L,30000L,28730L,27530L,26390L,25360L,
   24250L,23250L,22305L,21490L,20545L,19720L,18925L,18175L,17463L,16800L,
   16120L,15492L,14892L,14322L,13776L,13250L,12746L,12268L,11810L,11372L,
   10950L,10544L,10157L,9788L,9432L,9090L,8762L,8448L,8147L,7859L,
   7581L,7314L,7059L,6815L,6580L,6354L,6136L,5927L,5727L,5535L,5349L};			//  60.. 70
// Widerstände von -20°C bis 100°C in 5-K-Schritten
 float T;
 int i;
 for (i=1; i<elemof(RT)-1; i++) {
  if (R>=RT[i]) break;
 }
 T=1/*K/Schritt*/*((R-RT[i-1])/(RT[i]-RT[i-1])/*Teilschritt*/+i/*ganzer Schritt*/-1)-20/*K*/;	// Lineare Approximation
 return T;
}

float Q2T(float Q) {
// Quotient (dimensionslos) wird übergeben
// Liefert Temperatur des Strahlers in °C (theoretisch)
 static float QT[]={
 0.50554061F,0.50789839F,
		//100 110 

 0.5364199F,0.56267237F,		//  260
 0.59180434F,0.59976594F,0.605F,0.60985294F,0.61592107F,0.63174045F,0.65456072F,0.66221956F,0.67252484F,0.67965979F,	//270..360
 0.7185702F,0.73512444F,0.74871173F,0.77667818F,0.80532793F,0.80115051F,0.812F,0.81503029F,0.83436849F,0.85061072F,	//370..460
 0.8824144F,0.90055857F,0.92755385F,0.94768855F,0.9655249F,0.9833883F,1.00501006F,1.03118098F,1.04859687F,1.07569943F, //470, 480
 1.09730519F,1.11491076F,1.14050132F,1.15840797F,1.18329521F,1.201486F,1.21613201F,1.23606546F,1.26362535F,1.2823049F,
 1.30699879F,1.3313783F,1.35827114F,1.36887124F,1.39230524F,1.4085834F,1.43332827F,1.45833877F,1.48067617F,1.50420801F,
 1.519656754F
 };					//770
 int i;
 for (i=1; i<elemof(QT)-1; i++) {
  if (Q<=QT[i]) break;
 }
 return 10*((Q-QT[i-1])/(QT[i]-QT[i-1])+(i-1))+110;
}


BOOL CALLBACK MainDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam){
 static TCHAR DefaultTitle[64];
 switch (Msg) {
  case WM_INITDIALOG: {
   int i;
   MainWnd=Wnd;
   for (i=0; i<4; i++) ADWert[i].idx=i;
   GetWindowText(Wnd,DefaultTitle,elemof(DefaultTitle));
   LargeFont=CreateFont(40,0,0,0,700,0,0,0,0,0,0,0,0,T("Times"));
   BlueBrush=CreateSolidBrush(0xFFC0C0L);
   if (LargeFont) for (i=0; i<4; i++) {
    SendDlgItemMessage(Wnd,108+2*i,WM_SETFONT,(WPARAM)LargeFont,0);
   }
   PorttalkStart();
   PostMessage(Wnd,WM_CONTINUEINIT,0,0); // ShowWindow verzögern (geht sonst nicht)
  }return TRUE;

  case WM_CONTINUEINIT: {
   ShowWindow(Wnd,SW_SHOWDEFAULT);
   SendMessage(Wnd,WM_WININICHANGE,0,0);
   SetTimer(Wnd,2,200,NULL);
  }break;

  case WM_WININICHANGE: {	// Wenn in Systemsteuerung Punkt/Komma wechselt
   GetProfileString(T("intl"),T("sDecimal"),T("."),sDecimal,elemof(sDecimal));
  }break;

  case WM_TIMER: switch (wParam) {
   case 2: {
    KillTimer(Wnd,wParam);
    LoadConfig();	// per Timer funktioniert EnableWindow (jetzt erst!)
    DdeStart();
    SetTimer(Wnd,1,Millisek,NULL);
   }break;
   case 1: {
    static int j;
    long v;
    if (GetMesswert3x(ADWert+j,&v)) {
     int k=KanalNext(j);	// nächster Index
     if (j!=k) StartMessung(ADWert+k);	// wenn Kanalumschaltung erforderlich
     SetWert(ADWert+j,v);	// Bildschirm aktualisieren (jetzt erst!)
     if (k<=j) {	// Wenn alle Kanäle durch...
      float Qs=ADWert[1].anz?ADWert[0].anz/ADWert[1].anz:0;
      float Ts=Q2T(((Qs)));			// Strahler-Temperatur (Abweichung zz. 1000%)
      float Ra=U2R(ADWert[2].anz);	// Thermistor-Widerstand
      float Ta=R2T(Ra);			// Thermistor-Temperatur
      TCHAR s[32];
      Float2String(s,Qs); SetDlgItemText(Wnd,120,s);
      Float2String(s,Ts); SetDlgItemText(Wnd,122,s);
      Float2String(s,Ra); SetDlgItemText(MainWnd,121,s);
      Float2String(s,Ta); SetDlgItemText(Wnd,123,s);
      if (f) {	// Wenn Loggen aktiv...
       int kk=k;
       do{
        fprintf(f,"%f ",ADWert[kk].anz);	// Aktive Kanäle loggen
        kk=KanalNext(kk);
       }while (kk!=k);
       fprintf(f,"%f %f\n",Qs,Ta);
		// Quotient und Thermistortemperatur am Zeilenende loggen
       if (LogZaehler) {
        if (--LogZaehler) fflush(f);
        else{
         MessageBeep(MB_OK);
         SwitchLog();		// Log ausschalten
        }
       }else{
        fflush(f);
       }
      }
     }
     j=k;
     if (IsIconic(Wnd)) goto settitle;
    }
   }break;
  }break;
  case WM_SIZE: if (wParam==SIZEICONIC) {
   TCHAR buf[64];
settitle:
   GetDlgItemText(Wnd,108,buf,32);
   lstrcat(buf,T("; "));
   GetDlgItemText(Wnd,110,buf+lstrlen(buf),32);
   SetWindowText(Wnd,buf);
  }break;
  case WM_QUERYOPEN: {
   SetWindowText(Wnd,DefaultTitle);
  }break;
  case WM_CTLCOLORSTATIC: switch (GetDlgCtrlID((HWND)lParam)) {
   case 108:
   case 110:
   case 112:
   case 114: {
    SetBkMode((HDC)wParam,TRANSPARENT);
    if (GetProp((HWND)lParam,(LPTSTR)AtomBack)) return (BOOL)YellowBrush;
    return (BOOL)BlueBrush;
   }
  }break;

  case WM_COMMAND: switch (LOWORD(wParam)){
   case 0x11: SwitchLog(); break;
   case 0x1F: SendMessage(Wnd,WM_CLOSE,0,0); break;
   case 0x21: DialogBox(HInstance,MAKEINTRESOURCE(0x21),Wnd,PortDlgProc); break;
   case 0x22: DialogBox(HInstance,MAKEINTRESOURCE(0x22),Wnd,KoeffDlgProc); break;
   case 0x23: DialogBox(HInstance,MAKEINTRESOURCE(0x23),Wnd,KonfDlgProc); break;
   case 0x2E: SaveConfig(); break;
   case 0x31: WinHelp(Wnd,HelpFileName,HELP_INDEX,0); break;
   case 0x32: WinHelp(Wnd,HelpFileName,HELP_CONTEXT,0x32); break;
   case 0x3F: DialogBox(HInstance,MAKEINTRESOURCE(0x3F),Wnd,AboutDlgProc); break;
  }break;

  case WM_CLOSE: {
   if (f) fclose(f);
   WinHelp(Wnd,HelpFileName,HELP_QUIT,0);
   EndDialog(Wnd,0);
  }break;

  case WM_DESTROY: {
   DdeStop();
   PorttalkStop();
   DeleteObject(LargeFont);
   DeleteObject(BlueBrush);
  }break;
 }
 return FALSE;
}

int _fltused;

// Hauptprogramm

void CALLBACK WinMainCRTStartup() {
 static WNDCLASS wc={
  CS_DBLCLKS,
  DefDlgProc,
  0,
  DLGWINDOWEXTRA,
  0,0,0,
  (HBRUSH)(COLOR_BACKGROUND+1),
  NULL,
  T("ADS121x")};
 wc.hInstance=HInstance=GetModuleHandle(NULL);
 wc.hIcon=LoadIcon(wc.hInstance,MAKEINTRESOURCE(100));
 wc.hCursor=LoadCursor(0,IDC_ARROW);
 if (!RegisterClass(&wc)) {
#ifdef UNICODE
  CHAR buf[256];
  LoadStringA(wc.hInstance,210,buf,elemof(buf));
  MessageBoxA(0,buf,NULL,MB_OK|MB_ICONSTOP);	// Zwang: ANSI-Version
#endif
  ExitProcess(3);
 }
 GetModuleFileName(0,IniFileName,elemof(IniFileName));
 lstrcpy(PathFindFileName(IniFileName),T("ADS121x.INI"));
 ExitProcess(DialogBox(wc.hInstance,MAKEINTRESOURCE(100),0,MainDlgProc));
}

Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded