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.


Anmelden zum Antworten