PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : RS-232 (serielle Schnittstelle) auslesen



derBenny
31.08.2006, 21:46
Folgende Situation:

Ich habe ein Messgerät, dass allerlei hübsche Dinge messen kann, beispielsweise Stromstärke oder Spannung. Das Gerät kann über die serielle Schnittstelle an einen Computer angeschlossen werden.
Ziel der ganzen Aktion ist es, mit dem Gerät Messreihen aufzunehmen und sie auf dem Computer (zur weiteren Verarbeitung) zu speichern.

Das Problem ist nun, dass ich keine Ahnung habe, was das Gerät an die Schnittstelle sendet und in welcher Form. Wahrscheinlich sendet es permanent irgendetwas, man muss also wohl keine Anfragen schicken. Nun würde ich ganz gerne herausfinden, wie ich an meine schönen Messwerte komme.

Weiß irgendjemand, wie ich einfach alles, was der Computer über die serielle Schnittstelle empfängt, erstmal irgendwie aufzeichnen kann, um es auszuwerten? Ich hoffe, dass das irgendwelche Schlüsse darüber zulässt, was ich machen muss, um aus dem ganzen empfangenen Kram meine Messwerte herauszufiltern.

Ideal wäre ein Ansatz in PHP, aber eine C- oder Delphi-Lösung würde mir auch schon weiterhelfen. Betriebssystem ist Windows XP.

DFYX
31.08.2006, 23:47
Wir hatten irgendwann mal nen Thread über selbstgebaute Roboter. Schau mal, ob da was brauchbares dabei war. Ansonsten könnt ich dir raussuchen, wie das in QBasic gehen würde, aber in C oder Delphi? Nicht mein Gebiet, tut mir leid. Und dass PHP sowas hat, bezweifel ich mal, aber vielleicht hast du ja Glück. Allerdings könnte ich mir vorstellen, dass die Schnittstelle unter Linux wie eine Datei angesprochen werden kann.

Mog
01.09.2006, 03:01
Wenn du mit PHP arbeiten willst, musst du dir wohl dein eigenes Modul fuer den Interpreter in c schreiben, um darauf zugreifen zu wollen.
Ich selber wuerde wohl als Schnittstelle etwas in Art von Plan 9 bauen; also mit Filediscreptoren arbeiten und mir einfach ein kleines C-Programm basteln, das eben den Windowshandler verwaltet. Alternativ kann man ueber PHP sicher auch noch Sockets auslesen.
Mit PHP wirst du aber generell nicht viel auswerten koennen, es sei denn du rufst das Script immer per Messinformation neu auf. Dafuer ist das ganze wirklich nicht geeignet. C/Cpp waere wohl optimal dafuer.


Wie du die RS232 ansprichst findest du eigentlich schnell unter google; aber da ich nicht genau weiss was du machen willst kann ich auch keine Sprache empfehlen. Fuer optische Auswertung waere z.B. Python sehr nett. HIer hilft die USPP weiter.

codec
01.09.2006, 06:25
Mit PHP wirst du aber generell nicht viel auswerten koennen, es sei denn du rufst das Script immer per Messinformation neu auf. Dafuer ist das ganze wirklich nicht geeignet. C/Cpp waere wohl optimal dafuer.

Naja, wenn er 'ne permanente While-Schleife hat, die die Daten ausliest, dann geht da schon
was. Ich halte PHP hier allerdings für extrem unangebracht und würde eher auf Python, Perl
oder C zurückgreifen.

Mit wconsd (ftp://ftp.h07.org/pub/h07.org/projects/system/wconsd-0.1.zip) (Directlink zum Download) kannst du die serielle Schnittstelle (unter Windows)
an einen Netzwerksocket binden, vllt. hilft dir das in irgendeiner Weise.

Ich weis nicht ob du unter Linux mit PHP das serielle Interface direkt auslesen kannst,
aber wie bereits gesagt, schätz ich mal, dass du ein eigenes Modul für PHP schreiben müsstest.

Mog
01.09.2006, 12:33
Naja, wenn er 'ne permanente While-Schleife hat, die die Daten ausliest, dann geht da schon
was. Ich halte PHP hier allerdings für extrem unangebracht und würde eher auf Python, Perl
oder C zurückgreifen.

Mit wconsd (ftp://ftp.h07.org/pub/h07.org/projects/system/wconsd-0.1.zip) (Directlink zum Download) kannst du die serielle Schnittstelle (unter Windows)
an einen Netzwerksocket binden, vllt. hilft dir das in irgendeiner Weise.

Ich weis nicht ob du unter Linux mit PHP das serielle Interface direkt auslesen kannst,
aber wie bereits gesagt, schätz ich mal, dass du ein eigenes Modul für PHP schreiben müsstest.


Insofern PHP ihrem Einsatzort gerecht werden mag, waere ein Script immer von einer angemessenen Timeout-Zeit beschraenkt.

Dingsi
01.09.2006, 13:12
Insofern PHP ihrem Einsatzort gerecht werden mag, waere ein Script immer von einer angemessenen Timeout-Zeit beschraenkt.Schmarrn, set_time_limit(0); (http://php.net/set_time_limit) :p

Mog
01.09.2006, 13:24
Schmarrn, set_time_limit(0); (http://php.net/set_time_limit) :p


rofl. PHP ist einfach nur schwachsinnig. xD


Ruby rockt. :P


In dem fall wuerde ich aber auch schauen inwiefern ich Speicher allokiere und freigebe. Ich denke nicht, dass PHP da einen sehr effizienten GC hat.

derBenny
03.09.2006, 01:47
Danke, die Vorschläge klingen ganz brauchbar (wenn auch nach viel Arbeit ;_;).

Ich habe gestern entdeckt, dass es bei Assembler mit dem Interrupt 14h (http://www.tu-chemnitz.de/informatik/RA/lehre/mop/dos_stuff/int14d.html) möglich ist, direkt auf die serielle Schnittstelle zuzugreifen. Ich habe, um das erstmal auszuprobieren, folgendes Programm geschrieben:


; ************************************
; Auslesen der seriellen Schnittstelle
; ************************************

daten segment
signal db 0 ; Variable Signal, 1 Byte groß
daten ends

stapel segment byte stack
dw 128 dup(?)
stapel ends

code segment
assume cs:code,ds:daten,es:nothing,ss:stapel

start:
; Serielle Schnittstelle initialisieren
mov ah, 00h
mov dx, 00h ; Nummer der Schnittstelle (0: COM1)
int 14h

; Zeichen empfangen
mov ah, 02h
mov dx, 00h
int 14h

; Zeichen in "signal" speichern
mov [signal], al

; Ausgabe
mov ax, daten
mov ds, ax
mov dx, offset [signal]

mov ah,9h
int 21h

mov ah,4ch
int 21h

code ends

end startDas Programm soll ein Zeichen (8 Bit) aus der seriellen Schnittstelle (in diesem Fall COM1) auslesen (landet in AL), das Zeichen unter der Variablen "signal" speichern und diese dann ausgeben.

Weil ich an diesem Computer keine seriellen Schnittstellen habe, habe ich es am Computer meiner Eltern probelaufen lassen. Allerdings wurde nicht wie erwartet ein Zeichen ausgegeben (schließlich habe ich "signal" ja nur den 8 Bit großen Inhalt von AL übergeben), sondern mehrere Zeilen wilder ASCII-Zeichen.

Als ich das Programm mit DEBUG Schritt für Schritt ausgeführt habe, habe ich allerdings andere Zeichen ausgegeben bekommen, darunter sogar ein wenig Klartext, der mir verriet, dass der COM-Port bereit ist.

Kennt sich vielleicht jemand etwas besser mit Assembler aus und kann mir sagen, wo diese ganzen ASCII-Zeichen herkommen? (Eigentlich wollte ich ja zunächst nur ein einzelnes Zeichen empfangen.)

Rolus
03.09.2006, 09:20
Hm, TASM. Also INT 21h mit AH=9 gibt eine $-terminierte Zeichenkette aus (also werden solange Zeichen ausgegeben, bis im Speicher ein $ steht). Zum Ausgeben eines einzelnen Zeichens sollte AH auf 2 gesetzt werden und das Zeichen muss sich in DL befinden. Also etwa so:

; Ausgabe
mov dl, [signal]
mov ah,2h
int 21h
Ob du das Gerät richtig ansprichst, kann ich dir aufgrund mangelnder Produktinformationen aber nicht sagen. Eventuell solltest du aber überprüfen, ob die Schnittstelle erfolgreich initialisiert wurde.

freundliche Grüße, Rolus

Mog
03.09.2006, 09:40
Ganz generell wuerde ich dir empfehlen mit NASM zu assemblieren und den weltweiten Pseudostandart zu verwenden.

Sonst ist es btw doch etwas uebertrieben fuer soetwas ASM zu benuetzen. Such dir lieber einen C-Codefetzen der ueber die API den Port anspricht. So wird es auch fuer dich verstaendlicher, denke ich. Mit Hochsprachen scheinst du ja besser vertraut zu sein.


Die beiden Links koennten dir dabei eventuell helfen:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnfiles/html/msdn_serial.asp
(Der Umweg ueber CygWin)
http://www.mingw.org/download.shtml#hdr2

derBenny
15.09.2006, 15:22
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:
; ------------------------------
; 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 0000hEs ü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) (http://www.produktinfo.conrad.com/datenblaetter/125000-149999/128686-in-01-de-MXD-4660A_Pinbelegung_Schnittstelle.pdf) 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 (http://www.datasource.de/programmierung/tab11_biosint14h.html#tab2)).

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?

Crash-Override
16.09.2006, 20:00
http://dsdt.info/projekte/internet/?id=35

vll ist das ja ganz interessant für dich.

derBenny
16.09.2006, 21:32
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.

derBenny
17.09.2006, 10:42
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:
out (Portadresse), (Allgemeines Register)

Kann mir jemand Folgendes erklären:
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:
"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"

Rolus
17.09.2006, 12:17
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

derBenny
17.09.2006, 12:58
Danke, das erwähnen viele Quellen, die ich im Internet gefunden habe, gar nicht.
out [com1], whatever 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.

Rolus
17.09.2006, 21:10
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

derBenny
17.09.2006, 21:51
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. ^^

derBenny
19.09.2006, 16:01
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.


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

derBenny
28.09.2006, 20: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. ;_;

Ineluki
28.09.2006, 21:46
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.

derBenny
29.09.2006, 14:31
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.

; ------------------------------
; 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 RegisterAch 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).

Ineluki
03.10.2006, 01:27
Wegen der Binaerausgabe ...
Wenn du die einzelnen Bits abfragen kannst, kannst du auch entsprechend eine Kette von Einsen und Nullen auf dem Bildschirm ausgeben. Eine Nette moeglichkeit waere beispielsweise ein Right Shift oder Right Rotate, wobei das ausgeschobene Bit ins Carryflag kommt. Dann koenntest du sowas machen ...



MOV CX, 8 ; Schleifenzaehler auf 8 bit
MOV AL, Datenbyte ; Da steht das auszugebende Byte drin
:Start_Schleife ; Label fuer die Schleife
RCR AL,1 ; Rotiert nach Rechts durch Ca<rry Flag
JC CarryIsSet ; Wenn CarryFlag 1 ist, springe zur Ausgabe von "1"
CALL SCHREIBE_0 ; Gib eine "0" aus und ruecke Cursor nach rechts
JMP NEXT_LABEL ; Springe ans Ende der Schleife
:CarryIsSet
CALL SCHREIBE_1 ; Gib eine "1" aus und ruecke Cursor nach rechts
:NEXT_LABEL ; Das Ende der Schleife
LOOP Start_Schleife ; Wiederhole Schleife bis alle 8 bit abgearbeitet sind
CALL SCHREIBE_BLANK ; Gib ein Leerzeichen aus und ruecke Cursor nach rechts


Wie du die Unterprogramme/Makros zum Ausgeben der Buchstaben machst, solltest du eigentlich wissen ...

derBenny
14.10.2006, 21:38
Aus irgendeinem Grund hat es sich mein Programm anders überlegt und funktioniert jetzt, dabei bin ich der Meinung, nichts gemacht zu haben. Falls es jemanden interessiert ist hier nochmal der Quelltext des funktionierenden Programms. Es ist zwar sehr speziell an diese eine Aufgabe angepasst, aber mit ausreichend Kommentaren versehen, so dass man es mit ein paar Veränderungen auch für andere Aufgaben umschreiben könnte.

; ------------------------------
; 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



; ------------------------------
; Variablen
; ------------------------------

segment daten
zeichen: resb 1 ; Zum Zwischenspeichern eines empfangenen Bytes
datei_name: db 'messwert.dat'
datei_handle: resb 1
messwert: times 56 db 'x'
counter: db 00h ; Zähler

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



segment code

; ------------------------------
; Hauptprogramm
; ------------------------------

..start:
; Das Segment "daten" als Datensegment festlegen
; (da liegen unsere Variablen)
mov ax, daten
mov ds, ax

call initialisieren

call d_senden
call timer_start

schleife_empfang:
; Wird wiederholt, solange noch keine
; 14 Bytes empfangen worden sind
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_in_string_speichern
call zeichen_ausgeben

call timer_start
call counter_up_and_check
jb schleife_empfang

ende_schleife:

call string_in_datei_schreiben

; Beenden
mov ah, 4Ch
int 21h

; Ende des Programmes



; ------------------------------
; Unterprogramme
; ------------------------------

; ---------------
initialisieren:
; ---------------
; Empfangsregister auslesen (muss für Initialisierung leer sein)
call zeichen_empfangen

; 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

; 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



; ---------------
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



; ---------------
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

; ---------------
zeichen_in_string_speichern:
; ---------------
mov bx, messwert
mov ax, [counter]
add bx, ax
; Zeichen
mov al, [zeichen]
; Zeichen in String speichern
mov byte [ds:bx], al
ret

; ---------------
string_in_datei_schreiben:
; ---------------
; Datei erstellen
mov ah, 3Ch ; Erstellen
lea dx, [datei_name]
int 21h

; Datei öffnen
mov ah, 3Dh ; Öffnen
mov al, 01h ; Zugriffsmodus, nur schreiben
lea dx, [datei_name]
int 21h
mov [datei_handle], ax

; Datei beschreiben
mov ah, 40h ; Schreiben
mov bx, [datei_handle]
mov cx, 38h ; 56 Zeichen (eine Messung)
mov dx, messwert ; Offset von messwert

; Datei schließen
mov ah, 3Eh ; Schließen
mov bx, [datei_handle]
int 21h
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 ; 14 Bytes empfangen?
ret

Jetzt versuche ich noch, das Ganze interruptgesteuert ablaufen zu lassen, das wäre natürlich etwas eleganter. Um das Ganze zu testen habe ich erstmal versucht, bei Tastatureingabe bzw. dem entsprechenden Interrupt eine bestimmte Aktion auszuführen, aber irgendwie funktioniert es nicht. Weiß vielleicht jemand, woran es liegen könnte? Ich habe inzwischen rausgefunden, dass offensichtlich irgendwas schiefläuft, während ich die Speicheradresse meiner Interrupt-Service-Routine in die Interrupt-Vektor-Tabelle schreibe. Es funktioniert aber auch nicht, wenn ich die vorgefertigten Funktionen dafür benutze, das habe ich schon ausprobiert.
; ------------------------------
; Interrupt-Test
;
; Bei Tastatureingabe wird eine
; eigene Interruptroutine vor der
; Standard-ISR aufgerufen
; ------------------------------


segment daten
alter_iv: resb 4 ; 4 Byte für alten Interrupt-Vektor



segment code

..start:
mov ax, daten
mov ds, ax

call iv_ersetzen

call timer_start

; 5 Sekunden warten
; Zeit zum Ausprobieren
schleife:
nop
nop
nop
call timer_check
jb schleife

; Beenden
mov ah, 4Ch
int 21h



; ---------------
iv_ersetzen:
; ---------------
; Alten IV speichern
xor ax, ax
mov es, ax
mov bx, 0024h

mov ax, word [es:bx]
mov word [alter_iv], ax

mov ax, word [es:bx+2]
mov word [alter_iv+2], ax

; Neuen IV in Tabelle eintragen
xor ax, ax
mov es, ax
mov bx, 0024h
cli
mov word [es:bx], cs
lea ax, [neue_isr]
mov word [es:bx+2], ax
sti

ret



; ---------------
neue_isr:
; ---------------
pushf
pusha
push es

; Eigene ISR
in al, 60h ; Eingegebenes Zeichen (Scancode)
mov bx, 0B800h
mov es, bx ; ES auf den Bildschirmspeicher
mov bx, 0000h ; Erstes Zeichen, oben links vermutlich
mov byte [es:bx], al

pop es
popa

; Alte ISR
call far dword [alter_iv]

iret



; ---------------
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