In diesem Teil geht es um die Ansteuerung der Hardware.
Die RTK-Computer sind jeweils mit einer Motoren Controllerkarte vom Typ C-812 und einer Karte vom Typ C-832 ausgerüstet. Hersteller dieser Karten ist Physik Instrumente.
Die C-812 kann 4 Gleichstrom-Motoren und die C-832
2 Gleichstrom-Motoren ansteuern. Die Kommunikation zwischen Host-Rechner
und Controller-Karte kann unterschiedlich erfolgen.
Bei der C-812 Controllerkarte hat man die Wahl
zwischen RS232, PC-Bus und IEEE-488
Schnittstelle.
Die Kommunikation über die RS232 Schnittstelle funktioniert ähnlich
wie die bei einem Modem.
Die Steuerbefehle können beispielsweise mit einem Terminalprogramm
an die Controllerkarte gesendet werden.
Die Kommunikation mit der Schnittstelle
PC-Bus
erfolgt über einen Dual-Ported-RAM, d.h. ein Speicherbereich über
640 KByte wird von der Controllerkarte als auch vom Host-Rechner für
Schreib-/Lese- Operationen benutzt.
Bei der dritten Möglichkeit der Kommunikation, also der IEEE-488
Schnittstelle, wird vom Hersteller eine 16 Bit DLL zur Verfügung gestellt,
mit der man Funktionen zur Steuerung der Motoren importieren kann.
Bei der Controllerkarte C-832 hat man nur eine Möglichkeit zur Kommunikation. Hier kann man nur über Port-Befehle, die auf den I/O-Adress-Bereich zugreifen, mit der Karte kommunizieren.
Die zentralen Funtionen in der motors.dll, die dann endgültig den
Zugriff auf die Hardware realisieren, sind
Drive628 bzw. Drive628c
(C-832) bzw. TC_812::PutChar und TC_812::GetChar
(C-812).
- Dual Port RAM zum Direktzugriff (ohne Kommandos) z.B. zum Positionlesen
- 2 Speicherregister für Kommunikation (schreiben/lesen der Daten)
- Timing-Kontrolle über Handshake Flags
- 20 Bit IBM-Bus-Adresse:
- die oberen 8 Bits der Basisadresse werden durch DIP-Switches
auf
Controllerboard eingestellt
- die nächsten 2 Bits auf 0 (gesetzt durch PAL)
- restliche 10 Bits ueber Dual Port RAM erreichbar
- normalerweise: Basis Adresse = D8000 hex
- damit steht 1KByte grosser Speicherbereich von 000 bis 3FF hex für
Zugriff bereit
- auf diesen Speicherbereich kann Prozessor auf Controller-Board als
auch PC-Bus zugreifen
Lesen:
- über "mailbox" mit Adresse 3FE hex relativ zur Basisadresse
- Data-Flag = Bit 1 von 800 hex rel. zur Basisadresse
wenn gesetzt -> Daten liegen im Speicher an Adresse 3FE hex
Schreiben:
- Busy-Flag = Bit 0 an Adr. 800 hex relativ zu Basisadr.
wenn nicht gesetzt -> Controller erwartet Daten
- Schreiben eines Bytes an Adresse 3FC hex relativ zur Basisadresse
- selbes Byte an Adresse 3FF hex (wichtig: in dieser Reihenfolge)
Lesen in motors.cpp
char TC_812ISA::GetChar(void)
{
char chr = 0;
Delay(1);
// Wenn Bit 1 gesetzt ist, so liegen Daten
von der Steuerung an.
if(!(*lpFlag & 0x02)) //
text ready-flag (Data-Flag)
return 0;
// no data available
chr = *lpIn; //
read data
Delay(2);
// wozu ???
return chr;
};
Schreiben in motors.cpp
int TC_812ISA::PutChar(const char c)
{
int time1,time2;
Delay(3); //
wozu ????
//solange Bit 0 (Busy-Flag) an Adresse
lpFlag gesetzt (=1)
//oder time1 > 0 wird die Schleife
abgearbeitet (gewartet)
for(time1 = 30000; (*lpFlag & 0x01) && time1;
time1--); // solange Busy-Flag gesetzt
//checken ob timeout (time1=0) aufgetreten
//--> falls nicht (time1>0)
und damit Bit 0 (Busy-Flag) an lpFlag=0
//wird an Adresse lpOut1 und
danach an lpOut2 mit Verzoegerung geschrieben
if(time1)
{
*lpOut1 = c; //
write data
Delay(3); //
Verzoegerung
*lpOut2 = c; //
generate internal signal
}
//Warten bis Controller wieder schreibbereit
(Bit 0 (Busy-Flag) von lpFlag = 0)
for(time2 = 30000; (*lpFlag & 0x00) && time2;
time2--);
Delay(3); //
wozu ?????
return time1; //
Returnwert 0=timeout, sonst OK
};
ieee.h (Prototypen
der dll-Fkt., Typedefs für Fkt.typen (zum 'Überladen' der dll-Funktionen)
win488.dll (16-Bit DLL)
motors.cpp (Motortyp TC_812GPIB)
im Headerfile werden Typedefs für Funktionshüllen erstellt,
die später die Funktionen
aus win488.dll übernehmen sollen
Beispiel:
ieee.h
Prototyp:
int far pascal ieee488_transmit (char far *,unsigned,int far *);
Typedef:
typedef int (WINAPI *TTransmit) (LPSTR,WORD,LPINT); //
char far * -> LPSTR
// unsigned -> WORD
// int far * -> LPINT
motors.cpp
// Laden der 16-Bit Dll
hGPIBModule = LoadLibrary("win488.dll");
// Ueberladen von ieee488_transmit aus win488.dll
nach gTransmit
(FARPROC)gTransmit = GetProcAddress(hGPIBModule,"IEEE488_TRANSMIT");
- Motorcontroller fuer 2 Achsen basierend auf dem Controller-
chip LM628 von National Semiconductor
- die Kommunikation zwischen dem C-832 und dem Computer erfolgt
ueber den I/O Adress Bereich ueber 200 hex mittels Portbefehle
- um zu schreiben oder zu lesen sind folgende 2 Schritte not-
wendig:
- als erstes wird ein select byte in das interne Adressregister
(siehe oben) geschrieben
- als naechstest wird das Kommando oder die Daten in den ge-
waehlten Kanal (entsprechende Achse) geschrieben
- z.B.: will man ein Kommando schreiben oder den Lesestatus
fuer den Kanal 1 erfragen, so schreibt man eine 00 in das
Register, welches als I/O Adresse festgelegt wurde
( Standard Einstellung auf dem Board mittels DIP-Schalter
ist 210 hex )
- die Adresse 210hex ist fuer das Adressregister und 211hex ist
fuer das Datenregister
- um den jeweiligen Controller fuer eine Achse auszuwaehlen,
ist eine Auswahl im Adressregister zu machen
- Regadr 7 6 5 4 3 2 1 0
Bit 4 bis 7 werden nicht benutzt !
Aufschluesselung fuer Bit 0 bis 3 :
x000 Command/Status register, Controller #1
x001 Data register, Controller #1
x010 Command/Status register, Controller #2
x011 Data register, Controller #2
x111 Read and Reset interrupt source
- desweiteren werden ueber diese Bits noch interne Funktionen
aktiviert oder deaktiviert
z.B. Interrupt Enable/Disable
Zentrale Funktionen in unserem Programm:
long TC_832::Drive628(BYTE cmd,WORD ctrl_word,long param)
{
// load static variables
::config = cConfig; //
Konfigurations Register
::drive = nOnBoardId;
::baddr = wBaseAddr; //
210 hex, wird aus ini-File gelesen
::raddr = (::config | (::drive<<1));
return Drive628c(cmd,ctrl_word,param,::baddr,::raddr);
};
typedef struct
{
BYTE cmd; //
command
int report; //
Ctrl(0)/Report(1) command
int data; //
max. datawords
int length32; //
16(0)/32(1) bit valus
} TCSet;
static TCSet CmdSet[] =
{// LM628/629-commands exept RDSTAT
{RESET, 0,0,0}, {PORT8, 0,0,0}, {PORT12,0,0,0}, {DFH,
0,0,0},
{SIP, 0,0,0}, {LPEI, 0,1,0}, {LPES,
0,1,0}, {SBPA, 0,2,1},
{SBPR, 0,2,1}, {MSKI, 0,1,0}, {RSTI, 0,1,0},
{LFIL, 0,4,0},
{UDF, 0,0,0}, {LTRJ, 0,3,1}, {STT,
0,0,0}, {RDSTAT,1,1,0},
{RDSIGS,1,1,0}, {RDIP, 1,1,1}, {RDDP, 1,1,1},
{RDRP, 1,1,1},
{RDDV, 1,1,1}, {RDRV, 1,1,0}, {RDSUM, 1,1,0}
};
long Drive628c(BYTE cmd,WORD ctrl_word,long param,WORD base,WORD
regaddr)
{
TCSet *pCmdDesc;
if(TC_832::bIdle)
// Ist Drive nicht in Benutzung ?
{
TC_832::bIdle = FALSE; //
Ja! Dann Status in "benutzt"
};
TC_832::bIdle = TRUE;
// sonst, Status wird in "nicht benutzt" gesetzt
// Wozu macht er das eigentlich ??
// an dieser Stelle ist TC_832::bIdle immer True!
pCmdDesc = CmdSet;
// Kommandosatz durchgehen
if(cmd==RDSTAT)
// Liegt Kommando "RDSTAT"(Read Status) vor ?
{
outp(base,regaddr);
// Ja! Fordere Auslesen des Configuration Data Registers
TC_832::bIdle = FALSE; //
Setze Status in "benutzt"
return((long)inp(base
+ 1)); // Lese die entsprechenden Daten aus dem Datenregister
und
}
// gebe sie zurueck
else
while(cmd != pCmdDesc->cmd)
pCmdDesc++; // Wenn nicht RDSTAT, dann gehe Kommandosatz
durch
LM628Ready(base);
// Test ob Busy-Bit gesetzt,(Test ist aber sinnlos,
// wenn er nicht ausgewertet wird !!)
outp(base + 1,cmd); // Sende Kommando an Controller
if(pCmdDesc->data)
// nur alle Data Kommandos
{
// read/write data
if((cmd==LTRJ) | (cmd==LFIL))
// wenn "LTRJ"- oder "LFIL"-Kommando
PutWord(ctrl_word,base,regaddr);
//
dann schreibe
if(pCmdDesc->report)
// Liegt ein Report-Kommando vor?
{
// Ja! Report Kommando liegt vor
if(pCmdDesc->length32)
// Hat es einen 32Bit Parameter ?
{
TC_832::bIdle = FALSE; //
Setze Status in "benutzt"
return GetDWord(base,regaddr); // hole 32 Bit data
}
else
// ansonsten
{
TC_832::bIdle = FALSE; //
Setze Status in "benutzt"
return
(long)GetWord(base,regaddr);// hole 16 Bit data
}
}
else
// wenn kein Report Kommando
{
// dann nur noch Control Kommando moeglich
if(pCmdDesc->length32)
// 32Bit ?
PutDWord(param,base,regaddr); //
hole 32 Bit parameter
else
// ansonsten
PutWord((int)param,base,regaddr);// hole 16 Bit parameter
}
}
TC_832::bIdle = FALSE;
// Setze Status in "benutzt"
return 0;
};
int GetWord(WORD base,WORD regaddr)
{
union {
int integer;
BYTE byte[2];
} temp;
LM628Ready(base);
outp(base,regaddr + 1);
temp.byte[1] = inp(base + 1);
temp.byte[0] = inp(base + 1);
return temp.integer;
};
long GetDWord(WORD base,WORD regaddr)
{
union {
long lval;
BYTE byte[4];
} temp;
LM628Ready(base);
outp(base,regaddr + 1);
temp.byte[3] = inp(base + 1);
temp.byte[2] = inp(base + 1);
LM628Ready(base);
outp(base,regaddr+1);
temp.byte[1] = inp(base + 1);
temp.byte[0] = inp(base + 1);
return temp.lval;
};
void PutWord(int data,WORD base,WORD regaddr)
{
union {
int integer;
BYTE byte[2];
} temp;
LM628Ready(base);
outp(base,regaddr + 1);
Delay(2);
temp.integer = data;
outp(base + 1,temp.byte[1]);
outp(base + 1,temp.byte[0]);
data = 1;
};
void PutDWord(long data,WORD base,WORD regaddr)
{
union {
long lval;
BYTE byte[4];
} temp;
temp.lval = data;
LM628Ready(base);
outp(base,regaddr+1);
outp(base + 1,temp.byte[3]);
outp(base + 1,temp.byte[2]);
LM628Ready(base);
outp(base,regaddr+1);
outp(base + 1,temp.byte[1]);
outp(base + 1,temp.byte[0]);
data = 1;
};
BOOL LM628Ready(WORD base)
{
int tt;
int time_out=1000;
// command register vom aktiven drive
outp(base,::config | (::drive<<1));
do {
tt = inp(base+1);
if(!(time_out--)) return FALSE; //
wenn timeout, dann false ->"nicht bereit"
}
while(tt & 0x01); //
teste ob bit 0 von "tt" gesetzt (BUSY-Bit)
if(tt & 0x02) //
teste ob bit 1 von "tt" gesetzt, welche Bedeutung ?
{
time_out++; //
warum nochmal time_out um eins erhoehen ??
}
return TRUE; // BUSY-Bit geloescht
};