Ergebnis 1 bis 20 von 24

Thema: RS-232 (serielle Schnittstelle) auslesen

Hybrid-Darstellung

Vorheriger Beitrag Vorheriger Beitrag   Nächster Beitrag Nächster Beitrag
  1. #1
    So, da es eine Gruppenarbeit ist und sich jeder um einen anderen Ansatz zum Auslesen der seriellen Schnittstelle kümmert (damit wir am Ende ein möglichst breites Spektrum an Möglichkeiten zumindest untersucht haben), arbeite ich trotz der gut gemeinten Ratschläge, das Problem mit einer höheren Sprache zu lösen, vorerst weiter mit Assembler (Aber zumindest den vorgeschlagenen Netwide Assembler).

    Ich habe dieses Programm geschrieben:
    Code:
    ; ------------------------------
    ; Empfängt 14 Byte vom Multimeter
    ; an der seriellen Schnittstelle
    ; und gibt sie aus.
    ; ------------------------------
    
    
    segment stapel stack 
      resb 80h ; 128 Byte für den Stack
      ; (auch wenn er hier nicht gebraucht wird)
    
    
    
    segment code
    
    ; ------------------------------
    ; Hauptprogramm
    ; ------------------------------
    
    ..start:
      mov ax, daten
      mov ds, ax
    
      call initialisieren
      call status_ausgeben
      
    
      ; Schleife von 13 bis 0
      ; d.h.: 14 Bytes empfangen (siehe Datenformat)
      ; Datenformat des A-4660-Multimeters:
        ; Byte      1 2 3 4 5 6 7 8 9 A B C D E
        ; Beispiel  D C - 1 . 9 9 9 9       V CR (CR: Zeilenumbruch)
     
      mov cx, 000Dh       
      schleife_auslesen:
        ; Status prüfen
          mov ah, 03h
          mov dx, [com1]
          int 14h
          and ah, 00000001b
          cmp ah, 00000001b ; Wenn Daten vorhanden sind ...
          je empfangen ; ... direkt empfangen, sonst ...
          call d_senden ; ... "D"-Befehl senden.
        empfangen:
          call zeichen_empfangen
          call zeichen_ausgeben
      loop schleife_auslesen
    
    
      ; Beenden
      mov ah, 4Ch
      int 21h
      
    ; Ende des Programmes  
    
    
    
    ; ------------------------------
    ; Unterprogramme
    ; ------------------------------  
      
    initialisieren:
      mov ax, 0000h
      mov dx, [com1]
      mov al, 10100111b
      ; Konfigurationsbyte
        ; Zeichenkodierung: 7 bit >> 10
        ; Stoppbits: 2            >> 1
        ; Parität: None           >> 00
        ; Durchsatz: 9600 bit/s   >> 111
      int 14h
      ret
    
    
    status_ausgeben:
      mov ah, 03h
      mov dx, [com1]
      int 14h
      mov dl, ah
      mov ah, 02h
      int 21h
      ret
    
    
    zeichen_empfangen:
      ; Zeichen empfangen
      mov dx, [com1]
      mov ax, 0000h
      mov ah, 02h
      int 14h
      mov [zeichen], al
      ret
    
    
    zeichen_ausgeben:
      mov dx, [com1]
      mov dl, [zeichen]
      mov ah, 02h
      int 21h
      ret
    
    
    d_senden:
      mov ah, 01h
      mov al, 44h ; ASCII für "D"
      mov dx, [com1]
      int 14h
      ret
      
      
      
    ; ------------------------------
    ; Variablen
    ; ------------------------------
    
    segment daten
      zeichen db 00h  ; Zum Zwischenspeichern eines empfangenen Bytes
      com1 dw 0000h
    Es überprüft, ob an der seriellen Schnittstelle Daten vorhanden sind und gibt sie aus. Wenn keine Daten vorhanden sind, wird der "D"-Befehl an das Gerät geschickt, der nach dem >> Informationsblatt des Gerätes (PDF, ~600 kB), (siehe Seite 3) die Datenübertragung aktivieren soll.

    Allerdings werden neben dem Status der seriellen Schnittstelle keine Daten ausgegeben, sondern nur "D"s, bzw, wie ich inzwischen herausgefunden habe, das, was gerade im AL-Register (und damit wegen einer Zuweisung in der Variable "zeichen") steht, in ASCII. Dabei sollte die Funktion "zeichen_empfangen" eigentlich das an der Schnittstelle vorhandene Zeichen in AL speichern (siehe >> Interrupt 14).

    Ist das Gerät kaputt (die Anzeige am Gerät selber funktioniert noch problemlos und richtig, es ist eben nur ein bischen älter) oder habe ich irgendetwas falsch gemacht?

  2. #2

  3. #3
    Zitat Zitat von Crash-Override
    http://dsdt.info/projekte/internet/?id=35
    vll ist das ja ganz interessant für dich.
    Nun habe ich mich 10 Minuten lang darüber geärgert, dass das Programm nicht funktioniert, bis mir einfiel, dass ich an diesem Computer gar keinen COM-Port habe. -_-
    Naja, das Messgerät ist leider Eigentum der Schule, wo es aus diesem Grund auch momentan steht, aber ich werde das Programm Montag in einer Freistunde mal ausprobieren, um zu sehen, ob das Gerät überhaupt funktioniert. Danke für den Link.

  4. #4
    Um mich etwas genauer mit der Funktionsweise der seriellen Schnittstelle zu beschäftigen und mir nicht bloß von Microsoft und ihrem Interrupt 14h den Arsch hinterhertragen zu lassen habe ich versucht, die serielle Schnittstelle mit einer Anleitung aus dem Internet selber zu initialisieren. Allerdings habe ich Probleme mit einem Assembler-Befehl. Es geht um out.
    Die allgemeine Syntax lautet:
    Code:
    out (Portadresse), (Allgemeines Register)
    Kann mir jemand Folgendes erklären:
    Code:
    Im Register "al" bzw "ah" steht das auszugebende
    Byte, in "dx" die Adresse von COM1.
    
    out dx, al     --> Funktioniert problemlos
    out dx, ah     --> "Invalid combination of opcode and operands"
    Sowohl AL als auch AH sind doch 8-Bit-Register, warum verhalten sie sich dann an dieser Stelle unterschiedlich?


    Damit kann ich allerdings noch leben. Was mir merkwürdig vorkommt ist allerdings, dass folgende Syntax des out-Befehls einen Fehler verursacht:
    Code:
    "com1" hat den Wert 03F8h, die Adresse von COM1.
    
    out [com1], al     --> "Invalid combination of opcode and operands"
    out 03F8h, al     --> "Warning: Unsigned byte value exceeds bounds"

    Geändert von derBenny (17.09.2006 um 10:46 Uhr)

  5. #5
    Out akzeptiert als zweiten Operanden prinzipiell nur al. Das hat vermutlich intern einige Vorteile und man muss sich einfach daran halten. Die Adresse darf, soweit ich weiß, bis 255 als Direktwert angegeben werden. Danach sollte man dafür das dx Register nutzen und und keine Variable.

    freundliche Grüße, Rolus

  6. #6
    Danke, das erwähnen viele Quellen, die ich im Internet gefunden habe, gar nicht.
    [FONT="Courier New"]out [com1], whatever[/FONT] wäre so schön anschaulich gewesen. Dadurch, dass ich nun vorher den zu versendenden Wert an AL und die Port-Adresse an DX senden muss, verdreifacht sich die Schreibarbeit und der Quelltext wird recht unübersichtlich, aber dagegen lässt sich wohl nicht viel machen.

    Gibt es eigentlich irgendeine Möglichkeit, einen Timer unter Assembler einzusetzen? Ich würde die Schleife, die auf Daten von dem Gerät wartet, ganz gerne unterbrechen, wenn nach beispielsweise 2 Sekunden noch keine Daten gesendet worden sind.

  7. #7
    Zitat Zitat von derBenny
    Gibt es eigentlich irgendeine Möglichkeit, einen Timer unter Assembler einzusetzen? Ich würde die Schleife, die auf Daten von dem Gerät wartet, ganz gerne unterbrechen, wenn nach beispielsweise 2 Sekunden noch keine Daten gesendet worden sind.
    Du könntest dir ja selbst einen kleinen Timer schreiben. Wie man die Uhrzeit ausliest etc. dürfte man mit Google schnell finden. Oder du nutzt einen fertigen, wie zum Beispiel den Timer der WinAPI. Am einfachsten wäre es aber, eine Schleife zu schreiben, die immer überprüft, ob ein neues Zeichen da ist, und nur dann vom Port liest, wenn ein neue Wert vorhanden ist.

    freundliche Grüße, Rolus

  8. #8
    Zitat Zitat von Rolus
    Am einfachsten wäre es aber, eine Schleife zu schreiben, die immer überprüft, ob ein neues Zeichen da ist, und nur dann vom Port liest, wenn ein neue Wert vorhanden ist.
    Das habe ich ja getan. Die Schleife bricht im Moment nur dann ab, wenn 14 Bytes von dem Gerät empfangen wurden (14 hängt mit dem Datenformat des Multimeters zusammen). Aber es kann ja auch vorkommen, dass das Gerät aus irgendeinem Grund nichts sendet, dann muss die Schleife nach einer gewissen Zeit auch abgebrochen und eine Fehlermeldung ausgegeben werden. Dafür brauche ich den Timer.

    Naja, ich werde einfach mal suchen, dafür gibt es sicher fertige DOS-Interrupts, die man aufrufen kann. Aber nicht mehr heute. ^^

  9. #9
    Ich habe das von Crash-Override gepostete Programm mal ausprobiert und festgestellt, dass das Gerät funktioniert. Man sendet ein großes "D" und das Gerät gibt die derzeitige Messwertanzeige zurück.
    Mein Programm funktioniert allerdings nicht, es gibt nichts aus und wird einfach kurz nach dem Start beendet (nichteinmal eine halbe Sekunde). Ich habe keine Ahnung, wo der Fehler liegt.
    Die Empfangsschleife wird mehrfach durchlaufen, dass habe ich schon überprüft. Genaueres kann ich nicht sagen, da das Gerät in der Schule steht und ich es hier (zuhause) nicht testen kann.

    Code:
    Der Quellcode ist überholt, ich nehm' ihn mal raus, bevor sich noch jemand die Mühe macht, ihn durchzulesen.
    Nachtrag:
    Ok, ich habe bei der Initialisierung vergessen, die Interrupts richtig einzustellen, das habe ich jetzt geändert (allerdings nicht im obigen Quelltext). Ich werde es morgen erstmal ausprobieren.

    Nachtrag #2:
    Es hat zwar noch nicht funktioniert, aber ich bin auf dem richtigen Weg. ^^

    Geändert von derBenny (21.09.2006 um 18:37 Uhr)

  10. #10
    Ich habe jetzt verschiedene Fehler beseitigt, aber das Programm läuft immernoch nicht. Irgendwas stimmt noch nicht. Ich überprüfe im Programm den Status der seriellen Schnittstelle und bekomme einen Apostroph raus (ASCII-Codierung des Statusbytes). Allerdings gibt es im ASCII-Zeichensatz 4 oder 5 verschiedene Apostrophen, die in meinen Augen alle relativ gleich aussehen, aber eben durch unterschiedliche Bitkombinationen codiert werden. Weiß zufällig jemand, wie ich die einzelnen Bits eines Bytes direkt ausgeben kann?

    Ja, ich bin tatsächlich skrupellos genug für einen tripple post.

    Nachtrag:
    Oh, vergesst es, ist nicht so wichtig. 3 der 5 möglichen Zeichen fallen raus, weil das höchste Bit, das eigentlich immer 0 ist, dann 1 sein müsste. Es gibt also noch zwei mögliche Statusmöglichkeiten:

    00100111 >> Daten vorhanden, Überlauf, Paritätsfehler, Senderegister enthält noch Daten
    01100000 >> Senderegister enthält noch Daten / sendet gerade Daten

    Irgendwie ergibt beides keinen Sinn. ;_;

    Geändert von derBenny (28.09.2006 um 21:05 Uhr)

  11. #11
    Zu deiner Frage nach den einzelnen Bits abfragen ... sowas macht man idR mit AND und einer Konstanten.

    Das AND wird bitweise auf die ganze Zahl angewendet.
    Zur Erinnerung: 0 AND 0 = 0, 1 AND 0 = 0; 0 AND 1 = 0; 1 AND 1 = 1

    Wenn die verwendete Konstante nun gerade die Zweierpotenz ist, die dein Bit repraesentiert (z.B. 1 SHL 5 = 32 = 00100000b = nur bit 5 gesetzt [merke nullindexiert]) und man diese Konstante mit der Zahl AND verknuepft, dann kommt nur dann ein Ergebniss ungleich 0 raus, wenn das entsprechende Bit gesetzt ist.

    10110110b AND
    00100000b
    00100000b

    01011010b AND
    00100000b
    00000000b

    Das geht natuerlich auch, wenn du mehere Bits gleichzeitig abfragen willst. Hierbei treten dann 3 verschiedenen Faelle ein
    a) Zahl AND Konstante == Konstante -> alle Bits aus Konstante sind gesetzt
    b) Zahl AND Konstante != 0 -> mindestens ein Bit stimmt ueberein, Wert sind die uebereinstimmenden Bits
    c) Zahl AND Konstante == 0 -> keines der Bits ist gesetzt.

    Und um die Sache komplett zu machen: mit OR kann man Bits in einem Wert setzen und mit XOR abwechselnd an und aus stellen.

    Geändert von Ineluki (28.09.2006 um 21:53 Uhr)

  12. #12
    Joa, die AND-Verknüpfung wende ich in meinem Programm auch mehrfach an. Meine Frage war eher, wie man ein Byte, also zum Beispiel irgendein Statusregister, das einen interessiert, in Form von Einsen und Nullen auf dem Bildschirm ausgeben kann. Ich habe nur Möglichkeiten gefunden, ASCII-Zeichen auszugeben, die durch die Kombination aus Einsen und Nullen im auszugebenden Byte codiert sind.
    Ich gebe also das Register, das ich betrachten will, als ASCII-Zeichen aus und erhalte ein Apostroph (oder irgendwas in der Art). Das Problem war nur, dass ich zwischen "acute accent", "accent grave", dem Apostroph, "left single quotation mark" und "right single quotation mark" keinen großen Unterschied entdecken konnte, zumindest nicht in der von DOS verwendeten Schriftart.
    Das Problem hat sich aber inzwischen gelöst. Ich muss das Programm ja bloß mit DEBUG Schritt für Schritt ausführen lassen und prüfen, welcher Wert im jeweiligen Register steht.

    Hier ist mal der aktuelle Quelltext. Ich habe keine Ahnung, warum es nicht funktioniert.
    Code:
    ; ------------------------------
    ; Kommunikation mit einem Multimeter
    ; an der seriellen Schnittstelle
    ;
    ; Verwendetes Verfahren: POLLING
    ; Der Empfangspuffer wird auf
    ; empfangene Zeichen geprüft und
    ; gegebenenfalls ausgelesen.
    ; ------------------------------
    
    segment stapel stack 
      resb 80h ; 128 Byte für den Stack
    
    
    
    segment code
    
    ; ------------------------------
    ; Hauptprogramm
    ; ------------------------------
    
    ..start:
      mov ax, daten
      mov ds, ax
    
      call initialisieren
      call status_out ; Status der seriellen Schnittstelle prüfen (nur Test, wird später entfernt)
      call d_senden ; Um Datenübertragung vom Multimeter zu starten
      call timer_start
    
      schleife_empfang:
        call timer_check
        ja ende_schleife ; Wenn mehr als 5 Sekunden nichts empfangen
        
        call zeichen_empfangen_check ; Zeichen im Empfangsregister?
        jne schleife_empfang ; Wenn nein, zurück zum Anfang ...
        
        ; sonst (wenn Zeichen im Empfangsregister sind)
        call zeichen_empfangen
        call zeichen_ausgeben
    
        call timer_start
        call counter_up_and_check
      jb schleife_empfang
        ; Wird wiederholt, solange noch keine
        ; 14 Bytes empfangen worden sind
      ende_schleife:
    
      ; Beenden
      mov ah, 4Ch
      int 21h
      
    ; Ende des Programmes  
    
    
    
    ; ------------------------------
    ; Unterprogramme
    ; ------------------------------  
    
    ; ---------------
    initialisieren:
    ; ---------------
      ; Empfangsregister auslesen (muss für Initialisierung leer sein)
      mov dx, [com1]
      in al, dx
    
      ; Interrupts ausschalten
      mov dx, [com11]
      mov al, 00h
      out dx, al
      mov dx, [com14] ; Modem Control
      mov al, 00000011b
        ; PC betriebsbereit                     >> .......1
        ; PC will senden (nicht empfangsbereit) >> ......1.
        ; Interrupts aus                        >> ....0...
      out dx, al
      
      ; Empfangsregister auslesen (doppelt hält besser)
      mov dx, [com1]  
      in al, dx
      
      ; DLAB einschalten
      mov dx, [com13]
      mov al, 10000000b
        ; DLAB >> 1.......
      out dx, al
      
      ; Einstellen der Baudrate: 115200/Baudrate(=9600) = 12
      ; Low Byte = 12
      mov dx, [com1]
      mov al, 00001100b
      out dx, al
      ; High-Byte = 0
      mov dx, [com11]
      mov al, 00h
      out dx, al
    
      ; DLAB ausschalten
      mov dx, [com13]
      mov al, 00h
      out dx, al
     
      ; Line Control setzen
      mov dx, [com13]
      mov al, 00000110b
        ; Kodierung (7 Bit) >> ......10
        ; 2 Stopbits        >> .....1..
        ; keine Parität     >> ..000...
      out dx, al
      ret
    
    
    
    ; ---------------
    zeichen_empfangen_check:
    ; ---------------
      mov dx, [com15] ; Line Status
      in al, dx
      and al, 00000001b
      cmp al, 00000001b
        ; Empfangsregister enthält Daten >> .......1
      ret
    
    ; ---------------
    zeichen_empfangen:
    ; ---------------
      mov dx, [com1]
      in al, dx
      mov [zeichen], al
      ret
    
    ; ---------------
    zeichen_ausgeben:
    ; ---------------
      mov ah, 02h
      mov dl, [zeichen]
      int 21h
      ret
    
    
    
    ; ---------------
    d_senden:
    ; ---------------
    ; Sendet "D" und setzt PC auf empfangsbereit  
      ; PC will ein Zeichen senden
      mov dx, [com14]
      mov al, 00000011b
      out dx, al
      
      ; Zeichen ins Senderegister
      mov dx, [com1]
      mov al, 44h; "D" in ASCII
      out dx, al
      
      mov dx, [com15]; Line Status 
      d_senden_warten_bis_senderegister_leer:
        nop
        in al, dx
        and al, 01100000b
        cmp al, 01100000b
      jne d_senden_warten_bis_senderegister_leer
    
      ; PC auf empfangsbereit setzen
      mov dx, [com14]
      mov al, 00000001b
      out dx, al
      ret
      
    
    
    ; ---------------
    timer_start:
    ; ---------------
    ; Timer wird auf 0 gesetzt
    ; 18,2 Erhöhungen pro Sekunde
      mov ah, 01h
      mov cx, 0000h
      mov dx, 0000h
      int 1Ah ; Zugriff auf Systemuhr
      ret
    
    ; ---------------
    timer_check:
    ; ---------------
      mov ah, 00h
      int 1Ah
      ; Gibt Timerstand in DX aus
      cmp dx, 005Bh ; 5 Sekunden
      ret
    
    
    
    ; ---------------
    counter_up_and_check:
    ; ---------------
      ; Überprüft, ob schon 14 Bytes empfangen wurden
      mov ah, [counter]
      inc ah
      mov [counter], ah
      cmp ah, 0Eh
      ret
    
    
    
    ; ---------------
    status_out:
    ; ---------------
      push ax
      push dx
      mov dx, [com15]
      in al, dx
      mov dl, al
      mov ah, 02h
      int 21h
      pop dx
      pop ax
      ret
    
    
    
    ; ------------------------------
    ; Variablen
    ; ------------------------------
    
    segment daten
      zeichen db 00h   ; Zum Zwischenspeichern eines empfangenen Bytes
      counter db 00h   ; Zähler
      meldung_1: db '!', 13, 10, '$'
    
      com1  dw 03F8h   ; Adresse des COM1-Ports / I/O-Register / DLL
      com11 dw 03F9h   ; Interrupt-Enable-Register (IER) / DLH
      com12 dw 03FAh   ; Interrupt-ID-Register (IIR)
      com13 dw 03FBh   ; Line-Control-Register
      com14 dw 03FCh   ; Modem-Control-Register
      com15 dw 03FDh   ; Line-Status-Register
      com16 dw 03FEh   ; Modem Status Register
    Ach ja, das Gerät hat nur die Anschlüsse TxD (Senden), RxD (Empfangen), GND (Erdung/Masse), RTS (Request-To-Send, PC will Daten empfangen) und DTR (Data-Terminal-Ready, PC signalisiert, dass er eingeschaltet ist).

    Geändert von derBenny (29.09.2006 um 14:48 Uhr)

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •