Problem mit Programm für AVR

A

Arne Rossius

Guest
Hallo,

ich habe versucht, ein kleines RS232-Interface mit 5 Eingängen und 8
Ausgängen mit einem AT90S2313 zu bauen. Leider scheine ich bei der
Programmierung etwas falsch gemacht zu haben, denn es funktioniert nicht
richtig. Den Code habe ich hier hochgeladen:
http://arne.rossius.bei.t-online.de/rs232int.txt
Funktionieren soll es so:
kommt in einer kurzen Zeitspanne das gleiche Byte 2 mal, soll der
aktuelle Zustand der Eingänge gesendet werden. Wird dagegen nur 1 Byte
oder 2 verschiedene Bytes gesendet, soll das letzte gesendete Byte an
den Ausgängen ausgegeben werden.
Folgende Reaktion war aber zu beobachten:
Die Ausgänge sind alle immer 0. Sendet man 2x das gleiche Byte
(Zeitabstand egal!), so kommt ein "Dach" (schätzungsweise ASCII 30)
zurück, die Eingänge sind durch die Pullups alle auf 1. Sendet man das
Zeichen noch 2x, passiert nichts mehr, erst, nachdem man ein anderes
Zeichen gesendet hat (oder ein anderes Zeichen 2x sendet, dann kommt
wieder das Dach).
Was habe ich falsch gemacht?

Gruß,
Arne
 
Arne Rossius schrieb:

kommt in einer kurzen Zeitspanne das gleiche Byte 2 mal, soll der
aktuelle Zustand der Eingänge gesendet werden. Wird dagegen nur 1 Byte
oder 2 verschiedene Bytes gesendet, soll das letzte gesendete Byte an
den Ausgängen ausgegeben werden.
Hast du die Zeitspanne ausreichend dimensioniert.
Die Uebertragung eines Bits dauert bei 9600baud ueber 1000 CLK (10MHz).
Da kann man sich bei Warteschleifen verschaetzen.

Folgende Reaktion war aber zu beobachten:
Die Ausgänge sind alle immer 0. Sendet man 2x das gleiche Byte
(Zeitabstand egal!), so kommt ein "Dach" (schätzungsweise ASCII 30)
zurück, die Eingänge sind durch die Pullups alle auf 1. Sendet man das
Zeichen noch 2x, passiert nichts mehr, erst, nachdem man ein anderes
Zeichen gesendet hat (oder ein anderes Zeichen 2x sendet, dann kommt
wieder das Dach).
Meinst du ^ mit Dach? Hat dieses nicht einen hoeheren ASCII Wert?

servus thomas
 
Hast du die Zeitspanne ausreichend dimensioniert.
Die Uebertragung eines Bits dauert bei 9600baud ueber 1000 CLK
(10MHz). Da kann man sich bei Warteschleifen verschaetzen.
Das wäre ja erst mal egal, wenigstens das Setzen der Ausgänge müsste
dann ja funktionieren. Tut es blos leider nicht.

Meinst du ^ mit Dach? Hat dieses nicht einen hoeheren ASCII Wert?
Nein. "Dach" ist vielleicht etwas schlecht ausgedrückt, es ist mehr so
eine Art Pfeil ohne Stiel, eben ASCII 30 (dezimal), eigentlich ein
Steuerzeichen.

Gruß,
Arne
 
Arne Rossius wrote:

Funktionieren soll es so:
kommt in einer kurzen Zeitspanne das gleiche Byte 2 mal, soll der
aktuelle Zustand der Eingänge gesendet werden. Wird dagegen nur 1
Byte oder 2 verschiedene Bytes gesendet, soll das letzte gesendete
Byte an den Ausgängen ausgegeben werden.
Folgende Reaktion war aber zu beobachten:
Die Ausgänge sind alle immer 0. Sendet man 2x das gleiche Byte
(Zeitabstand egal!), so kommt ein "Dach" (schätzungsweise ASCII 30)
zurück, die Eingänge sind durch die Pullups alle auf 1. Sendet man
das Zeichen noch 2x, passiert nichts mehr, erst, nachdem man ein
anderes Zeichen gesendet hat (oder ein anderes Zeichen 2x sendet,
dann kommt wieder das Dach).
Was habe ich falsch gemacht?
Hallo,

zunächst mal ist das Protokoll ziemlich schwierig. Wieso machst Du es
so kompliziert?

Alternativen:
- Schreib' jedes empfangene Byte raus und sende dann die Eingänge
zurück
- Benutze einen Teil des Bytes als Befehlscode und einen Teil als
Daten. Dann brauchst Du zwar zwei Befehle, um die Ausgänge zu setzen,
aber besser als warten ist es bestimmt

Dann hast Du noch den Code schön unübersichtlich gestaltet. Darin
einen Fehler zu suchen, lohnt IMHO den Aufwand nicht. Falls Du
wirklich an Deinem Protokoll festhalten willst/mußt, solltest Du da
ein bißchen umstrukturieren (oder gleich neuschreiben ;-(

BTW: Der Fehler könnte daher kommen, daß Du nach dem Lesen der
Eingänge, "wait" nicht zurückgesetzt wird.

/Jan-Hinnerk
 
zunächst mal ist das Protokoll ziemlich schwierig. Wieso machst Du es
so kompliziert?
Es war die beste Lösung, die mir einfiel, und für den anderen Part auch
sicherlich die einfachste.

Alternativen:
- Schreib' jedes empfangene Byte raus und sende dann die Eingänge
zurück
Problem: wenn ich nicht weiß, wie die Ausgänge gesetzt sind, diese aber
so bleiben sollen, kann ich die Eingänge nicht lesen.

- Benutze einen Teil des Bytes als Befehlscode und einen Teil als
Daten. Dann brauchst Du zwar zwei Befehle, um die Ausgänge zu setzen,
aber besser als warten ist es bestimmt
Das wäre eine Idee, braucht aber eben, wie du schon gesagt hast, ein
weiteres Byte (was dann auch noch zu 7/8 nicht genutzt würde). Man
könnte zwar einen der Ausgänge zu einem Eingang umbauen (oder Aus- und
Eingänge komplett vertauschen), was aber eigentlich nicht vorgesehen
war.

Dann hast Du noch den Code schön unübersichtlich gestaltet. Darin
Ich hab's mir fast gedacht, dass der nicht "gut" ist. Aber ich wüsste
nicht, wie ich den übersichtlicher gestalten könnte. Irgendeine Idee,
wo/wie ich das übersichtliche Gestalten von ASM lernen könnte?

BTW: Der Fehler könnte daher kommen, daß Du nach dem Lesen der
Eingänge, "wait" nicht zurückgesetzt wird.
wait? wait ist nur ein Label, daran kann es also nicht liegen. Meintest
du möglicherweise "count"? Das werd' ich gleich mal probieren
zurückzusetzen.

Gruß,
Arne
 
BTW: Der Fehler könnte daher kommen, daß Du nach dem Lesen der
Eingänge, "wait" nicht zurückgesetzt wird.
Danke für den Tipp, es funktioniert jetzt! Es waren wohl noch ein paar
mehr Register, die da nicht zurückgesetzt wurden, genau weiß ich es
nicht, aber ich habe an einigen Stellen mal ein paar Register
zurückgesetzt, deren Inhalte nicht mehr gebraucht werden. Und die
Wartezeit ist auch nicht zu niedrig, ich habe in der Simulation Zeiten
zwischen 15 und 20ms herausbekommen - nötig wäre nur eine gewesen! Aber
so viel kann auch nicht schaden, falls das ansteuernde Programm die
Zeichen nicht so schnell nacheinander senden kann.
Übrigens: das "Dach" war doch keins, sondern der Pfeil nach unten ohne
"Stiel" (ASCII 31) und stellte somit den Zustand der Eingänge dar (5
Eingänge, alle auf 1).

Gruß,
Arne
 
Arne Rossius wrote:

zunächst mal ist das Protokoll ziemlich schwierig. Wieso machst Du
es so kompliziert?

Es war die beste Lösung, die mir einfiel, und für den anderen Part
auch sicherlich die einfachste.
Ich kenne den anderen Part natürlich nicht. Im allgemeinen ist es aber
nicht einfach langsam genug zu sein. Ich denke da nur an alte
PC-Spiele, die auf schnelleren Rechnern nicht mehr benutzbar waren
;-(

Alternativen:
- Schreib' jedes empfangene Byte raus und sende dann die Eingänge
zurück

Problem: wenn ich nicht weiß, wie die Ausgänge gesetzt sind, diese
aber so bleiben sollen, kann ich die Eingänge nicht lesen.
Ist klar. Falls das für Deine Anwendung ein Problem ist entfällt die
Lösung.

- Benutze einen Teil des Bytes als Befehlscode und einen Teil als
Daten. Dann brauchst Du zwar zwei Befehle, um die Ausgänge zu
setzen, aber besser als warten ist es bestimmt

Das wäre eine Idee, braucht aber eben, wie du schon gesagt hast, ein
weiteres Byte (was dann auch noch zu 7/8 nicht genutzt würde). Man
könnte zwar einen der Ausgänge zu einem Eingang umbauen (oder Aus-
und Eingänge komplett vertauschen), was aber eigentlich nicht
vorgesehen war.
Wo ist das Problem mit einem Byte mehr? Langsamer dürfte es eigentlich
kaum werden. Die zusätzliche Übertragungszeit liegt auf jeden Fall
unter Deiner Wartezeit. Falls Dein Sender einen FIFO hat dauert es da
auch nicht länger. Außerdem sparst Du Dir das Senden eines zweiten
Bytes für das Lesen, sparst hier also Zeit und Aufwand.

Das könnte dann so aussehen:
0000 0000 -> Eingänge lesen
1000 xxxx -> Ausgänge 0-3 auf xxxx setzen
1100 xxxx -> Ausgänge 4-7 auf xxxx setzen

Falls es für Dich wichtig ist, daß die Ausgänge gleichzeitg geändert
werden, kannst Du auch die Werte vom ersten Schreiben
zwischenspeichern.

Und Du hast noch jede Menge Möglichkeiten das Protokol zu erweitern.
z.B.
1000 xxxx -> out = xxxx
1001 xxxx -> out = out OR xxxx
1010 xxxx -> out = out AND (NOT xxxx)
1011 xxxx -> out = out XOR xxxx

BTW: Der Fehler könnte daher kommen, daß Du nach dem Lesen der
Eingänge, "wait" nicht zurückgesetzt wird.

wait? wait ist nur ein Label, daran kann es also nicht liegen.
Meintest du möglicherweise "count"? Das werd' ich gleich mal
probieren zurückzusetzen.
Ich meinte natürlich count ;-)

/Jan-Hinnerk
 
Arne Rossius wrote:

Dann hast Du noch den Code schön unübersichtlich gestaltet. Darin

Ich hab's mir fast gedacht, dass der nicht "gut" ist. Aber ich
wüsste nicht, wie ich den übersichtlicher gestalten könnte.
Irgendeine Idee, wo/wie ich das übersichtliche Gestalten von ASM
lernen könnte?
Ich habe keine Ahnung, wo man sowas lernen kann. Auf jeden Fall sollte
man nicht von C-Compilern generierten Code als Vorbild nehmen. IMHO
haben die AppNotes von Atmel leider oft ähnliche Qualität.

Da helfen wohl nur Erfahrung und gesunder Menschenverstand.

Mal ein paar kurze Tipps zu einem Teil Deines Codes:

loop:
inc count ;increment counter
brne ok ;if overflow:
Nie in Kommentaren die Befehle übersetzen. Wenn der Leser nicht weiß,
daß "inc" inkrementiert oder das "count" ein Counter ist, kann er den
Code sowieso nicht verstehen.

Entweder den Sinn erklären oder (falls es zu offensichtlich ist)
lieber ganz weglassen. Im Gegensatz zu Hochsprachen passiert oft
weniger pro Zeile, d.h. man braucht auch weniger Kommentare.

"ok" ist ein sehr schlecht gewählter Name. Er gibt keine nützliche
Information und führt sogar ein bißchen in die Irre: Es ist nicht
"nicht OK", wenn der Sprung nicht ausgeführt wird ;-)

ldi temp, 0 ; if something to output:
cpse outp, temp
out PORTB, last ; output it!
IMHO ist der "cpse"-Befehl meistens eher verwirrend. Er spart hier auf
jeden Fall keine Zeit.

Kommentare sind hier gut (IMHO ist Pseudo-Code eine tolle Sache)

Nun das ganze in etwas aufgeräumter Form:

main_loop:
tst outp ; if something to output:
breq dont_output
out PORTB,last ; output it!
clr outp ; blah
dont_output:
ldi last,0 ; blah

ldi count,200 ; wait approx. 200*256*3 cycles
clr temp ; that is ??? ms @ ??? MHz
wait: dec temp
brne wait
dec count
brne wait

rjmp main_loop

Ich benutze gerne Leerzeilen, um Funktionsblöcke voneinander zu
trennen.

Ich hoffe, daß bringt Dich ein bißchen weiter.

/Jan-Hinnerk
 
Ich habe keine Ahnung, wo man sowas lernen kann. Auf jeden Fall sollte
man nicht von C-Compilern generierten Code als Vorbild nehmen. IMHO
haben die AppNotes von Atmel leider oft ähnliche Qualität.
Keine Sorge, den Programmierstil hab ich mir komplett selbst ausgedacht
(auch wenn ihn das nicht besser macht :-().

[viele Tipps]

Danke! Ich werde mal probieren, ob ich nächstes mal einiges davon
umsetzen kann.

main_loop:
tst outp ; if something to output:
^^^
Danke, den kannte ich noch gar nicht. Das kommt wohl davon, wenn man die
Befehle nach irgend einem seltsamen Tutorial anstatt dem PDF von Atmel
lernt ;-)

clr outp ; blah
[...]
ldi last,0 ; blah
Jetzt widersprichst du dir selbst ;-)

ldi count,200 ; wait approx. 200*256*3 cycles
clr temp ; that is ??? ms @ ??? MHz
wait: dec temp
brne wait
Das verstehe ich nicht ganz. Wird nicht schon beim ersten mal nicht
gesprungen, weil das Flag bei 0 --> 255 gesetzt wird? Und warum nimmst
du so gerne DEC und nicht INC? Hat das irgendeinen Grund?

dec count
brne wait
rjmp main_loop
Aber wenn ich mir deinen Code so ansehe, merk ich gerade, dass ich meine
Warteschleife *ziemlich* vermurkst habe! So einen verqueren Code
schreibe selbst ich normalerweise nicht ;-)
Das liegt wohl daran, dass ich den Code schon vor einiger Zeit
geschrieben habe, dann aufgrund von Nichtfunktion zur Seite gelegt und
nach ein paar Monaten jetzt noch mal geändert habe. Sollte man nicht tun
;-)

Ich benutze gerne Leerzeilen, um Funktionsblöcke voneinander zu
trennen.
Gute Idee.

Ich hoffe, daß bringt Dich ein bißchen weiter.
Ja, danke, das hilft schon mal sehr weiter!

Gruß,
Arne
 
Arne Rossius wrote:

main_loop:
tst outp ; if something to output:
^^^
Danke, den kannte ich noch gar nicht. Das kommt wohl davon, wenn man
die Befehle nach irgend einem seltsamen Tutorial anstatt dem PDF von
Atmel lernt ;-)
Lade Dir von Atmel die Befehlsbeschreibung runter (nicht so leicht zu
finden):
http://www.atmel.com/dyn/resources/prod_documents/DOC0856.PDF

Den Rest per Mail (wird doch langsam etwas off-topic)

/Jan-Hinnerk
 
anstatt dem PDF von Atmel lernt ;-)
^^^^^^^^^^^^^^^^^
http://www.atmel.com/dyn/resources/prod_documents/DOC0856.PDF
Das PDF hatte ich gemeint, hatte ich nach deinem Hinweis auf TST nämlich
auch rausgesucht (und gefunden). Trotzdem danke.


Gruß,
Arne
 

Welcome to EDABoard.com

Sponsor

Back
Top