Tach auch!
Vielleicht hilft's ja ergänzend: Ich hatte damals das Protokoll zum Parallel-Interface aus dem im Begleitheft abgedruckten Assembler-Listing reverse-engineered und in ein Modula-2-Modul gegossen. Lesbar müsste es auch für C-Programmierer sein, sonst einfach fragen:
Code: Alles auswählen
DEFINITION MODULE FischerInterface;
(* Low-Level-Hardwareschnittstelle zum fischertechnik-computing-
Interface. *)
PROCEDURE Init;
(* Schaltet alle Ausg„nge inaktiv. *)
CONST
Motors = 4;
(* Anzahl der ansteuerbaren Motoren. *)
TYPE
Motor = [1..Motors];
(* Die Nummer eines Motors. *)
Direction = (* Die Richtung, in der sich ein Motor dreht. *)
(off,
(* Der Motor (der Ausgang) ist abgeschaltet. *)
left,
(* Der Motor wird zum Rechtsdrehen eingeschaltet (abh„ngig von der
Verkabelung am Modell). *)
right);
(* Der Motor wird zum Linksdrehen eingeschaltet (abh„ngig von der
Verkabelung am Modell). *)
PROCEDURE SetMotor (n: Motor;
Dir: Direction);
(* Schaltet einen der Motoren.
in: n Die Nummer des anzusteuernden Motors.
Dir Die gewnschte Drehrichtung bzw. Polung des Ausgangs.
*)
CONST
Switches = 8;
(* Anzahl der abfragbaren Taster/Schalter. *)
TYPE
Switch = [1..Switches];
(* Die Nummer eines Schalters. *)
PROCEDURE SwitchStatus (n: Switch): BOOLEAN;
(* Fragt einen Schalter ab.
in: n Die Nummer des abzufragenden Schalters.
out: (RETURN) TRUE genau dann, wenn der Schalter auf Durchgang
geschaltet ist bzw. die entsprechende
Eingangsleitung des Interfaces auf +5 V liegt).
*)
CONST
AnalogInputs = 2;
(* Anzahl analoger Eingabekan„le. *)
TYPE
AnalogInput = [1..AnalogInputs];
(* Die Nummer eines Analog-Eingangs. *)
PROCEDURE AnalogStatus (n: AnalogInput): CARDINAL;
(* Fragt einen der Analog-Eing„nge ab.
in: n Die Nummer des abzufragenden Eingangs.
out: (RETURN) Eine Zahl, deren H”he in einem linearen Zusammenhang
mit dem an dem Analogeingang n anliegenden
Widerstand steht. Sonst ist ber diese Zahl nichts
bekannt.
Bevor diese Funktion fr eine Steuerung verwendet werden kann, muá der
m”gliche Wertebereich durch geeignete Maánahmen des Anwenderprogramms
kalibriert werden.
*)
END FischerInterface.
Code: Alles auswählen
IMPLEMENTATION MODULE FischerInterface;
(* Low-Level-Hardwareschnittstelle zum fischertechnik-computing-
Interface. *)
(* UPDATES:
14.07.1989 sf SwitchStatus f„llt nicht mehr so leicht auf prellende
Schalter herein, da ein Schalterzustand erst dann als
gltig angesehen wird, wenn zweimal sofort hintereinander
der selbe Zustand gemessen wird.
15.07.1989 sf Erkennung von Schalterprellung verbessert.
22.07.1989 sf InstallTermProc (Init) eingefhrt.
24.02.1990 sf Portadressen fr Geosoft-Laptop ge„ndert. Die Motoren
und Schalter waren verkehrt herum durchnumeriert;
korrigiert.
09.04.1992 sf Anpassung an Modula-2 V4.0.
13.10.1992 sf Die Port-Adressen werden selbst berechnet. Das Interface
kann an eine beliebige parallele Schnittstelle von LPT1:
bis LPT4: angeschlossen sein. Dies wird ber ein IniFile
gesteuert.
02.08.1994 sf Beim 486/66DX2 festgestellt: Die Schalter werden nicht
sauber abgefragt - der in Modula-2 programmierte PC ist
(selbst in einer Windows-DOS-BOX) zu schnell fr das
Interface. Im IniFile ist eine neue Variable
"ProcessorSpeed" mit einem LONGINT-Wert definiert.
Die neue Prozedur KleineVerzoegerung bremst das ganze
damit ein biáchen.
*)
FROM SYSTEM IMPORT DISABLE, ENABLE, INB, OUTB;
FROM RTSTerm IMPORT InstallTermProc;
FROM Delay IMPORT Delay;
FROM IniFiles IMPORT ReadIniFileAlone, IniMessage, IniAction;
FROM Strings IMPORT CompareStr;
FROM NumberConversion IMPORT StringToLongInt;
TYPE
BitInByte = [0..7];
Byte = SET OF BitInByte;
VAR
PrinterPort, BusyPort: CARDINAL;
(* Adresse des Printerports und des Busy-Ports der verwendeten
Parallel-Schnittstelle. *)
MotorStatus: Byte;
(* Angaben ber den aktuellen Zustand aller Ausg„nge. *)
ProcessorSpeed: LONGINT;
(* Je h”her die Zahl (aus FISCHERI.INI), desto l„nger wird dem
Interface an manchen Stellen ber die Prozedur KleineVerzoegerung
Zeit gelassen. *)
PROCEDURE KleineVerzoegerung;
(* Wartet ein kleines biáchen. *)
VAR i: LONGINT;
BEGIN
i := 0;
WHILE i < ProcessorSpeed DO
INC (i)
END
END KleineVerzoegerung;
PROCEDURE SetOutput (Status: Byte);
(* Setzt Ausgaberegister entsprechend Status.
in: Status Die Information ber den gewnschten Zustand aller
Ausg„nge.
*)
VAR Bit: BitInByte;
BEGIN
MotorStatus := Status;
DISABLE;
FOR Bit := 0 TO 7 DO
IF Bit IN Status THEN
OUTB (Byte {5, 4, 2}, PrinterPort);
OUTB (Byte {5, 4, 3, 2}, PrinterPort)
ELSE
OUTB (Byte {5, 4}, PrinterPort);
OUTB (Byte {5, 4, 3}, PrinterPort)
END
END;
OUTB (39H, PrinterPort);
ENABLE
END SetOutput;
(* Exportierte Prozeduren: *)
PROCEDURE Init;
(* Schaltet alle Ausg„nge inaktiv. *)
BEGIN
SetOutput (Byte {})
END Init;
PROCEDURE SetMotor (n: Motor;
Dir: Direction);
(* Schaltet einen der Motoren.
in: n Die Nummer des anzusteuernden Motors.
Dir Die gewnschte Drehrichtung bzw. Polung des Ausgangs.
*)
VAR MotorIndex, DirByte: Byte;
BEGIN
CASE n OF
1:
MotorIndex := Byte {7, 6} |
2:
MotorIndex := Byte {5, 4} |
3:
MotorIndex := Byte {3, 2} |
4:
MotorIndex := Byte {1, 0}
END;
CASE Dir OF
off:
DirByte := Byte {0..7} |
left:
DirByte := Byte {1, 3, 5, 7} |
right:
DirByte := Byte {0, 2, 4, 6}
END;
SetOutput ((MotorStatus + MotorIndex) / (DirByte * MotorIndex))
END SetMotor;
PROCEDURE SwitchStatus (n: Switch): BOOLEAN;
(* Fragt einen Schalter ab.
in: n Die Nummer des abzufragenden Schalters.
out: (RETURN) TRUE genau dann, wenn der Schalter auf Durchgang
geschaltet ist bzw. die entsprechende
Eingangsleitung des Interfaces auf +5 V liegt).
*)
PROCEDURE ActualStatus (n: Switch): BOOLEAN;
(* Fragt den Schalterzustand ab. *)
VAR
Bit: BitInByte;
SwitchByte, InputByte: Byte;
BEGIN (* ActualStatus *)
SwitchByte := Byte {};
DISABLE;
OUTB (32H, PrinterPort);
KleineVerzoegerung;
OUTB (3AH, PrinterPort);
FOR Bit := 7 TO 0 BY -1 DO
KleineVerzoegerung;
INB (InputByte, BusyPort);
IF 7 IN InputByte THEN
INCL (SwitchByte, Bit)
END;
OUTB (30H, PrinterPort);
KleineVerzoegerung;
OUTB (38H, PrinterPort)
END;
ENABLE;
RETURN n - 1 IN SwitchByte
END ActualStatus;
VAR s1, s2: BOOLEAN;
BEGIN
(* Prellen abfangen: *)
s1 := ActualStatus (n);
s2 := ActualStatus (n);
WHILE s1 # s2 DO
Delay (1);
s1 := s2;
s2 := ActualStatus (n)
END;
RETURN s2
END SwitchStatus;
PROCEDURE AnalogStatus (n: AnalogInput): CARDINAL;
(* Fragt einen der Analog-Eing„nge ab.
in: n Die Nummer des abzufragenden Eingangs.
out: (RETURN) Eine Zahl, deren H”he in einem linearen Zusammenhang
mit dem an dem Analogeingang n anliegenden
Widerstand steht. Sonst ist ber diese Zahl nichts
bekannt.
Bevor diese Funktion fr eine Steuerung verwendet werden kann, muá der
m”gliche Wertebereich durch geeignete Maánahmen des Anwenderprogramms
kalibriert werden.
*)
CONST max = MAX (CARDINAL);
VAR
count: CARDINAL;
InputByte: Byte;
BEGIN
count := 0;
DISABLE;
CASE n OF
1:
OUTB (0A0H, PrinterPort) |
2:
OUTB (090H, PrinterPort)
END;
OUTB (38H, PrinterPort);
REPEAT
INB (InputByte, BusyPort);
INC (count)
UNTIL NOT (7 IN InputByte) OR (count = max);
ENABLE;
RETURN count
END AnalogStatus;
PROCEDURE UseLPTPort (PortNumber: CHAR);
(* Berechnet die Portadressen des angegebenen LPT-Ports ("1" - "4"). *)
VAR LPTPorts [40H:08H]: ARRAY ["1".."4"] OF CARDINAL;
BEGIN
PrinterPort := LPTPorts [PortNumber];
BusyPort := PrinterPort + 1
END UseLPTPort;
PROCEDURE HandleIniMessage (VAR E: IniMessage);
(* Behandelt das IniFile. *)
VAR ok: BOOLEAN;
BEGIN
WITH E DO
CASE Action OF
StartSectionRead:
interested := CompareStr (OldSection, "FischerInterface") = 0 |
ReadEntry:
IF (CompareStr (EntryName, "LPTPort") = 0)
AND (EntryInfo [0] >= "1") AND (EntryInfo [0] <= "4")
AND (EntryLen = 1) THEN
UseLPTPort (EntryInfo [0])
ELSIF CompareStr (EntryName, "ProcessorSpeed") = 0 THEN
StringToLongInt (EntryInfo, ProcessorSpeed, ok);
IF NOT ok THEN
ProcessorSpeed := 0
END
END
END
END
END HandleIniMessage;
PROCEDURE InitPortAddresses;
(* Bestimmt die zu verwendenden Portadressen. *)
VAR ok: BOOLEAN;
BEGIN
UseLPTPort ("1");
ProcessorSpeed := 0;
ok := ReadIniFileAlone ("FISCHERI.INI", HandleIniMessage)
END InitPortAddresses;
BEGIN (* FischerInterface *)
InitPortAddresses;
Init;
InstallTermProc (Init)
END FischerInterface.
CARDINAL ist ein nicht vorzeichenbehaftetes 16-Bit-Integer. OUTB (Byte {5, 4, 2}, PrinterPort) sendet ein Byte mit gesetzten Bits 2, 4, 5 (also ein 00110100 aka int 52) über das I/O-Chip vom Parallelport an den entprechenden I/O-Port. ENABLE/DISABLE erlauben bzw. verhindern andere Software-Interrupts. In SetOutput ((MotorStatus + MotorIndex) / (DirByte * MotorIndex)) findet bitweise Arithmetik statt: + ist logisches ODER, * ist logisches UND und / ist logisches XOR. Die Umlaute in den Kommentaren waren MS-DOS-Umlaute von Codepage 437 damals, die hab ich nicht extra gewandelt.
Hey - damals war das ft-Interface noch Open Source Software!
Gruß,
Stefan