A
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.