Rs232-Übertragung

Diskussion zum Thema Programmierung unter DOS (Intel x86)
Antworten
psko

Rs232-Übertragung

Beitrag von psko »

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
elianda
DOS-Übermensch
Beiträge: 1150
Registriert: Mi 31. Jan 2007, 19:04
Wohnort: Halle
Kontaktdaten:

Beitrag von elianda »

Ich habe zwar kein C Quellcode, aber eine kurze Vorlage in Turbo Pascal, die sich sicher ganz flott konvertieren laesst.

(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 }
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:

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

Beitrag von bttr »

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.
Verstehe ich noch nicht so recht. Willst du nur die Konsole eines PCs umleiten oder soll es eine Art Chat-Programm werden?
...aber ich finde nichts im Internet.
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

Hier ein Tipp: ftp://rtfm.mit.edu/pub/usenet/comp.os.msdos.programmer/
Antworten