COM-Ports ansteuern unter DOS (16 Bit)
-
Hallo,
heute mal keine Frage, sondern eine Lösung (bezugnehmend auf einen Thread im ANSI-C Forum). Warum ich es dann hier poste? Weil es eigentlich nicht zu ANSI-C gehört und im FAQ des Konsolenforums bereits ein ähnlicher Thread besteht, aber nur für 32 Bit Programme. Deswegen hier gleich die Bitte diesen Thread ins Konsolen-FAQ zu stellen.
Nun zum Quellcode:
//comports.c #include "comports.h" #define EOI 0x20 #define MIN_BUFSIZE 100 //*** Strukturen //Datenstruktur für den Interrupt eines COM-Ports struct comport { int buffsize; //Speichert die Größe des Buffers int head; //Index zum Einlesen der Daten vom COM-Port in den Buffer int tail; //Index zum Einlesen der Daten aus dem Buffer für das Programm char handshake; unsigned int buffer[MIN_BUFSIZE]; //Zeiger auf den reservierten Bereich für den Buffer }; //*** "versteckte" Funktionen void initinterrupt(char port); int holintadresse(char port); void deinitinterrupt(char port); //*** Variablen //Speichert die Daten für die COM-Ports struct comport *cport[MAX_COM]={0}; //Speichert die Adressen der COM-Ports für inp und outp int com[MAX_COM]={0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x2f8, 0x2f8, 0x2f8, 0x2f8}; //Speichert die Interruptnummer für die COM-Ports int intnr[MAX_COM]={4, 3, 15, 5, 3, 3, 3, 3}; //*** Funktionsdeklarationen für die Interrupts short int_lescomport(char port); void __interrupt __far neu_int_com1(); void __interrupt __far neu_int_com2(); void __interrupt __far neu_int_com3(); void __interrupt __far neu_int_com4(); void (__interrupt __far *alt_int_com1)(); void (__interrupt __far *alt_int_com2)(); void (__interrupt __far *alt_int_com3)(); void (__interrupt __far *alt_int_com4)();
//Initialisiert den angegebenen COM-Port int initcomport(char port, long baudrate, char paritaet, char wortlaenge, char stopbits, char handshake, char irq, int bufferlaenge) /* Parameter: port = Nummer des COM-Ports - 1 (wegen den Arrays com, cport und intnr) -> Interruptnummer je nach COM-Port paritaet wortlaenge stopbits = Anzahl der Stopbits 1 oder 2 handshake bufferlaenge = L?nge des Buffers f?r die einkommenden Daten Rückgabewert: Kennzeichen, ob die Initialisierung erfolgreich war < 0 => Fehlerkennzeichen Fehlerkennzeichen: -1 = COM-Port wird nicht unterstützt -2 = Interrupt wird nicht unterstützt Setzt die Baudrate, Parität, Wortlänge, Stopbits, Handshake, Interrupt (initinterrupt()) und bufferlänge für den angegebenen COM-Port. */ { int tmp=0; if(port<0||port>=MAX_COM) { return(-1); } if(irq>15) { return(-2); } if(irq<0) { irq=intnr[port]; } if(bufferlaenge<MIN_BUFSIZE) { bufferlaenge=MIN_BUFSIZE; } handshake&=0xF; if(!cport[port]) { deinitcomport(port); } outp(com[port] + IER,0x00); //Interrupts deaktivieren outp(com[port] + LCR,0x80); //BRDL (niedriger Baudteiler) und BRDH (hoher Baudteiler) zur Verfügung stellen outp(com[port], (int)(115200L/baudrate)&0xFF); //Einstellen des BRDL outp(com[port] + IER, (int)(115200L/baudrate)>>8); //Einstellen des BRDH outp(com[port] + MCR, 0x0); outp(com[port] + MCR, 0x08 | handshake); // Set MCR user output 2 to enable ints and DTR line if((toupper(paritaet) == 'E')||(paritaet==3)) tmp = 0x18; if((toupper(paritaet) == 'O')||(paritaet==1)) tmp = 0x08; tmp |= (wortlaenge-5); tmp |= (stopbits-1) << 2; outp(com[port] + LCR, tmp & 0x1F); // Reset Bit 7 to zero for TXR and RDR intnr[port]=irq; initinterrupt(port); cport[port]=(struct comport *)malloc(sizeof(struct comport)+(bufferlaenge-MIN_BUFSIZE)*sizeof(unsigned int)); cport[port]->buffsize=bufferlaenge; cport[port]->head=0; cport[port]->tail=0; cport[port]->handshake=handshake; return port; }
//Initialisiert den Interrupt zum angegebenen COM-Port void initinterrupt(char port) /* Parameter: port = Nummer des COM-Ports -1 (wegen den Arrays com, cport und intnr) Speichert die alte Funktion des angegebenen Interrupts und ersetzt diese durch eine neue, eigene Funktion. Kennzeichnet den Interrupt als benutzt in der Interrupttabelle. */ { int tmp=0; if(intnr[port]<0||intnr[port]>15) { return; } switch(port) { case 0: alt_int_com1=_dos_getvect(holintadresse(port)); _dos_setvect(holintadresse(port), neu_int_com1); break; case 1: alt_int_com2=_dos_getvect(holintadresse(port)); _dos_setvect(holintadresse(port), neu_int_com2); break; case 2: alt_int_com3=_dos_getvect(holintadresse(port)); _dos_setvect(holintadresse(port), neu_int_com3); break; case 3: alt_int_com4=_dos_getvect(holintadresse(port)); _dos_setvect(holintadresse(port), neu_int_com4); break; default: return; } outp(com[port]+IER,0x01); //Interrupts aktivieren _disable(); tmp = inp(0x21); //Aktuelle Interrupts holen tmp &= ~(0x01 << intnr[port]); //Interrupt aktivieren outp(0x21,tmp); //Interrupts setzen _enable(); }
//ermittelt die Speicheradresse eines Interrupts int holintadresse(char port) /* Parameter: port = Nummer des COM-Ports -1 (wegen den Arrays com, cport und intnr) Rückgabewert: Speicheradresse des Interrupts ermittelt zur Interruptnummer des angegebenen COM-Ports die Speicheradresse und gibt diese zurück */ { if(intnr[port]>7) { return(intnr[port]+0x68); } else { return(intnr[port]+0x8); } }
//Holt ein Byte aus dem Buffer int holbyte(char port) /* Parameter: port = Nummer des COM-Ports - 1 (wegen den Arrays com, cport und intnr) Rückgabewert: Byte, das gelesen wurde. -1 = Kein neues Byte -2 = COM-Port nicht verfügbar (entweder nicht unterstützt oder nicht initialisiert) Holt ein Byte aus dem Buffer des angegebenen Ports, wenn ein neues Zeichen vorhanden ist. Setzt dabei tail auf den nächsthöheren Wert für das nächste einzulesende Zeichen. Ist tail am Ende des Buffers wird tail wieder auf den Anfang des Buffers gesetzt. Das neue Zeichen wird zurückgegeben. Ansonsten wird -1 zurückgegeben als Kennzeichen, dass kein neues Zeichen vorhanden ist. */ { int rueck=0; if(port>-1&&port<MAX_COM) { if(cport[port]) { if(cport[port]->head==cport[port]->tail) { return(-1); } rueck=cport[port]->buffer[cport[port]->tail++]; if(cport[port]->tail>=cport[port]->buffsize) { cport[port]->tail=0; } return(rueck); } } return(-2); }
//Sendet ein Byte void sendebyte(char port, int byte) /* Parameter: port = Nummer des COM-Ports - 1 (wegen den Arrays com, cport und intnr) Sendet ein Byte an den angegebenen Port. */ { int status = 0; if(port>-1&&port<MAX_COM) { if(cport[port]) { do { status = (inp(com[port]+LSR)) & 0x60; if(status == 0x60) { outp(com[port], byte); } }while(status != 0x60); } } }
//Sendet einen String void sendestring(char port, const char *zeichen) /* Parameter: port = Nummer des COM-Ports - 1 (wegen den Arrays com, cport und intnr) Sendet einen String an den angegebenen Port. Benutzt dabei die Funktion sendebyte(). */ { while(*zeichen) { sendebyte(port, *zeichen++); } }
//Deinitialisiert den angegebenen COM-Port void deinitcomport(char port) /* Parameter: port = Nummer des COM-Ports -1 (wegen den Arrays com, cport und intnr) */ { if(!cport[port]) { return; } deinitinterrupt(port); outp(com[port] + IER,0x00); outp(com[port] + MCR,0x00); free(cport[port]); cport[port]=NULL; }
//Deinitialisiert den angegebenen Interrupt void deinitinterrupt(char port) /* Parameter: port = Nummer des COM-Ports -1 (wegen den Arrays com, cport und intnr) Überschreibt die eigene Funktion für den Interrupt mit der alten Funktion. Kennzeichnet den Interrupt als frei in der Interrupttabelle. */ { int tmp; _disable(); tmp = inp(0x21); // Get current 8259 state tmp |= 0x01 << intnr[port]; outp(0x21,tmp); _enable(); switch(port) { case 0: _dos_setvect(holintadresse(port), alt_int_com1); break; case 1: _dos_setvect(holintadresse(port), alt_int_com2); break; case 2: _dos_setvect(holintadresse(port), alt_int_com3); break; case 3: _dos_setvect(holintadresse(port), alt_int_com4); break; } }
void setzrts(char port, int kz) //aj //Setzt die RTS-Leitung des Ports port auf kz (1=AN; 0=AUS) { int hilf; if(cport[port]) { hilf=inp(com[port]+MCR); if(kz) { hilf|=1; } else { hilf&=0xfe; } outp(com[port]+MCR, hilf); } }
void setzdtr(char port, int kz) //aj //Setzt die DTR-Leitung des Ports port auf kz (1=AN; 0=AUS) { int hilf; if(cport[port]) { hilf=inp(com[port]+MCR); if(kz) { hilf|=2; } else { hilf&=0xfd; } outp(com[port]+MCR, hilf); } }
int holcts(char port) //aj //gibt zurück, ob die CTS-Leitung des Ports port aktiv ist { if(cport[port]) { return(inp(com[port]+MSR)&0x10); } else { return(0); } }
int holdcd(char port) //aj //gibt zurück, ob die DCD-Leitung des Ports port aktiv ist { if(cport[port]) { return(inp(com[port]+MSR)&0x80); } else { return(0); } }
//liest Daten aus dem angegebenen COM-Port short int_lescomport(char port) /* Parameter: port = Nummer des COM-Ports - 1 (wegen den Arrays com, cport und intnr) Rückgabewert: gibt den Status des COM-Ports zurück Liest neue Daten vom angegebenen COM-Port und überprüft den aktuellen Status. Schreibt das neue Zeichen in den zugehörigen Buffer des COM-Ports an der Stelle von head. Überschreitet head die Buffergröße, dann wird head auf 0 gesetzt, um am Anfang des Buffers wieder zu beginnen. */ { short status = 0; status = inp(com[port]+IIR); status&=6; if(status & 4) cport[port]->buffer[cport[port]->head++]=inp(com[port]); if(status & 2) { inp(com[port]+LSR); //Löscht Fehler (?) } if(status == 0) { inp(com[port]+MSR); } if(cport[port]->head >= cport[port]->buffsize) cport[port]->head=0; return(status); }
//neue Interruptfunktion für COM-Port 1 void __interrupt __far neu_int_com1() /* schreibt eingehende Daten in den Buffer */ { short status; do { status=int_lescomport(0); }while(status&4); outp(0x20,EOI); }
//neue Interruptfunktion für COM-Port 2 void __interrupt __far neu_int_com2() /* schreibt eingehende Daten in den Buffer */ { short status; do { status=int_lescomport(1); }while(status&4); outp(0x20,EOI); }
//neue Interruptfunktion für COM-Port 3 void __interrupt __far neu_int_com3() /* schreibt eingehende Daten in den Buffer */ { short status; do { status=int_lescomport(2); }while(status&4); outp(0x20,EOI); }
//neue Interruptfunktion für COM-Port 4 void __interrupt __far neu_int_com4() /* schreibt eingehende Daten in den Buffer */ { short status; do { status=int_lescomport(3); }while(status&4); outp(0x20,EOI); }
//comports.h #ifndef _COMPORTS_H #define _COMPORTS_H "comports.h" #include <dos.h> #include <ctype.h> #include <alloc.h> #include <stdio.h> #define MAX_COM 8 #define IER 1 #define IIR 2 #define LCR 3 #define MCR 4 #define LSR 5 #define MSR 6 #ifdef __cplusplus extern "C" { #endif //*** Funktionsdeklarationen int initcomport(char port, long baudrate, char paritaet, char wortlaenge, char stopbits, char handshake, char irq, int bufferlaenge); int holbyte(char port); void sendebyte(char port, int byte); void sendestring(char port, const char *zeichen); void deinitcomport(char port); void setzrts(char port, int kz); void setzdtr(char port, int kz); int holcts(char port); int holdcd(char port); //*** Variablen extern int com[MAX_COM]; #ifdef __cplusplus } #endif #endif
Anmerkung:
Da dieser Code sehr nahe an der Hardware arbeitet, sollte man damit vorsichtig umgehen, besonders, wenn man selber dran rumdoktort. Ich übernehme keine Verantwortung für eventuelle Schäden.
Ich habe den Quellcode unter Borland Compiler 3.0 und Borland Compiler 5.02 ohne Fehler kompiliert. Es läuft auf jeden Fall unter DOS, Win9x und WinNT.EditBySide: Hab mir erlaubt CPP-Code-Tags einzufügen damit es in der FAQ besser aussieht :).
EditByAJ: Sorry für die Aufdrüselung mit den Codetags, aber ohne könnte man den Beitrag generell nicht mehr ansehen.