Moin moin zusammen,
ich arbeite momentan daran, ein 16x2 LCD an meinem ATmega8A zu benutzen.
Ich bin mit der Initialisierung des LCDs bereits fertig und habe auch
schon Text, welcher mit .db abgespeichert ist, anzeigen lassen.
Da ich nun aber den Text mit meinem myAVR Workpad per UART-Schnittstelle
ändern möchte und .db nur konstant ist, frage ich mich, wie ich einen
ankommenden String in mein SRAM speichere und diesen dann im Nachhinein
an das LCD schicken kann.
Aufgrund dessen, dass ich nicht jedes Zeichen einzeln verschicken
möchte, habe ich mir eine Funktion mittels der Zeigerregister gebaut:
1
;Einen konstanten Text aus dem Flash Speicher ausgeben. Der Text wird mit einer 0 beendet
2
string:
3
push temp1
4
5
string_1:
6
lpm temp1, Z+
7
cpi temp1, 0
8
breq string_2
9
10
rcall lcd_data
11
rjmp string_1
12
13
string_2:
14
pop temp1
15
ret
Den Text habe ich bisher mit einer 0 beendet.
Liebe Grüße
Christoph
Du musst die UART Schnittstelle initialisieren und mittels ISR die
empfangenen Bytes in einen Puffer ablegen. Außerhalb der ISR sendest du
den Pufferinhalt dann an das Display.
Damit du während der Ausgabe weitere Zeichen empfangen kannst (falls das
gewollt ist), solltest du entweder einen Ringpuffer benutzen, oder zwei
Puffer immer abwechselnd.
Danke erstmals für die super schnellen Antworten :D
(damit hätte ich nicht gerechnet)
Ich habe jetzt im SRAM ein paar bytes mit :
1
.dseg
2
3
text: .byte 20
4
5
.eseg
reserviert.
Dank Gustav habe ich auch bereits die Ladefunktion soweit ich es
beurteilen kann richtig umgesetzt:
1
receive:
2
warten:
3
SBIS UCSRA,RXC ; Prüfen ob was im Empfang ist
4
rjmp warten
5
6
lds r22,UDR
7
st Y+, r22
8
9
rcall funktionkommtnoch
10
11
reti
Jetzt stellt sich mir die Frage auf, wie ich die Zeichen nun lade. Ich
habe ja zuvor die Zeigeregister benutzt um das Ende des Strings mit der
0 zu finden. Kann ich diese Methode auch hier wieder anwenden? Und aus
mir unerklärlichen Gründen musste der Text bzw. die Adresse 2mal in die
Zeigeregister geladen werden. Ist das hier auch so?
Hi,
schau mal drüber.
Ist aber nicht sauber.
Aufgabe:
Gleichzeitig Uhrzeit und unten auf Zeile 2 Message via Rs232 , wenn sie
kommt, auf LCD.
Man kann nicht gleichzeitig auf dieselben Adressen Schreib- und
Lesezugriff machen. Das ist im Proggi nicht beachtet worden.
Trotzdem mal gucken. Vielleicht bringt Dich das auf eine Idee.
ciao
gustav
Karl B. schrieb:> schau mal drüber.
Vielen Dank für dein Codebeispiel...Da bin ich doch aber leider viel zu
neu in Assembler um das zu verstehen... Ich hab es mir länger angeguckt
und versucht über meine Funktion auszugeben.
Funktioniert nur wie erwartet nicht :D
Einige Gedanken:
Zu verschwinden und nach vier Tagen ohne Erklärung wiederzukommen, ist
unfair gegenüber denjenigen, die hier nach bestem Wissen versuchen zu
helfen.
Offenbar wurde weder die Verwendung von Indexregistern noch das Konzept
des Interrupts verstanden. Also würde ich erstmal ohne dieses arbeiten:
ein Zeichen vom UART einlesen und auf das LCD ausgeben. Erst dann den
UART-Interrupt benutzen, immer noch mit einem einzigen Zeichen. Dann
vielleicht eine zwischengespeicherte Zeichenkette, als Schlußkennung,
analog zu dieser '0', ein spezielles Zeichen, '!' oder ähnliches. Erst
wenn das läuft (und vor allem verstanden ist), kann man den Ringpuffer
in Angriff nehmen.
S. Landolt schrieb:> Offenbar wurde weder die Verwendung von Indexregistern noch das Konzept> des Interrupts verstanden.
Ich würde sagen: er hat garnix verstanden (und will, wie alle diese
unsäglichen C&P-"Programmierer" auch garnichts verstehen). Das ist ganz
klar sinnlos zusammenkopierter Code.
Und was er tatsächlich will, ist: dass wir ihm sein Programm schreiben.
S. Landolt schrieb:> Zu verschwinden und nach vier Tagen ohne Erklärung wiederzukommen
Bei allem Respekt, aber ich muss nun wirklich nicht darlegen, dass ich
momentan meinen Internetanbieter wechsele und somit kein Internet hatte.
Es ist wirklich nett, dass hier Hilfe angeboten wird. Jedoch ist es für
keinen User eine Pflicht sofort Rede und Antwort stehen zu müssen. Also
bitte verzeih mir, dass ich nicht direkt antworten konnte.
S. Landolt schrieb:> Offenbar wurde weder die Verwendung von Indexregistern noch das Konzept> des Interrupts verstanden
Teils teils. Ich bin mir bewusst wie ein Interrupt funktioniert und
frage mich, aus welchen Gründen du daran zweifelst. Bei den
Indexregistern kann ich dir zustimmen.
c-hater schrieb:> und will, wie alle diese> unsäglichen C&P-"Programmierer" auch gar nichts verstehen
Finde ich tatsächlich echt schade, dass man gleich als "Neuling" so
abgestempelt wird, nur weil man nicht gleich komplettes Verständnis
gegenüber einem Thema hat. Wie erwähnt, bin ich noch recht neu in
Assembler und kenne mich deshalb auch wenig aus. Es ist also nicht
notwendig jemanden anhand seines Wissens zu beurteilen. Schlussendlich
habe ich ja nun nicht geschrieben, dass ich Assembler nicht verstehen
will und keine Motivation dazu hätte.
c-hater schrieb:> Und was er tatsächlich will, ist: dass wir ihm sein Programm schreiben.
Einspruch...Das hab ich zu keinem Moment gefordert/erfragt. Mir war nur
anfangs unklar, mit welcher Methodik meine Idee umzusetzen ist.
Ich hoffe, dass meine Antwort nicht negativ angekommen ist, da dies
wirklich nicht intentioniert ist.
Liebe Grüße
Christoph
zu Punkt 1:
Ich halte es nach wie vor für schlechten Stil; Sie werden es vielleicht
besser verstehen, wenn Sie selbst einmal soweit sind, anderen helfen zu
können.
zu Punkt 2:
Die Ursache für den Receive-Interrupt ist RXC - in der ISR nochmals zu
"Prüfen ob was im Empfang ist", und das in einer Warteschleife, ist
sinnlos.
Aus dem Datenblatt: "The RXC Flag can be used to generate a Receive
Complete interrupt ..."
Und auch ich "hoffe, dass meine Antwort nicht negativ angekommen ist";
ich helfe gerne, packen wir's an.
Hi,
also Polling und Interrupt sind doch nicht gleichzeitig nötig.
Doppelt gemoppelt oder stören sich sogar gegenseitig.
Das jetzt noch einmal ausführlicher zu erläutern, wäre hier zu viel.
Nur ein paar Stichworte in den Bildchen.
ciao
gustav
an Karl B.:
Wozu cli und sei zu Beginn bzw. Ende der ISR?
Welchem Zweck dient die UDRE-Abfrage innerhalb der RXC-ISR?
0x0009 als URXCaddr bei einem ATmega8A?
Also, wenn die UART Routine dann geht, haben wir den String in einem
Buffer.
Von dort wird er per Timer, zB alle 10ms ein Zeichen geschrieben.
Ich wuerd kontinuierlich den gesammten Inhalt des festen Buffers per
Timer ins Display kopieren.
Ich mach das schon seit Jahren so.
Christoph schrieb:> ...frage ich mich, wie ich einen> ankommenden String in mein SRAM speichere und diesen dann im Nachhinein> an das LCD schicken kann.>> Aufgrund dessen, dass ich nicht jedes Zeichen einzeln verschicken> möchte, habe ich mir...
Bedenke, daß ein UART eine Schnittstelle ist für asynchronen Betrieb.
Dort hast du es mit einzelnen Zeichen zu tun, die du auch einzeln
auswerten solltest.
Nun, die eigentliche Ausgabe deiner Textzeichen auf das LCD scheint ja
gelöst zu sein. Was da bleibt, ist einfach nur eine simple
Empfangsroutine, die am UART guckt, ob da ein Zeichen eingetrudelt ist
oder nicht. Und wenn eines da ist, dann brauchst du es ja im Prinzip nur
zum LCD auszugeben.
Allerdings sollte diese Empfangsroutine ein paar Sonderzeichen verstehen
und separat behandeln.
Als da wären:
- Schreibzeiger auf eine bestimmte Position stellen, z.B. Anfang der 1.
Zeile oder de 2. Zeile oder irgendwo mittendrin
- Display komplett ablöschen
- Zeile von aktueller Position bis Zeilenende ablöschen
- eventuell Blinken oder Cursor-Strich einstellen
Für so eine einfache Anwendung brauchst du keinen Interrupt-Betrieb, da
reicht simples Pollen aus. Die LCD-Ausgabe ist da allemal schnell genug,
um kein Zeichen zu verpassen.
W.S.
Christoph schrieb:> ändern möchte und .db nur konstant ist, frage ich mich, wie ich einen> ankommenden String in mein SRAM speichere und diesen dann im Nachhinein> an das LCD schicken kann.
Hier:
1
Main:
2
rcallGetStr
3
rcalllcd_data
4
rjmpMain
5
6
GetStr:
7
ldizl,low(text)
8
ldizh,high(text)
9
pushzl
10
pushzh
11
pushtemp1
12
13
Wt4Char:
14
sbisUCSRA,RXC
15
rjmpWt4Char
16
intemp1,UDR
17
stZ+,temp1
18
cpitemp1,0x0D
19
brneWt4Char
20
21
clrtemp1
22
st-Z,temp1
23
poptemp1
24
popzh
25
popzl
26
ret
27
28
.dseg
29
text:.byte20
Irgendein Text über UART eingeben, mit CR(0x0D) abschliessen - das
kann man z.B. bei HTerm sehr einfach einstellen.
Hi,
Das Beispielproggi. war für einen ATMEGA8515 gedacht.
Diente als Denkanstoß.
Schau mal hier:
http://ww1.microchip.com/downloads/en/DeviceDoc/en590320.pdf
Die targetspezifischen Einstellungen könntest Du im Analogieverfahren
herausfinden.
Trotzdem möchte ich noch auf Deine Fragen kurz eingehen:
S. Landolt schrieb:> Wozu cli und sei zu Beginn bzw. Ende der ISR?
Weil noch ein anderer Interrupt zeitkritisch dazwischenfunken könnte.
Im konkreten Falle alle Hundertselsekunde ein Timer-Interrupt.
Schau mal auf das Video.
Ja,
quasi gleichzeitig Ausgabe der vom Decoder gelieferten Zeit
sowohl auf LCD als auch über Teraterm via RS232/V24.
Wobei bei der RS232-Ausgabe die Nullen und Einsen des Zeitzeichensignals
optisch dargestellt werden sollten, um besser herausfinden zu können,
wann der Decoder etwas an Bitfehlern produziert. Bzw. der Empfänger
falsche Bits liefert. So habe ich auch herausgefunden, dass ein
systematischer Fehler vorlag. Immer bestimmte Bits waren falsch. Immer
an derselben Stelle.
Konnte im Programm dann entsprechend abgeändert werden.
Die Begriffe cli und sei werden Dir noch öfter begegnen. Z.B. Bei
"atomic"
Zugriffen,-bei SRAM-Zugriffen, oder bei EEPROM-Zugriffen.
Immer da, wo ein Interrupt mit der nachfolgenden Routine eine Aktion,
die nicht unterbrochen werden darf, stören würde.
S. Landolt schrieb:> Welchem Zweck dient die UDRE-Abfrage innerhalb der RXC-ISR?
Gucken, ob der Puffer schon wieder für neue Aufgaben bereit ist.
Empfangsfertigmeldung. Wenn nicht, wird auch nicht irgendetwas, was
gerade im Puffer steht, auf die Ausgabe gegeben. Der Vorgang nimmt ja
auch ein bisschen Zeit in Anspruch. Dann wird bisweilen noch ein
Frame-Error-Flag abgefragt. Muss man nicht, kann man aber.
S. Landolt schrieb:> 0x0009 als URXCaddr bei einem ATmega8A?
Das soll der Interruptvektor voll ausformuliert sein. Das hat mehr
didaktische Gründe. Und deutet darauf hin, dass bei verschiedenen
Targets das auch durchaus 'mal anders sein kann. Und deswegen
"portierte" Programme nicht laufen.
Ist vielleicht zuviel an Redundanz am Anfang. Aber mir hat das geholfen,
die Sachen dann besser zu verstehen. Es empfiehlt sich, erst später
Dinge wegzulassen, die man für überflüssig hält.
ciao
gustav
Karl B. schrieb:> S. Landolt schrieb:>> Wozu cli und sei zu Beginn bzw. Ende der ISR?>> Weil noch ein anderer Interrupt zeitkritisch dazwischenfunken könnte.
Kann passieren, natürlich.
Nur wird (bei AVR) das gerade laufende Interrupt erst bis zum Ende
bzw. reti abgearbeitet.
Also, total überflüssig und das hat Landolt gemeint.
Karl B. schrieb:> Schau mal auf das Video.> Ja,> quasi gleichzeitig Ausgabe der vom Decoder gelieferten Zeit> sowohl auf LCD als auch über Teraterm via RS232/V24.
Ich kann nur auf ein schwarzes Bildschirm schauen.
Karl B. schrieb:> Die Begriffe cli und sei werden Dir noch öfter begegnen. Z.B. Bei> "atomic" Zugriffen,-bei SRAM-Zugriffen, oder bei EEPROM-Zugriffen.
Aber bestimmt NICHT in einer AVR-ISR.
Karl B. schrieb:> S. Landolt schrieb:>> 0x0009 als URXCaddr bei einem ATmega8A?>> Das soll der Interruptvektor voll ausformuliert sein. Das hat mehr> didaktische Gründe.
Didaktisch?
Bei URXC Interrupt auf OVF0addr springen?
Karl B. schrieb:> Ist vielleicht zuviel an Redundanz am Anfang. Aber mir hat das geholfen,> die Sachen dann besser zu verstehen.
Bist du ganz sicher, die Sachen auch richtig verstanden zu haben?
an Karl B.:
Marc V. hat zwar schon einiges klargestellt, aber ich denke, ich sollte
auch antworten:
> ... Interrupt zeitkritisch dazwischenfunken ...
Aus dem Datenblatt: "When an interrupt occurs, the Global Interrupt
Enable I-bit is cleared and all interrupts are disabled."
In der Empfangsroutine abzufragen, ob der Sendepuffer leer ist, ergibt
für mich erstmal keinen Sinn - haben Sie hierzu eine konkrete Anwendung?
Statt (dem falschen) ".org 0x0009" bevorzuge ich ".org URXCaddr".
PS:
Wir reden hier vom ATmega8A und seiner Großfamilie.
Die megaAVR® 0-series, zum Beispiel, hat ein aufwändigeres
Interruptsystem, dort gilt "... Global Interrupt Enable bit (I) in the
CPU Status register (CPU.SREG). This bit is not cleared when an
interrupt is acknowledged."
S. Landolt schrieb:> In der Empfangsroutine abzufragen, ob der Sendepuffer leer ist, ergibt> für mich erstmal keinen Sinn - haben Sie hierzu eine konkrete Anwendung?
Had dein Chip getrennte Interrupts für Rx voll und Tx leer? Oder
erzeugen die denselben Interrupt?
W.S.
Marc V. schrieb:> Bei URXC Interrupt auf OVF0addr springen?
Meine Güte,
hab mich beim Kopieren in der Zeile vertan.
Das kann jedem mal passieren.
Sollte doch nur ein "Schnipsel" sein.
Aber die passende richtige Kopie oben,
Ja, ist für den ATMEGA8515.
Marc V. schrieb:> Bist du ganz sicher, die Sachen auch richtig verstanden zu haben?
In der besagten ISR ist ein "cli" und "sei" drin.
Der Programmabschnitt wird auch von anderen so verwendet,
denke, schaden kann es nicht. Obwohl das vorerst irgendwie unlogisch
ist,
wenn eine Interruptstreviceroutine ausgeführt wird, läuft die erst
einmal durch. Egal was sonst noch passiert.
ciao
gustav
W.S. schrieb:> Had dein Chip getrennte Interrupts für Rx voll und Tx leer? Oder> erzeugen die denselben Interrupt?
Ich bin nicht sicher, ob ich die Frage richtig verstanden habe, ich
versuche mal als Antwort:
1
.equ URXCaddr = 0x000b ; USART, Rx Complete
2
.equ UDREaddr = 0x000c ; USART Data Register Empty
3
.equ UTXCaddr = 0x000d ; USART, Tx Complete
"The UDRE Flag indicates if the transmit buffer (UDR) is ready to
receive new data. If UDRE is one, the buffer is empty, and therefore
ready to be written."
Hi,
schau mal in die def.incs.
Da ist mir offensichtlich noch ein Lapsus drunter weggelaufen.
Der 8515 hat ADC und dann Ende.
der 8535 hat die Liste versetzt, so dass tatsächlich der Overflow dabei
rauskommt.
Oder noch besser AT90S8515
da sind wir auf dem richtigen Dampfer.
Wie gesagt,
das sind die Tücken.
Liste korrigieren tu ich jetzt aber nicht, da das Prob. ja jetzt
auch klar ist und entsprechend richtig geproggt werden kann.
ciao
gustav
PS an W.S.:
Falls die doppelte Bedeutung von UDR unklar sein sollte:
"The USART Transmit Data Buffer Register and USART Receive Data Buffer
Registers share the same I/O address referred to as USART Data Register
or UDR. The Transmit Data Buffer Register (TXB) will be the destination
for data written to the UDR Register location. Reading the UDR Register
location will return the contents of the Receive Data Buffer Register
(RXB)."
S. Landolt schrieb:> an Karl B.:>> Wozu cli und sei zu Beginn bzw. Ende der ISR?> Welchem Zweck dient die UDRE-Abfrage innerhalb der RXC-ISR?> 0x0009 als URXCaddr bei einem ATmega8A?
Sicher ist sicher. Der HW ist nicht zu trauen, denn die hat sicher nicht
das DB gelesen. Gut wenn man alles unter Kontrolle hat.
Oder so ;-)
Carl D. schrieb:> Sicher ist sicher. Der HW ist nicht zu trauen, denn die hat sicher nicht> das DB gelesen. Gut wenn man alles unter Kontrolle hat.
Genau.
Nur manchmal tut HW so, als ob.
Das sind dann die schlimmsten Fälle überhaupt, weil man den Fehler bei
sich selbst suchen muss und das ist so etwas von unwahrscheinlich...
S. Landolt schrieb:> Genau dieser hat doch keinen ADC.
Vergiss es,
wir sind mittlerweile da:
Christoph schrieb:> meinem ATmega8AKarl B. schrieb:> Hi,> ATmega8A>> welche def. inc ganz genau?
Erst diese Angabe, dann sehen wir weiter. Alles andere ist nicht
zielführend.
Marc V. schrieb:> Nur manchmal tut HW so, als ob.
In den Atmel Docus sind bei Revisions manchmal Dinge einfach unkritisch
per copy and paste von anderen übernommen worden.
Wenn man das dann nachprüft, merkt man, dass da was nicht stimmen kann.
Insbesondere gilt das für Programmbeispiele.
Beitrag "Fehler in ATMEL ATMega32U2 Doku 7799"
ciao
gustav
Also ich habe jetzt den Interrupt so konfiguriert:
1
receive:
2
cli
3
SBI PORTC,3
4
push temp1
5
clr r27 ;XH auf 0
6
ldi r26, 0x0060 ;Anfang des SRAMs beladen = XL
7
in temp1,UDR
8
st X+, temp1
9
out UDR,temp1
10
rcall senden ;Feedback, was gesendet wurde
11
pop temp1
12
sei
13
reti
An C3 ist eine LED die auslöst, wenn der Interrupt geladen wurde.
1
stop:
2
IN r20, PINC
3
ANDI r20,0b00100000
4
CPI r20,0b00000000
5
BREQ umschalten
6
LDS r22,0x0060
7
CPI r22, 0b00000000
8
BREQ stop
9
rjmp string
Hier habe ich mein Schleifenprogramm, welches einen Taster überprüft,
und bei Betätigung das LCD an/aus schaltet. Danach habe ich nach meinem
Verständnis versucht zu gucken, ob ein Text im Text im SRAM steht.
Sollte dort einer stehen, müsste an der Speicherzelle ja etwas ungleich
0 sein.
1
string:
2
push temp1
3
ldi r22,0
4
5
string_1:
6
ldi r26, 0x0060
7
inc r22
8
ld temp1, X+
9
cpi r22, 80
10
breq string_2
11
12
rcall lcd_data
13
rjmp string_1
14
15
string_2:
16
pop temp1
17
ret
Und hier habe ich versucht, die Speicherzeile 0x0060 und folgende Zellen
mit Post-Increment auszulesen und einzeln an das LCD zu senden. Leider
sehe ich an dieser Stelle die Buchstaben alle doppelt und dreifach.
Mir ist bewusst, dass es bestimmt eine schönere Lösung für versiertere
Programmierer gibt, jedoch spiegelt dies ungefähr mein Verständnis, wie
der SRAM funktioniert. Vielleicht könntet ihr mir hier unter die Arme
greifen, da mir langsam die Ideen ausgehen, wie dies zu lösen ist.
Hi,
>string_1:> ldi r26, 0x0060> inc r22> rjmp string_1
Auf welchen Wert denkst du wird R27(XH) geladen?
>Leider sehe ich an dieser Stelle die Buchstaben alle doppelt und dreifach.
Ev. solltest du X mal in Ruhe lassen.
Uwe schrieb:> Auf welchen Wert denkst du wird R27(XH) geladen?
Ich habe es ja im Interrupt auf 0 gesetzt...
Uwe schrieb:> Ev. solltest du X mal in Ruhe lassen.
Hab die Funktion mit X mal weggelassen. Aber dann weiß mein LD ja gar
nicht, welcher Speicherzelle er laden soll.
Niklas W. schrieb:> Hab die Funktion mit X mal weggelassen. Aber dann weiß mein LD ja gar> nicht, welcher Speicherzelle er laden soll.
Mir scheint, du weisst ja gar nicht was du willst...
Ich habe dir ein funktionierndes Beispiel geschickt weil du einen
String anstatt aus Flash aus RAM anzeigen wolltest.
Du weisst ja nicht einmal wie man mit Registerpaaren richtig
umgeht, aber jetzt willst du auf einmal Taster, Interrupts usw. in
"deinem" Programm haben...
Gerade als Anfänger sollte man alles so einfach wie nur möglich halten.
Taster und Interrupt funktionieren ja super.
Marc V. schrieb:> Du weisst ja nicht einmal wie man mit Registerpaaren richtig> umgeht
Ich brauche doch bei 0x0060 nur das untere Register. Da kann doch das
obere bei 0 bleiben, oder nicht?
Marc V. schrieb:> Gerade als Anfänger sollte man alles so einfach wie nur möglich halten.
Im Grunde ist es eine Schulaufgabe und ich hab bereits bestimmte
Kriterien erfüllt. Die Initialisierung klappt und meine Tasterlogik
auch. Die Indexregister zeigen doch auf die Speicherzellen, wo jeweils
ein Zeichen im SRAM gespeichert ist. Verstehe ich das falsch?
Niklas W. schrieb:> auch. Die Indexregister zeigen doch auf die Speicherzellen, wo jeweils> ein Zeichen im SRAM gespeichert ist. Verstehe ich das falsch?
Ja.
0x60 ist eine feste Adresse, welche sich zwar im RAM befindet, aber
welcher Lehrer hat dir gesagt, dass das so richtig ist?
Ich habe dir doch gezeigt wie man das richtig macht.
1
ldixl,low(text)
2
ldixh,high(text)
Damit kann sich der text an einer beliebigen Adresse im RAM befinden.
Und wer sagt dir, dass der obere Register 0 enthält?
Christoph schrieb:> Sollte dort einer stehen, müsste an der Speicherzelle ja etwas ungleich> 0 sein.
Aber wenn ich es doch irgendwo im SRAM abspeichere, also bei text: ,
dann weiß ich doch gar nicht ab wann der Index anfangen soll... Es tut
mir wirklich leid, wenn ich mich gerade doof stelle...
Edit:
Ok jetzt denke ich es verstanden zu haben...
Niklas W. schrieb:> Hab es so umgesetzt...Klappt aber irgendwie nicht
du brauchst ein load indirect und increment
https://www.microchip.com/webdoc/avrassembler/avrassembler.wb_LD.html
dein reservierter Speicher für die Daten ist ja wenn ich das alles
richtig verfolgt hatte:
Christoph schrieb:> Ich habe jetzt im SRAM ein paar bytes mit :> .dseg>> text: .byte 20>> reserviert.
text ist die Adresse vom Speicher genauerweise vom ersten Byte also Byte
0 bis zu text +19 wegen 20 Byte (falls sich nichts in dne letzten Jahren
geändert hat (aber ich komme vom 6502 der nur einen Adressraum kannte)
also musst du irgendwo den post increment machen
https://www.microchip.com/webdoc/avrassembler/avrassembler.wb_LD.html
The X pointer register can either be left unchanged by the operation, or
it can be post-incremented or pre-decremented.
Ich bin da nicht sattelfest beim AVR aber einiges gilt noch immer, siehe
ASM Befehle AVR.
Niklas W. schrieb:> Aber wenn ich es doch irgendwo im SRAM abspeichere, also bei text: ,> dann weiß ich doch gar nicht ab wann der Index anfangen soll...
? der Index fängt da an wo der ASM den text hinlegt, wie lang er ist
hast du festgelegt und wo dein Indexzeiger aktuall steht musst du wissen
wenn du ein increment machst, also schön mitzählen!
Joachim B. schrieb:> der Index fängt da an wo der ASM den text hinlegt, wie lang er ist> hast du festgelegt und wo dein Indexzeiger aktuall steht musst du wissen> wenn du ein increment machst, also schön mitzählen!
Da ich ihn ja hier:
1
string:
2
push temp1
3
push zl
4
push zh
5
ldi r22,0
6
ldi zl, low(text)
7
ldi zh, high(text)
8
9
string_1:
10
11
inc r22
12
ld temp1, Z+
13
cpi r22, 0
14
breq string_2
15
16
rcall lcd_data
17
rjmp string_1
18
19
string_2:
20
pop temp1
21
pop zl
22
pop zh
23
ret
neu reinlade und danach immer inkrementiere müsste das doch passen
Niklas W. schrieb:> Ok jetzt denke ich es verstanden zu haben...
Nein.
Die Reihenfolge deiner push und pop Befehle stimmt nicht, es wird
LIFO benutzt, heisst letzter rein geht als erster raus.
push temp1
push zl
push zh
...
...
pop zh
pop zl
pop temp1
Marc V. schrieb:> Die Reihenfolge deiner push und pop Befehle stimmt nicht, es wird> LIFO benutzt, heisst letzter rein geht als erster raus.
top! +1 von mir
ich wusste irgendwas sah merkwürdig aus, aber ich bin zulange raus!
Marc V. schrieb:> Nein.> Die Reihenfolge deiner push und pop Befehle stimmt nicht, es wird> LIFO benutzt, heisst letzter rein geht als erster raus.
Oh stimmt ganz vergessen... Jetzt wirds langsam immer peinlicher ._.
1
receive:
2
cli
3
SBI PORTC,3
4
5
6
push temp1
7
push zl
8
push zh
9
ldi zl, low(text)
10
ldi zh, high(text)
11
12
13
warten:
14
sbis UCSRA, RXC
15
rjmp warten
16
in temp1, UDR
17
st Z+, temp1
18
out UDR,temp1
19
rcall senden
20
cpi temp1, 0x0D ;<------ funktioniert nicht
21
brne warten
22
23
clr temp1
24
st -Z, temp1
25
pop zh
26
pop zl
27
pop temp1
28
rcall string
29
sei
30
reti
Mein Vergleich in der Interruptfunktion will aber auch iwie nicht...Ich
prüfe doch auf Carriage Return...Wenn ich den Vergleich auskommentiere,
dann wird das ganze LCD einfach dunkel
mach mal kleinere Schritte
Niklas W. schrieb:> Wenn ich den Vergleich auskommentiere,> dann wird das ganze LCD einfach dunkel
welches LCD?
du schickst über USART raus
bist du sicher das deine 20 Byte nicht überlaufen?
was ist mit NL, manche wollen CR & NL manche nur NL
Dein "String" sollte trotzdem NULL terminiert sein und der Pointer nie
größer text + 19 sein!
vor allem WAS schickst du raus? evtl. ESC Sequenzen? oder nicht
druckbare Zeichen?
Joachim B. schrieb:> welches LCD?
Ein 16x2 LCD...
Joachim B. schrieb:> du schickst über USART raus
Ja ich schicke einfach nochmal raus, was ich gesendet habe, um ein
Feedback zu bekommen, dass es überhaupt geklappt hat.
Joachim B. schrieb:> bist du sicher das deine 20 Byte nicht überlaufen?
Momentan schicke ich das Wort Moin...Das sollte passen.
Joachim B. schrieb:> Dein "String" sollte trotzdem NULL terminiert sein und der Pointer nie> größer text + 19 sein!
Wenn ich mehr Bytes reserviere macht es keinen Unterschied....Er bleibt
bei dem Vergleich und geht gar nicht in die Stringfunktion
Niklas W. schrieb:> Momentan schicke ich das Wort Moin...Das sollte passen.
und wieso dann
Niklas W. schrieb:> ...Ich> prüfe doch auf Carriage Return...
wenn du es nicht mitschickst?
Niklas W. schrieb:> rcall senden> cpi temp1, 0x0D ;<------ funktioniert nicht> brne warten
deine Texte bleiben unverständlich!
Wie sieht deine Routine "senden" aus?
Irgendwie macht das keinen Spass.
Wenn ich ein Puzzle haben will, dann kaufe ich mir eins.
Sende doch alles, dann kann man mehr sagen, so ist es nur raten...
Marc V. schrieb:> Irgendwie macht das keinen Spass.> Wenn ich ein Puzzle haben will, dann kaufe ich mir eins.
+1
wenn immer nur Bruchstüke kommen verliert man den Überblick und die Lust
zu helfen!
immer vor und zurückscrollen hält mein Mausrad nicht aus und es gibt
eine Codeansicht die hier besser geeignet wäre!
Joachim B. schrieb:> wenn immer nur Bruchstüke kommen verliert man den Überblick und die Lust> zu helfen!>> immer vor und zurückscrollen hällt mein Mausrad nicht aus und es gibt> eine Codeansicht die hier besser geeignet wäre!
Tut mir wirklich leid und ich bin sehr dankbar für die Hilfe, die mir
hier zuteil wird. Ich konnte es jetzt an das LCD übertragen und es
funktioniert problemlos. Leider schreibt es noch nicht in die zweite
Zeile aber das werde ich denke ich iwie mit dem Control Command für die
RAM-Adresse hinbekommen...Denke ich
Wirklich vielen Dank für die Hilfe
Wie man an diesem Thread hier sieht,
hat es überhaupt keinen Sinn, irgendetwas an Hilfestellung zu leisten,
ohne dass man sich vom Kenntnisstand des Fragenden überzeugt hat.
Was ich verlange, bevor ich hier weitermache:
Ein sauber aufgeführes Programmbeispiel.
Seite 33
http://avr-asm-download.de/beginner_de.pdf
ciao
gustav
S. Landolt schrieb:> PS an W.S.:>> Falls die doppelte Bedeutung von UDR unklar sein sollte:
Also, die Ur-Frage war ja, ob man sich in der ISR auch um den
umgekehrten Datenstrom kümmern müßte. Mit den Atmels befasse ich mich
NICHT, deswegen die vorsichtige Frage. Bei vielen Cortexen (wie z.B.
den hier beliebten STM32) gibt es nur einen Interrupt-Handler für beide
Richtungen. Deshalb muß der sich auch um Rx und Tx kümmern.
Joggel E. schrieb:> Also, wenn die UART Routine dann geht, haben wir den String in einem> Buffer.> Von dort wird er per Timer, zB alle 10ms ein Zeichen geschrieben.>> Ich wuerd kontinuierlich den gesammten Inhalt des festen Buffers per> Timer ins Display kopieren.
So eine Strategie halte ich für ausgesprochen schlecht durchdacht.
Immerhin kommen an einem UART irgendwelche Zeichen in lockerer Folge der
Reihe nach an - und es ist genug Zeit zwischen 2 Zeichen, um jedes
einzeln verwerten zu können, OHNE diese zuvor in irgend einem String
zwischenspeichern zu müssen.
Wozu also einen Timer benützen? Wozu all die Zeichen zwischenspeichern?
Es reicht doch wohl aus, wenn selbige auf dem Display zu lesen sind.
Eigentlich geht das doch sehr einfach:
1. Man definiere sich ein paar Sonderzeichen für Positionierungen etc:
0xE0..0xFF = Schreibposition auf Zeichen 0..15 in Zeile 1 setzen
0xC0..0xDF = Schreibposition auf Zeichen 0..15 in Zeile 2 setzen
0x03 = Clear bis Zeilenende
.. und bei Bedarf noch weitere Steuerzeichen.
2. Und beim Empfang eines Zeichens:
C = LiesZeichenAusUART
if C>=0xE0
then Setze Schreibzeiger auf Zeile 0, Stelle C-0xE0, exit;
if C>=0xC0
then Setze Schreibzeiger auf Zeile 1, Stelle C-0xE0, exit;
if C = 3 then
{ merke Schreibzeiger in Rettvariable;
while Schreibzeiger noch in Zeile do
{ Gib ' ' an LCD aus;
Inkrementiere Schreibzeiger;
}
Restauriere Schreibzeiger von Rettvariable;
exit
}
Gib C an LCD aus;
exit
ende;
So, das mal quasi symbolisch hingeschrieben. Das braucht lediglich je
ein Byte für C, Schreibzeiger und Rettvariable.
W.S.
W.S. schrieb:> So eine Strategie halte ich für ausgesprochen schlecht durchdacht.> Immerhin kommen an einem UART irgendwelche Zeichen in lockerer Folge der> Reihe nach an - und es ist genug Zeit zwischen 2 Zeichen, um jedes> einzeln verwerten zu können,
stimme zu
W.S. schrieb:> Wozu also einen Timer benützen?
weil es keinen Sinn hat zu oft Zeichen auf ein LCD zu schreiben, wer
soll das in 10ms lesen?
W.S. schrieb:> Wozu all die Zeichen zwischenspeichern?
weil man im RAM Zeichen, wenn neue Daten kommen, schneller überschreiben
kann als auf dem Display und wer im 10ms Timer IRQ (z.B.
Tastenentprellung) einen counter hochzählt, ich bis 25 und somit alle
250ms den LCD Text aktualisiert, hat weniger Flimmern auf dem LCD und
bremst den µC weniger aus und es bleibt mehr Zeit und Takte für andere
Routinen übrig.
Joachim B. schrieb:> weil es keinen Sinn hat zu oft Zeichen auf ein LCD zu schreiben, wer> soll das in 10ms lesen?
Und wieso sollte man das Zeichen überhaupt mehrfach auf's LCD schreiben?
Einmal genügt doch vollkommen, lesbarer wird's auch bei mehrfachem
Schreiben nicht.
Und worum handelt es sich denn TATSÄCHLICH??
Also
1. Mit einem ATmega Zeichen vom UART auf's LCD schreiben
2. Zeichen von anderen Teilen der Firmware im ATmega auf's LCD
schreiben?
3. Beides, also Zeichen von der Firmware und vom UART gemischt auf's LCD
schreiben?
Eigentlich hat der TO jetzt genug Tips gekriegt, um klar zu kommen.
Alles Weitere sollte jetzt seine Angelegenheit sein.
W.S.
Hi,
also ich habe für das LCD immer einen Puffer. Da kann man dann auch mal
prüfen wo denn die Zeichen hingehören. Ein 2x16 hat ja auch einen
Zeichenspeicher von 2x40, ok nicht alle aber viele. Man hat auch die
Möglichkeit mal Sonderzeichen zu nutzen. ($EF = ö)
Übrigens wenn man wissen will ob der Puffer voll ist(20 Zeichen) kann
man auch mit
.dseg
.org 0x60
text: .byte 20
st Z+, temp1
CPI Zl,(low(text)+20)
prüfen und dann die Zeile komplett ausgeben.
Übrigens:
> ldi r22,0> ldi zl, low(text)> ldi zh, high(text)>string_1:> inc r22> ld temp1, Z+> cpi r22, 0> breq string_2> rcall lcd_data> rjmp string_1
zählt bis 256!
Viel Erfolg, Uwe
W.S. schrieb:> Und wieso sollte man das Zeichen überhaupt mehrfach auf's LCD schreiben?
was genau verstehst du an
Joachim B. schrieb:> wenn neue Daten kommenJoachim B. schrieb:> den LCD Text aktualisiert
nicht?
ich erkläre dir es gerne gaaaanz laaaangsaaaam
z.B. eintrudelner neuer Text oder Telegramm kommt schneller als man
lesen kann, was tun genauso schnell ausgeben wie er kommt?
1. sinnlos
Den Text stehen lassen bis man hätte lesen können?
2. bringt nicht unbedingt wichtige News, die dann weg sind, ebenfalls
sinnlos!
3. beste Methode die mir gefällt, letzten neusten Text im RAM
zwischenspeichern und wieder ans LCD schicken wenn die Wartezeit z.B.
250ms (oder nach Belieben) um ist
Ich mache 250ms damit eine fortlaufende Uhr soft läuft und nicht holpert
denn nicht immer erfolgt die LCD Ausgabe zum Sekundenwechsel.
Will man z.B. eine Ubat überwachen reicht natürlich alle Sekunde
Will man die aktuellen Börsenwerte anzeigen braucht man zum Lesen wohl
mehr Zeit.
Wenn du allerdings eine Methode kennst im Standard LCD nur die
relevanten mittelsten Zeichen zu tauschen von Ubat = 5,1V zu Ubat =
4,9V würde man noch weniger Zeichen neu schreiben, kann einen
Zeitvorteil bringen, aber im Interesse der Einfachheit würde ich die
ganze Zeile neu schreiben.
Joachim B. schrieb:> ich erkläre dir es gerne gaaaanz laaaangsaaaam
Das war überhaupt keine Erklärung. Ich zitiere dich nochmal:
> weil es keinen Sinn hat zu oft Zeichen auf ein LCD zu schreiben, wer> soll das in 10ms lesen?
Und von dem 10 ms Timer hat du und nicht ich geschrieben.
Also: Wenn Zeichen vom UART hereinkommen, dann sind die ja wohl dazu
gedacht, um laut Eröffnungpost auf dem LCD angezeigt zu werden. ODER?
Also kann man sie auch gleich beim Empfang anzeigen.
Und wenn jemand so unsäglich DOOF ist, das alte Testament in voller
Länge ohne Pausen auf einem 2 zeiligen LCD anzeigen zu wollen, dann ist
er selber dran schuld, daß er vor lauter Datenrauschen nix lesen kann.
Joachim B. schrieb:> Wenn du allerdings eine Methode kennst im Standard LCD nur die> relevanten mittelsten Zeichen zu tauschen
Da ist doch ne völlig übliche Sache: einfach den Schreibzeiger im LCD
auf die gewünschte Position stellen und dann die Zeichen schreiben. Ist
genau dasselbe wie das Stellen des Schreibzeigers auf Zeile 1 oder 2
(oder 3 oder 4 bei vierzeiligen Typen).
W.S.
W.S. schrieb:> einfach den Schreibzeiger im LCD> auf die gewünschte Position stellen und dann die Zeichen schreiben
du denkst zu einfach!
Ich habe LCD die eben nicht alte Pixel löschen wenn neue Pixel an
anderer Stelle geschrieben werden!
Bevor man nun pixelweise löscht und davor erst mal feststellt welche
Pixel noch stehen (die LCD können auch nicht ausgelesen werden und ein
pixelgenaues Schattenram halte ich für oversized) schreibe ich doch
lieber komplett neu mit löschen vorher (bei Bedarf)
W.S. schrieb:> Und wenn jemand so unsäglich DOOF ist, das alte Testament in voller> Länge ohne Pausen auf einem 2 zeiligen LCD anzeigen zu wollen, dann ist> er selber dran schuld
OK du hast Recht, deine Begründung ist für alle die Beste.
Ich muss dir ja nicht folgen und auch kein anderer mir :)
Jeder darf mit jedem Tempo alles aufs LCD zaubern ob lesbar oder nicht
ist ja egal.
Joachim B. schrieb:> Ich habe LCD die eben nicht alte Pixel löschen wenn neue Pixel an> anderer Stelle geschrieben werden!
Soso. Das ist ja auch normal, daß ein GRAFISCHES Display nicht "alte"
Pixel löscht, wenn an eine andere Stelle was geschrieben wird. Wäre ja
noch schöner!!!
Aber hier haben wir:
Christoph schrieb:> ich arbeite momentan daran, ein 16x2 LCD an meinem ATmega8A zu benutzen.
Und das ist ein alphanumerisches Display mit 2 Zeilen zu je 16
Textzeichen.
Dort gibt es keine "alten" Pixel, sondern es gibt dort drinnen einen RAM
für die Textzeichen und die kann man dadurch sehen, daß ein Pixelmuster
aus dem ebenfalls enthaltenen Zeichen-ROM an der zuständigen Stelle
dargestellt wird.
Wenn man so ein Textzeichen überschreibt, dann ändert sich damit auch
dessen Darstellung. Da braucht man garnix zu löschen.
Klaro?
W.S.
W.S. schrieb:> W.S.
du hast ja soooo Recht, es ist ermüdend und nun hast du deinen Willen.
Ob das dem TO mehr half als alles vorher?
Auch ohne dich kam der TO der Lösung näher, hier ungefähr:
Beitrag "Re: String über UART an LCD"
@Joachim B
Du weist doch, dass W.S. hier nur rumnervt und keine Ahnung vom
Programmieren hat und dazu Ansichten aus den 80ern.
Das Problem ist nur, dass er jetzt auch schon anfängt Forennutzer aktiv
zu vergraulen.
Joachim B. schrieb:> ich meine mich dunkel an gute Beiträge von ihm erinnern zu können, muss> aber sehr lange her sein, vielleicht irre ich mich auch.
Egal, was in der Vergangenheit war, im konkreten Fall hat er einfach mal
nur Recht. Es ging tatsächlich nur um ein doofes Textdisplay. Der Exkurs
Richtung (funktional beschränkter) Grafikdisplays war vollkommen
überflüssig und NULL hilfreich für den TO, weil der eben ein solches
nicht verwendet und auch nicht verwenden will.
Und all der Bratz bezüglich Updaterate usw. war auch vollkommen
überflüssig, denn es geht ja ganz offensichtlich um die Übertragung und
Anzeige einzelner, manuell erzeugter Strings.
Die kann man natürlich einfach live Zeichen für Zeichen auf das Display
schreiben, wenn die Baudrate hinreichend gering ist. Wenn nicht, muss
man den String halt buffern und schreibt ihn nach Eintreffen des
Ende-Zeichens auf das Display.
Das Problem des TO ist einfach nur, dass er keinen Plan entwickeln kann,
auch nur eins der beiden Konzepte umzusetzen. Von den Kompetenzmängeln
bei der Beherrschung seines Werkzeugs ist das erstmal noch völlig
unabhängig, deswegen lasse ich seine Ausrede mit den mangelnden
Assemblerkenntnissen auch nicht gelten. Er würde das auch in C oder
irgendeiner anderen Sprache nicht umgesetzt bekommen, weil er einfach
nicht weiss, was er da eigentlich tut...
Joachim B. schrieb:> ich meine mich dunkel an gute Beiträge von ihm erinnern zu können, muss> aber sehr lange her sein, vielleicht irre ich mich auch.
Wenn dem so ist würde ich wirklich gerne mal wissen was dann
zwischenzeitlich schiefgelaufen ist :/
c-hater schrieb:> Es ging tatsächlich nur um ein doofes Textdisplay.
beim TO im ersten Beitrag ja, aber nicht lt. Titel und es soll ja auch
andere LCD geben.
Ich für meinen Teil habe mich recht schnell vom doofen Text LCD
verabschiedet.
c-hater schrieb:> Das Problem des TO ist einfach nur, dass er keinen Plan entwickeln kann,> auch nur eins der beiden Konzepte umzusetzen.
das sah zumindest eine Weile so aus, aber er kam ja voran, wie er
weitermacht liegt bei ihm.
Der kleine Ausflug zurück zu ASM hat mir aber trotzdem Spass gemacht,
manches ändert sich nie: load & increment obgleich die mnemonics nun
anders heissen.
aus ldi load & increment wurde ldi load immediate
aber das kann man ja neu lernen.
Hi,
die HD 44780 LCDs nehmen einem die meiste Arbeit schon ab.
Bei der Initialisierung des LCD kann man unter anderem auch einen
Automatismus bei der Darstellung der Zeichen festlegen. Man braucht
nicht extra pro Charakter einen Befehl zur Positionierung machen, wenn
ein fortlaufender Text dargestellt werden soll. Kann man schon, muss man
nicht.
Auch, wenn Zeichen vom UART kommen. Wie im Beispielfilmchen.
Die "normale" Einstellung ist, dass nach jedem ausgegebenen Zeichen der
Cursor um eine Position nach rechts rückt, das wird dann beim Init in
der Doku als Adressenincrement bezeichnet. Es gibt auch ein
Adressendecrement.
Und Shift. Gesamten Inhalt des Display-RAMS schieben. Und Cursor nicht
darstellen trotz Increment/Decrement. Und mehr -> Doku.
ciao
gustav
Karl B. schrieb:> Und Shift. Gesamten Inhalt des Display-RAMS schieben.
Du meinst vermutlich den Befehl, der das "Fenster" horizontal über dem
RAM verschiebt. Wann braucht man das?
Ich hätte lieber einen Befehl, der Text nach oben scrollt. Den gibt's
leider nicht.
Stefan ⛄ F. schrieb:> Wann braucht man das?
Hi,
selten.
Das wird besser anders gelöst.
Zum Beispiel ein Messgeräteprogramm.
Legende konstant und Messwerte dann "variabel", wenn Stellenzahl
überschritten 12,34 Dann 23,45 es sollen 4 bzw. mit Komma 5 Stellen
bleiben.
Manche Programme wechseln sogar innerhalb des laufenden Programms noch
einmal und initialisieren LCD entsprechend neu.
Da kann man viel "rumbasteln."
Und die Befehlsbezeichnungen können ganz schön missverständlich sein.
Wenn man vom Effekt ausgeht.
Zwar no shift.
aber im Folgebefehl dann doch "shift".
Na ja. Dabla genau lesen und ausprobieren.
;....
;Entry Mode/Shift Set:
;
cbi daten, 0
ldi temp, 0x06 ;Cursor "increment/increase"
; ;Displayinhalt wird nicht ;geschoben "no shift"
rcall kommando
rcall verzoegerung3
cbi daten, 0
ldi temp, 0x1C ;Display rechts schieben
rcall kommando
;...
Siehe Video, was dabei rauskommt.
ciao
gustav