Hallo Gemeinde,
ich bin auf der Suche nach C oder C++ Quellcode, welcher auf Daten zwischen zwei RS232-Schnittstellen transferiert.
Mein Ziel ist eine Art Konsole, bei der ich auf beiden Computern ein paar Eingaben machen kann, und diese danach auf der Konsole des anderen Rechners sehe.
Es ist sicherlich Standard, aber ich finde nichts im Internet.
Ich bin Java-Programmierer, aber leider noch nicht ganz so tief in der C/C++-Programmierung, dass ich das Programm nun auf Hardwareebene ohne weiteres hinbekomme.
Habt Ihr Beispiele oder Quellcode-Snipplets, die ich mir anschauen kann und damit zum Endergebnis komme?
Vielen Dank
Michael
P.S.: Sry. Das Ding muss später in DOS laufen (16-Bit).
P.P.S.:Entwicklungsumgebung ist Microsoft Visual C++ und OpenWatcom als 16-Bit-Kompiler
Rs232-Übertragung
-
- DOS-Übermensch
- Beiträge: 1035
- Registriert: Mi 31. Jan 2007, 19:04
- Wohnort: Halle
- Kontaktdaten:
Ich habe zwar kein C Quellcode, aber eine kurze Vorlage in Turbo Pascal, die sich sicher ganz flott konvertieren laesst.
(Aus der SWAG):
So ich empfehle zusaetzlich auf jeden Fall Literatur zu den UARTs, zB TI16C550.PDF um zu verstehen, welche Chip Register was bewirken.
Bitte Latches dabei beachten.
Desweiteren solltest Du Hardware Flusskontrolle uber RTS/CTS und Auto-Flow Control verwenden.
Bei einem PC muss man zusaetzlich den Interrupt Controller 8259 korrekt setzen, so dass der UART Interrupt auch beim Programm ankommt.
Man kann im Gegensatz zu oben auch beim senden den IRQ nutzen.
In Anlehnung ein weiteres Codebeispiel in 6502 Assembler:
Beide Routinen funktionieren nahezu identisch, bei der zweiten ist zu beachten, dass die Teiler andere sind fuer die Baudrate, da der Referenzquartz hoeher getaktet ist.
(Aus der SWAG):
Code: Alles auswählen
{$B-} { Short circuit boolean ON }
{$I-} { I/O checking OFF }
{$R-} { Range checking OFF }
{$S-} { Stack checking OFF }
{$V-} { Var-str checking OFF}
UNIT ASYNC2;
{PD async unit debugged and modified for doorgame use by Joel Bergen}
{added com3 & com4 support and xon/xoff handshaking }
{various bug fixes by Gary Gordon & Joel Bergen Jan 1990}
{Last revised: 1/14/90}
{still needs check for existance of comm port in Async_Open routine}
INTERFACE
USES DOS, CRT;
VAR
Async_CheckCTS : BOOLEAN;
PROCEDURE Async_Init;
{ initialize variables, call first to initialize }
PROCEDURE Async_Close;
{ reset the interrupt system when UART interrupts no longer needed }
{ Turn off the COM port interrupts. }
{ **MUST** BE CALLED BEFORE EXITING YOUR PROGRAM; otherwise you }
{ will see some really strange errors and have to re-boot. }
FUNCTION Async_Open(ComPort,BaudRate : WORD) : BOOLEAN;
{ open a communications port at 8/n/1 with supplied port & baud }
{ Sets up interrupt vector, initialies the COM port for }
{ processing, sets pointers to the buffer. Returns FALSE if COM }
{ port not installed. }
FUNCTION Async_Buffer_Check : BOOLEAN;
{ see if a character has been received }
{ If a character is available, returns TRUE }
{ Otherwise, returns FALSE }
FUNCTION Async_Read : CHAR;
{ read a character, assuming it is ready}
PROCEDURE Async_Send(C : CHAR);
{ transmit a character }
PROCEDURE Async_Hangup;
{ drop carrier by dropping DTR}
FUNCTION Async_CarrierDetect : BOOLEAN;
{ true if carrier detected }
{----------------------------------------------------------------------------}
IMPLEMENTATION
CONST
I8088_IMR = $21; { port address of the Interrupt Mask Register }
AsyncBasePort : ARRAY[1..4] OF WORD = ($03F8,$02F8,$03E8,$02E8);
AsyncIRQ : ARRAY[1..4] OF WORD = (4,3,4,3);
Async_Buffer_Max = 1024; { size of input buffer }
Ier = 1;
Lcr = 3;
Mcr = 4;
Lsr = 5;
Msr = 6;
VAR
Async_OriginalVector : POINTER;
Async_OriginalLcr : INTEGER;
Async_OriginalImr : INTEGER;
Async_OriginalIer : INTEGER;
Async_Buffer : ARRAY[0..Async_Buffer_Max] OF CHAR;
Async_Open_Flag : BOOLEAN; { true if Open but no Close }
Async_Pause : BOOLEAN; { true if paused (Xoff received) }
Async_Port : INTEGER; { current Open port number (1..4) }
Async_Base : INTEGER; { base for current open port }
Async_Irq : INTEGER; { irq for current open port }
Async_Buffer_Overflow: BOOLEAN; { True if buffer overflow has happened }
Async_Buffer_Used : WORD; { number of characters in input buffer }
{ Async_Buffer is empty if Head = Tail }
Async_Buffer_Head : WORD; { Locn in Async_Buffer to put next char }
Async_Buffer_Tail : WORD; { Locn in Async_Buffer to get next char }
PROCEDURE DisableInterrupts; INLINE($FA {cli} ); {MACROS}
PROCEDURE EnableInterrupts; INLINE($FB {sti} );
PROCEDURE Async_Isr; INTERRUPT;
{ Interrupt Service Routine
Invoked when the UART has received a byte of data from the
communication line }
CONST
Xon = #17; {^q resume}
Xoff = #19; {^s pause}
VAR
c : CHAR;
BEGIN
EnableInterrupts;
IF Async_Buffer_Used < Async_Buffer_Max THEN BEGIN
c := CHR(PORT[Async_Base]);
CASE c OF
Xoff : Async_Pause:=TRUE;
Xon : Async_Pause:=FALSE;
ELSE BEGIN
Async_Pause:=FALSE;
Async_Buffer[Async_Buffer_Head] := c;
IF Async_Buffer_Head < Async_Buffer_Max THEN
INC(Async_Buffer_Head)
ELSE
Async_Buffer_Head := 0;
INC(Async_Buffer_Used);
END;
END;
END ELSE Async_Buffer_Overflow := TRUE;
DisableInterrupts;
PORT[$20] := $20;
END; { Async_Isr }
PROCEDURE Async_Init;
{ initialize variables }
BEGIN
Async_Open_Flag := FALSE;
Async_Buffer_Head := 0;
Async_Buffer_Tail := 0;
Async_Buffer_Overflow := FALSE;
Async_Buffer_Used := 0;
Async_Pause := FALSE;
Async_CheckCTS := TRUE;
END; { Async_Init }
PROCEDURE Async_Close;
{ reset the interrupt system when UART interrupts no longer needed }
VAR
i, m : INTEGER;
BEGIN
IF Async_Open_Flag THEN BEGIN
DisableInterrupts; { disable IRQ on 8259 }
PORT[Async_Base + Ier] := Async_OriginalIer;
PORT[Async_Base+Lcr] := Async_OriginalLcr;
PORT[I8088_IMR] := Async_OriginalImr;
EnableInterrupts;
SETINTVEC(Async_Irq + 8,Async_OriginalVector);
Async_Open_Flag := FALSE { flag port as closed }
END
END; { Async_Close }
FUNCTION Async_Open(ComPort,BaudRate : WORD) : BOOLEAN;
VAR
i, m : INTEGER;
b : BYTE;
BEGIN
IF Async_Open_Flag THEN Async_Close;
Async_Port := ComPort;
Async_Base := AsyncBasePort[Async_Port];
Async_Irq := AsyncIRQ[Async_Port];
{ set comm parameters }
Async_OriginalLcr := PORT[Async_Base+Lcr];
PORT[Async_Base+Lcr] := $03; {set 8/n/1. This shouldn't be hardcoded}
{ set ISR vector }
GETINTVEC(Async_Irq+8, Async_OriginalVector);
SETINTVEC(Async_Irq+8, @Async_Isr);
{ read the RBR and reset any possible pending error conditions }
{ first turn off the Divisor Access Latch Bit to allow access to RBR, etc. }
DisableInterrupts;
PORT[Async_Base+Lcr] := PORT[Async_Base+Lcr] AND $7F;
{ read the Line Status Register to reset any errors it indicates }
i := PORT[Async_Base+Lsr];
{ read the Receiver Buffer Register in case it contains a character }
i := PORT[Async_Base];
{ enable the irq on the 8259 controller }
i := PORT[I8088_IMR]; { get the interrupt mask register }
Async_OriginalImr := i;
m := (1 shl Async_Irq) XOR $00FF;
PORT[I8088_IMR] := i AND m;
{ enable the data ready interrupt on the 8250 }
Async_OriginalIer := PORT[Async_Base + Ier];
Port[Async_Base + Ier] := $01; { enable data ready interrupt }
{ enable OUT2 on 8250 }
i := PORT[Async_Base + Mcr];
PORT[Async_Base + Mcr] := i OR $08;
EnableInterrupts;
{ Set baudrate}
b := PORT[Async_Base+Lcr] OR 128;
PORT[Async_Base+Lcr]:= b;
PORT[Async_Base ] := LO(TRUNC(115200.0/BaudRate));
PORT[Async_Base+1] := HI(TRUNC(115200.0/BaudRate));
PORT[Async_Base+Lcr]:= b AND 127;
{ set flags }
Async_Open_Flag := TRUE;
Async_Open := TRUE;
END; { Async_Open }
FUNCTION Async_Buffer_Check : BOOLEAN;
{ return true if character ready to receive }
BEGIN
Async_Buffer_Check := (Async_Buffer_Used <> 0);
END; { Async_Buffer_Check }
FUNCTION Async_Read : CHAR;
{ return char, use Async_Buffer_Check first! }
BEGIN
Async_Read := Async_Buffer[Async_Buffer_Tail];
INC(Async_Buffer_Tail);
IF Async_Buffer_Tail > Async_Buffer_Max THEN
Async_Buffer_Tail := 0;
DEC(Async_Buffer_Used);
END; { Async_Buffer_Check }
PROCEDURE Async_Send(c : CHAR);
{ transmit a character }
BEGIN
PORT[Async_Base + Mcr] := $0B; {turn on OUT2, DTR, and RTS}
IF Async_CheckCTS THEN
WHILE (Port[Async_Base + Msr] AND $10) = 0 DO; {wait for CTS}
WHILE (Port[Async_Base + Lsr] AND $20) = 0 DO; {wait for Tx Holding Reg Empty}
WHILE Async_Pause AND Async_CarrierDetect DO; {wait for Xon}
DisableInterrupts;
PORT[Async_Base] := ORD(c); {send the character}
EnableInterrupts;
END; { Async_Send }
PROCEDURE Async_Hangup;
BEGIN
PORT[Async_Base+Mcr] := $00; {dtr off}
DELAY(1000); {wait 1 second}
PORT[Async_Base+Mcr] := $03; {dtr on}
END;
FUNCTION Async_CarrierDetect : BOOLEAN;
{true if carrier}
VAR
b : BOOLEAN;
w : WORD;
BEGIN
w:=0; b:=TRUE;
WHILE (w<500) AND b DO BEGIN {make sure carrier stays down}
INC(w); {and is not just a fluke }
b:=(PORT[Async_Base+Msr] AND 128) <> 128; {true = no carrier};
END;
Async_CarrierDetect := NOT b;
END;
BEGIN
Async_Init;
END. { ASYNC UNIT }
Bitte Latches dabei beachten.
Desweiteren solltest Du Hardware Flusskontrolle uber RTS/CTS und Auto-Flow Control verwenden.
Bei einem PC muss man zusaetzlich den Interrupt Controller 8259 korrekt setzen, so dass der UART Interrupt auch beim Programm ankommt.
Man kann im Gegensatz zu oben auch beim senden den IRQ nutzen.
In Anlehnung ein weiteres Codebeispiel in 6502 Assembler:
Code: Alles auswählen
processor 6502
include "memory.asm"
; How it works: INITUART - initalizes the Buffers and Status
; OPEN - Open the serial connection Baudrate according to Table delivered in X
; - installs the IRQ-Routine
; CLOSE - deactivates UART-IRQs and deinstalls the IRQ Routine
; SENDBYTE/GETBYTE - adds a Byte to the Software Buffer ( Byte in A )
; - if Carry is set everything worked OK
; - if carry is cleared buffer was full/empty
; ONLY USE THIS ROUTINES TO COMMUNICATE !!! , never access the UART directly when the ISR is installed, may crash the comp.
; FIFO size of UART, 16 with 16550
FIFSIZE equ 16 ; Number of Bytes to fill Send fifo max if empty
; MaxNumber of Bytes in Software Buffer-1
OLDIRQ equ $0a09
STATUS equ $0a14
; Bit 7 : Port Open
SBUFSTART equ $0a15
SBUFEND equ $0a16
SBUFUSE equ $0a17
RBUFSTART equ $0a18
RBUFEND equ $0a19
RBUFUSE equ $0a1a
; 128 Bytes Buffers, mustnt page border in between
BUFMAX equ $7f ; Number of Bytes
BUFMINS equ $00 ; min lowbyte
BUFMAXS equ $7f ; max lowbyte
R_SBUF equ $0b00
BUFMINR equ $80 ; min lowbyte
BUFMAXR equ $ff ; max lowbyte
R_RBUF equ $0b80
seg code
org $3a00
LSRSTAT BYTE 0
; has the state of the LSR when a ELSI occured
; Bit 1 : Overrun Error - Receiver FIFO wasnt Read and the latest received Byte got lost
; Bit 2 : Parity Error - occurs if the particular character reached the FIFOs TOP
; Bit 3 : Framing Error - lost Stop a Bit, occurs same way like at PE
; Bit 4 : Break Interrupt - SIN was held low for longer than 2 Byte transmission time
MSRSTAT BYTE 0
INITUART subroutine
lda #$00
sta STATUS
sta LSRSTAT
sta MSRSTAT
sta SBUFSTART
sta SBUFEND
sta SBUFUSE
sta RBUFSTART
sta RBUFEND
sta RBUFUSE
rts
; Divisor Table
; div=XIN / (baudrate * 16)
BAUDRATE BYTE $68,$34,$1a,9
; Baudrate delivered in X
; 0 : 9600
; 1 : 19200
; 2 : 38400
; 3 :115200
OPEN subroutine
lda STATUS ; check if Port is already open
bpl ISCLOSED ; if yes close it
jsr CLOSE
ISCLOSED sei
lda IRQVEC ; setup RS232 IRQ Routine here
sta OLDIRQ
lda IRQVEC+1
sta OLDIRQ+1
lda #<ISR
sta IRQVEC
lda #>ISR
sta IRQVEC+1
lda U_LSR ; clear LSR
lda U_RBR ; clear RBR
lda U_MSR ; clear MSR
lda #$80 ; set DLAB 1 to access DivisorLatch
sta U_LCR ; to set Baudrate
lda BAUDRATE,X ; set Divisor Latch
sta UART
lda #$00
sta UART+1
lda #$03 ; set DLAB 0 , 8N1
sta U_LCR
lda #%11000111 ; enable/clear FIFOs, set Receive Trigger Level to 14 Bytes
sta U_FCR
lda #$23 ; enable Auto /RTS /CTS , /DTR
sta U_MCR
lda #$0f ; enable all IRQ conditions
sta U_IER
cli
lda STATUS
ora #$80
sta STATUS
rts
; do not change X here, it is used for setting baudrate in OPEN
CLOSE subroutine
sei
lda #$00 ; disable all UART IRQs
sta U_IER
sta U_MCR
sta U_FCR
lda OLDIRQ ; restore old IRQ
sta IRQVEC
lda OLDIRQ+1
sta IRQVEC+1
cli
lda STATUS
and #$7f
sta STATUS
.EXIT rts
; Byte in Akku
SENDBYTE subroutine
ldx SBUFUSE ; Check if Sendbuffer is full
cpx #BUFMAX
beq BUFERR
ldx SBUFEND ; add Byte to Sendbuffer
sta R_SBUF,X
cpx #BUFMAXS ; compare against bufmax, cause r_sbuf is page aligned
bne .INCEND
lda #BUFMINS ; start from beginning
sta SBUFEND
bcs .INCUSE
.INCEND inc SBUFEND
.INCUSE inc SBUFUSE
sei ; thre triggern
lda U_IER
ora #%00000010
sta U_IER
cli
sec ; everything went ok
rts
GETBYTE subroutine
ldx RBUFUSE ; check if byte in receivebuffer
beq BUFERR
ldx RBUFSTART ; first get byte then adjust buffpointers, or it may interfer with irq routine
lda R_RBUF,X ;
cpx #BUFMAXR ;
bne .INCSTART
ldx #BUFMINR
stx RBUFSTART
bcs .DECUSE
.INCSTART inc RBUFSTART
.DECUSE dec RBUFUSE
bne .1 ; buffer empty?
tax
sei
lda U_IER ; enable RDA irq
ora #$01
sta U_IER
cli
txa
.1 sec
rts
BUFERR clc ; if buffer full or empty
rts
; ISR is Hardware triggered by UART
; it checks if Receiver FIFO must be emptied
; always tries to send...
ISR subroutine
lda U_IIR
lsr
bcc .UARTIRQ ; No UART irq pending
jmp (OLDIRQ)
.UISR lda U_IIR
lsr
bcc .UARTIRQ
jmp IRQEXIT
.UARTIRQ and #$07 ; mask out the ID Bits
cmp #%00000011 ; Receiver Line Status IRQ
bne .1
; Receiver Line Status IRQ
lda U_LSR ; on Error transfer LSR to USTATUS
sta LSRSTAT
; sta $0400 ; DEBUG LSR
jmp .UISR
.1 cmp #%00000010 ; Received Data available
bne .2
jsr .GETBYT
jmp .UISR
.2 cmp #%00000110 ; Character Time Out Indication
bne .3
jsr .GETBYT
jmp .UISR
.3 cmp #%00000001 ; THRE IRQ ?
bne .4 ; then go to TransferLoop
jsr .TXLOOP
jmp .UISR
; Modem Status IRQ
.4 lda U_MSR
sta MSRSTAT
; sta $0401
jmp .UISR
; Receive Loop UART -> Memory FIFO
; transfer until uart receive fifo is empty, usually 14 bytes (see trigger lvl)
.GETBYT lda RBUFUSE
cmp #BUFMAX
beq .BUFFULL
lda UART ; get Byte
ldx RBUFEND
sta R_RBUF,X ; write to Buffer
cpx #BUFMAXR ; adjust Bufferend
bne .INCEND
lda #BUFMINR
sta RBUFEND
bcs .INCRUSE
.INCEND inc RBUFEND
.INCRUSE inc RBUFUSE
lda U_LSR ; check if still data available
lsr
bcs .GETBYT ; get next byte
bcc .ENDGBYT ; no? then go on
.BUFFULL lda U_IER ; disable RDA IRQ
and #%11111110
sta U_IER
.ENDGBYT rts
; now two trigger can be set
; - THRE IRQ
; - Bit5 THRE Indicator of LSR is set
;.CHECKTHRE lda U_LSR ; THRE Indicator set?
; and #$20
; beq .ENDTHRE ; send fifo still filled
; Transmit Loop Memory FIFO -> UART , max. 16 Bytes
; fills send fifo with max fifsize bytes
.TXLOOP ldy #FIFSIZE ; down Counter for Bytes
.SENDBYT ldx SBUFUSE ; Bytes available to send?
beq .ENDTHRE ; no? then adjust THRE IRQ?
ldx SBUFSTART ; get a Byte from Buffer
lda R_SBUF,X
sta UART ; send
cpx #BUFMAXS ; adjust Buffer Start
bne .INCSTART
lda #BUFMINS
sta SBUFSTART
bcs .DECSUSE
.INCSTART inc SBUFSTART
.DECSUSE dec SBUFUSE
dey
bne .SENDBYT
.ENDTHRE rts
Verstehe ich noch nicht so recht. Willst du nur die Konsole eines PCs umleiten oder soll es eine Art Chat-Programm werden?Mein Ziel ist eine Art Konsole, bei der ich auf beiden Computern ein paar Eingaben machen kann, und diese danach auf der Konsole des anderen Rechners sehe.
Sorry, aber das klingt nach einer faulen Ausrede! Eine simple Google-Anfrage liefert brauchbare Ergebnisse innerhalb von Sekunden: http://www.google.de/search?q=dos+serial+communication...aber ich finde nichts im Internet.
Hier ein Tipp: ftp://rtfm.mit.edu/pub/usenet/comp.os.msdos.programmer/