Hallo,
ich wollt ne variable deklarieren wie in c aber ich bekomm des einfach
nicht hin. muss ich des mit nem Register machen??
folgend soll es aussehen:
ldi temph, "Test Adresse"
Wie lad ich den text in die variable damit ich se dann an das lcd
dispaly übergeben kann??
Der Befehl zum übertragen sieht so aus:
daten:
.db $temph,0
Ich bin mir nicht sicher ob das $-Zeichen vorne dran sein muss oder ob
ein anderes zeichen hin muss???
also versteh ich des richtig im asm kannst du so viele variablen
definieren wie du register hast oder?? Das mit den Registern weis ich
schon aber ich wollt nur wissen ob man auch ohne register variablen
deklarieren kann aber ich glaub das geht nicht bzw. gibt keinen sinn!!
An das LCD Display kannst du nur zeichenweise Daten übergeben. D.h.
deine Variable ist grundsätzlich ein Zeiger auf eine Zeichenkette. Nach
und nach wird das Zeichen auf das dieser Zeiger zeigt an das LCD gegeben
und der Zeiger wird auf die nächste Adresse gesetzt - eine Schleife bis
zum Endezeichen (in C Nullbyte, in ASM im Prinzip egal, Nullbyte ist
praktisch).
Die Ausgabe ist im AVR Tutorial beim LCD erklärt...
http://www.mikrocontroller.net/articles/AVR-Tutorial:_LCD#Ausgabe_eines_konstanten_Textes
[ASM]
text:
.db "Test",0 ; Stringkonstante, durch eine 0
; abgeschlossen
[/ASM]
Legt den Text unter der (16 BiT) Adresse (Label) text ab. Die Null dient
später als Endezeichen.
[ASM]
ldi ZL, LOW(text*2) ; Adresse des Strings in den
ldi ZH, HIGH(text*2) ; Z-Pointer laden
[/ASM]
Lädt das 16-Bit Z-Register mit obiger Anfangsadresse des Textes. Die
beiden 8-Bit Hälften der 16-Bit Adresse werden auf die beiden 8 Bit
Registerhälften verteilt.
[ASM]
lcd_flash_string_1:
lpm temp1, Z+ ; Zeichen von Anfangsadresse (Z)
; nach temp1 schauffeln, Z um
; eins erhöhen
cpi temp1, 0 ; Endezeichen (0) erreicht?
breq lcd_flash_string_2 ; JA, dann Sprung zu
; lcd_flash_string_2
call lcd_data ; (NEIN), dann Zeichen ausgeben
rjmp lcd_flash_string_1 ; Sprung zum nächsten Zeichen
holen
lcd_flash_string_2:
[/ASM]
> also versteh ich des richtig im asm kannst du so viele variablen> definieren wie du register hast oder?? Das mit den Registern weis ich> schon aber ich wollt nur wissen ob man auch ohne register variablen> deklarieren kann aber ich glaub das geht nicht bzw. gibt keinen sinn!!
Du definierst dir mit z.B. .def temp1 = r16 nur einen neuen Namen temp1
für das Register r16. Das kann die Lesbarkeit deines Quelltextes
erhöhen.
Wenn dir die Register als Speicherplatz für Variablen ausgehen, kannst
du die Variablen auch im Speicher (RAM) anlegen. Im obigen Fall kann man
text als eine (konstante ;-) Variable ansehen.
Speicherzugriffe sind langsamer als Registerzugriffe, deshalb arbeitet
man - vereinfacht - so lang es geht mit Registern.
> Speicherzugriffe sind langsamer als Registerzugriffe, deshalb arbeitet> man - vereinfacht - so lang es geht mit Registern.
...Und nur in Registern kann man Daten auch bearbeiten (berechnen).
Deshalb müssen alle Daten, mit denen eine Operation durchgeführt werden
soll, in Register geladen werden. Im SRAM kann man Daten "nur"
speichern.
Ah ok ich versteh!
jetzt nur nochmal zu meinem Porblem wie kann ich dann folgendes
realisieren:
cpi menu_cnt, 0x01
ldi temph, "Menuepkt 1"
cpi menu_cnt, 0x02
ldi temph, "Menuepkt 2"
cpi menu_cnt, 0x03
ldi temph, "Menuepkt 3"
also ich geb über taster ein signal rein das mit hochzählt und dann soll
im lcd display der passende Text stehen. Mein gedanke war der dass ich
dann hier die daten in die variable speicher und an das unterprog
weitergeb. also wie oben schon geschrieben mit
daten:
.db $temph, 0
Also quasi wenn menu_cnt 0x01 ist dann soll im display menuepkt 1 stehn
und so weiter.
> ldi temph, "Menuepkt 1"
Das hat so keinen Zweck. Dir fehlen die absoluten Assembler-Grundlagen.
In ein 8-Bit-Register kannst Du keinen Textstring legen. Da passt nur
ein einziges ASCII-Zeichen rein. Wenn Du mit .db eine Tabelle im
Programmspeicher angelegt hast, dann kannst Du mit lpm auf die einzelnen
Zeichen zugreifen. Schau Dir bitte erst gründlich das Tutorial an. Und
wenn Du dazu Fragen hast, dann kannst Du die hier stellen. Assembler und
C sind zwei völlig verschiedene Paar Schuhe!
ja ich weis dass ich in nem reg nur 8 bit speichern kann deshalb frag
ich ja hier nach wie cih das lösen kann. ich weis auch dass nur asci
zeichen gespeichert werden können und da geht ja immer nur ein
buchstabe. soviel weis ich auch! Des programm oben soll nur den gedanken
verdeutlichen. lies mal den betreff dann weist du um was es geht!
Thx stefan! so hab ich mir das auch gedacht aber ich hab halt gemeint ob
vielleicht das geht so wie in c dass ich in ner variablen einen ganzen
text speichern kann! Sowas nenn ich mal ne vernünftige antwort!
In C speichert es in diesem Fall eigentlich auch nur eine Adresse in der
Variable. Es erscheint dir so, als ob der Text dann dort drin steht. Die
Fummelei mit Platz im Speicher für die eigentlichen Zeichen anlegen und
Laden der Speicheradresse ins Arbeitsregister bei Zugriff ist bei C
"versteckt". Du könntest (und solltest ruhig mal zu Lernzwecken) dir aus
dem C ein ASM machen lassen (ASM-Listing oder ASM-Ansicht im Debugger)
und wenn du dort nachsiehst, wirst du obiges Verfahren erkennen.
Wahrscheinlich und wegen der Registerretterei sogar besser. Ich habe das
gestrippte aus der ersten Antwort nur eingefügt, um zu zeigen, was
ausgabe_menue machen muss. Du kannst mit Unterfunktion so arbeiten:
ausgabe_menue:
rcall lcd_flash_string ; ausgeben
rjmp sonstwohinzumbeispielneuentastereinlesen ; fertig, nächste
Taste?
funktioniert das ganze dann mit einer einzelnen zahl?
also ich mein ich hab in ner variable eine zahl z.b. 5. ist ja weniger
als 8 bit! kann ich die so verarbeiten wie ich oben schon gedacht hab?
Meinst du, ob du eine Zahl direkt in ein Register laden kannst? Ja das
geht.
ldi temp1, $21
Lädt die hexadezimale Zahl $21 (=0x21 in C Schreibweise) in das
Register, für das du den Namen temp1 vergeben hast. Andere Schreibweise
mit 5 als Beispiel:
ldi temp1, 5
Im Wiki steht das unter
http://www.mikrocontroller.net/articles/Adressierung#Immediate-Werte
ja das weis ich aber ich meinte dann mit dem variablenaufruf.
also:
daten:
.db $temp, 0
wenn ich jetzt davor eine Zahl in temp geladen hab macht der des dann???
Das hängt von dem dir nicht angegebenen Teil ab.
Wenn das "Laden" ein Laden als Register bedeutet, d.h. temp als Name für
ein Register steht, kommt nichts sinnvolles raus
.def temp = r16
ldi temp, 5
daten:
.db $temp, 0 ; ???? wäre ja .db $r16, 0 nach der .def Regel
Wenn das Laden ein C-ähnliches Define (Textersetzung) bedeutet, also
temp ein symbolischer Name für die Zahl 5 bedeuten soll, könnte es
klappen.
Ich weiss nur nicht, ob der Hex-Prefix $ zum Bezeichner gehört, also ob
der Assembler "$temp" sieht oder $"temp", bzw. befürchte ich dass es
einen Syntaxfehler gibt weil Hex-Prefix + kein Zeichen aus 0-9A-F
vorkommt.
.equ temp = 5
daten:
.db $temp, 0 ; wäre dann .db $5
Bestimmt geht
.equ temp = $5
daten:
.db temp, 0 ; wäre dann .db $5
Nein geht so nicht. Du könntest mit direkter Adressierung arbeiten wie
es bei
http://www.mikrocontroller.net/articles/Adressierung#Direkte_Adressierung
beschrieben ist.
Wenn du den Wert 5 bereits im Register temp (bzw. r16) hast, schreibst
du den mit sts in den Speicher.
.def temp = r16 ; als beispiel
daten:
.db 23, 0
ldi temp, 5 ; daten sieht noch so aus: 23,0
sts daten, temp ; daten sieht danach so aus: 5, 0
Mit "" musst du aufpassen. "5" ist das ASCII-Zeichen 5. Das ist was
anderes als das Byte 5.
.def temp = r16 ; als beispiel
daten:
.db "5", 0
ldi temp, 5 ; daten sieht noch so aus: 53,0
sts daten, temp ; daten sieht danach so aus: 5, 0
ah ok des is schlecht dass des net geht!!!
ich hab folgendes problem ich lass einen zähler laufen und die gezählte
zahl soll im display erscheinen deshalb fragte ich wie des geht!!
Bittte, bitte, bitte:
Schau ins Tutorial.
> lass einen zähler laufen und die gezählte> zahl soll im display erscheinen deshalb fragte ich wie des geht!!
Auch das findest du im Tutorial: Wie man eine Zahl
so aufbereitet, dass man die einzelnen Stellen als
ASCII Code vorliegen hat und diese dann an ein LCD bzw.
an eine UART übergeben kann.
Es hat wirklich keinen Sinn, hier alles nochmal durchzukauen.
Wenn zum Tutorial Fragen bleiben dann stell sie. Das ist ok,
denn es hilft mir(uns) das Tutorial in diesen Punkten klarer
zu machen.
Danke.
Wichtig: Lies auch die Teile vom Tutorial, die dich momentan
nicht interessieren. Sehr oft sind Techniken und Verfahren
in Artikeln versteckt, in denen man sie nicht vermutet.
zb. Ist besagte Zahlenausgabe sowohl im LCD Artikel als auch
im Uhrenartikel drinnen (und wahrscheinlich auch noch an
anderen Stellen).
also hier jetzt mal mein code der vorläufig so ist! ich hab eine 10 bit
breite zahl und dann funktioniert das so nicht wie in dem tut!
adr_lcd:
push temp1
push temp2
push temp3
mov temp2, dmx_adrl
mov temp3, dmx_adrh
ldi hun, '0'
ldi ein, '0'
ldi zen, '0'
rjmp lcd_number1
lcd_number1:
subi temp2, 100
brcs lcd_number2
inc hun
rjmp lcd_number1
lcd_number2:
ldi temp1, 0x02
subi temp2, -100
cpi temp3, 0x01
add hun, temp1
rjmp lcd_number3
lcd_number3:
subi temp2, 10
brcs lcd_number4
inc zen
rjmp lcd_number3
lcd_number4:
ldi temp1, 0x05
subi temp2, -10
cpi temp3, 0x01
cpi zen, 0x09
rjmp overflow_zen
add zen, temp1
rjmp lcd_number5
overflow_zen:
ldi zen, 0x00
ldi temp1, 0x01
add hun, temp1
rjmp lcd_number5
lcd_number5:
ldi temp1, 0x06
add ein, temp2
cpi temp3, 0x01
cpi ein, 0x09
rjmp overflow_ein
add ein, temp1
pop temp1
pop temp2
pop temp3
rjmp adr
overflow_ein:
ldi ein, 0x00
ldi temp1, 0x01
add zen, temp1
pop temp1
pop temp2
pop temp3
rjmp adr
Big_Daddi wrote:
> ich hab das ganze doch nach dem tut gebaut aber etwas anders deshalb hab> ich hier gefragt ob es so auch geht!
Wo ist dann das Problem.
> lass einen zähler laufen und die gezählte> zahl soll im display erscheinen deshalb fragte ich wie des geht!!
Der Zähler ist in einem Register und im Tut gibt es
eine Funktion die den Inhalt eines Registers als Zahl
auf das LCD ausgeben kann.
> also hier jetzt mal mein code der vorläufig so ist! ich hab eine 10 bit> breite zahl und dann funktioniert das so nicht wie in dem tut!
Doch, das tut es.
Nur nicht direkt.
Aber du kannst dir vom Tutorial das Prinzip abschauen wie
man das macht.
Dein Code schaut doch gar nicht so schlecht aus.
Nur musst du berücksichtigen, dass
* es auch Tausender gibt (10 Bit: größte Zahl == 1023)
* du anstelle von nur 8Bit Arithmetik eine 16Bit Arithmetik
machen musst (weil die Zahl ja in 2 Registern verteilt ist)
Das Registerpärchen temp2/temp3 beschreibt bei dir eine 16 Bit
Zahl.
Um da 100 abzuziehen, musst du die Operation natürlich in
16 Bit machen:
subi temp2, 100 // low Byte
sbci temp3, 0 // High Byte
Der sbci temp3, 0 mag seltsam aussehen. Was soll es bringen
0 abzuziehen. Dazu muss man aber berücksichtigen, dass ja nicht
nur die 0 abgezogen wird, sondern auch das Carry Bit, welches
gesetzt wird wenn im unmittelbar vorhergehenden subi ein Unterlauf
entstanden ist.
-> Siehst du: Steht schon auf meiner TODO Liste:
Tutorial - LCD Funktionen - 16 Bit Zahlen ausgeben
Big_Daddi wrote:
> jetzt kommen wir der sache schon näher! ne die zahl geht max bis 511!> also reichen mir 9 bit!
OK. Dann kannst du die Tausender weglassen.
Ändert aber nichts an der Tatsache, dass du zum abzählen
der Hunderter mit 16 Bit Arithmetik arbeiten musst.
Karl heinz Buchegger wrote:
> Big_Daddi wrote:>> jetzt kommen wir der sache schon näher! ne die zahl geht max bis 511!>> also reichen mir 9 bit!>> OK. Dann kannst du die Tausender weglassen.> Ändert aber nichts an der Tatsache, dass du zum abzählen> der Hunderter mit 16 Bit Arithmetik arbeiten musst.
Nachdem du die Hunderter bearbeitet hast, kannst du
wieder bei 8 Bit Arithemtik bleiben. Denn wenn die
Hunderter erledigt sind, dann ist das 9. Bit schon
mit Sicherheit eine 0 und die übrig gebliebene Zahl
ist nur noch im Register temp2.
Nach Bearbeiten der Hunderter bleibt ja nach den
ganzen Subtraktionen nur noch eine Zahl <100 übrig.
Big_Daddi wrote:
> warum ne null??? wenn das 9. bit 1 ist dh 256 dann muss ich doch noch> bei den zehner 5 dazu zählen oder nicht??
Nein.
Wenn du wissen willst, weiviele Hunderter in 387 enthalten
sind, dann musst du schon von 387 jeweils 100 abziehen
und nicht nur von dem Teil der Zahl der kleiner als 256
ist.
ja des is klar ich hab mir nur des so gedacht also wenn bit 9 gesetzt
ist dann zähl 2 bei den hunderter 5 bei den zehner und 6 bei den einern
dazu! Also des war halt mein grundgedanke.
Big_Daddi wrote:
> ja des is klar ich hab mir nur des so gedacht also wenn bit 9 gesetzt> ist dann zähl 2 bei den hunderter 5 bei den zehner und 6 bei den einern> dazu! Also des war halt mein grundgedanke.
Machs nicht unnötig kompliziert durch die einführung
von Spezialfällen.
> 2 bei den hunderter 5 bei den zehner und 6 bei den einern
und dann musst du wieder berücksichtigen, dass die 6 bei den
Einern wieder einen Überlauf in die Zehner produzieren
können, die ihrerseits wieder einen Überlauf in die Hunderter
produzieren können.
Und dann gilt das Ganze nur bei 9 Bit Zahlen, wenn du aber mal
mehr hast (10 Bit), dann kannst du diese Spezialfunktion auf
den Müll werfen und dafür wieder eine Spezialfunktion schreiben.
Mit 16 Bit Arithmetik ist das doch viel einfacher. Es folgt
nach wie vor dem bewährten Muster:
10000-er zählen, indem 10000 abgezogen wird bis etwas
kleiner als 0 erreicht wird.
1000-er zählen, indem 1000 dazugezählt wird bis etwas
größer als 0 erreicht wird
100-er zählen, indem 100 abgezogen wird bis etwas
kleiner als 0 erreicht wird
10-er zählen, indem 10 dazugezählt wird bis etwas
größer als 0 erreicht wird
1er zählen, ... die braucht man nicht zählen, die bleiben
übrig.
Alles was dazu nötig ist, ist das man die Arithmetik von 8
Bit auf 16 Bit bringt. Dafür ist der Prozessor in Form des
Carry Bits vorbereitet.
(Nicht vergessen: Auch die Vergleiche auf kleiner/größer 0
müssen in 16 Bit ausgeführt werden. Das ist aber einfach, denn
das Vorzeichen ist immer im Highbyte das höchstwertige Bit,
es reicht also eine Abfrage mit dem HighByte aus temp3)
Den Sourcecode kann ich so nicht zuordnen bzw. mit deiner Zählerei in
Einklang bringen. Ist das vorhandener Code, den du für deine Zwecke
anpassen willst?
Es könnte sich um einen Teil einer Unterroutine handeln, die anscheinend
eine 16-Bit (10 Bit valid?) DMX Adresse (dmx_adrh, dmx_adrl) in eine
dreistellige dezimale Zahl (hun, zen, ein) aufsplittet.
Die Ausgabe an sich müsste straightforward sein. Die in Registern
gehalteten Ziffern hun, zen, ein sind bereits durch den Startwert '0' so
angelegt, dass sie direkt und nacheinander als Ziffer aufs LCD
ausgegeben werden können.
Wenn man hun, zen, ein unbedingt im RAM speichern möchte und eine
Zeichenkette ausgeben will, könnte man so vorgehen (obiges Beispiel
weiterverwendet):
daten:
daten_hun:
.db "H"
daten_zen:
.db "Z"
daten_ein:
.db "E"
.db 0
; neues adr
adr:
sts daten_hun, hun ; speicher <= register
sts daten_zen, zen
sts daten_ein, ein
Und dann kann man die komplette Ziffernfolge an der Adresse daten
ausgeben
ldi ZL, LOW(daten*2)
ldi ZH, HIGH(daten*2)
rcall lcd_flash_string
; ... alter Code ab adr ...
@stefan:
ne der code ist der code den ich geschrieben hab. g sorry bin noch
nicht so erfahren damit! Ich will mich dadurch eigentlich ein wenig in
die mcte einarbeiten da ich des ganze nur theoretisch lern!
@Karl Heinz:
ja das mit dem überlauf hab ich zwar schon abgefangen aber nicht
komplett. ich schau jetzt einfach mal dass ich des ganze nach deinen
vorschlägen versuch.
Big_Daddi wrote:
> Ist es dann so eher richtig?>> adr_lcd:> push temp1> push temp2> push temp3>> mov temp2, dmx_adrl> mov temp3, dmx_adrh> ldi temp1, '0'>> lcd_number1:> subi temp2, 100> sbci temp3, 0> brcs lcd_number2> inc temp1> rjmp lcd_number1>> lcd_number2:> rcall lcd_data
Ich kanns hier nicht ausprobieren (mangels Hardware und
Emulator), aber das sieht schon mal nicht schlecht aus.
Was passiert wenn du das laufen lässt?
(muss jetzt leider weg. Ich schau spätestens morgen wieder rein).